【FFmpeg实战】压缩数据AVPacket解析

news2024/11/8 2:50:46

因为FFmpeg更新的比较快,API也会跟着有所变动,所以声明一下,本文使用的FFmpeg版本为V3.3.5。

1.AVPacket简介

AVPacket是FFmpeg中很重要的一个数据结构,它保存了解复用(demuxer)之后,解码(decode)之前的数据(仍然是压缩后的数据)和关于这些数据的一些附加的信息,如显示时间戳(pts),解码时间戳(dts),数据时长(duration),所在流媒体的索引(stream_index)等等。

对于视频(Video)来说,AVPacket通常包含一个压缩的Frame;而音频(Audio)则有可能包含多个压缩的Frame。并且,一个packet也有可能是空的,不包含任何压缩数据data,只含有边缘数据side data(side data,容器提供的关于packet的一些附加信息,例如,在编码结束的时候更新一些流的参数,在另外一篇av_read_frame会介绍)

AVPacket的大小是公共的ABI(Public ABI)一部分,这样的结构体在FFmpeg很少,由此也可见AVPacket的重要性,它可以被分配在栈空间上(可以使用语句AVPacket pkt;在栈空间定义一个Packet),并且除非libavcodec 和libavformat有很大的改动,不然不会在AVPacket中添加新的字段。

官方文档:

AVPacket is one of the few structs in FFmpeg,whose size is a part of public ABI.Thus it may be allocated on stack and no new fields can be added to it without libavcodec and libavformat major bump.

2.AVPacket字段说明

img

AVPacket中的字段可分为两部分:数据的缓存及管理和数据的属性。

关于数据的属性有以下字段:

pts: (int64_t)显示时间,结合AVStream->time_base转换成时间戳 dts: (int64_t)解码时间,结合AVStream->time_base转换成时间戳 size: (int)data的大小 stream_index: (int)packet在stream的index位置 flags: (int)标示,结合AV_PKT_FLAG使用,其中最低为1表示该数据是一个关键帧。

#define AV_PKT_FLAG_KEY 0x0001 //关键帧 #define AV_PKT_FLAG_CORRUPT 0x0002 //损坏的数据 #define AV_PKT_FLAG_DISCARD 0x0004 /丢弃的数据

side_data_elems: (int)边缘数据元数个数 duration: (int64_t)数据的时长,以所属媒体流的时间基准为单位,未知则值为默认值0 pos: (int64_t )数据在流媒体中的位置,未知则值为默认值-1 **convergence_duration:**该字段已deprecated,不在使用

关于数据缓存,AVPacket本身只是个容器,不直接的包含数据,而是通过数据缓存的指针引用数据。AVPacket包含两种数据

**uint8_t *data:**指向保存压缩数据的指针,这就是AVPacket的实际数据。 **AVPacketSideData *side_data:**容器提供的一些附加数据 **AVBufferRef *buf:**用来管理data指针引用的数据缓存,其使用在后面介绍。

3.AVPacket中的内存管理

AVPacket实际上可看作一个容器,它本身并不包含压缩的流媒体数据,而是通过data指针引用数据的缓存空间。所以将Packet作为参数传递的时候,就要根据具体的需求,对data引用的这部分数据缓存空间进行特殊的处理。当从一个Packet去创建另一个Packet的时候,有两种情况:

1)两个Packet的data引用的是同一数据缓存空间,这个时候要注意数据缓存空间的释放问题和修改问题(相当于iOS的retain)

2)两个Packet的data引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy

第二种情况,数据空间的管理比较简单,但是数据实际上有多个copy造成内存空间的浪费。所以要根据具体的需求,来选择到底是两个Packet共享一个数据缓存空间,还是每个Packet拥有自己独立的缓存空间。值得注意的是:对于多个Packet共享同一个缓存空间,FFMPEG使用的引用计数的机制(reference-count)。当有新的Packet引用共享的缓存空间时,就将引用计数+1;当释放了引用共享空间的Packet,就将引用计数-1;引用计数为0时,就释放掉引用的缓存。

1) 共享同一个数据缓存 -----> av_packet_ref() 和 av_packet_unref() 引用计数管理 AVPacket中的AVBufferRef *buf;就是用来管理这个引用计数的,AVBufferRef有两个函数:av_packet_ref() 和av_packet_unref()增加和减少引用计数的,AVBufferRef的声明如下:

img

a) av_packet_ref()

img

创建一个src->data引用计数。

如果src已经设置了引用计数(src->buffer不为空);则直接将其引用计数+1;

若src没有设置引用计数(src->buffer为空),则dst创建一个新的引用计数buf,并复制src->data到buf->buffer中。

最后,复制src的其他字段到dst中。

b)av_packet_unref

img

将缓存空间的引用计数-1,并将Packet中的其他字段设为初始值。

如果引用计数为0,自动的释放缓存空间。

所以,两个Packet共享同一个数据缓存空间的时候可以这么用:

av_read_frame(pFormatCtx, &packet) // 读取Packet av_packet_ref(&dst,&packet) // dst packet共享同一个数据缓存空间… av_packet_unref(&dst);

2)创建独立的数据空间

传递Packet的时候,一般都是复制copy一个独立的数据缓存空间,每个Packet都拥有自己独立的数据缓存空间,放在AVPacket相关函数介绍。

3.AVPacket相关函数介绍

操作AVPacket的函数大约有30个,主要分为:AVPacket的创建初始化,AVPacket中的data数据管理(clone,free,copy),AVPacket中的side_data数据管理。

void av_init_packet(AVPacket *pkt);

初始化packet的值为默认值,该函数不会影响data引用的数据缓存空间和size,需要单独处理。

int av_new_packet(AVPacket *pkt, int size);

av_init_packet的增强版,不但会初始化字段,还为data分配了存储空间

AVPacket *av_packet_alloc(void);

创建一个AVPacket,将其字段设为默认值(data为空,没有数据缓存空间)。

*void av_packet_free(AVPacket *pkt);

释放使用av_packet_alloc创建的AVPacket,如果该Packet有引用计数(packet->buf不为空),则先调用av_packet_unref。

AVPacket *av_packet_clone(const AVPacket *src);

其功能是av_packet_alloc和av_packet_ref

int av_copy_packet(AVPacket *dst, const AVPacket *src);

复制一个新的packet,包括数据缓存

int av_copy_packet_side_data(AVPacket *dst, const AVPacket *src);

初始化一个引用计数的packet,并指定了其数据缓存

int av_grow_packet(AVPacket *pkt, int grow_by);

增大Packet->data指向的数据缓存

void av_shrink_packet(AVPacket *pkt, int size);

减小Packet->data指向的数据缓存

3.1 废弃函数介绍 ------> av_dup_packet和av_free_packet

int av_dup_packet(AVPacket *pkt);

复制src->data引用的数据缓存,赋值给dst。也就是创建两个独立packet,这个功能现在可用使用函数av_packet_ref来代替

void av_free_packet(AVPacket *pkt);

释放packet,包括其data引用的数据缓存,现在可以使用av_packet_unref代替

3.2 函数对比 --------->av_free_packet和av_packet_free

void av_free_packet(AVPacket *pkt);

只是清空里边的数据内容,内存地址仍然在。我的版本是3.3已经废弃,所以用av_packet_unref替代。

如果不清空会发生什么情况呢,举个简单的例子,一个char数组大小为128,里面有100个自己的内容。第二次使用你没有清空第一次的内容,第二次的数据大小为60,那么第一次的最后40个字节的数据仍会保留,造成数据冗余,极大可能对你的处理造成影响(这个跟自己的处理有关系,并不一定)。

*void av_packet_free(AVPacket *pkt);

类似于free§; p = Null;不仅清空内容还清空内存(一般就是如果用了av_packet_alloc后就要调用av_packet_free来释放。但如果有引用计数,在调用av_packet_free前一般先调用av_packet_unref)

上源码:

img

4.AVPacket队列

主要是涉及到多次的AVPacket的传递问题,下面就播放音频的教程中的AVPacket队列实现,分析下在AVPacket作为参数传递的过程中,应该如何更好的管理其data引用的缓存空间。

从流中读取AVPacket插入队列

img

如果是音频流则将读到Packet调用packet_queue_put插入到队列,如果不是音频流则调用av_packet_unref释放已读取到的AVPacket数据。

下面代码是packet_queue_put中将Packet放入到一个新建的队列节点的代码片段

img

