当前位置: 首页 > news >正文

FFmpeg源码:packet_alloc、av_new_packet、av_shrink_packet、av_grow_packet函数分析

一、packet_alloc函数

packet_alloc函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavcodec/avpacket.c中:

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;
}

该函数作用是:如果(*buf)为空,分配一个新的内存块给(*buf)指向的AVBufferRef对象,给(*buf)->buffer分配内存。无论(*buf)是否为空,给(*buf)->data重新分配大小为(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节内存。可以看到其内部调用了av_buffer_realloc函数,关于av_buffer_realloc函数的用法可以参考:《FFmpeg源码:av_buffer_is_writable、av_buffer_realloc函数分析》。

形参buf:既是输入型参数也是输出型参数。指针的指针,*buf指向某个AVPacket对象的AVBufferRef成员。

形参size:输入型参数。给(*buf)->data重新分配的大小是(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节。

返回值:返回0表示成功,返回AVERROR(EINVAL)和AVERROR(ENOMEM)表示失败。

这里大家可以注意到,给(*buf)->data重新分配的大小不是size个字节,而是(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节。宏AV_INPUT_BUFFER_PADDING_SIZE定义在libavcodec/defs.h中:

/*** @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中:

/*** 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中:

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中:

/*** 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中:

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函数的声明

/*** 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中:

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// reallocationsif (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;
}

将该函数化简,就是:

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函数一致。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 掌握NPM版本候选锁定:策略、实践与示例
  • 如果你懂开发,我真心劝你来试试网络安全
  • 自由职业四年,我整理了一些建议
  • 【数据结构】堆排序与TOP-K问题
  • Naive UI+vue一些组件的注意事项
  • element plus el-select修改后缀图标
  • 【双向链表】的建立、插入、删除、查找和销毁
  • 量化策略开发步骤系列(3)关键投资组合指标
  • firefly推理和微调qwen
  • Appium基础
  • 背包九讲(动态规划)
  • IO流(完善)
  • 2.4 playwright 实战-爬取某宝商品信息
  • 四款录屏大师,一键搞定!新手也能快速上手?
  • Python数值计算(24)——PCHIP
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • 【译】理解JavaScript:new 关键字
  • 345-反转字符串中的元音字母
  • Gradle 5.0 正式版发布
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Java 内存分配及垃圾回收机制初探
  • Java,console输出实时的转向GUI textbox
  • Javascript Math对象和Date对象常用方法详解
  • Linux Process Manage
  • Linux快速复制或删除大量小文件
  • nfs客户端进程变D,延伸linux的lock
  • Node项目之评分系统(二)- 数据库设计
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 大型网站性能监测、分析与优化常见问题QA
  • 观察者模式实现非直接耦合
  • 前端
  • 深入 Nginx 之配置篇
  • 使用Gradle第一次构建Java程序
  • 我从编程教室毕业
  • 最简单的无缝轮播
  • Java数据解析之JSON
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​MySQL主从复制一致性检测
  • ​如何在iOS手机上查看应用日志
  • #java学习笔记(面向对象)----(未完结)
  • #pragma data_seg 共享数据区(转)
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (1)Hilt的基本概念和使用
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (图文详解)小程序AppID申请以及在Hbuilderx中运行
  • (原)本想说脏话,奈何已放下
  • .NET Core 中插件式开发实现
  • .Net CoreRabbitMQ消息存储可靠机制
  • .Net FrameWork总结
  • .net 调用php,php 调用.net com组件 --
  • .Net中间语言BeforeFieldInit
  • /etc/skel 目录作用
  • /tmp目录下出现system-private文件夹解决方法