SDL Audio - Plays only Static Noise - c

Im having an issue with playing audio.
Im new to the SDL World of things so im learning from a tutorial.
http://dranger.com/ffmpeg/tutorial03.html
As far as audio goes, i have exactly what he put down and didnt get the result he says I should get. In the end of the lesson he specifies that the audio should play normally. However all i get is excessively loud static noise. This leads me to believe that the packets arent being read correctly. However I have no idea how to debug or look for the issue.
Here is my main loop for parsing the packets:
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if (frameFinished){
AVPicture pict;
pict.data[0] = yPlane;
pict.data[1] = uPlane;
pict.data[2] = vPlane;
pict.linesize[0] = pCodecCtx->width;
pict.linesize[1] = uvPitch;
pict.linesize[2] = uvPitch;
sws_scale(sws_ctx,
pFrame->data, pFrame->linesize,
0, pCodecCtx->height,
pict.data, pict.linesize);
//SDL_UnlockTexture(bmp);
SDL_UpdateYUVTexture(bmp, 0,
yPlane, pCodecCtx->width,
uPlane, uvPitch,
vPlane, uvPitch);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, bmp, NULL, NULL);
SDL_RenderPresent(renderer);
av_free_packet(&packet);
}
}
else if (packet.stream_index == audioStream) {
packet_queue_put(&audioq, &packet);
}
else
av_free_packet(&packet);
SDL_PollEvent(&event);
switch (event.type) {
case SDL_QUIT:
quit = 1;
SDL_DestroyTexture(bmp);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(screen);
SDL_Quit();
exit(0);
break;
default:
break;
}
}
this is my initialization of the audio device :
aCodecCtxOrig = pFormatCtx->streams[audioStream]->codec;
aCodec = avcodec_find_decoder(aCodecCtxOrig->codec_id);
if (!aCodec) {
fprintf(stderr, "Unsupported codec!\n");
return -1;
}
// Copy context
aCodecCtx = avcodec_alloc_context3(aCodec);
if (avcodec_copy_context(aCodecCtx, aCodecCtxOrig) != 0) {
fprintf(stderr, "Couldn't copy codec context");
return -1; // Error copying codec context
}
wanted_spec.freq = aCodecCtx->sample_rate;
wanted_spec.format = AUDIO_U16SYS;
wanted_spec.channels = aCodecCtx->channels;
wanted_spec.silence = 0;
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = aCodecCtx;
if (SDL_OpenAudio( &wanted_spec, &spec) < 0) {
fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
return -1;
}
avcodec_open2(aCodecCtx, aCodec, NULL);
// audio_st = pFormatCtx->streams[index]
packet_queue_init(&audioq);
SDL_PauseAudio(0);
The Call back (same as the tutorial):|
void audio_callback(void *userdata, Uint8 *stream, int len) {
AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;
int len1, audio_size;
static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
static unsigned int audio_buf_size = 0;
static unsigned int audio_buf_index = 0;
while (len > 0) {
if (audio_buf_index >= audio_buf_size) {
/* We have already sent all our data; get more */
audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));
if (audio_size < 0) {
/* If error, output silence */
audio_buf_size = 1024; // arbitrary?
memset(audio_buf, 0, audio_buf_size);
}
else {
audio_buf_size = audio_size;
}
audio_buf_index = 0;
}
len1 = audio_buf_size - audio_buf_index;
if (len1 > len)
len1 = len;
memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
len -= len1;
stream += len1;
audio_buf_index += len1;
}
}

I also had same problem when I learn tutorial 3. The comments by pprahul in https://github.com/mpenkov/ffmpeg-tutorial/issues/11 solve my problem when playing .MPG file with mp2 format audio. But the problem still occur when I play .MP4 file with AAC format audio.
A snapshot of that comment is to set the decode format manually, by
//after get the AVCodecContext *codecCtx(aCodecCtx in tutorial).
if (codecCtx->sample_fmt == AV_SAMPLE_FMT_S16P) {
codecCtx->request_sample_fmt = AV_SAMPLE_FMT_S16;
}
//...
It seems that the problem will not occur with early version of FFmpeg (1.01 and below).

Related

libav seeking to n seconds in a video and saving frames there onwards

I've been trying to seek in a mpegts video file. After exploring many codes, I've come up to the following stage of fetching frames and saving them.
However after using av_seek_frame also I'm getting following results:
Initial 7-8 frames are saved as grey frames.
Thereafter frames are fetched from beginning of the video only. i.e. 0 seconds onwards, while I'm trying to seek from say 12th seconds onwards.
Can you please explain how should I be calculating the timestamp and do the things correctly?
int main(int argc, const char *argv[])
{
int stream_id;
int64_t timestamp;
char ts_buf[60];
if (argc < 2) {
printf("You need to specify a media file.\n");
return -1;
}
logging("initializing all the containers, codecs and protocols.");
AVFormatContext *pFormatContext = avformat_alloc_context();
if (!pFormatContext) {
logging("ERROR could not allocate memory for Format Context");
return -1;
}
logging("opening the input file (%s) and loading format (container) header", argv[1]);
if (avformat_open_input(&pFormatContext, argv[1], NULL, NULL) != 0) {
logging("ERROR could not open the file");
return -1;
}
logging("format %s, duration %lld us, bit_rate %lld", pFormatContext->iformat->name, pFormatContext->duration, pFormatContext->bit_rate);
logging("finding stream info from format");
if (avformat_find_stream_info(pFormatContext, NULL) < 0) {
logging("ERROR could not get the stream info");
return -1;
}
AVCodec *pCodec = NULL;
AVCodecParameters *pCodecParameters = NULL;
int video_stream_index = -1;
// loop though all the streams and print its main information
for (int i = 0; i < pFormatContext->nb_streams; i++)
{
AVCodecParameters *pLocalCodecParameters = NULL;
pLocalCodecParameters = pFormatContext->streams[i]->codecpar;
logging("AVStream->time_base before open coded %d/%d", pFormatContext->streams[i]->time_base.num, pFormatContext->streams[i]->time_base.den);
logging("AVStream->r_frame_rate before open coded %d/%d", pFormatContext->streams[i]->r_frame_rate.num, pFormatContext->streams[i]->r_frame_rate.den);
logging("AVStream->start_time %" PRId64, pFormatContext->streams[i]->start_time);
logging("AVStream->duration %" PRId64, pFormatContext->streams[i]->duration);
logging("finding the proper decoder (CODEC)");
AVCodec *pLocalCodec = NULL;
pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id);
if (pLocalCodec==NULL) {
logging("ERROR unsupported codec!");
// In this example if the codec is not found we just skip it
continue;
}
// when the stream is a video we store its index, codec parameters and codec
if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) {
if (video_stream_index == -1) {
video_stream_index = i;
pCodec = pLocalCodec;
pCodecParameters = pLocalCodecParameters;
}
logging("Video Codec: resolution %d x %d", pLocalCodecParameters->width, pLocalCodecParameters->height);
} else if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_AUDIO) {
logging("Audio Codec: %d channels, sample rate %d", pLocalCodecParameters->channels, pLocalCodecParameters->sample_rate);
}
// print its name, id and bitrate
logging("\tCodec %s ID %d bit_rate %lld", pLocalCodec->name, pLocalCodec->id, pLocalCodecParameters->bit_rate);
}
if (video_stream_index == -1) {
logging("File %s does not contain a video stream!", argv[1]);
return -1;
}
AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
if (!pCodecContext)
{
logging("failed to allocated memory for AVCodecContext");
return -1;
}
if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0)
{
logging("failed to copy codec params to codec context");
return -1;
}
if (avcodec_open2(pCodecContext, pCodec, NULL) < 0)
{
logging("failed to open codec through avcodec_open2");
return -1;
}
AVFrame *pFrame = av_frame_alloc();
if (!pFrame)
{
logging("failed to allocate memory for AVFrame");
return -1;
}
AVPacket *pPacket = av_packet_alloc();
if (!pPacket)
{
logging("failed to allocate memory for AVPacket");
return -1;
}
/*seek for 20 seconds*/
int64_t incr, pos = 5;
int64_t seek_target = (pos * AV_TIME_BASE), stream_index = 0, how_many_packets_to_process = 50, response = 0;
printf("seek_target before : %lu\n", seek_target);
seek_target = av_rescale_q(seek_target, AV_TIME_BASE_Q, pFormatContext->streams[stream_index]->time_base);
printf("seek_target after: %lu\n", seek_target);
do
{
response = decode_packet(pFormatContext, pPacket, pCodecContext, pFrame, seek_target);
if (response < 0)
break;
avcodec_flush_buffers(pCodecContext);
/*av_frame_unref(pFrame);
av_packet_unref(pPacket);*/
// stop it, otherwise we'll be saving hundreds of frames
if (--how_many_packets_to_process <= 0)
break;
}while(1);
logging("releasing all the resources");
avformat_close_input(&pFormatContext);
av_packet_free(&pPacket);
av_frame_free(&pFrame);
avcodec_free_context(&pCodecContext);
return 0;
}
int decode_packet(AVFormatContext *pFormatContext, AVPacket *pPacket, AVCodecContext *pCodecContext, AVFrame *pFrame, int64_t seek_target)
{
if(av_seek_frame(pFormatContext, 0, /*(startTime + frameTimestamp) / 10*/seek_target, AVSEEK_FLAG_BACKWARD) < 0)
{
printf("error while seeking\n"/*, pFormatContext->filename*/);
return -1;
}
avcodec_flush_buffers(pCodecContext);
while(1)
{
if(av_read_frame(pFormatContext, pPacket) < 0)
{
logging("av_read_frame failure");
break;
}
/* I'm not able to get to correct timestamp to discard prior frames upto desired seconds. This if hasn't worked out well as of now. */
if((av_q2d(pFormatContext->streams[0]->time_base) * pPacket->pts) < (seek_target * 1000))
{
printf("skipping the frame\npFormatContext->streams[0]->time_base: %d %d\tpckt.pts: %lu\tseek: %lu", pFormatContext->streams[0]->time_base.num, pFormatContext->streams[0]->time_base.den, pPacket->pts, seek_target);
av_packet_unref(pPacket);
continue;
}
// Send Packet for decoding
int response = avcodec_send_packet(pCodecContext, pPacket);
if (response < 0) {
logging("Error while sending a packet to the decoder: %s", av_err2str(response));
return response;
}
while (response >= 0)
{
response = avcodec_receive_frame(pCodecContext, pFrame);
if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
break;
} else if (response < 0) {
logging("Error while receiving a frame from the decoder: %s", av_err2str(response));
return response;
}
if (response >= 0) {
logging(
"Frame %d (type=%c, size=%d bytes, format=%d) pts %d key_frame %d [DTS %d]: %d",
pCodecContext->frame_number,
av_get_picture_type_char(pFrame->pict_type),
pFrame->pkt_size,
pFrame->format,
pFrame->pts,
pFrame->key_frame,
pFrame->coded_picture_number,
pPacket->dts
);
char frame_filename[1024];
snprintf(frame_filename, sizeof(frame_filename), "%s-%d.pgm", "im/frame", pCodecContext->frame_number);
if (pFrame->format != AV_PIX_FMT_YUV420P)
{
logging("Warning: the generated file may not be a grayscale image, but could e.g. be just the R component if the video format is RGB");
}
// save a grayscale frame into a .pgm file
save_gray_frame(pFrame->data[0], pFrame->linesize[0], pFrame->width, pFrame->height, frame_filename);
av_frame_unref(m_pAVFrame);
}
}
}
return 0;
}

