音视频入门基础: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。
相关文章:
音视频入门基础:WAV专题(9)——FFmpeg源码中计算WAV音频文件每个packet的duration和duration_time的实现
一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以显示WAV音频文件每个packet(也称为数据包或多媒体包)的信息࿰…...
AI写的论文查重率高吗?分享6款实测AI论文生成免费网站
在当今学术研究和论文写作领域,AI技术的迅猛发展为研究人员提供了极大的便利。特别是AI论文自动生成助手,它们不仅能够提高写作效率,还能帮助生成高质量的论文内容。以下是六款经过实测且免费的AI论文生成网站推荐: 一、千笔-AIP…...
【专题】2024年8月中国企业跨境、出海、国际化、全球化行业报告汇总PDF合集分享(附原数据表)
原文链接: https://tecdat.cn/?p37584 在全球化浪潮汹涌澎湃的当下,中国企业积极探索海外市场,开启了出海跨境的新征程。本报告合集旨在全面梳理出海跨境全球化行业的发展态势,涵盖多个领域的深度洞察。 从游戏、快消品、医疗器…...
[算法]单调栈解法
目录 739. 每日温度 - 力扣(LeetCode) 42. 接雨水 - 力扣(LeetCode) 84. 柱状图中最大的矩形 - 力扣(LeetCode) 739. 每日温度 - 力扣(LeetCode) 解法: 通常是一维数…...
构建数据安全防线:MySQL数据备份策略的文档化实践
在数据驱动的商业环境中,数据备份策略是确保数据安全和业务连续性的关键。MySQL,作为广泛使用的数据库管理系统,其数据备份策略的文档化对于规范备份流程、提高恢复效率和满足合规要求至关重要。本文将深入探讨如何在MySQL中实现数据备份的策…...
4. GIS前端工程师岗位职责、技术要求和常见面试题
本系列文章目录: 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…...
软件测试-Selenium+python自动化测试
目录 会用到谷歌浏览器Chrome测试,需要下载一个Chromedriver(Chrome for Testing availability)对应自己的浏览器版本号选择。 一、元素定位 对html网页中的元素进行定位,同时进行部分操作。 1.1一个简单的模板 from selenium import webdriver from selenium.webdrive…...
SpringBoot与Minio的极速之旅:解锁文件切片上传新境界
目录 一、前言 二、对象存储(Object Storage)介绍 (1)对象存储的特点 (2)Minio 与对象存储 (3)对象存储其他存储方式的区别 (4)对象存储的应用场景 三、…...
Java 7.3 - 分布式 id
分布式 ID 介绍 什么是 ID? ID 就是 数据的唯一标识。 什么是分布式 ID? 分布式 ID 是 分布式系统中的 ID,它不存在于现实生活,只存在于分布式系统中。 分库分表: 一个项目,在上线初期使用的是单机 My…...
144. 腾讯云Redis数据库
文章目录 一、Redis 的主要功能特性二、Redis 的典型应用场景三、Redis 的演进过程四、Redis 的架构设计五、Redis 的数据类型及操作命令六、腾讯云数据库 Redis七、总结 Redis 是一种由 C 语言开发的 NoSQL 数据库,以其高性能的键值对存储和多种应用场景而闻名。本…...
基于单片机的自动浇花控制写设计任务书
一、内容要求: 任务 随着社会的进步,人们的生活质量越来越高。在家里养养盆花可以陶冶情操,丰富生活。同时盆花可以通过光合作用吸收二氧化碳,净化室内空气,在有花木的地方空气中阴离子聚集较多,所以空气…...
从零到精通:用C++ STL string优化代码
目录 1:为什么要学习string类 2:标准库中的string类 2.1:string类(了解) 2.2:总结 3:string类的常用接口 3.1:string类对象的常见构造 3.1.1:代码1 3.1.2:代码2 3.2:string类对象的遍历操作 3.2.1:代码1(begin end) 3.2.2:代码2(rbegin rend) 3.3:string类对象的…...
鸿蒙轻内核M核源码分析系列五 时间管理
往期知识点记录: 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总 持续更新中…… 在鸿蒙轻内核源码分析上一篇文章中,我们剖析了中断的源码,简单提到了Tick中断。本文会继续分析Tick和时间相关的源…...
Python Opencv鼠标回调
使用 OpenCV 的 cv2.setMouseCallback() 方法来捕捉鼠标事件,并实现以下功能: 实时在鼠标指针附近显示其位置的像素坐标。通过左键双击,将像素坐标记录到数组中。通过右键点击,取消上一次添加的坐标。 下面是实现代码的示例&…...
Ubuntu环境的MySql下载安装
下载压缩包 此文章下载的mysql版本位5.7.29 sudo wget https://downloads.mysql.com/archives/get/p/23/file/mysql-server_5.7.29-1ubuntu18.04_amd64.deb-bundle.tar解压缩 sudo tar -xvf mysql-server_5.7.29-1ubuntu18.04_amd64.deb-bundle.tar命令解释 -x:…...
Android系统去掉WIFI模块
先说应用场景,有些特定设备,不能连接wifi。需要隐藏的模块,QS面板模块的wifi,还有设置里面的wifi.由于QS属于SystemUI,熟悉SystemUI之后,就可以直接去SystemUi那里找,找到QSTitle 默认配置的地方。 一、…...
代码随想录 -- 二叉树 -- 翻转二叉树
226. 翻转二叉树 - 力扣(LeetCode) 递归比较简单 class Solution(object):def invertTree(self, root):if rootNone:returnnode rootif node.left or node.right:tempnode.leftnode.leftnode.rightnode.righttempself.invertTree(node.left)self.inve…...
Node.js之文件复制
1.方式一:readFile // 导入fs模块 const fs require("fs") // 导入process模块 const process require("process")// 读取文件内容 let data fs.writeFileSync(./test.txt) // 写入文件内容 fs.writeFileSync(./test1.txt, data) 2.方式二&…...
新手c语言讲解及题目分享(十六)--文件系统专项练习
在我刚开始学习c语言的时候就跳过了这一章节,但在后面慢慢发现这一章节还是比较重要的,如果你报考了计算机二级c语言的话,你应该可以看到后面的三个大题有时会涉及到这章。所以说这章还是非常重要的。 目录 前言 一.打开文件 1.Fopen( )函数返回值 2&…...
RabbitMQ本地Ubuntu系统环境部署与无公网IP远程连接服务端实战演示
文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 安装内网穿透工具3.1 安装cpolar内网穿透3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 💡 推荐 前些天发现了一个巨牛的人工智能学习网站&am…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...
