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

一、引言

从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以显示WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的size:

693ed79c59f94ce6af9b7593d45124df.png

这个“size”实际是AVPacket结构体中的成员变量size,为WAV音频文件中某个packet的大小(单位为字节),通过fftools/ffprobe.c中的show_packet函数打印出来:

static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
{
//...print_val("size",             pkt->size, unit_byte_str);
//...
}

本文讲述这个“size”值是怎样被计算出来的。如果想直接看结论,可以跳到本文的最后,直接看“总结”。

二、FFmpeg源码中计算WAV音频文件每个packet的size值的实现

(一)ff_pcm_default_packet_size函数

size值其实是通过源文件libavformat/pcm.c中的ff_pcm_default_packet_size函数计算出来的:

int ff_pcm_default_packet_size(AVCodecParameters *par)
{int nb_samples, max_samples, bits_per_sample;int64_t bitrate;if (par->block_align <= 0)return AVERROR(EINVAL);max_samples = INT_MAX / par->block_align;bits_per_sample = av_get_bits_per_sample(par->codec_id);bitrate = par->bit_rate;/* Don't trust the codecpar bitrate if we can calculate it ourselves */if (bits_per_sample > 0 && par->sample_rate > 0 && par->ch_layout.nb_channels > 0)if ((int64_t)par->sample_rate * par->ch_layout.nb_channels < INT64_MAX / bits_per_sample)bitrate = bits_per_sample * (int64_t)par->sample_rate * par->ch_layout.nb_channels;if (bitrate > 0) {nb_samples = av_clip64(bitrate / 8 / PCM_DEMUX_TARGET_FPS / par->block_align, 1, max_samples);nb_samples = 1 << av_log2(nb_samples);} else {/* Fallback to a size based method for a non-pcm codec with unknown bitrate */nb_samples = av_clip(4096 / par->block_align, 1, max_samples);}return par->block_align * nb_samples;
}

从《音视频入门基础:WAV专题(4)——FFmpeg源码中获取WAV文件音频压缩编码格式、采样频率、声道数量、采样位数、码率的实现》中可以知道:

par->bit_rate为从WAV Header解码出来的音频码率,单位为bits/s。

par->bits_per_coded_sample为从WAV Header解码出来的音频采样位数。

par->channels为从WAV Header解码出来的声道数量。

par->sample_rate为从WAV Header解码出来的音频采样频率,单位为Hz。

par->block_align为从WAV Header解码出来的“区块对齐”,即每个采样点所需的字节数。

ff_pcm_default_packet_size函数中,首先计算出“最大采样”:

max_samples = INT_MAX / par->block_align;

将拿到的音频采样位数保存到变量bits_per_sample中;把拿到的音频码率(单位为bits/s)保存到变量bitrate中:

bits_per_sample = av_get_bits_per_sample(par->codec_id);
bitrate = par->bit_rate;

如果满足条件:从WAV Header中解码出来的音频采样位数、音频采样频率、声道数量都大于0,不使用从WAV Header中解码出来的音频码率,而是根据公式:音频码率 = 采样位数*采样频率*声道,计算:

    /* Don't trust the codecpar bitrate if we can calculate it ourselves */if (bits_per_sample > 0 && par->sample_rate > 0 && par->ch_layout.nb_channels > 0)if ((int64_t)par->sample_rate * par->ch_layout.nb_channels < INT64_MAX / bits_per_sample)bitrate = bits_per_sample * (int64_t)par->sample_rate * par->ch_layout.nb_channels;

宏PCM_DEMUX_TARGET_FPS定义在源文件libavformat/pcm.c中:

#define PCM_DEMUX_TARGET_FPS  10

关于av_clip、av_clip64用法可以参考:《FFmpeg源码:av_clip、av_clip64宏定义分析》、《FFmpeg源码:av_log2函数分析》。

nb_samples为一帧音频数据中采样的数量(次数)。

情况一:如果音频码率大于0,计算上述音频码率(单位为bits/s) ‌÷ 8 ‌÷ 10 ‌÷ “区块对齐”的结果,将该结果裁剪到1到“最大采样”的范围内,然后求该值是2的多少次幂,保存到变量nb_samples中;

