音视频入门基础:AAC专题(9)——FFmpeg源码中计算AAC裸流每个packet的duration和duration_time的实现

=================================================================

音视频入门基础:AAC专题系列文章:

音视频入门基础:AAC专题(1)------AAC官方文档下载

音视频入门基础:AAC专题(2)------使用FFmpeg命令生成AAC裸流文件

音视频入门基础:AAC专题(3)------AAC的ADTS格式简介

音视频入门基础:AAC专题(4)------ADTS格式的AAC裸流实例分析

音视频入门基础:AAC专题(5)------FFmpeg源码中,判断某文件是否为AAC裸流文件的实现

音视频入门基础:AAC专题(6)------FFmpeg源码中解码ADTS格式的AAC的Header的实现

音视频入门基础:AAC专题(7)------FFmpeg源码中计算AAC裸流每个packet的size值的实现

音视频入门基础:AAC专题(8)------FFmpeg源码中计算AAC裸流AVStream的time_base的实现

音视频入门基础:AAC专题(9)------FFmpeg源码中计算AAC裸流每个packet的duration和duration_time的实现

音视频入门基础:AAC专题(10)------FFmpeg源码中计算AAC裸流每个packet的pts、dts、pts_time、dts_time的实现

=================================================================

一、引言

通过FFprobe命令:

cpp 复制代码
ffprobe -of json -show_packets XXX.aac

可以显示AAC裸流每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的duration和duration_time:

这个"duration"实际是AVPacket结构体中的成员变量duration,为该音频packet占用的以AVStream的time_base为单位的时间值。而"duration_time"为该音频packet占用的以秒为单位的时间值。这两个值通过fftools/ffprobe.c中的show_packet函数打印出来:

cpp 复制代码
static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
{
//...
    print_duration_ts("duration",        pkt->duration);
    print_duration_time("duration_time", pkt->duration, &st->time_base);
//...
}

本文讲述"duration"和"duration_time"的值是怎样被计算出来的。如果想直接看结论,可以跳到本文的最后,直接看"总结"。

二、FFmpeg源码中计算AAC裸流每个packet的duration和duration_time的实现

(一)得到每个packet的duration

FFmpeg对AAC裸流进行解封装(解复用)时,会调用avformat_find_stream_info函数,而该函数底层会调用compute_pkt_fields函数:

cpp 复制代码
static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
                               AVCodecParserContext *pc, AVPacket *pkt,
                               int64_t next_dts, int64_t next_pts)
{
//...
    if (pkt->duration <= 0) {
        compute_frame_duration(s, &num, &den, st, pc, pkt);
        if (den && num) {
            duration = (AVRational) {num, den};
            pkt->duration = av_rescale_rnd(1,
                                           num * (int64_t) st->time_base.den,
                                           den * (int64_t) st->time_base.num,
                                           AV_ROUND_DOWN);
        }
    }
//...
}

compute_pkt_fields函数内部,由于AVPacket结构体被初始化后,其成员变量duration会是0,(新版本的FFmpeg源码一般使用get_packet_defaults函数进行初始化,具体可以参考:《FFmpeg源码:av_init_packet、get_packet_defaults、av_packet_alloc函数分析》),所以会执行下面if语句为真时括号里的内容:

cpp 复制代码
if (pkt->duration <= 0) {
//...
}

通过compute_frame_duration函数,让变量num被赋值为该帧音频数据中采样的次数(对于规格为AAC LC和AAC LTP的AAC就是固定的1024),让变量den被赋值为该音频的采样频率(单位为Hz)。关于compute_frame_duration函数的用法可以参考:《FFmpeg源码:compute_frame_duration函数分析》:

cpp 复制代码
compute_frame_duration(s, &num, &den, st, pc, pkt);

最后通过av_rescale_rnd函数得到AVPacket结构体的成员变量duration。关于av_rescale_rnd函数的用法可以参考:《FFmpeg源码:av_rescale_rnd、av_rescale_q_rnd、av_rescale_q、av_add_stable函数分析》。下面语句相当于执行了:pkt->duration = 1 × num × st->time_base.den ÷ (den × st->time_base.num):

cpp 复制代码
pkt->duration = av_rescale_rnd(1,
        num * (int64_t) st->time_base.den,
        den * (int64_t) st->time_base.num,
        AV_ROUND_DOWN);