注意,在调用packet_queue_put时传递的是指针,也就是形参pkt和实参packet中的data引用的是同一个数据缓存。但是在循环调用av_read_frame的时候,会将packet中的data释放掉,以便于读取下一个帧数据。

所以就需要对data引用的数据缓存进行处理,保证在读取下一个帧数据的时候,其data引用的数据空间没有被释放。有两种方法,复制一份data引用的数据缓存或者给data引用的缓存空间加一个引用计数。

注释掉的部分是使用已废弃的APIav_dup_packet,该函数将pkt中data引用的数据缓存复制一份给队列节点中的AVPacket。

添加引用计数的方法则是调用av_apcket_ref将data引用的数据缓存的引用计数+1,这样其就不会被释放掉。

更好的管理其data引用的缓存空间。

从队列中取出AVPacket

img

注释掉的代码仍然是两个packet引用了同一个缓存空间,这样在一个使用完成释放掉缓存的时候,会造成另一个访问错误。所以扔给调用av_packet_ref将其引用计数+1,这样在释放其中一个packet的时候其引用的数据缓存就不会被释放掉,直到两个packet都被释放。

作者:片片碎
链接:https://www.jianshu.com/p/bb6d3905907e

>>> 音视频开发 视频教程: https://ke.qq.com/course/3202131?flowToken=1031864 
>>> 音视频开发学习资料、教学视频,免费分享有需要的可以自行添加学习交流群: 739729163  领取

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

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

相关文章

TS格式视频的使用

什么是TS文件格式 TS(Transport Stream,传输流)是一种封装的格式,它的全称为MPEG2-TS。MPEG2-TS是一种标准数据容器格式,传输与存储音视频、节目与系统信息协议数据,主要应用于数字广播系统,例…

在加密数据时,如何选择合适的算法?

我们将分解两种主要类型的加密 - 对称和非对称 - 然后深入研究 5 种最常用的加密算法列表,以前所未有的方式简化它们 加密经常被指责为政治实体隐藏恐怖活动,是始终成为头条新闻的网络安全话题之一。任何对不同类型的加密有充分了解的人都可能会觉得对这…

内网穿透工具cpolar——将内网站点发布至公网、远程连接公司内网电脑

在现代互联的时代,我们经常需要将局域网中的服务器或提供给外部网络进行访问。然而,由于内网通常使用私有IP地址,在没有合适的配置和设置的情况下,对外部网络是不可见的。这就引出了内网穿透技术,它允许我们在不暴露真…

从三个角度分析B端产品帮助中心的发展趋势和创新思路

随着企业级(B端)产品在市场上的快速增长,优秀的帮助中心对于提升用户体验和满意度变得尤为重要。本文将从三个维度讨论B端产品帮助中心的发展趋势和创新思路,以帮助企业构建更优秀的帮助体系。 个性化化的帮助体验 在B端产品帮助…

基于瑞芯微camera学习原创文章汇总pdf分享给大家

前面一段时间基于瑞芯微3568平台编写了camera系列文章, 一共12篇,目前更新完毕。 《瑞芯微文章合集》 camera系列文章编写前后一共花费2个月时间, 期间查阅了很多资料,做了很多实验, 根据自己的理解, …

实训笔记6.25

实训笔记6.25 6.25一、座右铭二、知识回顾2.1 JavaSE2.1.1 基本语法2.1.2 数组2.1.3 JVM内存2.1.4 面向对象2.1.5 Java常用类2.1.6 Java异常机制2.1.7 Java泛型2.1.8 Java集合2.1.9 JavaIO流2.1.10 Java注解2.1.11 Java反射机制2.1.12 Java多线程2.1.13 Java网络编程 三、Java多…

利用SpringBoot和javafx进行桌面开发

原文合集地址如下,有需要的朋友可以关注 本文地址 合集地址 众所周知,SpringBoot是一款强大的Javaweb开发程序,这得益于其构造了一个Spring容器,然后通过依赖注入和控制反转,维护起一套Java对象和实例的管理机制&…

Chatgpt三大新功能曝光:记住你是谁/文件上传/切换工作区

今日,一张神秘的截图,曝光了chatgpt还没发布的三个新功能: 除截图外,其对应的源代码也被扒了出来,甚至还有人为此建立了GitHub仓库,以此来追踪代码的变化情况。 根据观察,相关部分代码正在更新…