情况二:如果音频码率不大于0,计算4096  ‌÷ “区块对齐”的结果,将该结果裁剪到1到“最大采样”的范围内,保存到变量nb_samples中:

if (bitrate > 0) {nb_samples = av_clip64(bitrate / 8 / PCM_DEMUX_TARGET_FPS / par->block_align, 1, max_samples);nb_samples = 1 << av_log2(nb_samples);} else {/* Fallback to a size based method for a non-pcm codec with unknown bitrate */nb_samples = av_clip(4096 / par->block_align, 1, max_samples);}

最后返回“区块对齐” × 一帧音频数据中采样的次数:

return par->block_align * nb_samples;

(二)wav->max_size

从《音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现》中可以知道,FFmpeg源码通过wav_read_header函数解码WAV Header,该函数最后会调用set_max_size函数:

/* wav input */
static int wav_read_header(AVFormatContext *s)
{
//...WAVDemuxContext *wav = s->priv_data;set_max_size(st, wav);return 0;
//...
}

set_max_size函数定义在源文件libavformat/wavdec.c中。可以看到该函数内部会调用ff_pcm_default_packet_size函数,把“区块对齐” × 一帧音频数据中采样的次数的结果赋值给变量max_size。如果max_size小于0,wav->max_size=4096,否则wav->max_size=“区块对齐” × 一帧音频数据中采样的次数:

static void set_max_size(AVStream *st, WAVDemuxContext *wav)
{if (wav->max_size <= 0) {int max_size = ff_pcm_default_packet_size(st->codecpar);wav->max_size = max_size < 0 ? 4096 : max_size;}
}

(三)AVPacket结构体得到size值

对于WAV音频文件,FFmpeg源码通过源文件libavformat/wavdec.c的wav_read_packet函数读取一个packet:

static int wav_read_packet(AVFormatContext *s, AVPacket *pkt)
{
//...WAVDemuxContext *wav = s->priv_data;
//...left = wav->data_end - avio_tell(s->pb);
//...size = wav->max_size;if (st->codecpar->block_align > 1) {if (size < st->codecpar->block_align)size = st->codecpar->block_align;size = (size / st->codecpar->block_align) * st->codecpar->block_align;}size = FFMIN(size, left);ret  = av_get_packet(s->pb, pkt, size);if (ret < 0)return ret;pkt->stream_index = 0;return ret;
}

由《音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现》中可以知道,wav->data_end为该WAV文件的总大小(单位为字节)。avio_tell(s->pb)为读取到该WAV音频文件的第几个字节了(关于avio_tell函数用法可以参考:《FFmpeg源码:avio_tell函数分析》)。所以wav_read_packet函数中,变量left的值等于该WAV音频文件中还剩下多少个字节没被读取:

left = wav->data_end - avio_tell(s->pb);

让变量size拿到wav->max_size的值,也就是“区块对齐” × 一帧音频数据中采样的次数的结果:

size = wav->max_size;

如果“区块对齐” × 一帧音频数据中采样的次数的结果小于“区块对齐”,size的值等于“区块对齐”;否则size的值等于“区块对齐” × 一帧音频数据中采样的次数的结果:

if (st->codecpar->block_align > 1) {if (size < st->codecpar->block_align)size = st->codecpar->block_align;size = (size / st->codecpar->block_align) * st->codecpar->block_align;
}

让size的值取上述得到的size值和“该WAV音频文件中还剩下多少个字节没被读取”中的最小值,这是因为读取WAV音频文件到最后,剩下还未被读取的数据的字节数是不满一个packet的大小的:

size = FFMIN(size, left);

最后通过av_get_packet函数(关于该函数用法可以参考:《FFmpeg源码:append_packet_chunked、av_get_packet函数分析》),增加该packet大小至size个字节,也就是让pkt->size增至size字节,从而设置AVPacket结构体中的size成员变量:

ret  = av_get_packet(s->pb, pkt, size);

三、总结

1.区块对齐(每个采样点所需的字节数)是从WAV音频文件的WAV Header中解码出来的。

