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

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

一、引言

从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以显示WAV音频文件每个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源码中计算WAV音频文件每个packet的duration和duration_time的实现

(一)得到每个packet的duration

FFmpeg对WAV音频文件进行解封装(解复用)时,会调用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被赋值为该音频packet占用的以AVStream的time_base为单位的时间值,让变量den被赋值为该音频的采样频率(单位为Hz):

compute_frame_duration(s, &num, &den, st, pc, pkt);

从文章《FFmpeg源码:compute_frame_duration函数分析》中可以知道,compute_frame_duration函数内部调用了av_get_audio_frame_duration2函数。而从《FFmpeg源码:get_audio_frame_duration、av_get_audio_frame_duration2函数分析》中可以知道,av_get_audio_frame_duration2函数内部又通过get_audio_frame_duration函数来计算某个音频packet占用的时间值。计算公式是:该音频packet占用的以AVStream的time_base为单位的时间值 = packet的大小(单位为字节)×8÷(音频的采样位数×声道数量),比如,某个音频packet的大小为16384字节、音频的采样位数为16位、声道数为2,则该音频packet占用的时间值(以AVStream的time_base为单位)为:16384×8÷(16×2)= 4096。

关于av_rescale_rnd函数的用法可以参考:《FFmpeg源码:av_rescale_rnd、av_rescale_q_rnd、av_rescale_q、av_add_stable函数分析》。最后通过av_rescale_rnd函数得到AVPacket结构体的成员变量duration。下面语句相当于执行了: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为该音频packet占用的以AVStream的time_base为单位的时间值,变量den为该音频的采样频率(单位为Hz)。根据《音视频入门基础:WAV专题(8)——FFmpeg源码中计算WAV音频文件AVStream的time_base的实现》我们又可以知道WAV音频文件AVStream的time_base(st->time_base)为音频采样频率的倒数。

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

pkt->duration = num。

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

(二)得到每个packet的duration_time

duration和duration_time是通过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);
//...
}

print_duration_time为宏定义:

#define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1)

而writer_print_time函数的定义为:

static void writer_print_time(WriterContext *wctx, const char *key,int64_t ts, const AVRational *time_base, int is_duration)
{char buf[128];if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);} else {double d = ts * av_q2d(*time_base);struct unit_value uv;uv.val.d = d;uv.unit = unit_second_str;value_string(buf, sizeof(buf), uv);writer_print_string(wctx, key, buf, 0);}
}

其中,writer_print_time函数的形参ts为该音频packet占用的以AVStream的time_base为单位的时间值,形参time_base为AVStream的time_base。

关于av_q2d函数的用法可以参考:《FFmpeg有理数相关的源码:AVRational结构体和其相关的函数分析》。duration_time实际上是由writer_print_time函数中的下面语句计算出来的:

double d = ts * av_q2d(*time_base);

简单点来讲duration_time = duration × time_base。

三、总结

对于WAV音频文件:

AVPacket的“duration”为该音频packet占用的以AVStream的time_base为单位的时间值,其值等于: packet的大小(单位为字节)×8÷(音频的采样位数×声道数量),比如,某个音频packet的大小为16384字节、音频的采样位数为16位、声道数为2,则该音频packet的duration等于:16384×8÷(16×2)= 4096。

而“duration_time”为该音频packet占用的以秒为单位的时间值,其值等于:duration × time_base。比如,某个音频packet的duration为4096,time_base为44100分之一,其duration_time为4096乘以44100分之一,等于0.092880。

http://www.lryc.cn/news/430865.html

相关文章:

  • AI写的论文查重率高吗?分享6款实测AI论文生成免费网站
  • 【专题】2024年8月中国企业跨境、出海、国际化、全球化行业报告汇总PDF合集分享(附原数据表)
  • [算法]单调栈解法
  • 构建数据安全防线:MySQL数据备份策略的文档化实践
  • 4. GIS前端工程师岗位职责、技术要求和常见面试题
  • 软件测试-Selenium+python自动化测试
  • SpringBoot与Minio的极速之旅:解锁文件切片上传新境界
  • Java 7.3 - 分布式 id
  • 144. 腾讯云Redis数据库
  • 基于单片机的自动浇花控制写设计任务书
  • 从零到精通:用C++ STL string优化代码
  • 鸿蒙轻内核M核源码分析系列五 时间管理
  • Python Opencv鼠标回调
  • Ubuntu环境的MySql下载安装
  • Android系统去掉WIFI模块
  • 代码随想录 -- 二叉树 -- 翻转二叉树
  • Node.js之文件复制
  • 新手c语言讲解及题目分享(十六)--文件系统专项练习
  • RabbitMQ本地Ubuntu系统环境部署与无公网IP远程连接服务端实战演示
  • [C++#28][多态] 两个条件 | 虚函数表 | 抽象类 | override 和 final | 重载 重写 重定义
  • List 集合指定值升序降序排列Comparator实现
  • 【Day07】
  • shell 控制台显示彩色文字的方法
  • Nginx: 缓存, 不缓存特定内容和缓存失效降低上游压力策略及其配置示例
  • Python 全栈系列266 Kafka服务的Docker搭建
  • 集合框架,List常用API,栈和队列初识
  • 构建全景式智慧文旅生态:EasyCVR视频汇聚平台与AR/VR技术的深度融合实践
  • C++结构体声明时初始化
  • 基于微信的热门景点推荐小程序的设计与实现(论文+源码)_kaic
  • 9、设计模式