本人的描述基本是白话文, 我没有时间和能力去画图,真正的理解需要调试附带的代码. 代码是我整理的.
ffmpeg 中引入了很多概念,今天,
介绍一个重要的概念AVPacket
c 的时代还没有引入类class的概念, 放在一起的数据叫结构struct, 用结构声明一个变量结构变量.
文件中的各个函数,操作的就是这个结构变量.
这里有几个问题需要关注.
1. 为什么要引入这样一个结构?
答. 当然是为了方便使用. 否则写这么多代码干什么.
2. 关注变量的生成和释放, 相当于类的构造和析构.
当你声明了一个包, AVPacket packet;
你也可以从堆中声明一个包, AVPacket *pkt = av_packet_alloc();
堆中声明的,需要释放, 函数 av_packet_free(pkt);
包,当然是用来装东西的. 可以理解为包头加包体构成一个整体包
嗯,现在只是声明或创造了一个包头, 真正装数据的地方是包体.
继续执行函数调用 av_new_packet((pkt,nLen);
创建了包体,可以装nLen 长度的数据. 你可以用memcpy 向pkt->data 中copy 数据.
为啥要搞这么复杂,申请一块内存,copy数据,只要malloc 就可以了,为什么要用packet?
那是因为packet在处理av数据时有很多特殊需要, 例如,包的copy, 经常是只copy包头,不copy包体,
这样,包的copy 就非常有效率. 这就是包这个概念存在的价值了.
那包怎么copy呢? 千万不要这样写 packet = *pkt, 这样只是copy了包头,使得packet的包头与pkt
所指的包头完全一样, 好多问题都是这么引入的.
为什么呢? 究其原因还是c语言没有赋值构造函数, 只是简单的copy了数据和指针, 指针所指向的数据它就管不了了.
你应该调用函数av_packet_ref(&packet,pkt)
这样,packet 就copy 了pkt, 其实它真正的实现是把pkt所指向的数据引用计数增加了一次.
当不用这个packet了,你需要释放av_packet_unref(&packet), 此时包头就会恢复成默认值.
但是,包体却未必会释放, 只有当包体的引用计数减为0时,包体会被释放.
包体对应着一个结构,叫AVBuffer, 可以理解为malloc 的一个实现. 这个结构并没有对用户开放, 不过开源的代码
我们还是能看到它的实现.里面包含了一个refcount
包头中包含一个AVBufferRef 结构, 主要是一个AVBuffer 的指针.
如果比较两个packet, AVBufferRef 地址不同, data指针相同, 那这两个包都指向了同一个AVBuffer.
其实, 包的使用还是很简单的,主要是
av_packet_ref
av_packet_unref
同时还要掌握av_new_packet, av_packet_alloc,av_packet_free
认真掌握下面的程序:
#include <stdio.h>
#include <libavcodec/packet.h>
#include <libavcodec/avcodec.h>
char *string="hello";
// packet 的内存分配问题
int main()
{
int nLen = strlen(string);
AVPacket packet; // 一个局部变量
AVPacket *pkt= &packet;
av_new_packet(pkt,nLen); // new_packet 会分配packet 的存储数据,并由AVBufferRef 指向AVBuffer, AVBuffer是实际装数据的地方
memcpy(pkt->data,string,nLen);
AVPacket *pkt2 = av_packet_alloc();
// av_new_packet(pkt2,nLen);
// memcpy(pkt2->data,string,nLen);
av_packet_ref(pkt2,pkt);
av_packet_unref(pkt2);
av_packet_unref(pkt2);
av_packet_unref(pkt);
av_packet_free(&pkt2); //pkt2 是av_packet_alloc 分配的,所以需要free
return 0;
}
想进一步理解后面到底发生了什么,可以把玩下面这个程序, 一株小草,下面带了一大块泥巴才活的健康.
#include <stdio.h>
#include <libavcodec/packet.h>
#include <libavcodec/avcodec.h>
struct AVBuffer {
uint8_t *data; /**< data described by this buffer */
int size; /**< size of data in bytes */
/**
* number of existing AVBufferRef instances referring to this buffer
*/
int refcount;
/**
* a callback for freeing the data
*/
void (*free)(void *opaque, uint8_t *data);
/**
* an opaque pointer, to be used by the freeing callback
*/
void *opaque;
/**
* A combination of AV_BUFFER_FLAG_*
*/
int flags;
/**
* A combination of BUFFER_FLAG_*
*/
int flags_internal;
};
static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
{
AVBuffer *b;
b = (*dst)->buffer;
if (src) { // 源存在
**dst = **src; //源向目标copy
av_freep(src); //源被释放
} else
av_freep(dst); // 源为空,目标释放
// if (atomic_fetch_sub_explicit(&b->refcount, 1, memory_order_acq_rel) == 1)
b->refcount --;
if (b->refcount == 0) { //buf 参考为0时,
b->free(b->opaque, b->data); //buf 内容被释放
av_freep(&b); //buf 被释放
}
}
void av_buffer_default_free(void *opaque, uint8_t *data)
{
(void) opaque;
av_free(data);
}
AVBufferRef *av_buffer_create(uint8_t *data, int size,
void (*free)(void *opaque, uint8_t *data),
void *opaque, int flags)
{
AVBufferRef *ref = NULL;
AVBuffer *buf = NULL;
buf = av_mallocz(sizeof(*buf));
if (!buf)
return NULL;
buf->data = data;
buf->size = size;
buf->free = free ? free : av_buffer_default_free;
buf->opaque = opaque;
buf->refcount = 1;
buf->flags = flags;
ref = av_mallocz(sizeof(*ref));
if (!ref) {
av_freep(&buf);
return NULL;
}
ref->buffer = buf;
ref->data = data;
ref->size = size;
return ref;
}
int av_buffer_realloc(AVBufferRef **pbuf, int size)
{
AVBufferRef *buf = *pbuf;
uint8_t *tmp;
int ret;
if (!buf)
{
/* allocate a new buffer with av_realloc(), so it will be reallocatable
* later */
uint8_t *data = av_realloc(NULL, size);
if (!data)
return AVERROR(ENOMEM);
buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
if (!buf) {
av_freep(&data);
return AVERROR(ENOMEM);
}
buf->buffer->flags_internal |= 1; //BUFFER_FLAG_REALLOCATABLE;
*pbuf = buf;
return 0;
}
else if (buf->size == size) return 0;
if (!(buf->buffer->flags_internal & 1/*BUFFER_FLAG_REALLOCATABLE*/) ||
!av_buffer_is_writable(buf) || buf->data != buf->buffer->data)
{
/* cannot realloc, allocate a new reallocable buffer and copy data */
AVBufferRef *new = NULL;
ret = av_buffer_realloc(&new, size);
if (ret < 0)
return ret;
memcpy(new->data, buf->data, FFMIN(size, buf->size));
buffer_replace(pbuf, &new);
return 0;
}
tmp = av_realloc(buf->buffer->data, size);
if (!tmp)
return AVERROR(ENOMEM);
buf->buffer->data = buf->data = tmp;
buf->buffer->size = buf->size = size;
return 0;
}
static void get_packet_defaults(AVPacket *pkt)
{
memset(pkt, 0, sizeof(*pkt));
pkt->pts = AV_NOPTS_VALUE;
pkt->dts = AV_NOPTS_VALUE;
pkt->pos = -1;
}
static int packet_alloc(AVBufferRef **buf, int size)
{
int ret;
if (size < 0 || size >= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
return AVERROR(EINVAL);
ret = av_buffer_realloc(buf, size + AV_INPUT_BUFFER_PADDING_SIZE);
if (ret < 0)
return ret;
memset((*buf)->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
return 0;
}
int av_new_packet(AVPacket *pkt, int size)
{
AVBufferRef *buf = NULL;
int ret = packet_alloc(&buf, size);
if (ret < 0)
return ret;
get_packet_defaults(pkt);
pkt->buf = buf;
pkt->data = buf->data;
pkt->size = size;
return 0;
}
void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC
_aligned_free(ptr);
#else
free(ptr);
#endif
}
void av_freep(void *arg)
{
void *val;
memcpy(&val, arg, sizeof(val));
memcpy(arg, &(void *){ NULL }, sizeof(val));
av_free(val);
}
void av_packet_free_side_data(AVPacket *pkt)
{
int i;
for (i = 0; i < pkt->side_data_elems; i++)
av_freep(&pkt->side_data[i].data);
av_freep(&pkt->side_data);
pkt->side_data_elems = 0;
}
void av_buffer_unref(AVBufferRef **buf)
{
if (!buf || !*buf)
return;
buffer_replace(buf, NULL);
}
void av_packet_unref(AVPacket *pkt)
{
av_packet_free_side_data(pkt);
av_buffer_unref(&pkt->buf);
get_packet_defaults(pkt);
}
void av_packet_free(AVPacket **pkt)
{
if (!pkt || !*pkt)
return;
av_packet_unref(*pkt);
av_freep(pkt);
}
char *string="hello";
// packet 的内存分配问题
int main()
{
int nLen = strlen(string);
AVPacket packet; // 一个局部变量
AVPacket *pkt= &packet;
av_new_packet(pkt,nLen); // new_packet 会分配packet 的存储数据,并由AVBufferRef 指向AVBuffer, AVBuffer是实际装数据的地方
memcpy(pkt->data,string,nLen);
AVPacket *pkt2 = av_packet_alloc();
// av_new_packet(pkt2,nLen);
// memcpy(pkt2->data,string,nLen);
av_packet_ref(pkt2,pkt);
av_packet_unref(pkt2);
av_packet_unref(pkt);
av_packet_free(&pkt2); //pkt2 是av_packet_alloc 分配的,所以需要free
return 0;
}