一、packet_alloc函数
packet_alloc函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavcodec/avpacket.c中:
cpp
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;
}
该函数作用是:给一个AVPacket的AVBufferRef类型成员(*pbuf指向的结构体)和(*pbuf)->buffer分配内存,给(*pbuf)->data重新分配大小为(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节内存。
返回值:返回0表示成功,返回AVERROR(EINVAL)和AVERROR(ENOMEM)表示失败。
这里大家可以注意到,给(*pbuf)->data重新分配的大小不是size个字节,而是(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节。宏AV_INPUT_BUFFER_PADDING_SIZE定义在libavcodec/defs.h中:
cpp
/**
* @ingroup lavc_decoding
* Required number of additionally allocated bytes at the end of the input bitstream for decoding.
* This is mainly needed because some optimized bitstream readers read
* 32 or 64 bit at once and could read over the end.<br>
* Note: If the first 23 bits of the additional bytes are not 0, then damaged
* MPEG bitstreams could cause overread and segfault.
*/
#define AV_INPUT_BUFFER_PADDING_SIZE 64
这个宏AV_INPUT_BUFFER_PADDING_SIZE值是64,是在输入比特流末端为解码而额外分配的字节数。这主要是因为一些优化的比特流读取器一次读取32或64位,并且可以超过末尾读取。所以是重新分配(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节,比size个字节要大,防止某些优化过的读取器一次性读取过多导致越界。
二、av_new_packet函数
(一)av_new_packet函数的声明
av_new_packet函数声明在头文件libavcodec/packet.h中:
cpp
/**
* Allocate the payload of a packet and initialize its fields with
* default values.
*
* @param pkt packet
* @param size wanted payload size
* @return 0 if OK, AVERROR_xxx otherwise
*/
int av_new_packet(AVPacket *pkt, int size);
该函数作用是:给pkt->buf和pkt->buf->buffer分配内存,给pkt->buf->data重新分配大小为(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节内存。然后对pkt的其它成员变量进行初始化。注意:该函数不会给AVPacket本身分配内存,所以执行该函数前必须先给形参pkt指向的AVPacket类型变量分配内存,否则可能会导致程序崩溃。
返回值:返回0表示成功,返回AVERROR(EINVAL)和AVERROR(ENOMEM)表示失败。
(二)av_new_packet函数的定义
av_new_packet函数定义在源文件libavcodec/avpacket.c中:
cpp
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;
}
可以看到其内部调用了packet_alloc函数分配内存和get_packet_defaults函数进行初始化。
三、av_shrink_packet函数
(一)av_shrink_packet函数的声明
av_shrink_packet函数声明在头文件libavcodec/packet.h中:
cpp
/**
* Reduce packet size, correctly zeroing padding
*
* @param pkt packet
* @param size new size
*/
void av_shrink_packet(AVPacket *pkt, int size);
该函数作用是:减少数据包(pkt->data指向的缓冲区)的大小,让该大小减至size字节。让地址为(pkt->data + size)后的数据字节归零。执行该函数后,pkt->size会减至size字节。
(二)av_shrink_packet函数的定义
av_shrink_packet函数定义在源文件libavcodec/avpacket.c中:
cpp
void av_shrink_packet(AVPacket *pkt, int size)
{
if (pkt->size <= size)
return;
pkt->size = size;
memset(pkt->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
四、av_grow_packet函数
(一)av_grow_packet函数的声明
cpp
/**
* Increase packet size, correctly zeroing padding
*
* @param pkt packet
* @param grow_by number of bytes by which to increase the size of the packet
*/
int av_grow_packet(AVPacket *pkt, int grow_by);
该函数作用是:增加数据包(pkt->data指向的缓冲区)的大小,让该大小增至(pkt->size + grow_by)字节。让地址为(pkt->data + pkt->size + grow_by)后的数据字节归零。执行该函数后,pkt->size会增至(pkt->size + grow_by)字节。
(二)av_grow_packet函数的定义
av_grow_packet函数定义在源文件libavcodec/avpacket.c中:
cpp
int av_grow_packet(AVPacket *pkt, int grow_by)
{
int new_size;
av_assert0((unsigned)pkt->size <= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE);
if ((unsigned)grow_by >
INT_MAX - (pkt->size + AV_INPUT_BUFFER_PADDING_SIZE))
return AVERROR(ENOMEM);
new_size = pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE;
if (pkt->buf) {
size_t data_offset;
uint8_t *old_data = pkt->data;
if (pkt->data == NULL) {
data_offset = 0;
pkt->data = pkt->buf->data;
} else {
data_offset = pkt->data - pkt->buf->data;
if (data_offset > INT_MAX - new_size)
return AVERROR(ENOMEM);
}
if (new_size + data_offset > pkt->buf->size ||
!av_buffer_is_writable(pkt->buf)) {
int ret;
// allocate slightly more than requested to avoid excessive
// reallocations
if (new_size + data_offset < INT_MAX - new_size/16)
new_size += new_size/16;
ret = av_buffer_realloc(&pkt->buf, new_size + data_offset);
if (ret < 0) {
pkt->data = old_data;
return ret;
}
pkt->data = pkt->buf->data + data_offset;
}
} else {
pkt->buf = av_buffer_alloc(new_size);
if (!pkt->buf)
return AVERROR(ENOMEM);
if (pkt->size > 0)
memcpy(pkt->buf->data, pkt->data, pkt->size);
pkt->data = pkt->buf->data;
}
pkt->size += grow_by;
memset(pkt->data + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
return 0;
}
将该函数化简,就是:
cpp
int av_grow_packet(AVPacket *pkt, int grow_by)
{//...
new_size = pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE;
if (pkt->buf) {
{
//...
ret = av_buffer_realloc(&pkt->buf, new_size + data_offset);
//...
}
else
{
//...
pkt->buf = av_buffer_alloc(new_size);
//...
}
pkt->size += grow_by;
memset(pkt->data + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
av_grow_packet函数内部会判断pkt->buf是否为空。如果不为空,通过av_buffer_realloc函数给pkt->buf->data重新分配大小为(pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE+data_offset)个字节内存。如果为空,通过av_buffer_alloc函数给pkt->buf、pkt->buf->buf和pkt->buf->data分配大小为(pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE)个字节的内存。然后让地址为(pkt->data + pkt->size + grow_by)后的数据字节归零。
关于av_buffer_realloc函数和av_buffer_alloc函数的用法可以参考:《FFmpeg源码:av_buffer_is_writable、av_buffer_realloc函数分析》、《FFmpeg源码:buffer_create、av_buffer_create、av_buffer_default_free、av_buffer_alloc、av_buffer_allocz函数分析》
这里重新分配的大小得加上AV_INPUT_BUFFER_PADDING_SIZE个字节,理由跟上面的packet_alloc函数一致。