FFmpeg内存管理:一帧背后的引用计数
FFmpeg 内存管理一帧背后的引用计数一帧图像不只是一块像素内存在 FFmpeg 里它背后可能挂着解码器、引用计数、buffer pool 和一串“谁还没松手”的生命周期。很多人第一次写 FFmpeg 代码都会被一个问题绊住“我拿到了一个AVFrame如果想保存一份直接memcpy可以吗”答案是可以但经常不是最好的选择。因为 FFmpeg 的很多对象不是“普通结构体 一块裸内存”。它更像一套引用计数系统你看到的是AVFrame真正的大块数据可能在AVBuffer里你以为拷贝了一帧实际可能只是复制了几个指针你以为释放了解码器某个忘记释放的 frame 还可能把整个 buffer pool 顶住。这篇文章就从“拷贝一帧”这个小问题出发聊清楚 FFmpeg 的内存管理模型。1. 想拷贝 AVFrame先问你要拷贝什么AVFrame这个名字很容易误导人。它看起来像“一帧数据”但更准确地说它是一帧媒体数据的描述对象。一个视频AVFrame里通常有这些东西成员 / 概念作用容易误解的点data[]指向各个平面的数据地址例如 Y、U、V它通常只是指针不代表内存归AVFrame结构体本身所有linesize[]每个平面的行跨度不一定等于图像宽度可能有对齐 paddingbuf[]保存数据 buffer 的引用这是释放大块像素内存的关键引用extended_buf超过固定数组数量时的额外 buffer 引用音频多声道等场景常见width/height/format描述图像尺寸和像素格式只描述数据不负责管理底层内存pts/pkt_dts时间戳信息拷贝像素时不一定要保留做同步时必须关注所以“拷贝 AVFrame”至少有两种含义需求典型做法结果只想多持有一份引用av_frame_ref(dst, src)快通常不复制像素底层 buffer 引用计数 1想要独立像素副本av_frame_clone()后必要时av_frame_make_writable()或新分配 frame 后拷贝数据更安全但可能产生真实内存拷贝只想临时传递所有权av_frame_move_ref(dst, src)不复制数据把引用从src转移到dst只想拷贝元信息av_frame_copy_props(dst, src)只复制时间戳、色彩信息等属性不复制像素这里最重要的区别是av_frame_ref()不是深拷贝像素它是在共享底层 buffer 的前提下增加引用。这正是 FFmpeg 高效的地方高清视频一帧动辄几 MB如果每个模块都深拷贝一次内存和 CPU 很快就会爆炸。FFmpeg 默认更倾向于“共享数据 引用计数”。2.xxx_ref/xxx_unrefFFmpeg 的“借书登记表”理解 FFmpeg 内存管理先抓住一组命名习惯API 形态含义你可以怎么理解xxx_alloc()创建对象壳子或分配对象从仓库拿一个新对象xxx_free()销毁对象并通常把指针置空彻底归还对象xxx_ref()增加一份引用多登记一个借阅人xxx_unref()释放当前引用当前借阅人还书xxx_move_ref()转移引用所有权把借书卡从 A 换到 B不新增借阅人xxx_clone()创建新对象并引用同一份底层数据新建一个壳子但底层书可能还是同一本拿AVFrame来说操作会发生什么注意点av_frame_alloc()只分配AVFrame结构体不一定分配像素数据av_frame_get_buffer()给 frame 分配底层数据 buffer数据通常挂在buf[]引用里av_frame_ref(dst, src)dst引用src的底层 buffer两个 frame 共享数据引用计数增加av_frame_unref(frame)释放 frame 当前持有的 buffer 引用frame 壳子还在可以复用av_frame_free(frame)先 unref再释放 frame 结构体并置空指针最常见的收尾方式这里的设计非常像“借书登记表”角色类比真实对象书真正占空间的数据像素 buffer / packet data借书卡一个引用AVBufferRef借阅登记表引用计数AVBuffer内部 refcount读者持有引用的对象AVFrame、AVPacket、解码器内部结构图书馆管理 buffer 的地方AVBufferPool或普通 allocator只有最后一个读者还书书才真正能从系统里移走。3.AVBuffer真正管理大块内存的底座在 FFmpeg 里很多大块数据最终都会落到AVBuffer/AVBufferRef这套机制上。简单说名称作用重点AVBuffer真正管理底层 data 和引用计数的对象里面知道 data 怎么释放AVBufferRef指向AVBuffer的引用句柄可以有多个 ref 指向同一块数据av_buffer_ref()复制一个引用refcount 1av_buffer_unref()释放一个引用refcount -1归零时调用 free callbackav_buffer_alloc()分配一块带引用计数的内存常用于 frame/packet 底层数据AVBufferPoolbuffer 池用于复用频繁申请释放的大块内存普通AVBuffer的释放逻辑很好理解分配一块 data用AVBuffer包起来每多一个使用者就多一个AVBufferRef每个使用者结束时av_buffer_unref()refcount 归零时调用释放回调真正释放 data。但AVBufferPool又多了一层“复用”。当解码器需要一帧 buffer 时它可能不是每次都直接向系统 malloc而是阶段行为结果取 bufferav_buffer_pool_get(pool)从池里拿一个可用 buffer没有空闲才新分配frame 使用中AVFrame::buf[]持有AVBufferRefbuffer 不能被回收frame 释放av_frame_unref/free()引用释放buffer 可能回到 poolpool 销毁pool 自身引用也释放且没有外部 buffer ref池里缓存的底层内存才真正 free这就是很多 FFmpeg 内存问题难查的地方av_frame_free()不一定等于“立刻把像素内存还给系统”它可能只是把 buffer 还给池。真正释放要看 pool 是否销毁以及所有引用是否都归零。4. 为什么 FFmpeg 要这么设计因为视频处理太吃内存也太吃拷贝成本。一帧 1080p YUV420P 8bit 大概 3MB如果是 10bit接近 6MB。4K、10bit、多线程解码时这个数字还会继续上升。如果每个环节都做深拷贝典型链路会一路膨胀环节如果深拷贝直接后果解码输出产出第一份像素数据基础帧内存已经产生滤镜处理再拷贝一份给滤镜CPU 多一次大块内存读写缩放处理再拷贝一份给缩放器临时峰值继续抬高渲染显示再拷贝一份给渲染层帧率和功耗都受影响缓存复用再拷贝一份给缓存多路播放、批量缩略图时更容易爆内存这条链路真正的问题可以概括成两类问题后果典型表现CPU 拷贝成本高帧率下降、耗电上升高清视频、实时滤镜更明显瞬时内存膨胀多份大 frame 同时存在多路播放、缩略图批处理时尤其明显所以 FFmpeg 更偏向“共享引用而不是层层复制”的模型设计选择做法收益数据只放一份像素数据放在底层 buffer 里避免重复存储大块内存对象通过 ref 共享AVFrame/AVPacket持有引用模块间传递更轻量用完主动 unref谁增加引用谁负责释放引用生命周期可追踪最后一个引用释放refcount 归零后释放底层内存保证没人使用时再回收这套模型很强但也有代价你必须认真对待每一个ref和unref。5. 常见对象的 ref / unref 心智模型下面这张表可以作为写 FFmpeg 代码时的速查。对象常见创建增加引用 / 复制引用释放引用销毁对象典型坑AVFrameav_frame_alloc()av_frame_ref()/av_frame_clone()av_frame_unref()av_frame_free()只 free 了壳子思维忘了buf[]才是大头AVPacketav_packet_alloc()av_packet_ref()/av_packet_clone()av_packet_unref()av_packet_free()循环读包时忘记av_packet_unref()AVBufferRefav_buffer_alloc()/av_buffer_ref()av_buffer_ref()av_buffer_unref()通常没有单独 free靠 unref多一个 ref 就多一个释放责任AVCodecContextavcodec_alloc_context3()通常不 ref不适用avcodec_free_context()释放 context 不代表外部 frame 引用也会消失AVFormatContextavformat_open_input()通常不 ref不适用avformat_close_input()demux 和 decode 是两套生命周期SwsContextsws_getContext()不适用不适用sws_freeContext()尺寸/格式变化时旧 context 忘释放一个很实用的习惯是谁拿到 ownership谁负责释放谁调用了ref谁就必须配一个unref。6. 忘记释放一个 AVFrame为什么可能不只漏“一帧”现在回到最开头的问题如果一个AVFrame忘记释放会发生什么直觉上很多人会以为只是漏了一个结构体sizeof(AVFrame) 几百字节小问题。真实情况完全不是这样。AVFrame自己只是壳。它可能通过buf[]挂着几 MB 的像素内存如果这些 buffer 来自AVBufferPool漏掉一个 frame 引用还可能让整个 pool 没法销毁。后果可以分三层层级泄漏内容影响第一层AVFrame结构体本身很小通常不是主因第二层AVFrame::buf[]持有的像素 buffer可能是几 MB 到几十 MB第三层AVBufferPool因引用未归零无法销毁可能拖住一批已归还 pool 的大块 buffer这也是为什么某些问题看起来只是“漏了一帧”最后却表现成几百 MB 甚至 GB 级 native 内存增长。尤其在这些场景里放大效应更明显场景为什么更危险批量缩略图每个视频都要解一帧次数多容易线性累积10bit / 4K 视频单帧内存更大多线程解码每个线程可能持有 delayed frame / reference frameH.264 / HEVC解码器内部有参考帧队列和 DPB异常 fallback正常路径释放了错误路径反而容易漏所以 FFmpeg 内存排查里一个非常重要的原则是不要只看“我最后有没有 free codec context”还要看中间所有 frame / packet 的引用有没有归零。avcodec_free_context()能释放解码器自己持有的资源但它不能替你释放已经丢到外面的AVFrame。如果某个AVFrame指针被覆盖、丢失、或者异常路径漏掉里面的AVBufferRef仍然可能让底层 buffer 活着。7. 写 FFmpeg 代码的几个自检问题最后给一组很实用的检查清单。每次写到AVFrame/AVPacket生命周期时可以顺手过一遍。自检问题为什么要问每个av_frame_alloc()是否都有对应av_frame_free()frame 壳子和其中的 buffer 引用都要释放每次循环复用AVFrame*前旧值是否已经 unref/free防止旧指针被覆盖后永久失联每个av_frame_ref()是否都有对应av_frame_unref()引用计数不归零底层 buffer 就不能释放错误路径、continue、break前是否清理了 frame内存泄漏最常出现在异常分支成功码和失败码是否可能冲突成功被误判失败时资源释放路径可能被绕过AVPacket循环读包后是否及时av_packet_unref()packet 也常持有引用计数 buffer释放 codec context 前外部是否还持有输出 framecontext 释放不了外部 frame 的引用是否把 shallow ref 当成 deep copy会导致意外共享或释放时机错误如果只能记住一句话我建议记这句在 FFmpeg 里真正的大内存经常不在你手里的结构体里而在它引用的 buffer 里忘记释放一个引用可能拖住一整片池化内存。结尾FFmpeg 的内存管理并不神秘它只是把“谁拥有数据、谁还在使用数据、什么时候能释放数据”这件事做得非常细。AVFrame、AVPacket、AVBufferRef、AVBufferPool看起来是一堆 API背后其实是一套统一的思路核心问题FFmpeg 的回答大块数据要不要反复拷贝尽量不要优先共享引用多个对象共享数据怎么管理引用计数频繁申请释放大块内存怎么办buffer pool 复用什么时候真正释放最后一个引用释放时最容易出问题在哪里异常路径、覆盖旧指针、成功/失败语义混乱写 FFmpeg最怕的不是少写一个free()而是少想清楚一次“这个引用现在归谁”。

