SDL_AudioSpec结构中call_back函数解析
参考教程http://dranger.com/ffmpeg/tutorial03.html
void audio_callback(void *userdata, Uint8 *stream, int len)
第一个参数userdata是AVCodecContext,为了获取AVPacket传入AVCodecContext结构体,用于解码
第二个参数stream指向需要填充的音频缓冲区
第三个参数len,表示音频缓存区的大小
对于何时调用callback回调函数,文档指出:Callback function for filling the audio buffer,应该是音频设备准备好之后开始定时通过callback取回数据到stream中。具体代码注释如下
/*
*
第一个参数userdata是AVCodecContext,为了获取AVPacket传入AVCodecContext结构体,用于解码
第二个参数stream指向需要填充的音频缓冲区
第三个参数len,表示音频缓存区的大小
*
*/
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) {
//通过while循环判断缓冲区大小是否大于0,如果len > 0,说明缓存区stream需要填充数据,那么就判断
if (audio_buf_index >= audio_buf_size) {
/* We have already sent all our data; get more */
//audio缓冲区拷贝索引指针指向buf结尾,说明缓冲区所有数据都已拷贝到stream中,需要重新解码获取帧数据
audio_size = audio_decode_frame(aCodecCtx, audio_buf, audio_buf_size);
if (audio_size < 0) {
/* If error, output silence */
audio_buf_size = 1024; // arbitrary?
memset(audio_buf, 0, audio_buf_size);
}
else {
//解码成功,则记录buf大小
audio_buf_size = audio_size;
}
//重置buf索引为0
audio_buf_index = 0;
}
//记录buf数据字节总数
len1 = audio_buf_size - audio_buf_index;
if (len1 > len) //如果buf数据大于缓存区大小
len1 = len; //先拷贝缓冲区大小的数据
memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1); //拷贝固定大小的数据
len -= len1; //记录下次需要拷贝的字节数
stream += len1; //stream后移len1字节
audio_buf_index += len1; //buf索引值后移len1字节
}
}
拷贝过程如下:
上图中缓冲区大小是4224,实际情况可能不同,每次判断缓存区是否填满,如果没有填满,则检查解码得到的buf是否用完,如果没有用完,则继续从buf中拷贝到stream,并记录已拷贝的字节数,移动stream指针以及索引,记录stream缓冲区剩余要拷贝的字节数,如果剩余字节数为0,则stream已拷贝完成。audio_callback函数中使用了静态变量,静态变量的生命周期是整个程序结束,因此阅读代码时注意。
/*
*
*第一个参数 aCodecCtx,用于解码获取帧数据
*第二个参数 audio_buf,用户存储解码后的音频数据
*第三个参数 buf_size是audio_buf的大小
*/
int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) {
static AVPacket pkt;
static uint8_t *audio_pkt_data = NULL;
static int audio_pkt_size = 0;
static AVFrame frame;
int len1, data_size = 0;
for (;;) {
//audio_pkt_size表示需要解码的AVPacket大小,每次解码后会减去相应的avcodec_decode_audio4返回值,
//判断是否一次能够解码完成,否则可能分多次解码
while (audio_pkt_size > 0) {
int got_frame = 0;
try
{
len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);
if (len1 < 0) {
/* if error, skip frame */
audio_pkt_size = 0;
break;
}
}
catch (...)
{
int error;
AVERROR(error);
}
//audio_pkt_data是一个指针,指向pkt.data
audio_pkt_data += len1;
//audio_pkt_size记录剩余需要解码的大小
audio_pkt_size -= len1;
//获取帧数据成功
if (got_frame)
{
//获取拷贝buf的大小
data_size =
av_samples_get_buffer_size
(
NULL,
aCodecCtx->channels,
frame.nb_samples,
aCodecCtx->sample_fmt,
1
);
//把frame.data中的数据拷贝到audio_buf
memcpy(audio_buf, frame.data[0], data_size);
}
if (data_size <= 0) {
/* No data yet, get more frames */
continue;
}
/* We have data, return it and come back for more later */
return data_size;
}
//只有下次进入audio_decode_frame函数中,并且上次的解码完成,才会释放pkt
if (pkt.data)
av_free_packet(&pkt);
if (quit) {
return -1;
}
//获取pkt数据
if (packet_queue_get(&audioq, &pkt, 1) < 0) {
return -1;
}
audio_pkt_data = pkt.data;
audio_pkt_size = pkt.size;
}
}