c# - 媒体样本长时间保存在图形中(累积效果)

标签 c# c++-cli directshow

几个月前,我写了this question关于DirectShow图上的缓冲区不足。

通过实现自定义分配器解决了饥饿问题,该分配器在饥饿时会扩展大小。但是,这仅减轻了实际问题。如果有足够的时间,图表中保存的样本数量将变得过多,并且不断扩大的池会造成内存不足的情况。

这是我设法收集的一些事实:


该图基本上是将MPEG2-TS流转码为MP4文件,以及提取音频和视频数据以进行一些实时DSP处理。
该流作为UDP多播流来。该流正在承载14种不同的SD节目。
我正在使用从DsNetwork示例派生的自定义过滤器读取UDP流。按照上述示例,将在UDP接收的数据块(8KiB块)周围创建媒体样本(不带时间戳),并将其传递到Microsoft的MPEG2多路分解器过滤器,该过滤器配置为过滤感兴趣的程序。 (我应该给样品加上时间戳吗?)
需要可扩展分配器的滤波器是MPEG2多路分解器,特别是输出视频引​​脚传送的样本是必需的。输出音频引脚可以使用默认分配器正常工作,音频解码器或多路分配器不会保留任何样本。
视频样本正在由LAV视频解码器解码。将LAV过滤器交换为ffdshow过滤器没有积极作用-仍然存在累积。我在LAV或ffdshow中都没有找到缓解累积问题的设置(包括示例队列设置)。
问题与接收到的流的质量完全相关。在流上检测到的不连续性越多(由MPEG多路分配器输出样本标记),倾向于累积的样本越多。顺便说一句,并行运行消耗相同数据流的VLC播放器会记录相同的不连续性,因此我看来这似乎并不是由错误的网络代码引起的。
挥之不去的样本不会丢失,它们最终将由图形处理。我编写了一些看门狗逻辑来检测丢失样本的可能性,并且每个样本最终都会正确释放并返回到池中。
滞后与CPU饥饿无关。如果我停止将样品输送到多路分配器,则多路分配器将停止将样品输送到输出引脚。我需要将新样品推入多路分配器中,以使残留样品得以正确释放并返回池中。
我尝试从捕获图以及多路复用器图(由GDCL桥接滤波器桥接)中删除时钟。这不能解决问题,并且实际上可以阻止数据流。


我不知道样本是由解复用器还是由视频解码器保存。事实是,对于如何调试并希望解决此问题,我一无所知,因此欢迎任何指教或建议。

附录:

我还有一些其他信息:


转码后的视频相对于音频滞后。
滞后时间与残留样本量成正比。


因此,我认为在图处理的某个时刻,已解码的音频和视频采样时间戳不同步,并且图的多路复用器端点可能正在阻塞视频解码线程,等待相应的音频到达。

关于如何检测有问题的过滤器的任何提示,或者如何使同步“变基”?

附录2:

如您在对Roman的答案的评论中所见,我实际上发现了一个错误,该错误会导致流中出现虚假的中断。通过修复该错误,我减少了问题发生的次数,但并未解决根本原因!

事实证明,问题的根源是由Monogram AAC编码器过滤器(至少是我设法获得的版本,因为似乎不再支持该项目)。

编码器通过将接收到的采样量乘以输入的采样频率来增量计算输出时间戳。过滤器假定数据流始终是连续的,甚至不检查传入样本的不连续性!一旦发现问题,修复起来很容易,但这确实是我作为开发人员一生中必须调试的最困难的问题,因为所有问题都指向MPEG2多路分配器(时间戳在编码的输出音频和视频引脚之间漂移并且该过滤器首先用尽了合并的样本),但这是由于视频输出引脚的工作线程在图的末尾被MPEG4多路复用器阻塞而间接引起的音频和视频之间的采样方式不同步,并限制了视频输入以尝试保持同步。

确实,当线程沿着图形流动时,必须谨慎考虑过滤器是“黑匣子”的错觉,并且下游过滤器上的问题可能表现为上游过滤器中的错误问题。

最佳答案

首先,所描述的行为听起来像个错误。也就是说,意外行为会导致不良后果。但是,我同意,解决此问题的方法需要确定犯罪者并就已注册的问题进行详细调查。

由于视频的音频量相对滞后于相对滞后的样本,并且没有其他副作用(例如丢失帧),因此我同意面临的挑战是找到谁来准确地保存媒体样本。

我可以建议两种方法。

检查内存分配器

出于简洁的原因,这种方法不太流行,但是仍然有很好的机会无法使用。背景是引脚连接假定协商内存分配器。内存分配器是引脚的私有业务,因此在大多数情况下,控制应用程序无法直接控制(和前夕访问)数据流。通常每个引脚对都有自己的分配器定义,但是有时(并非罕见)多个引脚对使用相同的分配器。请注意,它是连接上的输出引脚,由谁来决定要使用的分配器。

如果您碰巧熟悉我的DirectShowSpy工具,那么它的作用之一就是枚举内存分配器:



它可以显示内存分配器,哪些连接共享内存分配器以及缓冲区计数和可用缓冲区计数的快照。

为简洁起见,我忽略了这种情况不准确的情况。

另一个重要说明是,仅当您从运行DirectShow图形的进程中调用间谍UI时才可以使用此数据,这与通过“运行对象表”远程访问过滤器图形相反。

这意味着您应该执行以下操作:


注册间谍
使您的应用程序运行(带有过滤器图)
从控制线程(通常)从IUnknown::QueryInterface接口指针为AlaxInfoDirectShowSpy::ISpy
执行IGraphBuilder以显示有问题的用户界面


您可以通过间谍类型库的ISpy::DoPropertyFrameModal获得AlaxInfoDirectShowSpy::ISpy。如果间谍没有通过COM注册,并且没有挂接到OS Filter Graph Manager对象,则上面#3中的#import将失败。

通过C#代码(分别标记问题),可以将DirectShowSpy.dll导入为COM引用。

即使不能保证此方法有效,它也有很好的机会通过可视化内存分配器状态向您显示违规者,并且需要在应用程序中插入大约10行代码。

添加临时诊断过滤器以跟踪引脚连接通信

另一种有更多机会进行总体计算,但需要编写一些代码的方法,是开发和过滤,将数据从输入透明传输到输出引脚,例如QueryInterface,将媒体样本数据记录到共享输出。您可能要为此特别使用GraphStudioNext的分析器过滤器。

想法是尽早将此滤波器安装在解复用器输出引脚上,并在其从滤波器下游传输时监视/记录数据。在流传输数据时比较各个分支上的时间戳,您应该能够检测到违规者。如果看到滞后监控多路分解器输出引脚的连接,则说明多路分解器是违法者。如果那里进展顺利,您将向下游移动跟踪,尤其是。通过解码器,并在您移动跟踪过滤器时隔离他的冒犯者。

可能的解决方法

一旦发现违规者,您将不得不考虑诱使它释放其持有的媒体样本,这本身可能就是一个挑战。此时,由于没有其他有用的信息,我将准备通过发送流通知的结尾,刷新或使用动态媒体类型协商来最终耗尽其内部队列,从而以某种方式耗尽它。

关于c# - 媒体样本长时间保存在图形中(累积效果),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55545328/

相关文章:

c# - 如何将 Dictionary 对象传递给泛型类型参数?

用于 C# 库的 C++/CLI 库的 NuGet 包

multithreading - 使用 win32 线程的 C++/CLI 引用类

c# - 在整个 WPF 窗口上设置 KeyBinding

c# - 在WPF中从后面的代码中删除代码

C# 编写 Tic Tac Toe 中的领带

c# - 从非托管 C++/CLI 调用重载的 C# 数组访问

c++ - 谁可以告诉我一些有关 IPropertyBag::Read 的信息?

streaming - DirectShow:从 MP4 容器中选择源视频流

c - 如何从流中获取视频和音频并输出到视频文件和图像