相关新闻

机器人手术技术现状与挑战:从达芬奇系统到AI辅助的工程实践

机器人手术技术现状与挑战:从达芬奇系统到AI辅助的工程实践

1. 这篇文章真正要解决的问题当马斯克在社交媒体上谈论机器人手术时,他描绘的图景往往是“未来几年内,外科医生将被淘汰”、“机器人将自主完成所有复杂手术”。这类言论在科技圈总能引发巨大关注,但也让许多医疗技术从业者和一线开发者感到困…

2026/8/8 10:44:28 阅读更多 →
FutureBridge-OPD:基于前瞻性验证的主动知识蒸馏框架

FutureBridge-OPD:基于前瞻性验证的主动知识蒸馏框架

大家好,我是专注于AI模型优化与部署的技术博主。在模型压缩与加速的实践中,知识蒸馏是一种极为有效的手段,但传统的蒸馏方法往往让学生模型被动地模仿教师模型,缺乏对教师建议有效性的主动判断。今天,我们将深入探讨一…

2026/8/8 10:44:28 阅读更多 →
Bilibili-Evolved:免费开源工具彻底改造你的哔哩哔哩体验

Bilibili-Evolved:免费开源工具彻底改造你的哔哩哔哩体验

Bilibili-Evolved:免费开源工具彻底改造你的哔哩哔哩体验 【免费下载链接】Bilibili-Evolved 强大的哔哩哔哩增强脚本 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved 你是否对B站的标准界面感到审美疲劳?是否希望拥有更强大的视…

