音视频入门基础: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。

相关推荐
byte轻骑兵23 分钟前
【LE Audio】CAP精讲[10]: 多设备协同的通关秘籍——协调集全流程实战
音视频·蓝牙耳机·蓝牙音箱·le audio·低功耗音频
AI创界者1 小时前
LTX-Video 2.3 最新渐变版整合包!文生视频/图生视频双重进化,解压即用(附超详细避坑指南)
人工智能·aigc·音视频
爱睡懒觉的焦糖玛奇朵10 小时前
【从视频到数据集:焦糖玛奇朵的魔法工具使用说明】
人工智能·python·深度学习·学习·算法·yolo·音视频
潜创微科技12 小时前
IT68353:双 DP1.4a+HDMI2.0 转 HDMI2.0 单芯片 KVM 切换方案
嵌入式硬件·音视频
沐禾安信13 小时前
同一画面,如何两个视频同时播放,两个方法
电脑·音视频·分屏·视频转换
5008414 小时前
Conv + BN + ReLU 融合:省掉两次显存读写
flutter·架构·开源·wpf·音视频
爱睡懒觉的焦糖玛奇朵16 小时前
【从视频到数据集:焦糖玛奇朵的魔法工具Video To YOLO Dataset】
人工智能·python·学习·yolo·音视频
神秘的摄影师18 小时前
2026年AE音乐素材下载网站TOP5评测——短视频与自媒体创作者专属指南
音视频·媒体
2601_9577867718 小时前
短视频矩阵系统的信号处理密码:用奈奎斯特采样定理破解“限流“黑箱
矩阵·音视频·信号处理
若兰幽竹20 小时前
【大模型应用】抖音爆款视频深度分析系统:流水线式AI逆向拆解流量密码,精准预测播放量!
人工智能·python·音视频·抖音爆款分析