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

news2024/11/13 9:34:24

一、引言

从文章《音视频入门基础: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.coloradmin.cn/o/2116054.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

苹果电脑可以玩《原神》吗?原神可以在Mac上玩吗?苹果电脑玩原神怎么样

《原神》是一款由米哈游开发的开放世界冒险游戏&#xff0c;自从2020年正式上线以来&#xff0c;就受到了全球玩家的热烈欢迎。《原神》的画面精美&#xff0c;音乐动听&#xff0c;剧情丰富&#xff0c;角色多样&#xff0c;玩法多变&#xff0c;让人沉浸在一个充满奇幻和魅力…

【Python 学习】Pandas基础与应用(1)

题目 1 Pandas 简介1.1 主要特征1.2 Pandas 安装 2 Pandas中的数据结构2.1 Series 数据结构和操作2.1.1 Series的数据结构2.1.2 Seres的操作 2.2 DataFrame 数据结构和操作2.2.1 DataFrame 数据结构2.2.2 Dataframe 操作2.2.3 DateFrame 的特殊操作 2.3 Series 和 DataFrame 的…

LRU go cache的实现

目录 LRU算法LRU原理LRU实现Redis LRU算法实现1. 内存淘汰策略2. LRU算法的实现3. LRU vs LFURedis中的LRU使用场景 基于LRU的缓存库go-cache安装使用代码解析 hashicorp/golang-lru安装使用代码解析 groupcache安装使用代码解析缓存淘汰算法并发缓存组一致性哈希防止缓存击穿—…

从电商行业的变化引出未来技术趋势

时间&#xff1a;2024年09月08日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频地址&#xff1a;喜马拉雅 希望大家帮个忙&#xff01;如果大家有工作机会&#xff0c;希望帮小蒋推荐一下&#xff0c;小蒋希望遇到一个认真…

无人机人工增雨技术详解

无人机&#xff0c;全称为无人驾驶飞行器&#xff08;Unmanned Aerial Vehicle, UAV&#xff09;&#xff0c;是一种不需要人员直接操控&#xff0c;而是利用先进的遥控技术、自主飞行控制系统和传感器技术来实现空中飞行和完成特定任务的飞行器。 一、技术原理 无人机人工增雨…

轻NAS系统CasaOS设备安装Memos笔记结合内网穿透公网访问与同步教程

文章目录 前言1. 使用Docker部署memos2. 注册账号与简单操作演示3. 安装cpolar内网穿透4. 创建公网地址5. 创建固定公网地址 前言 本文主要介绍如何在安装了轻NAS系统CasaOS设备中使用Docker本地部署开源云笔记服务memos&#xff0c;并结合cpolar内网穿透工具配置公网地址&…

C/C++两点坐标求距离以及C++保留两位小数输出,秒了

目录 1. 前言 2. 正文 2.1 问题 2.2 解决办法 2.2.1 思路 2.2.2 代码实现 3. 备注 1. 前言 依旧是带来一个练手的题目&#xff0c;目的就一个&#xff0c;方法千千万&#xff0c;通向终点的方式有很多种&#xff0c;没有谁与谁&#xff0c;我们都是为了成为更好的自己。…

VMware Fusion 虚拟机Mac版 安装CentOS系统教程

Mac分享吧 文章目录 CentOS安装完成&#xff0c;软件打开效果一、Mac中使用虚拟机安装CentOS系统1️⃣&#xff1a;下载镜像2️⃣&#xff1a;创建虚拟机3️⃣&#xff1a;设置虚拟机4️⃣&#xff1a;安装虚拟机5️⃣&#xff1a;设置成从磁盘启动 安装完成&#xff01;&…

哈希表如何避免冲突

系列文章&#xff1a; 1. 先导片--Map&Set之二叉搜索树 2. Map&Set之相关概念 3. 哈希表如何避免冲突 目录 1.概念 2. 冲突-概念 3. 冲突-避免 3.1 冲突-避免-哈希函数设计 3.2 冲突-避免-负载因子调节 4. 冲突-解决 4.1 冲突-解决-闭散列 4.1.1 线性探…

