某些解码器的 H.264 帧内存泄漏

Posted

技术标签:

【中文标题】某些解码器的 H.264 帧内存泄漏【英文标题】:H.264 Frames Memory Leak With Some Decoders 【发布时间】:2012-06-20 15:12:04 【问题描述】:

我正在使用其 SDK 从 DVR 接收H.264 stream。存在内存泄漏,我认为是 SDK 导致了所有泄漏。但是当我记录流并从磁盘读取的帧一一播放时(不涉及任何第 3 方 dll),我注意到问题不在于 dll,而在于流本身。

很奇怪,DivX H264 Decoder 是唯一不会导致内存泄漏的编解码器,但是当流运行很长时间时,有时 DivX 解码器也会崩溃。我更喜欢使用Microsoft DTV-DVD Video Decoder,但它会导致巨大的内存泄漏并丢掉很多帧。我尝试过的许多其他 H.264 解码器的行为方式相同。

我使用一些h.264 parsers 与其他一些无问题的流相比检查了h.264 frames,但我没有从日志中注意到任何明显的东西。

由于我的问题是关于 h.264 帧结构,我准备了一个名为 FramesFromFileSourceFilter 的源过滤器,您可以在下面下载。

http://www.akaydin.com/directshow/FramesFromFileSourceFilter.zip

这是一个Visual Studio 2008 项目,所有依赖项都包含在相对位置文件夹(包括 h.264 框架)的 zip 文件中。因此,您需要做的就是编译项目,使用regsvr32.exe 注册输出,然后使用您想要的来自 GraphEdit 或 GraphStudio 的任何 h.264 解码器运行过滤器。示例图如下。

h264 帧也可以在下面的链接中作为单个原始 h264 文件提供,该文件可由 VLC 播放(由于原始帧为 12 FPS,因此 FPS 错误)。

http://www.akaydin.com/directshow/stream.zip

问题:

除了 DivX 解码器之外,许多著名的 H264 解码器可能会导致内存泄漏问题。这个流有什么问题?

更新 1

读取数据线程被删除,功能移入 FillBuffer 而不使用任何缓冲区和标志。问题依旧。

http://www.akaydin.com/directshow/FramesFromFileSourceFilterUpdate1.zip

更新 2

Update1 在FillBuffer() 函数中使用了Sleep(),这导致了一些问题。现在我删除了Sleep() 并使用SetTime() 来获得~12 FPS。这也解决了Microsoft DTV-DVD Video Decoder 的丢帧问题,但没有解决内存问题。

http://www.akaydin.com/directshow/FramesFromFileSourceFilterUpdate2.zip

内存增加仅发生在Working SetVirtual BytesPrivate Bytes 似乎很稳定。什么可能导致持续的Working Set 内存增量,而这种增量只发生在Microsoft DTV-DVD Video Decoder 上?

【问题讨论】:

【参考方案1】:

您不会围绕变量进行任何同步

BYTE* m_buffer;
DWORD m_bufferSize;
bool isFrameReady;

它们被两个并发线程使用。您只是通过这种不准确的分配/释放和/或让您的代码因访问冲突而崩溃而泄漏您的内存。 DLL 的调试版本通过在运行测试时向您显示“堆损坏”警报来表明这一点。运行时行为可能因解码器和环境而异,但这绝对是一个需要修复的严重错误。

例如,您可以在填充缓冲区的线程中使用CAutoLock cAutoLock(m_pLock);,以防止在从文件中读取数据时流式线程访问。

请注意,您将下一帧读入同一个缓冲区指针,而不检查先前分配的内存是否被释放,您只是覆盖指针可能会留下泄漏。

内存泄漏/工作集更新:现在,当代码问题得到解决时,不需要的运行时行为是 Working Set 大小的增加。这不是泄漏。这表明 Windows 将进程视为一种优先级(为什么不呢?它是活动的并与内存一起工作)并向该进程抛出更多真实页面以促进其性能。请参阅this answer,详细了解进程内存指标如何对应于应用程序中的内存泄漏。

您可能看到的解码器之间的差异可能是由于某些解码器在缓冲区数量较少的情况下表现良好,或者更积极地重用它们,例如更喜欢从池中取出相同的缓冲区,而不是从所有可用的缓冲区中一一挑选。

【讨论】:

其实我的真实过滤器有一个更好的机制,这是简化的。但由于帧之间存在 83 毫秒的睡眠,因此这里不应该出现并发。另外,DivX 解码器不会导致任何内存泄漏。我仍然相信流导致了这个问题。此外,相同的过滤器适用于来自其他设备的其他 h.264 流,使用相同的解码器没有任何内存泄漏。 也许你真正的过滤器做得更好,但这段代码会导致堆损坏。在它成功通过的另一次运行中,我没有注意到任何泄漏(使用 MS DTV-DVD 解码器),您如何判断泄漏是否发生? 只要过滤器运行,内存就会在这个过程中不断增加。当然,在这个例子中,帧数是有限的。但是在使用实时流时,内存使用量会在几个小时后达到千兆字节。当您使用 MS DTV-DVD 视频解码器运行过滤器时,您没有注意到 GraphEdit/GraphStudio 的内存增加吗?如果是这样,您的操作系统是什么?我已经在 Win7 32 位上使用了这个。 我用 Win 7 x64 检查过,但没关系。泄漏只发生在您的m_buffer 周围,我认为解码器本身没有任何问题。您可能看到的差异来自解码器的行为,有时它们可​​能会立即消耗数据,有时会涉及延迟 - 这会导致您这边的错误出现或不出现。 我刚刚在读取数据部分周围添加了一个关键部分。但是这里的问题仍然是一样的。顺便说一句,使用 MS DTV-DVD 视频解码器,这里会丢掉很多帧。所以,我无法获得流畅的视频。它开始平稳,然后下降到 1-2 FPS。使用 DivX 解码器,我可以获得稳定的 12 FPS 视频。你不也有这个问题吗?你身边的视频播放流畅吗?

以上是关于某些解码器的 H.264 帧内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 帧缓冲附件泄漏 GPU 内存

OpenCv - 从网络摄像头捕获帧时发生内存泄漏

使用ffmpeg解码 需要注意的内存泄漏问题

AWS EMR - 将数据帧保存到 S3 上的配置单元外部表中 - 出现内存泄漏错误

内存泄漏篇--1

内存泄漏篇--1.基础问题