2.nb_samples为一帧音频数据中采样的次数。如果音频码率大于0,计算音频码率(单位为bits/s) ‌÷ 8 ‌÷ 10 ‌÷ “区块对齐”的结果,将该结果裁剪到1到“最大采样”的范围内,然后求该值是2的多少次幂,这个最终计算的得到结果就是nb_samples。

3.WAV音频文件每个packet的size值一般为:区块对齐 × nb_samples。如果读取到WAV音频文件的最后,size值为剩下的还未被读取的不满一个packet大小的字节数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/412059.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLO环境搭建备忘教程

注&#xff1a;该文本是在完成anaconda、pycharm后进行的过程&#xff0c;请注意&#xff01; 1、conda下创建一个新环境&#xff1a; conda create -n 环境名称 python版本号 #注意各类代码的具体Python版本号 conda create -n Pysidey6 python3.8.1 #注意&#xff1a;3…

rk3566刷机openWrt

文章目录 说明硬件工具软件工具简介驱动安装运行刷机工具配置bootloader和刷机固件连接设备 设备未识别执行刷机执行效果访问openwrt变砖问题 说明 本教程由csdn缘友一世原创&#xff0c;经过亲身实践总结&#xff0c;可以保证有效性&#xff01; 硬件工具 Window电脑(windo…

KEYSIGHT是德 Infiniium EXR系列 示波器

Infiniium EXR系列 示波器 苏州新利通 引言 概述 Infiniium EXR系列 出色的信号完整性让信号纤毫毕现 该系列的所有型号都集成了一个 10 位 ADC&#xff0c;并且在所有通道上同时提供 16 GSa/s 的采样率。高分辨率 ADC 的效用取决于示波器的前端底噪是否足够低以提供与之匹…

二叉搜索树进阶之红黑树

前言&#xff1a; 在上文我们已经学习了AVL树的相关知识以及涉及的四种旋转的内容&#xff0c;但是AVL树追求平衡导致旋转操作过多&#xff0c;一些情况下影响性能&#xff0c;由此我们就来了解一下二叉搜索树的另外一个分支&#xff0c;红黑树。 &#xff08;倘若对旋转知识…

学习笔记——后端项目中的相关技术 【随时更新】

文章目录 1. Session 共享1.0 cookie和session的工作流1.1 Cookie范围1.2 为什么要共享&#xff1f;1.3 如何共享存储1.4 session共享实现 2. 缓存的实现2.1 缓存分类2. 2 Redis 缓存实现2.1.1 Spring data redis&#xff08;推荐使用&#xff09;2.1.2 Redis 的数据结构&#…

C++----简单了解vector

大家好&#xff0c;今天我们来讲讲与string相似的向量类型。之所以说他们是相似的原因是他们其中的数据类型有些效果都是一样的。当然大家不能说&#xff0c;既然是差不多的干嘛还有一个这个啊。不如直接用string就可以了。当然世界名言存在即合理。既然我们都能想到的东西&…

金融知识普及月答题活动

金融知识普及月答题活动 关键词&#xff1a;金融安全、风险防范、金融常识、反诈宣传 推荐功能&#xff1a;答题、倡议书 宣传角度&#xff1a; 1. 普及金融知识&#xff1a;讲解货币、信用、利率、汇率等基本金融概念&#xff0c;以及储蓄、贷款、信用卡、保险等常见金融产…

Unity 图表插件Xcharts的一些坑

XY轴、图例文字不清晰。 2种方法解决 1&#xff1a;老套路&#xff0c;先放大再缩小&#xff0c;像素点多了就清晰了。 2&#xff1a;设置一个单独渲染的UI相机&#xff0c;把canvs所在的UI层级使用深度相机单独渲染,另一个选剔除UI的纯色或天空盒。同时&#xff0c;把改Canva…

基于Spring Boot的文字识别系统

前端使用htmlcssjs&#xff0c;后端使用Spring Boot&#xff0c;数据库使用mysql&#xff0c;识别算法有两个&#xff0c;一个是使用百度OCR接口&#xff0c;一个是自己写一个python&#xff0c;用flask包装。 其中百度OCR接口可以去免费申请&#xff0c;然后把appid、apikey、…

Java Web_00001

