FFmpeg源码:get_audio_frame_duration、av_get_audio_frame_duration2函数分析
一、get_audio_frame_duration函数
(一)get_audio_frame_duration函数的定义
get_audio_frame_duration函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavcodec/utils.c中:
static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba,uint32_t tag, int bits_per_coded_sample, int64_t bitrate,uint8_t * extradata, int frame_size, int frame_bytes)
{int bps = av_get_exact_bits_per_sample(id);int framecount = (ba > 0 && frame_bytes / ba > 0) ? frame_bytes / ba : 1;/* codecs with an exact constant bits per sample */if (bps > 0 && ch > 0 && frame_bytes > 0 && ch < 32768 && bps < 32768)return (frame_bytes * 8LL) / (bps * ch);bps = bits_per_coded_sample;/* codecs with a fixed packet duration */switch (id) {case AV_CODEC_ID_ADPCM_ADX: return 32;case AV_CODEC_ID_ADPCM_IMA_QT: return 64;case AV_CODEC_ID_ADPCM_EA_XAS: return 128;case AV_CODEC_ID_AMR_NB:case AV_CODEC_ID_EVRC:case AV_CODEC_ID_GSM:case AV_CODEC_ID_QCELP:case AV_CODEC_ID_RA_288: return 160;case AV_CODEC_ID_AMR_WB:case AV_CODEC_ID_GSM_MS: return 320;case AV_CODEC_ID_MP1: return 384;case AV_CODEC_ID_ATRAC1: return 512;case AV_CODEC_ID_ATRAC9:case AV_CODEC_ID_ATRAC3:if (framecount > INT_MAX/1024)return 0;return 1024 * framecount;case AV_CODEC_ID_ATRAC3P: return 2048;case AV_CODEC_ID_MP2:case AV_CODEC_ID_MUSEPACK7: return 1152;case AV_CODEC_ID_AC3: return 1536;case AV_CODEC_ID_FTR: return 1024;}if (sr > 0) {/* calc from sample rate */if (id == AV_CODEC_ID_TTA)return 256ll * sr / 245;else if (id == AV_CODEC_ID_DST)return 588ll * sr / 44100;else if (id == AV_CODEC_ID_BINKAUDIO_DCT) {if (sr / 22050 > 22)return 0;return (480 << (sr / 22050));}if (id == AV_CODEC_ID_MP3)return sr <= 24000 ? 576 : 1152;}if (ba > 0) {/* calc from block_align */if (id == AV_CODEC_ID_SIPR) {switch (ba) {case 20: return 160;case 19: return 144;case 29: return 288;case 37: return 480;}} else if (id == AV_CODEC_ID_ILBC) {switch (ba) {case 38: return 160;case 50: return 240;}}}if (frame_bytes > 0) {/* calc from frame_bytes only */if (id == AV_CODEC_ID_TRUESPEECH)return 240 * (frame_bytes / 32);if (id == AV_CODEC_ID_NELLYMOSER)return 256 * (frame_bytes / 64);if (id == AV_CODEC_ID_RA_144)return 160 * (frame_bytes / 20);if (id == AV_CODEC_ID_APTX)return 4 * (frame_bytes / 4);if (id == AV_CODEC_ID_APTX_HD)return 4 * (frame_bytes / 6);if (bps > 0) {/* calc from frame_bytes and bits_per_coded_sample */if (id == AV_CODEC_ID_ADPCM_G726 || id == AV_CODEC_ID_ADPCM_G726LE)return frame_bytes * 8 / bps;}if (ch > 0 && ch < INT_MAX/16) {/* calc from frame_bytes and channels */switch (id) {case AV_CODEC_ID_FASTAUDIO:return frame_bytes / (40 * ch) * 256;case AV_CODEC_ID_ADPCM_IMA_MOFLEX:return (frame_bytes - 4 * ch) / (128 * ch) * 256;case AV_CODEC_ID_ADPCM_AFC:return frame_bytes / (9 * ch) * 16;case AV_CODEC_ID_ADPCM_PSX:case AV_CODEC_ID_ADPCM_DTK:frame_bytes /= 16 * ch;if (frame_bytes > INT_MAX / 28)return 0;return frame_bytes * 28;case AV_CODEC_ID_ADPCM_4XM:case AV_CODEC_ID_ADPCM_IMA_ACORN:case AV_CODEC_ID_ADPCM_IMA_DAT4:case AV_CODEC_ID_ADPCM_IMA_ISS:return (frame_bytes - 4 * ch) * 2 / ch;case AV_CODEC_ID_ADPCM_IMA_SMJPEG:return (frame_bytes - 4) * 2 / ch;case AV_CODEC_ID_ADPCM_IMA_AMV:return (frame_bytes - 8) * 2;case AV_CODEC_ID_ADPCM_THP:case AV_CODEC_ID_ADPCM_THP_LE:if (extradata)return frame_bytes * 14LL / (8 * ch);break;case AV_CODEC_ID_ADPCM_XA:return (frame_bytes / 128) * 224 / ch;case AV_CODEC_ID_INTERPLAY_DPCM:return (frame_bytes - 6 - ch) / ch;case AV_CODEC_ID_ROQ_DPCM:return (frame_bytes - 8) / ch;case AV_CODEC_ID_XAN_DPCM:return (frame_bytes - 2 * ch) / ch;case AV_CODEC_ID_MACE3:return 3 * frame_bytes / ch;case AV_CODEC_ID_MACE6:return 6 * frame_bytes / ch;case AV_CODEC_ID_PCM_LXF:return 2 * (frame_bytes / (5 * ch));case AV_CODEC_ID_IAC:case AV_CODEC_ID_IMC:return 4 * frame_bytes / ch;}if (tag) {/* calc from frame_bytes, channels, and codec_tag */if (id == AV_CODEC_ID_SOL_DPCM) {if (tag == 3)return frame_bytes / ch;elsereturn frame_bytes * 2 / ch;}}if (ba > 0) {/* calc from frame_bytes, channels, and block_align */int blocks = frame_bytes / ba;int64_t tmp = 0;switch (id) {case AV_CODEC_ID_ADPCM_IMA_WAV:if (bps < 2 || bps > 5)return 0;tmp = blocks * (1LL + (ba - 4 * ch) / (bps * ch) * 8);break;case AV_CODEC_ID_ADPCM_IMA_DK3:tmp = blocks * (((ba - 16LL) * 2 / 3 * 4) / ch);break;case AV_CODEC_ID_ADPCM_IMA_DK4:tmp = blocks * (1 + (ba - 4LL * ch) * 2 / ch);break;case AV_CODEC_ID_ADPCM_IMA_RAD:tmp = blocks * ((ba - 4LL * ch) * 2 / ch);break;case AV_CODEC_ID_ADPCM_MS:tmp = blocks * (2 + (ba - 7LL * ch) * 2LL / ch);break;case AV_CODEC_ID_ADPCM_MTAF:tmp = blocks * (ba - 16LL) * 2 / ch;break;case AV_CODEC_ID_ADPCM_XMD:tmp = blocks * 32;break;}if (tmp) {if (tmp != (int)tmp)return 0;return tmp;}}if (bps > 0) {/* calc from frame_bytes, channels, and bits_per_coded_sample */switch (id) {case AV_CODEC_ID_PCM_DVD:if(bps<4 || frame_bytes<3)return 0;return 2 * ((frame_bytes - 3) / ((bps * 2 / 8) * ch));case AV_CODEC_ID_PCM_BLURAY:if(bps<4 || frame_bytes<4)return 0;return (frame_bytes - 4) / ((FFALIGN(ch, 2) * bps) / 8);case AV_CODEC_ID_S302M:return 2 * (frame_bytes / ((bps + 4) / 4)) / ch;}}}}/* Fall back on using frame_size */if (frame_size > 1 && frame_bytes)return frame_size;//For WMA we currently have no other means to calculate duration thus we//do it here by assuming CBR, which is true for all known cases.if (bitrate > 0 && frame_bytes > 0 && sr > 0 && ba > 1) {if (id == AV_CODEC_ID_WMAV1 || id == AV_CODEC_ID_WMAV2)return (frame_bytes * 8LL * sr) / bitrate;}return 0;
}
该函数的作用是:计算某个音频packet占用的时间值,也就是AVPacket结构体中的成员变量duration的值。注意:该时间值以AVStream的time_base为单位,还需要额外跟time_base进行运算,才能得到以秒为单位的时间值。
形参id:输入型参数。编解码器唯一标识符,指明码流对应的标准。
形参sr:输入型参数。音频的采样频率。
形参ch:输入型参数。声道数量。
形参ba:输入型参数。“区块对齐”,即每个采样点所需的字节数。
形参tag:输入型参数。关于编解码器的附加信息,一般可忽略。
形参bits_per_coded_sample:输入型参数。音频的采样位数。
形参bitrate:输入型参数。音频的码率,单位为bits/s。
形参extradata:输入型参数。初始化解码器所需的额外二进制数据,依赖于编解码器,一般可忽略。
形参frame_size:输入型参数。一般可忽略。
形参frame_bytes:输入型参数。该音频packet的大小,单位为字节。
返回值:该音频packet占用的时间值。
(二)get_audio_frame_duration函数的内部实现分析
大部分情况下get_audio_frame_duration函数可以化简为:
static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba,uint32_t tag, int bits_per_coded_sample, int64_t bitrate,uint8_t * extradata, int frame_size, int frame_bytes)
{int bps = av_get_exact_bits_per_sample(id);/* codecs with an exact constant bits per sample */if (bps > 0 && ch > 0 && frame_bytes > 0 && ch < 32768 && bps < 32768)return (frame_bytes * 8LL) / (bps * ch);
}
get_audio_frame_duration函数中,在一开始不会直接用形参bits_per_coded_sample存贮的采样位数,而是会先通过av_get_exact_bits_per_sample函数得到音频的采样位数:
int bps = av_get_exact_bits_per_sample(id);
然后判断音频采样位数是否在0到32768之内、声道数量是否在0到32768之内、packet的大小是否大于0。如果都满足条件,返回 packet的大小(单位为字节)×8÷(音频的采样位数×声道数量)的结果,该结果就是该音频packet占用的时间值。比如,如果packet的大小为16384字节、音频的采样位数为16位、声道数为2,则该音频packet占用的时间值为:16384×8÷(16×2)= 4096:
/* codecs with an exact constant bits per sample */if (bps > 0 && ch > 0 && frame_bytes > 0 && ch < 32768 && bps < 32768)return (frame_bytes * 8LL) / (bps * ch);
二、av_get_audio_frame_duration2函数
av_get_audio_frame_duration2函数定义在源文件libavcodec/utils.c中:
int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes)
{int channels = par->ch_layout.nb_channels;int duration;duration = get_audio_frame_duration(par->codec_id, par->sample_rate,channels, par->block_align,par->codec_tag, par->bits_per_coded_sample,par->bit_rate, par->extradata, par->frame_size,frame_bytes);return FFMAX(0, duration);
}
该函数的作用是:计算某个音频packet占用的时间值,也就是AVPacket结构体中的成员变量duration的值。注意:该时间值以AVStream的time_base为单位,还需要额外跟time_base进行运算,才能得到以秒为单位的时间值。
形参par:输入型参数。指向一个AVCodecParameters(保存音视频流的基本参数信息)的结构体。
形参frame_bytes:输入型参数。该音频packet的大小,单位为字节。
返回值:该音频packet占用的时间值。
可以看到av_get_audio_frame_duration2函数内部就是通过调用get_audio_frame_duration函数来计算某个音频packet占用的时间值。