C++_15_类与对象

类与对象 什么是类&#xff1f; 描述有共同特征的事务的概念 作用&#xff1a;代码中 创建对象 什么是对象&#xff1f; 生活中&#xff1a; 就是指真实存在的事物。 代码中&#xff1a; 模拟真实的事物&#xff0c;使用类创建得到。 类与对象的关系 生活中&#xff1a; ​ 先…

VMware vSphere5.0关闭虚拟机电源时,报错从ESXI主机接收到错误

ESXI和VCENTER都是5.0版本的&#xff0c;有台虚拟机关机报错提示从ESXI主机接受到意外错误 具体报错信息如下&#xff1a; 从VCENTER平台对该虚拟机做任何操作都无法生效&#xff0c;后来查看了虚拟机的网络和端口&#xff0c;发现SSH能正常联通&#xff0c;进入虚拟机后使用命…

【算法专场】模拟(下)

目录 前言 38. 外观数列 算法分析 算法思路 算法代码 1419. 数青蛙 算法分析 算法思路 算法代码 2671. 频率跟踪器 算法分析 算法思路 算法代码 前言 在前面我们已经讲解了什么是模拟算法&#xff0c;这篇主要是讲解在leetcode上遇到的一些模拟题目~ 38. 外观数列…

Pencils Protocol生态新进展,即将上线 Vault 产品

“极高的盈利预期、通证的持续回购与销毁&#xff0c;Vault产品的推出正在成为Pencils Protocol生态发展的重磅利好。” Pencils Protocol是目前Scroll生态TVL最高的DeFi平台 &#xff0c;即便是行情整体较为平淡&#xff0c;其仍旧能够保持在3亿美元左右的锁仓价值&#xff0c…

史上最全的Linux常用命令汇总(超全面!超详细!)收藏这一篇就够了!

command &#xff1a;命令名&#xff0c;相应功能的英文单词或单词的缩写[-options] &#xff1a;选项&#xff0c;可用来对命令进行控制&#xff0c;也可以省略parameter &#xff1a;传给命令的参数&#xff0c;可以是 零个、一个 或者 多个 查阅命令帮助信息 -help 说明&…

LC1860C 后来怎么样了

这块芯片前身是大唐旗下联芯的LC1860C&#xff1b;这块传奇芯片在4G时代大放异彩&#xff0c;但是某些原因之后&#xff0c;技术打包转让给三家&#xff0c;分别是&#xff1a;小米&#xff0c;大疆&#xff0c;哲酷&#xff08;VIVO&#xff09;&#xff1b; 1、哲酷 哲酷目…

Infiniband网络架构的技术与性能分析

Infiniband格局寡头&#xff0c;性能占优 这篇文章探讨了网络交换机的性能优势&#xff0c;以及如何通过扩大模型参数量来提高语言模型的生成和预测能力。然而&#xff0c;计算约束对这种正向关系产生了重要影响&#xff0c;导致在相同的计算约束下&#xff0c;总存在最佳的模型…

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h> #include <stdlib.h> void shellSort(int data[], int n){// 划分的数组&#xff0c;例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量&#xff0c;换值int temp;…

基于java网页的纸业管理系统设计与实现

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…

【iOS】MVC设计模式

MVC 前言 如何设计一个程序的结构&#xff0c;这是一门专门的学问&#xff0c;叫做"架构模式"&#xff08;architectural pattern&#xff09;&#xff0c;属于编程的方法论。MVC 模式就是架构模式的一种。 它是Apple 官方推荐的 App 开发架构&#xff0c;也是一般…

C++中深拷贝与浅拷贝

描述&#xff1a; 在未定义显示拷贝构造函数的情况下&#xff0c;系统调用默认的拷贝函数——即浅拷贝&#xff0c;它能够完成成员的简单赋值拷贝操作。当数据成员中没有指针时&#xff0c;浅拷贝是可行的&#xff1b; 但当数据成员中有指针时&#xff0c;如果采用简单的浅拷…