制作看大片一样的推拉镜头效果,刚一个月AI绘图神器 Midjourney 又又更新了

5.1 版本刚更新一个月,官方又宣布 5.2 版本更新了:改进的美观性、连贯性、更好的文本理解、更清晰的图像、更高的变化模式、缩小绘画,以及用于分析提示符号的新命令 /short 命令‍‍‍‍‍‍‍‍‍ 视觉效果最神奇的功能要数 “Zoom Out” 缩…

使用Rabbitmq死信队列解锁库存

一、库存解锁的场景 RabbitMQ库存解锁的场景有很多,以下是一些常见的场景: 订单取消和订单回滚。下订单成功,订单过期没有支付被系统自动取消、被用户手动取消。都要解锁库存。 下订单成功,库存锁定成功,接下来的业务…

《面试1v1》Redis持久化

🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结&#xf…

css基础知识七:元素水平垂直居中的方法有哪些?如果元素不定宽高呢?

一、背景 在开发中经常遇到这个问题,即让某个元素的内容在水平和垂直方向上都居中,内容不仅限于文字,可能是图片或其他元素 居中是一个非常基础但又是非常重要的应用场景,实现居中的方法存在很多,可以将这些方法分成…

3.用python写网络爬虫,下载缓存

目录 3.1 为链接爬虫添加缓存支持 3.2 磁盘缓存 3.2.1 实现 3.2.2缓存测试 3.2.3节省磁盘空间 3.2.4 清理过期数据 3.2.5缺点 3.3 数据库缓存 3.3.1 NoSQL 是什么 3.3.2 安装 MangoDB 3.3.3 MongoDB 概述 3.3.4 MongoDB 缓存实现 3.3.5 压缩 3.3.6 缓存测试 3.4 本章…

变分自编码(VAE,Variational Auto-Encoder)知识点速览

目录 1. 主要思想 2. 训练和推理过程 3. 编码器和解码器的结构 4. 主要用途 5. 相较于 auto-encoder 的优劣 1. 主要思想 变分自编码器(Variational AutoEncoder,简称VAE)是一种生成模型,它通过对数据的隐含表示(l…

红日靶场(一)外网到内网速通

红日靶场(一) 下载地址:http://vulnstack.qiyuanxuetang.net/vuln/detail/2/ win7:双网卡机器 win2003:域内机器 win2008域控 web阶段 访问目标机器 先进行一波信息收集,扫一下端口和目录 扫到phpmyadmin,还有一堆…

韶音和cleer、南卡对比哪个好?韶音、南卡、Cleer开放式耳机横评

现如今,不管是通勤路上还是在家休闲娱乐,又或者是运动时,经常能看见很多人佩戴着耳机听音乐,但是,经常佩戴耳机听音乐的小伙伴都知道,入耳式耳机佩戴久了,容易造成耳部酸痛感、胀痛感&#xff0…

Android 安卓开发语言kotlin与Java该如何选择

一、介绍 如今在Android开发中,应用层开发语言主要是Java和Kotlin,Kotlin是后来加入的,主导的语言还是Java。kotlin的加入仿佛让会kotlin语言的开发者更屌一些,其实不然。 有人说kotlin的引入是解决开发者复杂的逻辑,并…

【VSCode】设置关键字高亮的插件 | Highlight Word

目录 一、概述二、安装 highlight-words 插件三、配置 highlight-words 插件3.1 默认配置3.2 修改 settings.json 配置文件 四、设置高亮快捷键F8五、效果演示 一、概述 本文主要介绍在 VSCode 看代码时,怎样使某个单词高亮显示,主要通过以下三步实现&am…

Docker的run流程

底层原理 Docker怎么工作? Docker为什么比VM虚拟机块? 1.Docker有比虚拟机更少的抽象层 2.docker利用的是宿主机的内核,vm需要是Guest OS 所以说,新建一个容器的时候,docker不需要像虚拟机一样加载一个系统内核&am…

[conda]tf_agents和tensorflow-gpu安装傻瓜式教程

1.打开终端或Anaconda Prompt&#xff08;Windows用户&#xff09;。 2.输入以下命令创建新的Python环境&#xff1a; conda create --name <env_name> python<version>其中&#xff0c;<env_name>是您想要创建的环境名称&#xff0c;<version>是您想…