问题: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!