目录 Web项目介绍网页的组成部分 HTMLHTML简介HTML示例HTML文件的书写规范HTML标签标签介绍标签的语法&#xff1a;常用标签font特殊字符标题标签超链接列表标签img标签表格标签跨行跨列表格iframe框架标签(内嵌窗口)表单标签表单的显示表单格式化表单提交细节 其他标签 CSSCSS…

OpenHarmony轻松玩转GIF数据渲染

OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;提供了Image组件支持GIF动图的播放&#xff0c;但是缺乏扩展能力&#xff0c;不支持播放控制等。今天介绍一款三方库——ohos-gif-drawable三方组件&#xff0c;带大家一起玩转GIF的数据渲染&#xff0c;搞…

CI/CD实践(五)Jenkins Docker 自动化构建部署Node服务

微服务CI/CD实践系列&#xff1a; 微服务CI/CD实践&#xff08;一&#xff09;环境准备及虚拟机创建 微服务CI/CD实践&#xff08;二&#xff09;服务器先决准备 微服务CI/CD实践&#xff08;三&#xff09;gitlab部署及nexus3部署 微服务CI/CD实践&#xff08;四&#xff09…

GraphPad Prism下载安装教程怎样中文汉化

GraphPad Prism下载安装教程怎样中文汉化&#xff1a; GraphPad Prism 是一款集生物统计、曲线拟合和科技绘图于一体的软件&#xff0c;主要用于医学和生物科学领域的数据分析和绘图&#xff0c;具有高效、简便、多功能和高质量的特点&#xff0c;被广泛应用于科研、教育和业界…

湖南的智榜样网络安全公司开的培训学校参加学习成为网络安全工程师

学习网络安全可以通过以下步骤进行&#xff1a; 获取基础知识&#xff1a;开始学习网络安全之前&#xff0c;建议先获取一些计算机基础知识&#xff0c;包括计算机网络、操作系统、编程语言等方面的知识。这些基础知识将为你理解和学习网络安全提供必要的背景。 学习网络安全基…

安卓13去掉权限动态申请,默认授权,不用动态申请权限

总纲 android13 rom 开发总纲说明 1、前言 2、问题分析 3.代码处理 4.代码修改 5.编译 6.彩蛋 1、前言

day44——C++对C的扩充

八、C对函数的扩充 8.1 函数重载&#xff08;overload&#xff09; 1> 概念 函数重载就是能够实现"一名多用"&#xff0c;是实现泛型编程的一种 泛型编程&#xff1a;试图以不变的代码&#xff0c;来实现可变的功能 2> 引入背景 程序员在写函数时&#x…

C++语法基础(二)

C复合类型 结构体 1. C的结构&#xff0c;定义结构体类型的变量时&#xff0c;可以省略struct关键字 2. 可以定义成员函数&#xff0c;在结构体中的成员函数内部可以直接访问本结构体的成员&#xff0c;无需通过“.”或“->” 联合 1. C的联合,定义联合体类型的变…

Linux系统ubuntu20.04 无人机PX4 开发环境搭建(失败率很低)

PX4固件下载 PX4的源码处于GitHub&#xff0c;因为众所周知的原因git clone经常失败&#xff0c;此处从Gitee获取PX4源码和依赖模块。 git clone https://gitee.com/voima/PX4-Autopilot.git 正克隆到 ‘PX4-Autopilot’… remote: Enumerating objects: 454209, done. remot…

Apache CloudStack Official Document 翻译节选(十二)

快速部署一朵 Apache CloudStack 云 &#xff08;一&#xff09; 部署前的准备工作 Apache CloudStack快速部署指南 我们究竟在构建什么&#xff1f; 构建IAAS云是一件很复杂的事项&#xff0c;根据相关定义&#xff0c;构建IAAS云的可选项有很多。这些纷繁复杂的概念通常给…

WLAN原理实验简述——AP上线

一、需求&#xff1a; AP通过AC上线。 AC通过控制VLAN管理AP,创建VLAN100和放行。 AP同AC建立CAPWAP关系。 二、实验拓扑图&#xff1a; 三、实验步骤&#xff1a; LSW1: sys Enter system view, return user view with CtrlZ. [Huawei]Sysname lsw1 [lsw1]undo info enable I…