繁体   English   中英

如何在 DirectShow 中水平镜像视频?

[英]How can I horizontally mirror video in DirectShow?

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

方法 A: VideoControlFlag_FlipHorizontal

我尝试在网络摄像头过滤器的 output 引脚上设置VideoControlFlag_FlipHorizontal ntal 标志,如下所示:

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 object”,所以理论上我可以在DirectShow中使用它。 不幸的是,我没有使用 DMO 的经验,而且无论如何,Video Resizer 的文档都没有说明它是否支持翻转图像。

方法 E: IVMRMixerControl9::SetOutputRect

我找到IVMRMixerControl9::SetOutputRect ,它明确表示:

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

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

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

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

方法 G:从媒体基金会重新开始

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

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

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

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

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

两者都将更容易做到,因为您不必使用 DirectShow BaseClasses,后者在 2020 年众所周知已过时。

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

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

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

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

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

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

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

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

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

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

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

另请参阅: DirectShow.NET 的镜像效果- 我似乎在 8 年前已经解释过类似的内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM