【FFmpeg】理解 av_packet_from_data 和 av_packet_unref 接口

问题:FFmpeg 8.0 使用下面这段代码有问题吗?里面说 外部缓冲 pStream 不会被 av_packet_unref 释放,所以不会影响原始外部缓冲 pStream ,对不对?

cpp 复制代码
BOOL32 SaveMP4(u8* pStream, u32 dwSize, u32 dwMediaType )
{

	AVFormatContext *fmt_ctx = NULL;
	AVStream *stream = NULL;
	AVPacket *pkt = NULL;
	
	// 分配 packet
	pkt = av_packet_alloc();
	
	// 将外部数据缓冲 pStream 绑定到 packet(零拷贝)
	ret = av_packet_from_data(pkt, pStream, (int)dwSize);
	
	//TODO.....
	
	// 清理 packet(为下一次调用准备)
	av_packet_unref(pkt);// 解引用 packet(不会释放 pStream,因来自 av_packet_from_data)
	        // 注意:pStream已被 pkt 拥有,av_packet_unref 不会 free 它
	        // 所以这里不需要 free(frame_data) ------ 由 av_packet_free 内部处理
	        // 但 av_packet_from_data 后,内存由 packet 管理,无需手动 free

//TODO.....
}

不对,这个理解是错误的!

🔍 源码佐证(FFmpeg 8.0 libavcodec/packet.c)

av_packet_from_data 的实现:

c 复制代码
/**
 * Initialize a reference-counted packet from av_malloc()ed data.
 *
 * @param pkt packet to be initialized. This function will set the data, size,
 *        and buf fields, all others are left untouched.
 * @param data Data allocated by av_malloc() to be used as packet data. If this
 *        function returns successfully, the data is owned by the underlying AVBuffer.
 *        The caller may not access the data through other means.
 * @param size size of data in bytes, without the padding. I.e. the full buffer
 *        size is assumed to be size + AV_INPUT_BUFFER_PADDING_SIZE.
 *
 * @return 0 on success, a negative AVERROR on error
 */

int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size)
{
    if (size >= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
        return AVERROR(EINVAL);

    pkt->buf = av_buffer_create(data, size + AV_INPUT_BUFFER_PADDING_SIZE,
                                av_buffer_default_free, NULL, 0);
    if (!pkt->buf)
        return AVERROR(ENOMEM);

    pkt->data = data;
    pkt->size = size;

    return 0;
}

av_packet_unref 的实现:

c 复制代码
/**
 * Wipe the packet.
 *
 * Unreference the buffer referenced by the packet and reset the
 * remaining packet fields to their default values.
 *
 * @param pkt The packet to be unreferenced.
 */

void av_packet_unref(AVPacket *pkt)
{
    av_packet_free_side_data(pkt);
    av_buffer_unref(&pkt->opaque_ref);
    av_buffer_unref(&pkt->buf);
    get_packet_defaults(pkt);
}

根据贴出的 FFmpeg 8.0 的真实源码,恰恰说明:

✅ av_packet_unref(pkt) 会释放 pStream!


🔍 源码分析(FFmpeg 8.0)

1. av_packet_from_data 做了什么?
c 复制代码
pkt->buf = av_buffer_create(data, size + AV_INPUT_BUFFER_PADDING_SIZE,
                            av_buffer_default_free, NULL, 0);
  • 它用 av_buffer_create 创建了一个 AVBufferRef
  • 引用的数据就是你传入的 data(即 pStream)
  • 释放回调函数是 av_buffer_default_free

而 av_buffer_default_free 的定义是:

c 复制代码
void av_buffer_default_free(void *opaque, uint8_t *data)
{
    av_free(data);  // ← 最终会调用 av_free(pStream)!
}
2. av_packet_unref 做了什么?
c 复制代码
av_buffer_unref(&pkt->buf);  // ← 释放 buf,触发 av_buffer_default_free
  • 当 buf 的引用计数归零时,会调用 av_buffer_default_free
  • 从而调用 av_free(pStream)

❌ 所以结论是:

av_packet_from_data(pkt, pStream, size) + av_packet_unref(pkt)
⇒ 会导致 av_free(pStream) 被调用!

这意味着:

pStream 来源 是否安全
通过 av_malloc 分配 ✅ 安全(av_free 匹配)
通过 malloc 分配 ⚠️ 可能安全(若 av_free 就是 free)
通过 new[] 分配 ❌ 危险(av_free ≠ delete[])
栈上数组(如 uint8_t buf[1000]) ❌ 崩溃!非法指针
网络/解码器内部 buffer(只读) ❌ 提前释放或 double-free

📌 官方文档怎么说?

FFmpeg 官方对 av_packet_from_data 的说明:

The packet data buffer is managed by the packet , and is freed when the packet is unreferenced .

The data must have been allocated with av_malloc() or a compatible allocator.

✅ 明确要求:data 必须由 av_malloc() 或兼容分配器分配!


✅ 正确使用方式

情况 1:你想"零拷贝"且拥有 pStream 生命周期
cpp 复制代码
// 必须用 av_malloc 分配 pStream!
uint8_t* my_data = (uint8_t*)av_malloc(dwSize + AV_INPUT_BUFFER_PADDING_SIZE);
memcpy(my_data, original_stream, dwSize);
memset(my_data + dwSize, 0, AV_INPUT_BUFFER_PADDING_SIZE); // padding required

av_packet_from_data(pkt, my_data, dwSize);
// ...
av_packet_unref(pkt); // 安全:内部 av_free(my_data)
情况 2:pStream 是外部只读 buffer(绝大多数情况)

→ 不要用 av_packet_from_data!改用拷贝:

cpp 复制代码
av_new_packet(pkt, dwSize);      // 内部 av_malloc
memcpy(pkt->data, pStream, dwSize);
// ...
av_packet_unref(pkt); // 安全释放内部 buffer

❌ 你的注释为什么错?

// av_packet_unref 不会 free 它
// pStream 已被 pkt 拥有,无需手动 free

事实正好相反:

  • pStream 被 pkt 拥有
  • 并且 av_packet_unref 会 free 它!

所以如果你传入的是普通指针(非 av_malloc),程序极可能崩溃。


✅ 总结

说法 正确? 说明
"av_packet_unref 不会释放 pStream" ❌ 错误 会释放!
"pStream 是外部 buffer,可安全传给 av_packet_from_data" ❌ 危险 除非它是 av_malloc 分配的
"应使用 av_new_packet + memcpy 处理外部数据" ✅ 正确 推荐做法

🔧 建议

在你的 SaveMP4 函数中,只要 pStream 不是你用 av_malloc 分配的,就绝对不要用 av_packet_from_data。

使用:

cpp 复制代码
av_new_packet(pkt, dwSize);
memcpy(pkt->data, pStream, dwSize);

这是最安全、最清晰、最符合常规用法的方式。

🔧 附加建议

如果你担心性能(频繁 memcpy),可以考虑:

  • 使用 AVBufferRef 自定义内存管理(高级用法)
  • 或者确保上游提供 av_malloc 分配的 buffer(不现实)

但在 99% 的封装场景中,一次 memcpy 的开销远小于播放失败或 crash 的代价。


✅ 请立即修改代码,避免使用 av_packet_from_data 处理外部 buffer!

相关推荐
Maynor99618 小时前
6张商品图→17秒带货短视频:开源12个Claude Skills,配音、字幕花字、BGM本地一条命令出片
ffmpeg·ai编程·claude·电商·agent skills
AIGC小尼18 小时前
8G 显存零基础本地部署 AI 漫剧全流程|ComfyUI+Wan2.2+FFmpeg 离线成片完整方案(含代码 / 指令 / 排坑)
人工智能·ffmpeg·php·comfyui·ai漫剧
开开心心就好19 小时前
以图搜图找重复图片,本地工具离线就能用
javascript·智能手机·ffmpeg·c#·ocr·word·音视频
可乐鸡翅yeah_2 天前
AES‑128 加密 M3U8,IV 初始化向量新手容易踩坑
前端·网络·数据库·ffmpeg·音视频·m3u8在线
企业数字化笔记3 天前
视频视觉任务怎样选择媒体处理技术?FFmpeg、OpenCV、GStreamer 的职责与适用场景
ffmpeg·音视频·媒体
可乐鸡翅yeah_4 天前
网页端 M3U8 播放器性能监控,怎么看播放卡顿指标
javascript·ffmpeg·音视频·safari·m3u8
开开心心就好4 天前
超市定时播音软件,免费版支持循环播放
智能手机·ffmpeg·ocr·word·vim·音视频·visual studio
企业数字化笔记4 天前
视频成片怎么稳定导出?编码选择、GPU/CPU降级与媒体交付验收
java·python·ffmpeg
企业数字化笔记4 天前
视觉智能体怎么选技术?目标检测、跟踪、分割、OpenCV和FFmpeg的职责划分
opencv·目标检测·ffmpeg
企业数字化笔记5 天前
视频字幕如何烧录到成片?硬字幕、软字幕、样式控制与播放验收
java·ffmpeg