Hi,
I'm playing around with the FFmpeg tutorials at
(http://dranger.com/ffmpeg/) and trying to update them to the most
recent version of FFmpeg. I'm having problems decoding audio. In
particular, the entire data array of the AVFrame I pass in is zero
after decoding.
Here's the code around the problem area:
int bytes_consumed;
int got_frame;
bytes_consumed =
avcodec_decode_audio4
(
aCodecCtx,
frame,
&got_frame,
&pkt
);
if (got_frame)
{
int bytes_decoded =
av_samples_get_buffer_size
(
NULL,
aCodecCtx->channels,
frame->nb_samples,
aCodecCtx->sample_fmt,
1
);
int sum = 0;
int i = 0;
for (i = i; i < bytes_decoded; ++i)
sum += abs(frame->data[0][i]);
fprintf(stderr, "sum: %d\n", sum);
return bytes_decoded;
}
The whole data array sums to zero every time, and I get silence as
output. I'm attaching my full source for reference.
Why does this happen? What am I doing wrong?
ffmpeg version N-41103-g67b7631
built on Jun 4 2012 17:00:05 with gcc 4.4.3
Thank you in advance for your help.
Cheers,
Michael
// tutorial03.c
// A pedagogical video player that will stream through every video frame as
// fast as it can and play audio (out of sync).
//
// Code based on FFplay, Copyright (c) 2003 Fabrice Bellard,
// and a tutorial by Martin Bohme ([email protected])
// Updated for ffmpeg version N-41103-g67b7631 by [email protected].
// Tested on Ubuntu 10.04 LTS, GCC 4.4.3.
// Use the Makefile to build all the samples.
//
// Run using
//
// Run using
// bin/tutorial03.out myvideofile.mpg
//
// to play the stream on your screen.
//
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif
#include <stdio.h>
#define SDL_AUDIO_BUFFER_SIZE 1024
#define INBUF_SIZE 4096
#define AUDIO_INBUF_SIZE 20480
struct PacketQueue
{
AVPacketList *first_pkt;
AVPacketList *last_pkt;
int nb_packets;
int size;
SDL_mutex *mutex;
SDL_cond *cond;
};
struct PacketQueue audioq;
int quit = 0;
/*
* Initialize the packet queue.
*/
void
packet_queue_init(struct PacketQueue *q)
{
memset(q, 0, sizeof(struct PacketQueue));
q->mutex = SDL_CreateMutex();
q->cond = SDL_CreateCond();
}
/*
* Puts a new packet onto the queue.
*
* @param[in] q The packet queue.
* @param[in] pkt The packet.
*
* @return 0 on success, non-zero on failure.
*/
int
packet_queue_put(struct PacketQueue *q, AVPacket *pkt) {
/*
* pkt1 A queue node.
*/
AVPacketList *pkt1;
/*
* This is a HACK to ensure the packet is allocated.
*/
if (av_dup_packet(pkt) < 0)
return -1;
pkt1 = av_malloc(sizeof(AVPacketList));
if (!pkt1)
return -1;
pkt1->pkt = *pkt;
pkt1->next = NULL;
SDL_LockMutex(q->mutex);
if (!q->last_pkt)
q->first_pkt = pkt1;
else
q->last_pkt->next = pkt1;
q->last_pkt = pkt1;
q->nb_packets++;
q->size += pkt1->pkt.size;
SDL_CondSignal(q->cond);
SDL_UnlockMutex(q->mutex);
return 0;
}
/*
* Grab a packet from the packet queue.
*
* @param[in] q The packet queue.
* @param[out] pkt The grabbed packet.
* @param[in] block If non-zero, the function will block until the queue
* is non-empty.
*
* @return 1 if the packet was successfully grabbed, 0 if the queue was
* empty (in non-blocking case) and -1 if the application was signalled
* to terminate.
*/
static int
packet_queue_get(struct PacketQueue *q, AVPacket *pkt, int block)
{
AVPacketList *pkt1;
int ret;
SDL_LockMutex(q->mutex);
for(;;)
{
if (quit)
{
ret = -1;
break;
}
pkt1 = q->first_pkt;
if (pkt1)
{
q->first_pkt = pkt1->next;
if (!q->first_pkt)
q->last_pkt = NULL;
q->nb_packets--;
q->size -= pkt1->pkt.size;
*pkt = pkt1->pkt;
av_free(pkt1);
ret = 1;
break;
}
else if (!block)
{
ret = 0;
break;
}
else
{
SDL_CondWait(q->cond, q->mutex);
}
}
SDL_UnlockMutex(q->mutex);
return ret;
}
/*
* Decode an audio frame.
*
* @param[in] aCodecCtx The codec context of the audio stream we're decoding.
* @param[out] frame The decoded audio frame.
*
* @returns the number of bytes decoded or -1 if the application was
* signalled to terminate.
*/
int
audio_decode_frame
(AVCodecContext *aCodecCtx, AVFrame *frame)
{
static init_flag = 1;
static AVPacket pkt;
/*
* If the function is being called for the first time, initialize the
* packet.
*/
if (init_flag)
{
av_init_packet(&pkt);
init_flag = 0;
}
/*
* pkt The current packet. Note that it's static, meaning it will persist
* between separate calls to this functions.
*
* The outer loop grabs new packets into pkt.
* The inner loop consumes the current packet in its entirety.
*/
for (;;)
{
while (pkt.size > 0)
{
int bytes_consumed;
int got_frame;
/*
* bytes_consumed
* The number of bytes consumed from the current packet
* during this iteration.
* got_frame
* avcodec_decode_audio4 sets this to non-zero if a
* frame could be decoded, zero otherwise.
*/
bytes_consumed =
avcodec_decode_audio4
(
aCodecCtx,
frame,
&got_frame,
&pkt
);
if (bytes_consumed < 0)
{
/*
* if error, skip frame
*/
break;
}
if (got_frame)
{
int bytes_decoded =
av_samples_get_buffer_size
(
NULL,
aCodecCtx->channels,
frame->nb_samples,
aCodecCtx->sample_fmt,
1
);
#if 0
int sum = 0;
int i = 0;
for (i = i; i < bytes_decoded; ++i)
sum += abs(frame->data[0][i]);
fprintf(stderr, "sum: %d\n", sum);
#endif
return bytes_decoded;
}
pkt.size -= bytes_consumed;
pkt.data += bytes_consumed;
}
/*
* Free the old packet.
*/
if (pkt.data)
av_free_packet(&pkt);
if (quit)
return -1;
/*
* This is where we actually get a new packet from the global
* packet queue.
*
* NB. The reason this happens all the way at the end of the loop is
* that the function may be called while a packet is half-consumed.
* In that case, we want to make sure we consume the entire packet
* before grabbing a new one.
*
* Keep in mind that pkt is static, so it persists between calls to
* this function.
*/
if (packet_queue_get(&audioq, &pkt, 1) < 0)
return -1;
}
}
/*
* Read packets from the queue and put them into a buffer that SDL will
* then play back. This function gets called by SDL periodically.
*
* @param[in] userdata Payload containing the AVCodecContext.
* @param[out] out_buf The buffer to write packets to.
* @param[in] out_buf_len The size of buffer.
*/
void
audio_callback(void *userdata, Uint8 *out_buf, int out_buf_len)
{
AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;
AVFrame frame;
static uint8_t tmp_buf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
static unsigned int tmp_buf_len = 0;
static unsigned int tmp_buf_index = 0;
/*
* aCodecContext The AVCodecContext corresponding to the audio
* stream that we are decoding.
* frame A frame to store decoded samples.
* tmp_buf A temporary buffer that we write the decoded samples
* into. Once this buffer fills up, we write its
* contents into the SDL output buffer.
* tmp_buf_len The number of audio bytes currently in the buffer.
* tmp_buf_index The current position in the buffer. Everything prior
* to this position has already been sent to SDL.
*
* The pipeline looks like this:
*
* stream --> frame --> tmp_buf --> out_buf
*/
/*
* Continue writing until the buffer output to SDL is full.
*/
while (out_buf_len > 0)
{
int incr;
/*
* incr The number of bytes to write to the output buffer during
* this iteration.
*
* If we've gone past the end of our temporary buffer, refill it by
* decoding another frame.
*/
if (tmp_buf_index >= tmp_buf_len)
{
int bytes_decoded;
/*
* nbytes The number of bytes decoded.
* We have already sent all our data. Get more data by decoding
* some more packets into our temporary buffer.
*/
avcodec_get_frame_defaults(&frame);
nbytes = audio_decode_frame(aCodecCtx, &frame);
/*
* If error (in particular, the app is terminating), output
* silence. Otherwise, write the decoded frame to the temporary
* buffer.
*/
if (bytes_decoded < 0)
{
tmp_buf_len = 1024;
memset(tmp_buf, 0, tmp_buf_len);
}
else
{
memcpy(tmp_buf, frame.data[0], bytes_decoded);
tmp_buf_len = nbytes;
}
tmp_buf_index = 0;
}
incr = tmp_buf_len - tmp_buf_index;
/*
* Make sure we don't overflow the output buffer.
*/
if (incr > out_buf_len)
incr = out_buf_len;
memcpy(out_buf, (uint8_t *)tmp_buf + tmp_buf_index, incr);
out_buf_len -= incr;
out_buf += incr;
tmp_buf_index += incr;
}
}
int
main(int argc, char *argv[])
{
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVPacket packet;
int i;
int videoStream;
int audioStream;
int frameFinished;
float aspect_ratio;
AVCodecContext *aCodecCtx = NULL;
AVCodec *aCodec = NULL;
SDL_Overlay *bmp = NULL;
SDL_Surface *screen = NULL;
SDL_Rect rect;
SDL_Event event;
SDL_AudioSpec wanted_spec;
SDL_AudioSpec spec;
struct SwsContext *sws_ctx = NULL;
AVDictionary *videoOptionsDict = NULL;
AVDictionary *audioOptionsDict = NULL;
if (argc < 2)
{
fprintf(stderr, "Usage: test <file>\n");
exit(1);
}
// Register all formats and codecs
av_register_all();
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
{
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
exit(1);
}
if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
{
fprintf(stderr, "Could not open file: %s\n", argv[1]);
return -1;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
fprintf(stderr, "Could not find stream information.\n");
return -1;
}
av_dump_format(pFormatCtx, 0, argv[1], 0);
// Find the first video stream
videoStream = -1;
audioStream = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
int codec_type = pFormatCtx->streams[i]->codec->codec_type;
if (codec_type == AVMEDIA_TYPE_VIDEO && videoStream < 0)
videoStream = i;
if (codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0)
audioStream = i;
}
if (videoStream == -1)
{
fprintf(stderr, "Unable to find a video stream!\n");
return -1;
}
if (audioStream == -1)
{
fprintf(stderr, "Unable to find an audio stream!\n");
return -1;
}
aCodecCtx=pFormatCtx->streams[audioStream]->codec;
//
// Set audio settings from codec info
//
wanted_spec.freq = aCodecCtx->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
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;
}
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if (!aCodec)
{
fprintf(stderr, "Unsupported audio codec!\n");
return -1;
}
if (avcodec_open2(aCodecCtx, aCodec, &audioOptionsDict) < 0)
{
fprintf(stderr, "Unable to open audio codec!\n");
return -1;
}
packet_queue_init(&audioq);
SDL_PauseAudio(0);
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec==NULL)
{
fprintf(stderr, "Unsupported video codec!\n");
return -1; // Codec not found
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec, &videoOptionsDict) < 0)
{
fprintf(stderr, "Unable to open video codec!\n");
return -1;
}
// Allocate video frame
pFrame=avcodec_alloc_frame();
// Make a screen to put our video
#ifndef __DARWIN__
screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
#else
screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
#endif
if (!screen)
{
fprintf(stderr, "SDL: could not set video mode - exiting\n");
exit(1);
}
//
// Allocate a place to put our YUV image on that screen
//
bmp =
SDL_CreateYUVOverlay
(
pCodecCtx->width,
pCodecCtx->height,
SDL_YV12_OVERLAY,
screen
);
sws_ctx =
sws_getContext
(
pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
PIX_FMT_YUV420P,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
// Read frames and save first five frames to disk
i=0;
while (av_read_frame(pFormatCtx, &packet)>=0)
{
// Is this a packet from the video stream?
if (packet.stream_index==videoStream)
{
// Decode video frame
avcodec_decode_video2
(
pCodecCtx,
pFrame,
&frameFinished,
&packet
);
// Did we get a video frame?
if (frameFinished)
{
SDL_LockYUVOverlay(bmp);
AVPicture pict;
pict.data[0] = bmp->pixels[0];
pict.data[1] = bmp->pixels[2];
pict.data[2] = bmp->pixels[1];
pict.linesize[0] = bmp->pitches[0];
pict.linesize[1] = bmp->pitches[2];
pict.linesize[2] = bmp->pitches[1];
sws_scale
(
sws_ctx,
pFrame->data,
pFrame->linesize,
0,
pCodecCtx->height,
pict.data,
pict.linesize
);
SDL_UnlockYUVOverlay(bmp);
rect.x = 0;
rect.y = 0;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height;
SDL_DisplayYUVOverlay(bmp, &rect);
av_free_packet(&packet);
}
}
else if(packet.stream_index==audioStream)
{
packet_queue_put(&audioq, &packet);
}
else
{
av_free_packet(&packet);
}
// Free the packet that was allocated by av_read_frame
SDL_PollEvent(&event);
switch(event.type) {
case SDL_QUIT:
quit = 1;
SDL_Quit();
exit(0);
break;
default:
break;
}
}
av_free(pFrame);
avcodec_close(pCodecCtx);
avcodec_close(aCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
_______________________________________________
Libav-user mailing list
[email protected]
http://ffmpeg.org/mailman/listinfo/libav-user