2026/8/8 10:43:28 阅读更多 →

最新新闻

2026实用盘点:pdf转文档用什么软件,办公老手亲测这七款就够了

2026实用盘点:pdf转文档用什么软件,办公老手亲测这七款就够了

上个月帮部门审一份续约合同,对方发来的是加密过的 PDF,只让看不让改。偏偏条款里有两个数据需要更新,我盯着屏幕上那个没法编辑的合同,突然想起去年用过的一款工具,叫青蓝PDF转换,支持 PDF 转 Word 还能保…

2026/8/8 14:42:41 阅读更多 →
Locus后训练方法解析:如何让大语言模型更善解人意

Locus后训练方法解析:如何让大语言模型更善解人意

这次我们来看一个在LLM后训练领域取得突破性进展的项目:Locus。它最近在PostTrainBench评测基准上登顶,其基于Qwen3模型进行后训练的效果甚至超越了人工标注数据。对于关注大模型微调、对齐和实际应用效果的开发者来说,这个结果值得深入探究。…

2026/8/8 14:42:41 阅读更多 →
Obsidian日历插件终极指南:3分钟学会高效时间管理

Obsidian日历插件终极指南:3分钟学会高效时间管理

Obsidian日历插件终极指南:3分钟学会高效时间管理 【免费下载链接】obsidian-calendar-plugin Simple calendar widget for Obsidian. 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-calendar-plugin 还在为笔记碎片化而烦恼吗?每天的想…

