【问题标题】:How can I horizontally mirror video in DirectShow?如何在 DirectShow 中水平镜像视频?
【发布时间】:2020-04-08 11:40:11
【问题描述】:

我需要在屏幕上显示本地网络摄像头流, 水平翻转, 使屏幕看起来像一面镜子。 我有一个 DirectShow 图表可以完成所有这些, 除了镜像图像。 我尝试了几种方法来镜像图像, 但没有一个有效。

方法A:VideoControlFlag_FlipHorizontal

我尝试设置the VideoControlFlag_FlipHorizontal flag 在网络摄像头过滤器的输出引脚上, 像这样:

IAMVideoControl* pAMVidControl;
IPin* pWebcamOutputPin;
// ...
// Omitting error-handing for brevity
pAMVidControl->SetMode(pWebcamOutputPin, VideoControlFlag_FlipHorizontal);

但是,这没有任何效果。 事实上,网络摄像头过滤器声称没有此功能, 或任何其他功能:

long supportedModes;
hr = pAMVidControl->GetCaps(pWebcamOutputPin, &supportedModes);

// Prints 0, i.e. no capabilities
printf("Supported modes: %ld\n", supportedModes);

方法 B:SetVideoPosition

我尝试通过翻转传递给SetVideoPosition 的矩形来翻转图像。 (我在无窗口模式下使用增强型视频渲染器过滤器。) 有两个矩形: 源矩形和目标矩形。 我都试过了。 这是方法 B(i), 翻转源矩形:

MFVideoNormalizedRect srcRect;
srcRect.left = 1.0;  // note flipped
srcRect.right = 0.0; // note flipped
srcRect.top = 0.0;
srcRect.bottom = 0.5;

return m_pVideoDisplay->SetVideoPosition(&srcRect, &destRect);

这不会显示任何内容。 它适用于其他配置, 但似乎不喜欢srcRect.left > srcRect.right

这是方法 B(ii), 翻转目标矩形:

RECT destRect;
GetClientRect(hwnd, &destRect);

LONG left = destRect.left;
destRect.left = destRect.right;
destRect.right = left;

return m_pVideoDisplay->SetVideoPosition(NULL, &destRect);

这也不会显示任何内容。 它适用于其他配置, 但似乎不喜欢destRect.left > destRect.right

方法 C:IMFVideoProcessorControl::SetMirror

IMFVideoProcessorControl::SetMirror(MF_VIDEO_PROCESSOR_MIRROR) 听起来像我想要的。 这个IMFVideoProcessorControl 接口由Video Processor MFT 实现。 不幸的是,这是一个媒体基础转换, 而且我看不到如何在 DirectShow 中使用它。

方法 D:视频调整器 DSP

Video Resizer DSP 是“可以充当 DMO 的 COM 对象”, 所以理论上我可以在 DirectShow 中使用它。 不幸的是,我没有使用 DMO 的经验, 无论如何, Video Resizer 的文档没有说明它是否支持翻转图像。

方法 E:IVMRMixerControl9::SetOutputRect

我发现 IVMRMixerControl9::SetOutputRect, 其中明确表示:

因为这个矩形存在于构图空间中, 没有“无效”矩形之类的东西。 例如,设置 left 大于 right 以在 x 方向上镜像视频。

但是,IVMRMixerControl9 似乎已被弃用, 我使用的是 EVR 而不是 VMR, 也没有关于如何获得IVMRMixerControl9 的文档。

方法 F:编写我自己的 DirectShow 过滤器

除非必须,否则我不愿意尝试这个。 这将是一项重大投资, 而且我不确定它的性能是否足够。

方法 G:从 Media Foundation 重新开始

媒体基金会可能会让我解决这个问题, 因为它提供了“媒体基础转换”。 但甚至不清楚 Media Foundation 是否符合我的所有其他要求。

我很惊讶我正在寻找如此激进的解决方案 对于看起来如此标准的转换。 还存在哪些其他方法? 在我尝试过的方法中有什么我忽略的吗? 如何在 DirectShow 中水平镜像视频?

【问题讨论】:

  • EVR 等效于选项 E 是 IMFVideoMixerControl::SetStreamOutputRect,也许这是第一个尝试的东西。接口本身可以通过IMFGetService获取(同here解释)。与 VMR-9 不同,在 EVR 中,混合器与渲染器本身是分离的,因此接口不能直接使用。选项 F 肯定行得通,G+C 也行,但这是一个备用计划。
  • @RomanR。谢谢!我忘了提到我实际上也试过那个!可悲的是,它也有同样的问题。这次文档实际上提到了它:“......边界矩形必须完全适合组合矩形,并且不能垂直或水平翻转。”现在,我正在考虑使用 MF 重新开始,因为我的应用程序足够小,以至于这种“升级”可能是比编写自己的过滤器更好的选择......

