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;
                    else
                        return 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占用的时间值。

  • 22
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CMake是一个跨平台的开源构建工具,可用于自动化管理项目的编译过程。FFmpeg是一个开源的音视频处理库,可以用来处理多种格式的音视频文件。而最后的下载的文件opencv_ffmpeg_64.dll是OpenCV库所需的FFmpeg动态链接库。 当我们在使用CMake构建一个项目时,可能会用到FFmpeg库来进行音视频处理。其中,OpenCV是一个广泛使用的计算机视觉库,它也能够使用FFmpeg进行音视频的编解码与处理。而opencv_ffmpeg_64.dll是OpenCV库所需的FFmpeg依赖库,这个库在运行OpenCV相关功能时需要被加载。 如果在使用CMake构建一个依赖于OpenCV和FFmpeg的项目时,若缺少opencv_ffmpeg_64.dll文件,可以通过下载获得该文件。可以通过在网上搜索opencv_ffmpeg_64.dll文件的下载链接,并将其下载到本地。下载完成后,将该文件放置在项目中指定的位置,一般来说是与其他的动态链接库(.dll文件)放在一起的。然后重新进行CMake构建,以确保项目能够正确加载该库文件。 需要注意的是,下载的文件必须与你的系统和项目的架构相匹配。例如,如果你的系统是64位的,那么你需要下载64位的opencv_ffmpeg_64.dll文件,而不是32位的。如果下载的文件与你的系统不匹配,可能会导致项目构建失败或运行时错误。 总结来说,下载opencv_ffmpeg_64.dll文件是为了满足OpenCV库在进行音视频处理时所依赖的FFmpeg库的加载需求。在使用CMake构建项目时,下载并正确放置该文件,可以确保项目能够正确运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值