FFmpeg数据结构AVPacket
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10410320.html
本文基于FFmpeg 4.1版本。
1. 数据结构定义
struct AVPacket定义于<libavcodec/avcodec.h>
struct AVPacket packet;
AVPacket中存储的是经过编码的压缩数据。在解码中,AVPacket由解复用器输出到解码器;在编码中,AVPacket由编码器输出到复用器。下图中,解复用器(demuxer)的输出和复用器(muxer)的输入“encoded data packets”的数据类型就是AVPacket:
_______ ______________
| | | |
| input | demuxer | encoded data | decoder
| file | ---------> | packets | -----+
|_______| |______________| |
v
_________
| |
| decoded |
| frames |
|_________|
________ ______________ |
| | | | |
| output | <-------- | encoded data | <----+
| file | muxer | packets | encoder
|________| |______________|
对于视频而言,一个AVPacket通常只包含一个压缩视频帧。而对于音频而言,一个AVPacket可能包含多个完整的音频压缩帧。AVPacket也可以不包含压缩编码数据,而只包含side data,这种包可以称为空packet。例如,编码结束后只需要更新一些参数时就可以发空packet。
AVPacket对象可以在栈上分配,注意此处指的是AVPacket对象本身。而AVPacket中包含的数据缓冲区是通过av_malloc()在堆上分配的。 TODO: AVPacket对象在栈上分配,原理不清楚,待研究
/**
* This structure stores compressed data. It is typically exported by demuxers
* and then passed as input to decoders, or received as output from encoders and
* then passed to muxers.
*
* For video, it should typically contain one compressed frame. For audio it may
* contain several compressed frames. Encoders are allowed to output empty
* packets, with no compressed data, containing only side data
* (e.g. to update some stream parameters at the end of encoding).
*
* 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.
*
* The semantics of data ownership depends on the buf field.
* If it is set, the packet data is dynamically allocated and is
* valid indefinitely until a call to av_packet_unref() reduces the
* reference count to 0.
*
* If the buf field is not set av_packet_ref() would make a copy instead
* of increasing the reference count.
*
* The side data is always allocated with av_malloc(), copied by
* av_packet_ref() and freed by av_packet_unref().
*
* @see av_packet_ref
* @see av_packet_unref
*/
typedef struct AVPacket {
/**
* A reference to the reference-counted buffer where the packet data is
* stored.
* May be NULL, then the packet data is not reference-counted.
*/
AVBufferRef *buf;
/**
* Presentation timestamp in AVStream->time_base units; the time at which
* the decompressed packet will be presented to the user.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
* pts MUST be larger or equal to dts as presentation cannot happen before
* decompression, unless one wants to view hex dumps. Some formats misuse
* the terms dts and pts/cts to mean something different. Such timestamps
* must be converted to true pts/dts before they are stored in AVPacket.
*/
int64_t pts;
/**
* Decompression timestamp in AVStream->time_base units; the time at which
* the packet is decompressed.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
*/
int64_t dts;
uint8_t *data;
int size;
int stream_index;
/**
* A combination of AV_PKT_FLAG values
*/
int flags;
/**
* Additional packet data that can be provided by the container.
* Packet can contain several types of side information.
*/
AVPacketSideData *side_data;
int side_data_elems;
/**
* Duration of this packet in AVStream->time_base units, 0 if unknown.
* Equals next_pts - this_pts in presentation order.
*/
int64_t duration;
int64_t pos; ///< byte position in stream, -1 if unknown
#if FF_API_CONVERGENCE_DURATION
/**
* @deprecated Same as the duration field, but as int64_t. This was required
* for Matroska subtitles, whose duration values could overflow when the
* duration field was still an int.
*/
attribute_deprecated
int64_t convergence_duration;
#endif
} AVPacket;
音视频数据缓冲区
-
uint8_t *data
: -
int size
: 数据缓冲区地址与大小。音视频编码压缩数据存储于此片内存区域。此内存区域由AVBufferRef *buf
管理。 -
AVBufferRef *buf
: 数据缓冲区引用,也可叫引用计数缓冲区。对上一字段uint8_t *data
指向的内存区域提供引用计数等管理机制。 AVBufferRef对数据缓冲区提供了管理机制,用户不应直接访问数据缓冲区。参考“FFmpeg数据结构AVBuffer” 如果buf
值为NULL,则data
指向的数据缓冲区不使用引用计数机制。av_packet_ref(dst, src)
将执行数据缓冲区的拷贝,而非仅仅增加缓冲区引用计数。 如果buf
值非NULL,则data
指向的数据缓冲区使用引用计数机制。av_packet_ref(dst, src)
将不拷贝缓冲区,而仅增加缓冲区引用计数。av_packet_unref()
将数据缓冲区引用计数减1,当缓冲区引用计数为0时,缓冲区内存被FFmpeg回收。 对于struct AVPacket pkt
对象,如果pkt.buf
值非NULL,则有pkt.data == pkt.buf->data == pkt.buf->buffer.data
额外类型数据
-
AVPacketSideData *side_data
-
int side_data_elems
由容器(container)提供的额外包数据。TODO: 待研究
packet属性
-
int64_t pts
: 显示时间戳。单位time_base,帧率的倒数。 -
int64_t dts
: 解码时间戳。单位time_base,帧率的倒数。 -
int stream_index
: 当前包(packet)所有流(stream)的索引(index)。 -
int flags
: packet标志位。比如是否关键帧等。 -
int64_t duration
: 当前包解码后的帧播放持续的时长。单位timebase。值等于下一帧pts减当前帧pts。 -
int64_t pos
: 当前包在流中的位置,单位字节。
2. 关键函数实现
这里列出的几个关键函数,主要是为了帮助理解struct AVPacket
数据结构
2.1 av_packet_ref()
int av_packet_ref(AVPacket *dst, const AVPacket *src)
{
int ret;
ret = av_packet_copy_props(dst, src);
if (ret < 0)
return ret;
if (!src->buf) {
ret = packet_alloc(&dst->buf, src->size);
if (ret < 0)
goto fail;
if (src->size)
memcpy(dst->buf->data, src->data, src->size);
dst->data = dst->buf->data;
} else {
dst->buf = av_buffer_ref(src->buf);
if (!dst->buf) {
ret = AVERROR(ENOMEM);
goto fail;
}
dst->data = src->data;
}
dst->size = src->size;
return 0;
fail:
av_packet_free_side_data(dst);
return ret;
}
av_packet_ref()作了处理如下:
a) 如果src->buf为NULL,则将src缓冲区拷贝到新创建的dst缓冲区,注意src缓冲区不支持引用计数,但新建的dst缓冲区是支持引用计数的,因为dst->buf不为NULL。
b) 如果src->buf不为空,则dst与src共用缓冲区,调用av_buffer_ref()
增加缓冲区引用计数即可。av_buffer_ref()
分析参考“FFmpeg数据结构AVBuffer”
2.2 av_packet_unref()
void av_packet_unref(AVPacket *pkt)
{
av_packet_free_side_data(pkt);
av_buffer_unref(&pkt->buf);
av_init_packet(pkt);
pkt->data = NULL;
pkt->size = 0;
}
av_packet_unref()
注销AVPacket *pkt
对象,并调用av_buffer_unref(&pkt->buf);
将缓冲区引用计数减1。
av_buffer_unref()
中将缓冲区引用计数减1后,若缓冲区引用计数变成0,则回收缓冲区内存。av_buffer_unref()
分析参考“FFmpeg数据结构AVBuffer”
3. 参考资料
[1] FFmpeg数据结构:AVPacket解析, https://www.cnblogs.com/wangguchangqing/p/5790705.html
4. 修改记录
2018-12-14 V1.0 初稿
- Flash/Flex学习笔记(23):运动学原理
- WordPress重定向作者归档链接到“关于”页面
- Flash/Flex学习笔记(25):摩擦力与屏幕环绕
- 搞事情!富士通和微软强强联手用AI加速工作方式转变
- MySQL管理工具HeidiSQL
- Centos 使用YUM安装MariaDB
- Div Scroll Bar (用层模拟滚动条)
- WordPress免插件仅代码实现文章浏览次数的方法(3)
- 定义新智慧,这AI很“远景X3”
- WordPress删除头部wp_head()多余代码
- 域名fde.com在国内平台以11万元被秒
- 微信小程序助力实体店对接互联网市场
- PostgreSQL 与 MySQL 相比,优势何在?
- Setting Up KeePass For Centos 6
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 猿蜕变9——一文搞定SpringMVC的RESTFul套路
- 猿蜕变10——一文学会ORM的正确打开姿势
- 猿蜕变11——一文掌握mybatis全局玩儿法
- 猿蜕变12——一文搞定mybatis花式玩法
- 猿蜕变13——一文搞懂mybatis规范和标签
- 猿蜕变14——一文搞懂AOP的套路
- 猿蜕变15——一文搞懂Spring AOP的正确姿势
- 猿蜕变16——一文搞懂Spring事务花式玩法
- 猿蜕变17——一文掌握SSM框架搭建姿势
- 猿蜕变18——一文掌控SSM玩耍方式
- 猿蜕变19——一文掌握代码自动生成
- 基于Java的俄罗斯方块游戏的设计与实现
- Docker中部署RabbitMQ并使用Python3.x操作全书(Python操作RabbitMQ看这一篇就够了)
- 【CPP】模板类线性表
- leetcode 剑指 Offer 32 - II. 从上到下打印二叉树 II