标签: c++ winapi video transform directshow


【解决方案1】:

如果选项 E 不起作用(请参阅上面的评论;源矩形和目标矩形都不允许镜像),并且鉴于它是 DirectShow,我会提供查看选项 F。

但是,如果您以前从未这样做过,编写一个完整的过滤器可能不是那么简单。不过这里有一些捷径。您不需要开发完整的过滤器:至少可以使用两种替代方法来实现类似的功能:

  1. 带有ISampleGrabberCB::SampleCB 回调的样本抓取过滤器。您会发现很多关于此技术的提及:当插入到图形中时,您的代码可以接收到每个处理帧的回调。如果在回调中重新排列帧缓冲区中的像素,图像将被镜像。
  2. 在 DMO Wrapper Filter 的帮助下实现一个 DMO 并将其插入到过滤器图中。您将有机会以类似方式重新排列帧的像素,从而获得更多灵活性,但需要编写更多代码。

上面提到的两个都将更容易做到,因为您不必使用 DirectShow BaseClasses,它们在 2020 年众所周知已经过时了。

以上提到的都不需要了解 DirectShow 过滤器中的数据流。两者以及开发完整的 DirectShow 过滤器都假定您的代码支持以有限的一组像素格式重新排列。例如,您可以使用 24 位 RGB,或典型的网络摄像头格式,例如 NV12(现在)。

如果您的像素数据重新排列做得很好,无需对代码进行超级优化,您可以忽略性能影响——无论哪种方式,在大多数情况下都可以忽略。

我预计 Media Foundation 解决方案的集成会更加复杂,如果 Media Foundation 解决方案要真正得到很好的优化,则要复杂得多。

问题的复杂性首先是以下因素的组合。

首先,您混合了不同的解决方案:

  1. 直接在网络摄像头(驱动程序)中进行镜像,您的镜像设置会导致视频帧在一开始就已被镜像。
  2. 在数据流经管道时进行镜像。尽管这听起来很简单,但事实并非如此:有时帧尚未压缩(网络摄像头经常发送 JPEG),有时帧可以由视频内存支持,有多种像素格式等
  3. 镜像作为视频呈现。

您的方法 A 是上面的#1。但是,如果不支持尊重模式,则无法镜像。

理论上,EVR 渲染器#3 中的镜像显然是可能的。 EVR 使用 Direct3D 9 并在内部将表面(纹理)渲染到场景中,因此绝对有可能以镜像的方式设置表面的 3D 位置。但是,这里的问题是 API 设计和坐标检查阻止传递镜像参数。

然后 Direct3D 9 几乎已被弃用,DirectShow 本身甚至 DirectShow/Media Foundation 的 EVR 都与当前的 Direct3D 11 不兼容。即使可能存在通过硬件进行镜像的功能,但您可能很难使用它通过旧版 API。

当您想要一个简单的解决方案时,您在数据流经时受到镜像的限制,#2 就是这样。即使这与合理的性能影响相关,您也无需依赖特定的相机或视频硬件支持:您只需交换每一帧中的像素即可。

正如我所提到的,最简单的方法是使用 24 位 RGB 和/或 NV12 像素格式设置 SampleCB 回调。这也取决于您的应用程序正在做什么,但是如果没有此类信息,我会说实现 24 位 RGB 并拥有视频帧数据就足够了,您只需逐行交换三字节像素数据宽度/2次。如果应用程序管道允许,您可能希望有额外的代码路径来翻转 NV12,这很相似,但没有首先将视频转换为 RGB,因此效率更高。如果 NV12 不能工作,RGB24 将是备用代码路径。

另请参阅:Mirror effect with DirectShow.NET - 我似乎在 8 年前就已经解释过类似的内容。

【讨论】:

  • 感谢您的详细回答!实际上,我在方法 G 上取得了一些成功——在 Media Foundation 中重写,并使用视频处理器 MFT。这也解决了我正在解决的另一个问题:我的 DirectShow 程序的帧速率非常低,例如 10fps。我的 Media Foundation 程序的帧速率非常流畅。不过,我不明白为什么。不过,我仍在努力解决 Media Foundation 的一些问题,如果这不起作用,那么我可能会在编写过滤器时尝试您的指导。再次感谢您的详细解答!
猜你喜欢
  • 2018-05-31
  • 2021-07-02
  • 2012-10-18
  • 2010-10-07
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 2018-07-07
  • 1970-01-01
相关资源
最近更新 更多