当前位置: 首页 > article >正文

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

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函数打印出来:

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函数:

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语句为真时括号里的内容:

if (pkt->duration <= 0) {
//...
}

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

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):

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。


http://www.kler.cn/a/315869.html

相关文章:

  • 三:网络为什么要分层:OSI模型与TCP/IP模型
  • LeetCode105.从前序与中序遍历构造二叉树
  • 越南很火的slots游戏投放Google谷歌广告策略
  • 第三十一天|贪心算法| 56. 合并区间,738.单调递增的数字 , 968.监控二叉树
  • 如何判定linux系统CPU的核心架构
  • Ubuntu 的 ROS 操作系统安装与测试
  • 详解 FFmpeg 中的 -map 选项
  • Qt-QLineEdit输入类控件(29)
  • 【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】011 - 第一个用户空间进程 init 进程 第一阶段初始化过程 源码分析
  • poi生成的ppt,powerPoint打开提示内容错误解决方案
  • 论文阅读:A Generalization of Transformer Networks to Graphs
  • [JavaEE] 网络初识(网络通信 及 TCP / UDP / IP 封装分用 )
  • 强大的重命名工具 | Bulk Rename Utility v4.0 便携版
  • 【Linux】入门【更详细,带实操】
  • flask + vue
  • CCF CSP题解:字符串变换(str)(202409-2)
  • 多态对象的内存结构
  • [Python学习日记-27] 文件操作练习题解析
  • Java的IO流(二)
  • 基于STM32残疾人辅助行走系统
  • Kotlin 基本介绍(二)
  • macos pyenv 安装python tk 、tkinter图形库方法步骤和使用总结
  • jQuery Mobile 方向改变事件
  • 01 基础request
  • Python进阶学习笔记(一)对象
  • vue的基本原理