2026/8/8 14:42:41 阅读更多 →
AI Skills生态:从工具调用到智能体协作的技术架构与实践

AI Skills生态:从工具调用到智能体协作的技术架构与实践

1. 项目概述:当AI需要“工具箱” 如果你最近在关注AI领域,尤其是那些能帮你自动处理邮件、分析数据、甚至写代码的智能助手(AI Agent),那你大概率会频繁听到一个词: Skills 。这听起来有点像给游戏角色安…

2026/8/8 14:42:41 阅读更多 →
测试工程师必学的5大AI技能:从用例生成到自动化脚本实战

测试工程师必学的5大AI技能:从用例生成到自动化脚本实战

1. 测试工程师的AI技能库:从概念到落地 最近和几个测试团队的朋友聊天,大家不约而同地提到了一个词:焦虑。不是对业务增长的焦虑,而是对自身技能迭代速度的焦虑。UI自动化脚本还没写利索,大模型已经开始接管一部分的用…

2026/8/8 14:42:41 阅读更多 →
从对话到工程化:六大GitHub项目构建高效AI代码智能体

从对话到工程化:六大GitHub项目构建高效AI代码智能体

1. 项目概述:从“聊天”到“工程化”的思维跃迁最近在社区里看到不少开发者朋友把 Claude Code 这类代码智能体当成一个“高级一点的代码补全工具”或者“能写代码的聊天机器人”在用。这其实是一个巨大的认知误区,也是效率的隐形杀手。我刚开始接触时也…

2026/8/8 14:41:40 阅读更多 →

日新闻

AI多智能体时代来临,读懂MCP与A2A架构,抢占企业数字化新风口

AI多智能体时代来临,读懂MCP与A2A架构,抢占企业数字化新风口

当下AI应用飞速普及,无数企业下场搭建智能体系统,可落地阶段难题接踵而至:上下文无限堆积频繁爆栈、AI工具调用准确率低下、Token成本居高不下、企业数据权限混乱暗藏安全隐患……很多团队卡在架构搭建环节,空有前沿技术概念&…

2026/8/8 0:00:07 阅读更多 →
PHP二维码生成终极指南:用chillerlan/php-qrcode打造专业级二维码

PHP二维码生成终极指南:用chillerlan/php-qrcode打造专业级二维码

PHP二维码生成终极指南:用chillerlan/php-qrcode打造专业级二维码 【免费下载链接】php-qrcode A PHP QR Code generator and reader with a user-friendly API. 项目地址: https://gitcode.com/gh_mirrors/ph/php-qrcode 在当今数字时代,二维码已…

2026/8/8 0:00:08 阅读更多 →
UniApp微信小程序隐私保护组件开发:从原理到实战

UniApp微信小程序隐私保护组件开发:从原理到实战

1. 项目缘起:为什么我们需要一个隐私保护通用组件?最近在维护一个基于uniapp开发的微信小程序矩阵时,我遇到了一个非常棘手的问题。随着平台对用户隐私保护的要求越来越严格,几乎每一个新版本发布,或者在某些特定机型&…

2026/8/8 0:00:08 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/6 22:02:27 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/8 8:58:26 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/7 23:24:08 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/7 17:02:37 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/7 23:54:54 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/7 17:02:36 阅读更多 →