3

我正在尝试使用 DirectShow 连续播放两个 AVI 文件(一个接一个),这样当播放器从一个文件转换到下一个文件时,音频或视频不会中断。

我的表单上有两个自定义控件。每个都预加载了一个 AVI 文件,在开始播放之前,我设置了所有 DirectShow 接口,设置视频窗口并调整它们的大小,调用 IMediaControl.Run(),然后调用 IMediaControl.Pause(),然后调用 IMediaSeeking.SetPositions在两个控件上重置为第 0 帧。在表单上,​​您可以看到两个文件都在其初始帧处暂停。

然后我在第一个控件上调用 IMediaControl.Run(),并等待它完成,然后再在第二个控件上调用 Run()。最初,我连接到第一个视频的 EC_COMPLETE 通知消息,并用它来启动第二个。考虑到这个事件可能会来得很慢(事实证明确实如此,但出于一个奇怪的原因),我尝试了另外两种方法:

  1. 检查第一个视频在每秒钟左右关闭的计时器内的当前位置(使用 IMediaPosition.get_CurrentPosition)。当当前位置在视频停止时间的一秒内(从 IMediaPosition.get_StopTime 提前知道),我进入一个紧密while循环并等待当前位置等于停止时间,然后在第二个调用 Run()视频。
  2. 与第一个相同,除了我将while循环替换为对timeSetEventfrom的调用winmm.dll,并设置了延迟,以便在第一个文件应该结束时立即触发。我使用回调来 Run() 第二个文件。

这两种方法中的任何一种都大大减少了第一个文件结束和第二个文件开始之间的延迟,表明 EC_COMPLETE 消息不会在文件完成后立即到达(我也尝试过挂钩 EC_SEGMENT_COMPLETE 消息,即应该用于在文件中循环,但显然没有人支持这一点 - 至少在我的机器上从未发生过)。

完成上述所有操作已将转换延迟从一秒减少到几乎无法察觉的故障;大约三分之一的时间文件完全没有中断地转换,这表明没有根本原因我不能让它一直工作。

不幸的是,轻微的延迟仍然是不可接受的。我假设(我很容易出错)剩余的延迟是由于调用 IMediaControl.Run() 和视频实际开始播放之间的轻微可变延迟。

有人知道我能做些什么来消除这个小滞后吗? 被告知无论出于何种原因这基本上是不可能的也将有所帮助,这不会让我感到惊讶。我从来没有在 Windows 中遇到过没有这个问题的视频播放器,所以它可能不可行。

更多信息:我正在播放的 AVI 文件完全未压缩(视频和音频未压缩),所以我认为延迟不是因为 DirectShow 必须在播放开始前解压缩视频,尽管它可能仍会提前缓冲当然(这可能是问题的根源)。我会认为开始播放,暂停然后倒回到开头会解决这个问题。

此外,我处理转换的方式实际上是将第二个控件放在第一个控件之下;当第一个完成播放时,我开始第二个,然后在其上调用 BringToFront,创建两个原件之间的单个视频过渡的外观。我不认为故障是由于这个原因,因为它在某些时候工作得很好,即使这是有问题的,它也不能解释匹配的音频故障。

更重要的是:我只是尝试“提前”30-50 毫秒开始第二个视频,这似乎消除了更多的差距,所以我猜 Run() 中的延迟大约有那么长。不过,它似乎是可变的,所以这仍然不是我需要的地方。

还有更多:也许我可以通过从内存而不是从文件中加载 AVI 来消除这种延迟。不幸的是,我不知道该怎么做。IMediaControl 只有一个RenderFile()方法,而不是类似RenderStreamorRenderMemory方法的东西。

4

1 回答 1

9

如果您在停止的图形上调用 IMediaControl::Run,图形管理器会将调用发布到工作线程(因此存在一些可变性)。在工作线程上,图表将被暂停。渲染过滤器只有在接收到数据后才完成暂停转换,因此一旦 GetState() 返回 S_OK,图形管理器就知道图形已完全提示。此时,它选择大约 10 毫秒后的时间,并以该时间为起点在每个过滤器上调用 Run。由于告诉每个过滤器运行需要时间,因此 dshow Run 方法有一个参数,该参数是应该播放时间戳为零的样本的 refclock 时间——即应该发生实际转换到运行模式的时间。

要将其与另一个图表同步,您首先必须确保两个图表具有相同的时钟。查询 IMediaFilter 的图形(不是过滤器),并在一个图形上调用 GetSyncSource 并在另一个图形上调用 SetSyncSource。然后你需要暂停第二个图表,以便它被提示并准备好。当你想启动它时,调用 IMediaFilter::Run 而不是 IMediaControl::Run,你可以传递你自己的启动时间。这仍然必须是未来几毫秒,所以最好的办法可能是将第二个图的开始时间设置为第一个图的开始时间加上它的持续时间(对于未压缩流的索引容器,持续时间应该是准确的)。

另一种方法是使用多个图。将源与渲染分离将允许您在源之间无缝切换,因为它们会输入到一个通用的渲染图中。在 www.gdcl.co.uk/gmfbridge 上有这种方法的示例源代码。

G

于 2010-03-18T14:28:05.623 回答