而从上面我们可以知道,变量num为该帧音频数据中采样的次数(对于规格为AAC LC和AAC LTP的AAC就是固定的1024),变量den为该音频的采样频率(单位为Hz)。根据《音视频入门基础:AAC专题(8)------FFmpeg源码中计算AAC裸流AVStream的time_base的实现》我们又可以知道,AAC裸流AVStream的time_base(st->time_base)固定为28224000分之一。

所以对于规格为AAC LC和AAC LTP的AAC,

语句pkt->duration = 1 × num × st->time_base.den ÷ (den × st->time_base.num)等价于

pkt->duration = 1024 × 28224000 ÷ 该音频的采样频率(这里的计算公式跟WAV音频文件是不一样的)

从而让AVPacket结构体中的成员变量duration可以被赋值为该音频packet占用的以AVStream的time_base为单位的时间值。

(二)得到每个packet的duration_time

音频的duration_time的计算公式都是一样的:duration_time = duration × time_base。具体可以参考:《音视频入门基础:WAV专题(9)------FFmpeg源码中计算WAV音频文件每个packet的duration和duration_time的实现》。

三、总结

1.对于标准的MPEG-2/4 AAC,其samples(一帧音频数据中采样的次数)为1024或者960次;规格为AAC LC和AAC LTP的AAC,一帧音频数据中采样的次数固定为1024次。具体可以参考:《音视频入门基础:AAC专题(3)------AAC的ADTS格式简介》。

2.音频AVPacket的"duration"为该音频packet占用的以AVStream的time_base为单位的时间值。对于AAC裸流,duration等于:samples × 28224000 ÷ 该音频的采样频率。对于规格为AAC LC和AAC LTP的AAC,samples固定为1024,duration等于:1024 × 28224000 ÷ 该音频的采样频率。比如,某个规格为AAC LC或AAC LTP的AAC裸流,其采样频率为44100Hz,则其一帧音频packet的duration等于:1024 × 28224000 ÷ 44100 = 655360。这个计算方法跟WAV音频文件是不一样的,各位同学可以把本文跟《音视频入门基础:WAV专题(9)------FFmpeg源码中计算WAV音频文件每个packet的duration和duration_time的实现》进行对比,以加深对音频帧duration值的理解。

3."duration_time"为该音频packet占用的以秒为单位的时间值,其值等于:duration × time_base(这是对任何格式的音频都通用的一种计算方式)。比如,某个音频packet的duration为655360,time_base为28224000分之一,则其duration_time等于655360乘以28224000分之一,等于0.02322。关于AAC音频time_base的计算方式可以参考:《音视频入门基础:AAC专题(8)------FFmpeg源码中计算AAC裸流AVStream的time_base的实现》。

4.对于AAC格式的音频,"duration_time"还有另外一种计算方式:duration_time = samples ÷ 该音频的采样频率。比如,samples(一帧音频数据中采样的次数)为1024,采样频率为44100Hz,则duration_time = 1024 ÷ 44100 = 0.02322。

相关推荐
正在走向自律2 小时前
第二章-AIGC入门-开启AIGC音频探索之旅:从入门到实践(6/36)
人工智能·aigc·音视频·语音识别·ai音乐·ai 音频·智能语音助手
Java患者·3 小时前
【小白】linux安装ffmpeg | java转码 【超详细】
ffmpeg
suifen_3 小时前
RK平台ffmpeg支持硬件编解码
ffmpeg
feiyangqingyun3 小时前
全网唯一/Qt结合ffmpeg实现手机端采集摄像头推流到rtsp或rtmp/可切换前置后置摄像头/指定分辨率帧率
qt·智能手机·ffmpeg
美狐美颜sdk9 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
melonbo13 小时前
使用FFmpeg将H.264码流封装为MP4
ffmpeg·音视频·h.264
aqi0014 小时前
FFmpeg开发笔记(七十七)Android的开源音视频剪辑框架RxFFmpeg
android·ffmpeg·音视频·流媒体
慢行的骑兵16 小时前
Android音视频探索之旅 | CMake基础语法 && 创建支持Ffmpeg的Android项目
ffmpeg·音视频
Just_Paranoid16 小时前
华为云Flexus+DeepSeek征文|基于Dify构建音视频内容转录工作流
华为云·音视频·dify·maas·deepseek·flexusx
go546315846517 小时前
修改Spatial-MLLM项目,使其专注于无人机航拍视频的空间理解
人工智能·算法·机器学习·架构·音视频·无人机