Decoding and resampling audio with FFmpeg for output with libao

I'm trying to write a program to read and play an audio file using FFmpeg and libao. I've been following the procedure outlined in the FFmpeg documentation for decoding audio using the new avcodec_send_packet and avcodec_receive_frame functions, but the examples I've been able to find are few and far between (the ones in the FFmpeg documentation either don't use libavformat or use the deprecated avcodec_decode_audio4). I've based a lot of my program off of the transcode_aac.c example (up to init_resampler) in the FFmpeg documentation, but that also uses the deprecated decoding function.
I believe I have the decoding part of the program working, but I need to resample the audio in order to convert it into an interleaved format to send to libao, for which I'm attempting to use libswresample. Whenever the program is run in its current state, it outputs (many times) "Error resampling: Output changed". The test file I've been using is just a YouTube rip that I had on hand. ffprobe reports the only stream as:
Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)
This is my first program with FFmpeg (and I'm still relatively new to C), so any advice on how to improve/fix other parts of the program would be welcome.
#include<stdio.h>
#include<libavcodec/avcodec.h>
#include<libavformat/avformat.h>
#include<libavutil/avutil.h>
#include<libswresample/swresample.h>
#include<ao/ao.h>
#define OUTPUT_CHANNELS 2
#define OUTPUT_RATE 44100
#define BUFFER_SIZE 192000
#define OUTPUT_BITS 16
#define OUTPUT_FMT AV_SAMPLE_FMT_S16
static char *errtext (int err) {
static char errbuff[256];
av_strerror(err,errbuff,sizeof(errbuff));
return errbuff;
}
static int open_audio_file (const char *filename, AVFormatContext **context, AVCodecContext **codec_context) {
AVCodecContext *avctx;
AVCodec *codec;
int ret;
int stream_id;
int i;
// Open input file
if ((ret = avformat_open_input(context,filename,NULL,NULL)) < 0) {
fprintf(stderr,"Error opening input file '%s': %s\n",filename,errtext(ret));
*context = NULL;
return ret;
}
// Get stream info
if ((ret = avformat_find_stream_info(*context,NULL)) < 0) {
fprintf(stderr,"Unable to find stream info: %s\n",errtext(ret));
avformat_close_input(context);
return ret;
}
// Find the best stream
if ((stream_id = av_find_best_stream(*context,AVMEDIA_TYPE_AUDIO,-1,-1,&codec,0)) < 0) {
fprintf(stderr,"Unable to find valid audio stream: %s\n",errtext(stream_id));
avformat_close_input(context);
return stream_id;
}
// Allocate a decoding context
if (!(avctx = avcodec_alloc_context3(codec))) {
fprintf(stderr,"Unable to allocate decoder context\n");
avformat_close_input(context);
return AVERROR(ENOMEM);
}
// Initialize stream parameters
if ((ret = avcodec_parameters_to_context(avctx,(*context)->streams[stream_id]->codecpar)) < 0) {
fprintf(stderr,"Unable to get stream parameters: %s\n",errtext(ret));
avformat_close_input(context);
avcodec_free_context(&avctx);
return ret;
}
// Open the decoder
if ((ret = avcodec_open2(avctx,codec,NULL)) < 0) {
fprintf(stderr,"Could not open codec: %s\n",errtext(ret));
avformat_close_input(context);
avcodec_free_context(&avctx);
return ret;
}
*codec_context = avctx;
return 0;
}
static void init_packet (AVPacket *packet) {
av_init_packet(packet);
packet->data = NULL;
packet->size = 0;
}
static int init_resampler (AVCodecContext *codec_context, SwrContext **resample_context) {
int ret;
// Set resampler options
*resample_context = swr_alloc_set_opts(NULL,
av_get_default_channel_layout(OUTPUT_CHANNELS),
OUTPUT_FMT,
codec_context->sample_rate,
av_get_default_channel_layout(codec_context->channels),
codec_context->sample_fmt,
codec_context->sample_rate,
0,NULL);
if (!(*resample_context)) {
fprintf(stderr,"Unable to allocate resampler context\n");
return AVERROR(ENOMEM);
}
// Open the resampler
if ((ret = swr_init(*resample_context)) < 0) {
fprintf(stderr,"Unable to open resampler context: %s\n",errtext(ret));
swr_free(resample_context);
return ret;
}
return 0;
}
static int init_frame (AVFrame **frame) {
if (!(*frame = av_frame_alloc())) {
fprintf(stderr,"Could not allocate frame\n");
return AVERROR(ENOMEM);
}
return 0;
}
int main (int argc, char *argv[]) {
AVFormatContext *context = 0;
AVCodecContext *codec_context;
SwrContext *resample_context = NULL;
AVPacket packet;
AVFrame *frame = 0;
AVFrame *resampled = 0;
int16_t *buffer;
int ret, packet_ret, finished;
ao_device *device;
ao_sample_format format;
int default_driver;
if (argc != 2) {
fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
return 1;
}
av_register_all();
printf("Opening file...\n");
if (open_audio_file(argv[1],&context,&codec_context) < 0)
return 1;
printf("Initializing resampler...\n");
if (init_resampler(codec_context,&resample_context) < 0) {
avformat_close_input(&context);
avcodec_free_context(&codec_context);
return 1;
}
// Setup libao
printf("Starting audio device...\n");
ao_initialize();
default_driver = ao_default_driver_id();
format.bits = OUTPUT_BITS;
format.channels = OUTPUT_CHANNELS;
format.rate = codec_context->sample_rate;
format.byte_format = AO_FMT_NATIVE;
format.matrix = 0;
if ((device = ao_open_live(default_driver,&format,NULL)) == NULL) {
fprintf(stderr,"Error opening audio device\n");
avformat_close_input(&context);
avcodec_free_context(&codec_context);
swr_free(&resample_context);
return 1;
}
// Mainloop
printf("Beginning mainloop...\n");
init_packet(&packet);
// Read packets until done
while (1) {
packet_ret = av_read_frame(context,&packet);
// Send a packet
if ((ret = avcodec_send_packet(codec_context,&packet)) < 0)
fprintf(stderr,"Error sending packet to decoder: %s\n",errtext(ret));
av_packet_unref(&packet);
while (1) {
if (!frame)
frame = av_frame_alloc();
ret = avcodec_receive_frame(codec_context,frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) // Need more input
break;
else if (ret < 0) {
fprintf(stderr,"Error receiving frame: %s\n",errtext(ret));
break;
}
// We have a valid frame, need to resample it
if (!resampled)
resampled = av_frame_alloc();
resampled->channel_layout = av_get_default_channel_layout(OUTPUT_CHANNELS);
resampled->sample_rate = codec_context->sample_rate;
resampled->format = OUTPUT_FMT;
if ((ret = swr_convert_frame(resample_context,resampled,frame)) < 0) {
fprintf(stderr,"Error resampling: %s\n",errtext(ret));
} else {
ao_play(device,(char*)resampled->extended_data[0],resampled->linesize[0]);
}
av_frame_unref(resampled);
av_frame_unref(frame);
}
if (packet_ret == AVERROR_EOF)
break;
}
printf("Closing file and freeing contexts...\n");
avformat_close_input(&context);
avcodec_free_context(&codec_context);
swr_free(&resample_context);
printf("Closing audio device...\n");
ao_close(device);
ao_shutdown();
return 0;
}
UPDATE: I've got it playing sound now, but it sounds like samples are missing (and MP3 files warn that "Could not update timestamps for skipped samples"). The issue was that the resampled frame needed to have certain attributes set before being passed to swr_convert_frame. I've also added av_packet_unref and av_frame_unref, but I'm still unsure as to where to best locate them.
ao_play(device,(char*)resampled->extended_data[0],resampled->linesize[0]);
You have problem in this line. Resampled audio frame has incorrect linesize parameters. swr_convert_frame aligns data and extended_data fields with silence. This silence is included into linesize parameter so you pass incorrect frame size into ao_play function.
ao_play(device, (char*)resampled->extended_data[0], av_sample_get_buffer_size(resampled->linesize, resampled->channels, resampled->nb_samples, resampled->format, 0));
Function av_sample_get_buffer_size() returns true sample size, without align. When I faced similar problem, this was the solution.

decode FLAC file with avcodec_decode_audio4 not work well

I use FFmpeg to decode my flac file and write it to pcm file, then use GoldenWave to play it with pcm signed 16bit, little endian, mono and the total play time is ok.
I doubt i write the 2 channel file in one place, but i don't know how to get every signal channel and write it to pcm file.
any help? thank you.
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
AVPacket orig_pkt = pkt;
do {
ret = decode_packet(&got_frame, 0);
if (ret < 0)
break;
pkt.data += ret;
pkt.size -= ret;
} while (pkt.size > 0);
av_free_packet(&orig_pkt);
}
pkt.data = NULL;
pkt.size = 0;
do {
decode_packet(&got_frame, 1);
LOG("flush cached frames");
} while (got_frame);
static int decode_packet(int *got_frame, int cached)
{
int ret = 0;
int decoded = pkt.size;
*got_frame = 0;
if (pkt.stream_index == audio_stream_idx) {
ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
LOG("Error decoding audio frame (%s)\n", av_err2str(ret));
return ret;
}
decoded = FFMIN(ret, pkt.size);
if (*got_frame) {
size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(audio_dec_ctx->sample_fmt);
//decode packet nb_samples:4608, xx:2, unpadded_linesize: 9216
LOG("decode packet nb_samples:%d, xx:%d, unpadded_linesize: %d",
frame->nb_samples, av_get_bytes_per_sample(audio_dec_ctx->sample_fmt), unpadded_linesize);
fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file);
//int nb_sample = frame->nb_samples;
//fwrite(frame->extended_data[0], 1, nb_sample, audio_dst_file);
//fwrite(frame->extended_data[0] + nb_sample, 1, nb_sample, audio_dst_file);
}
}
if (*got_frame && api_mode == API_MODE_NEW_API_REF_COUNT)
av_frame_unref(frame);
return decoded;
}
You didn't describe the problem you're having, but from what you're writing, I see two problems:
you're not checking the raw audio format of the frame, see frame->format (or audio_dec_ctx->sample_fmt). You're writing it as if it were AV_SAMPLE_FMT_S16, but you're not checking that it is
your unpadded_linesize is not multiplied by the number of channels (see e.g. frame->channels)

Forcing global headers in LibAV

I'm trying to write a C program using LibAV that takes input video from a webcam and saves it as an H264 MP4 file. I'm modifying a working program that saves .ppm frames from the webcam. I'm unable to convert the AVPackets so that they may be written, though--specifically, avformat_write_header() is failing, with the messages
[mp4 # 0050c000] Codec for stream 0 does not use global headers but container format requires global headers
[mp4 # 0050c000] Could not find tag for codec none in stream #0, codec not currently supported in container
The call is apparently returning error -22, but I can find no place where that error code is actually explained. How can I force avformat_write_header() to add in global headers when it's trying to write the MP4? Code below; some of it is adapted from this question, but I'm trying to adapt it from an input video file to a webcam.
int _tmain(int argc, _TCHAR* argv[])
{
AVInputFormat *inputFormat = NULL;
AVDictionary *inputDictionary= NULL;
AVFormatContext *inputFormatCtx = NULL;
AVFormatContext *outputFormatCtx = NULL;
AVCodecContext *inputCodecCtxOrig = NULL;
AVCodecContext *inputCodecCtx = NULL;
AVCodecContext *outputCodecCtx;
AVCodec *inputCodec = NULL;
AVCodec *outputCodec = NULL;
AVStream *stream = NULL;
AVIOContext *avioContext = NULL;
avcodec_register_all();
av_register_all();
avdevice_register_all();
av_dict_set(&inputDictionary, "Logitech HD Pro Webcam C920", "video", 0);
avformat_alloc_output_context2(&outputFormatCtx, NULL, NULL, "output.mp4");
avio_open(&avioContext, "output.mp4", AVIO_FLAG_WRITE);
outputFormatCtx->pb = avioContext;
stream = avformat_new_stream(outputFormatCtx, outputCodec);
inputFormat = av_find_input_format("dshow");
int r = avformat_open_input(&inputFormatCtx, "video=Logitech HD Pro Webcam C920", inputFormat, &inputDictionary);
if (r != 0) {
fprintf(stderr, "avformat_open_input() failed with error %d!\n", r);
return -1; }
r = avformat_find_stream_info(inputFormatCtx, NULL);
if (r != 0) {
fprintf(stderr, "avformat_find_stream_info() failed!\n");
return -1; }
av_dump_format(inputFormatCtx, 0, "video=Logitech HD Pro Webcam C920", 0);
unsigned int i;
int videoStream;
videoStream = -1;
for (i = 0; i < inputFormatCtx->nb_streams; i++) {
if (inputFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO {
videoStream = i;
break; }
}
if (videoStream == -1)
{ return -1; }
inputCodecCtxOrig = inputFormatCtx->streams[videoStream]->codec;
inputCodec = avcodec_find_decoder(inputCodecCtxOrig->codec_id);
if (inputCodec == NULL) {
fprintf(stderr, "avcodec_find_decoder() failed!\n");
return -1; }
else { printf("Supported codec!\n"); }
inputCodecCtx = avcodec_alloc_context3(inputCodec);
if (inputCodecCtx == NULL) {
fprintf(stderr, "avcodec_alloc_context3() failed!\n");
return -1; }
if (avcodec_copy_context(inputCodecCtx, inputCodecCtxOrig) != 0) {
fprintf(stderr, "avcodec_copy_context() failed!\n");
return -1; }
if (avcodec_open2(inputCodecCtx,inputCodec,&inputDictionary) < 0) {
fprintf(stderr, "avcodec_open2() failed!\n");
return -1; }
outputFormatCtx->oformat = av_guess_format(NULL, "output.mp4", NULL);
outputFormatCtx->oformat->flags |= AVFMT_GLOBALHEADER;
outputCodecCtx = avcodec_alloc_context3(outputCodec);
avcodec_copy_context(outputCodecCtx, inputCodecCtx);
outputCodec = inputCodec;
avcodec_open2(outputCodecCtx, outputCodec, NULL);
AVPacket packet;
printf("foo\n");
int errnum = avformat_write_header(outputFormatCtx, &inputDictionary);
printf("bar %d\n", errnum);
while(av_read_frame(inputFormatCtx, &packet)>=0) {
av_interleaved_write_frame(outputFormatCtx, &packet);
av_free_packet(&packet);
}
avcodec_close(inputCodecCtx);
avcodec_close(inputCodecCtxOrig);
avformat_close_input(&inputFormatCtx);
return 0;
}
How can I force avformat_write_header() to add in global headers
Global headers are written by the encoder, later the muxer reads them from the codec's extra_data field. So you should set this flag in the codec's context, before you call avcodec_open2().
outputCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
[mp4 # 0050c000] Could not find tag for codec none in stream #0, codec
not currently supported in container
You can try to setup the encoder explicitly (i.e. manually), or copy the codeccontext from original input codeccontext.
outputCodec = av_codec_find_encoder(AV_CODEC_ID_H264);
if(!outputCodec) return -1; //no encoder found
outputCodecCtx = avcodec_alloc_context3(outputCodec);
avcodec_copy_context(outputCodecCtx, inputCodecCtxOrig); //copy from orig
//You have to make sure each field is populated correctly
//with debugger or assertations
assert(outputCodecCtx->codec_id == AV_CODEC_ID_H264); //etc
outputCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if(avcodec_open2(outputCodecCtx, outputCodec, NULL) <0) return -1;

C - Transcoding to UDP using FFmpeg?

I'm trying to use the FFmpeg libraries to take an existing video file and stream it over a UDP connection. Specifically, I've been looking at the muxing.c and demuxing.c example files in the source code doc/example directory of FFmpeg. The demuxing file presents code which allows an input video to be converted into the video and audio streams. The muxing file presents code which creates fake data and can already be output to a UDP connection as I would like. I've begun work combining the two. Below can be found my code which is basically a copy of the muxing file with some parts replaced/appended with parts of the demuxing file. Unfortunately I'm running into plenty of complications attempting my goal through this approach. Is there an existing source code example which does the transcoding I'm looking for? Or at least a tutorial on how one might create this? If not, at least a few pointers might be helpful in directing my work in combing the two files to achieve my goal. Specifically, I'm getting the error:
[NULL # 0x23b4040] Unable to find a suitable output format for 'udp://localhost:7777'
Could not deduce output format from file extension: using MPEG.
Output #0, mpeg, to 'udp://localhost:7777':
Even though the muxing file could accept UDP formats. Any suggestions? Thank you much!
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libavutil/mathematics.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
/* 5 seconds stream duration */
#define STREAM_DURATION 200.0
#define STREAM_FRAME_RATE 25 /* 25 images/s */
#define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE))
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */
//FROM DE
static AVFormatContext *fmt_ctx = NULL;
static AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx;
static AVStream *video_stream = NULL, *audio_stream = NULL;
static const char *src_filename = NULL;
static const char *video_dst_filename = NULL;
static const char *audio_dst_filename = NULL;
static FILE *video_dst_file = NULL;
static FILE *audio_dst_file = NULL;
static uint8_t *video_dst_data[4] = {NULL};
static int video_dst_linesize[4];
static int video_dst_bufsize;
static uint8_t **audio_dst_data = NULL;
static int audio_dst_linesize;
static int audio_dst_bufsize;
static int video_stream_idx = -1, audio_stream_idx = -1;
static AVFrame *frame = NULL;
static AVPacket pkt;
static int video_frame_count = 0;
static int audio_frame_count = 0;
//END DE
static int sws_flags = SWS_BICUBIC;
/**************************************************************/
/* audio output */
static float t, tincr, tincr2;
static int16_t *samples;
static int audio_input_frame_size;
/* Add an output stream. */
static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec,
enum AVCodecID codec_id)
{
AVCodecContext *c;
AVStream *st;
/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (!(*codec)) {
fprintf(stderr, "Could not find encoder for '%s'\n",
avcodec_get_name(codec_id));
exit(1);
}
st = avformat_new_stream(oc, *codec);
if (!st) {
fprintf(stderr, "Could not allocate stream\n");
exit(1);
}
st->id = oc->nb_streams-1;
c = st->codec;
switch ((*codec)->type) {
case AVMEDIA_TYPE_AUDIO:
st->id = 1;
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->bit_rate = 64000;
c->sample_rate = 44100;
c->channels = 2;
break;
case AVMEDIA_TYPE_VIDEO:
c->codec_id = codec_id;
c->bit_rate = 400000;
/* Resolution must be a multiple of two. */
c->width = 352;
c->height = 288;
/* timebase: This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented. For fixed-fps content,
* timebase should be 1/framerate and timestamp increments should be
* identical to 1. */
c->time_base.den = STREAM_FRAME_RATE;
c->time_base.num = 1;
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c->pix_fmt = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
/* just for testing, we also add B frames */
c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
/* Needed to avoid using macroblocks in which some coeffs overflow.
* This does not happen with normal video, it just happens here as
* the motion of the chroma plane does not match the luma plane. */
c->mb_decision = 2;
}
break;
default:
break;
}
/* Some formats want stream headers to be separate. */
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
return st;
}
/**************************************************************/
/* audio output */
static float t, tincr, tincr2;
static int16_t *samples;
static int audio_input_frame_size;
static void open_audio(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
AVCodecContext *c;
int ret;
c = st->codec;
/* open it */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open audio codec: %s\n", av_err2str(ret));
exit(1);
}
/* init signal generator */
t = 0;
tincr = 2 * M_PI * 110.0 / c->sample_rate;
/* increment frequency by 110 Hz per second */
tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;
if (c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)
audio_input_frame_size = 10000;
else
audio_input_frame_size = c->frame_size;
samples = av_malloc(audio_input_frame_size *
av_get_bytes_per_sample(c->sample_fmt) *
c->channels);
if (!samples) {
fprintf(stderr, "Could not allocate audio samples buffer\n");
exit(1);
}
}
/* Prepare a 16 bit dummy audio frame of 'frame_size' samples and
* 'nb_channels' channels. */
static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels)
{
int j, i, v;
int16_t *q;
q = samples;
for (j = 0; j < frame_size; j++) {
v = (int)(sin(t) * 10000);
for (i = 0; i < nb_channels; i++)
*q++ = v;
t += tincr;
tincr += tincr2;
}
}
static void write_audio_frame(AVFormatContext *oc, AVStream *st)
{
AVCodecContext *c;
AVPacket pkt = { 0 }; // data and size must be 0;
AVFrame *frame = avcodec_alloc_frame();
int got_packet, ret;
av_init_packet(&pkt);
c = st->codec;
get_audio_frame(samples, audio_input_frame_size, c->channels);
frame->nb_samples = audio_input_frame_size;
avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
(uint8_t *)samples,
audio_input_frame_size *
av_get_bytes_per_sample(c->sample_fmt) *
c->channels, 1);
ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
if (ret < 0) {
fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
exit(1);
}
if (!got_packet)
return;
pkt.stream_index = st->index;
/* Write the compressed frame to the media file. */
ret = av_interleaved_write_frame(oc, &pkt);
if (ret != 0) {
fprintf(stderr, "Error while writing audio frame: %s\n",
av_err2str(ret));
exit(1);
}
avcodec_free_frame(&frame);
}
static void close_audio(AVFormatContext *oc, AVStream *st)
{
avcodec_close(st->codec);
av_free(samples);
}
/**************************************************************/
/* video output */
static AVFrame *frame;
static AVPicture src_picture, dst_picture;
static int frame_count;
static void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
int ret;
AVCodecContext *c = st->codec;
/* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
exit(1);
}
/* allocate and init a re-usable frame */
frame = avcodec_alloc_frame();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
/* Allocate the encoded raw picture. */
ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height);
if (ret < 0) {
fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret));
exit(1);
}
/* If the output format is not YUV420P, then a temporary YUV420P
* picture is needed too. It is then converted to the required
* output format. */
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
ret = avpicture_alloc(&src_picture, AV_PIX_FMT_YUV420P, c->width, c->height);
if (ret < 0) {
fprintf(stderr, "Could not allocate temporary picture: %s\n",
av_err2str(ret));
exit(1);
}
}
/* copy data and linesize picture pointers to frame */
*((AVPicture *)frame) = dst_picture;
}
/* Prepare a dummy image. */
static void fill_yuv_image(AVPicture *pict, int frame_index,
int width, int height)
{
int x, y, i;
i = frame_index;
/* Y */
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;
/* Cb and Cr */
for (y = 0; y < height / 2; y++) {
for (x = 0; x < width / 2; x++) {
pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5;
}
}
}
static void write_video_frame(AVFormatContext *oc, AVStream *st)
{
int ret;
static struct SwsContext *sws_ctx;
AVCodecContext *c = st->codec;
if (frame_count >= STREAM_NB_FRAMES) {
/* No more frames to compress. The codec has a latency of a few
* frames if using B-frames, so we get the last frames by
* passing the same picture again. */
} else {
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
/* as we only generate a YUV420P picture, we must convert it
* to the codec pixel format if needed */
if (!sws_ctx) {
sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P,
c->width, c->height, c->pix_fmt,
sws_flags, NULL, NULL, NULL);
if (!sws_ctx) {
fprintf(stderr,
"Could not initialize the conversion context\n");
exit(1);
}
}
fill_yuv_image(&src_picture, frame_count, c->width, c->height);
sws_scale(sws_ctx,
(const uint8_t * const *)src_picture.data, src_picture.linesize,
0, c->height, dst_picture.data, dst_picture.linesize);
} else {
fill_yuv_image(&dst_picture, frame_count, c->width, c->height);
}
}
if (oc->oformat->flags & AVFMT_RAWPICTURE) {
/* Raw video case - directly store the picture in the packet */
AVPacket pkt;
av_init_packet(&pkt);
pkt.flags |= AV_PKT_FLAG_KEY;
pkt.stream_index = st->index;
pkt.data = dst_picture.data[0];
pkt.size = sizeof(AVPicture);
ret = av_interleaved_write_frame(oc, &pkt);
} else {
AVPacket pkt = { 0 };
int got_packet;
av_init_packet(&pkt);
/* encode the image */
ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
if (ret < 0) {
fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
exit(1);
}
/* If size is zero, it means the image was buffered. */
if (!ret && got_packet && pkt.size) {
pkt.stream_index = st->index;
/* Write the compressed frame to the media file. */
ret = av_interleaved_write_frame(oc, &pkt);
} else {
ret = 0;
}
}
if (ret != 0) {
fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
exit(1);
}
frame_count++;
}
static void close_video(AVFormatContext *oc, AVStream *st)
{
avcodec_close(st->codec);
av_free(src_picture.data[0]);
av_free(dst_picture.data[0]);
av_free(frame);
}
static int open_codec_context(int *stream_idx,
AVFormatContext *fmt_ctx, enum AVMediaType type)
{
int ret;
AVStream *st;
AVCodecContext *dec_ctx = NULL;
AVCodec *dec = NULL;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
if (ret < 0) {
fprintf(stderr, "Could not find %s stream in input file '%s'\n",
av_get_media_type_string(type), src_filename);
return ret;
} else {
*stream_idx = ret;
st = fmt_ctx->streams[*stream_idx];
/* find decoder for the stream */
dec_ctx = st->codec;
dec = avcodec_find_decoder(dec_ctx->codec_id);
if (!dec) {
fprintf(stderr, "Failed to find %s codec\n",
av_get_media_type_string(type));
return ret;
}
if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) {
fprintf(stderr, "Failed to open %s codec\n",
av_get_media_type_string(type));
return ret;
}
}
return 0;
}
/**************************************************************/
/* media file output */
int main(int argc, char **argv)
{
const char *filename;
AVOutputFormat *fmt;
AVFormatContext *oc;
AVStream *audio_st, *video_st;
AVCodec *audio_codec, *video_codec;
double audio_pts, video_pts;
int ret = 0, got_frame;;
/* Initialize libavcodec, and register all codecs and formats. */
av_register_all();
if (argc != 3) {
printf("usage: %s input_file output_file\n"
"\n", argv[0]);
return 1;
}
src_filename = argv[1];
filename = argv[2];
/* allocate the output media context */
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
if (!oc) {
printf("Could not deduce output format from file extension: using MPEG.\n");
avformat_alloc_output_context2(&oc, NULL, "mpeg", filename);
}
if (!oc) {
return 1;
}
fmt = oc->oformat;
/* Add the audio and video streams using the default format codecs
* and initialize the codecs. */
video_stream = NULL;
audio_stream = NULL;
//FROM DE
/* open input file, and allocate format context */
if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
fprintf(stderr, "Could not open source file %s\n", src_filename);
exit(1);
}
/* retrieve stream information */
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
exit(1);
}
if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
video_stream = fmt_ctx->streams[video_stream_idx];
video_dec_ctx = video_stream->codec;
/* allocate image where the decoded image will be put */
ret = av_image_alloc(video_dst_data, video_dst_linesize,
video_dec_ctx->width, video_dec_ctx->height,
video_dec_ctx->pix_fmt, 1);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw video buffer\n");
goto end;
}
video_dst_bufsize = ret;
}
if (open_codec_context(&audio_stream_idx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) {
int nb_planes;
audio_stream = fmt_ctx->streams[audio_stream_idx];
audio_dec_ctx = audio_stream->codec;
nb_planes = av_sample_fmt_is_planar(audio_dec_ctx->sample_fmt) ?
audio_dec_ctx->channels : 1;
audio_dst_data = av_mallocz(sizeof(uint8_t *) * nb_planes);
if (!audio_dst_data) {
fprintf(stderr, "Could not allocate audio data buffers\n");
ret = AVERROR(ENOMEM);
goto end;
}
}
//END DE
/* Now that all the parameters are set, we can open the audio and
* video codecs and allocate the necessary encode buffers. */
if (video_stream)
open_video(oc, video_codec, video_stream);
if (audio_stream)
open_audio(oc, audio_codec, audio_stream);
av_dump_format(oc, 0, filename, 1);
/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open '%s': %s\n", filename,
av_err2str(ret));
return 1;
}
}
/* Write the stream header, if any. */
ret = avformat_write_header(oc, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file: %s\n",
av_err2str(ret));
return 1;
}
if (frame)
frame->pts = 0;
for (;;) {
/* Compute current audio and video time. */
if (audio_stream)
audio_pts = (double)audio_stream->pts.val * audio_stream->time_base.num / audio_stream->time_base.den;
else
audio_pts = 0.0;
if (video_stream)
video_pts = (double)video_stream->pts.val * video_stream->time_base.num /
video_stream->time_base.den;
else
video_pts = 0.0;
if ((!audio_stream || audio_pts >= STREAM_DURATION) &&
(!video_stream || video_pts >= STREAM_DURATION))
break;
/* write interleaved audio and video frames */
if (!video_stream || (video_stream && audio_st && audio_pts < video_pts)) {
write_audio_frame(oc, audio_stream);
} else {
write_video_frame(oc, video_stream);
frame->pts += av_rescale_q(1, video_stream->codec->time_base, video_stream->time_base);
}
}
/* Write the trailer, if any. The trailer must be written before you
* close the CodecContexts open when you wrote the header; otherwise
* av_write_trailer() may try to use memory that was freed on
* av_codec_close(). */
av_write_trailer(oc);
/* Close each codec. */
if (video_st)
close_video(oc, video_st);
if (audio_st)
close_audio(oc, audio_st);
if (!(fmt->flags & AVFMT_NOFILE))
/* Close the output file. */
avio_close(oc->pb);
/* free the stream */
avformat_free_context(oc);
end:
if (video_dec_ctx)
avcodec_close(video_dec_ctx);
if (audio_dec_ctx)
avcodec_close(audio_dec_ctx);
avformat_close_input(&fmt_ctx);
if (video_dst_file)
fclose(video_dst_file);
if (audio_dst_file)
fclose(audio_dst_file);
av_free(frame);
av_free(video_dst_data[0]);
av_free(audio_dst_data);
return 0;
}
The trouble you are facing is because udp is not a multimedia format. It is a network protocol. Ffmpeg converts to multimedia formats and can dump output to various locations such as disk (the most common case) or in this case on the network. So you must decide what format you want your content to be transmuxed to before you send it out over udp. From the command line this would be done using the -f format flag. Streamable formats are transport stream, webm in containers and elementary streams. You may want to use rtp for streaming over udp else on the reciever side you will face problems of missing packets and out of order arrival.
Given your current state, I suggest first you achieve what you want to do via the command line itself. This will give you an understanding of what are the various things you must do. Then you can go to the program.

Resources