[英]How can I horizontally mirror video in DirectShow?
我需要在屏幕上顯示本地網絡攝像頭stream,水平翻轉,使屏幕顯示為鏡像。 我有一個 DirectShow 圖表,它可以完成所有這些,除了鏡像圖像。 我嘗試了幾種方法來鏡像圖像,但都沒有奏效。
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);
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
。
IMFVideoProcessorControl::SetMirror
IMFVideoProcessorControl::SetMirror(MF_VIDEO_PROCESSOR_MIRROR)
聽起來像我想要的。 此IMFVideoProcessorControl
接口由Video Processor MFT實現。 不幸的是,這是一個媒體基礎轉換,我看不出如何在 DirectShow 中使用它。
Video Resizer DSP是“可以充當DMO的COM object”,所以理論上我可以在DirectShow中使用它。 不幸的是,我沒有使用 DMO 的經驗,而且無論如何,Video Resizer 的文檔都沒有說明它是否支持翻轉圖像。
IVMRMixerControl9::SetOutputRect
我找到IVMRMixerControl9::SetOutputRect
,它明確表示:
因為這個矩形存在於組合空間中,所以不存在“無效”矩形之類的東西。 例如,設置 left 大於 right 以在 x 方向上鏡像視頻。
但是, IVMRMixerControl9
似乎已被棄用,而且我使用的是 EVR 而不是 VMR,並且無論如何都沒有關於如何獲得IVMRMixerControl9
的文檔。
我不願意嘗試這個,除非我必須這樣做。 這將是一項重大投資,而且我不確定它的性能是否足夠。
Media Foundation 可能會讓我解決這個問題,因為它提供了“Media Foundation Transforms”。 但甚至不清楚媒體基金會是否符合我的所有其他要求。
我很驚訝我正在為看起來如此標准的轉換尋找如此激進的解決方案。 還存在哪些其他方法? 在我嘗試過的方法中有什么我忽略的嗎? 如何在 DirectShow 中水平鏡像視頻?
如果選項 E 不起作用(請參閱上面的評論;源矩形和目標矩形都不允許鏡像),並且鑒於它是 DirectShow,我會提供研究選項 F。
但是,如果您以前從未這樣做過,編寫一個完整的過濾器可能不是那么簡單。 不過這里有一些捷徑。 您不需要開發完整的過濾器:至少可以使用兩種替代方法來實現類似的功能:
ISampleGrabberCB::SampleCB
回調的示例抓取過濾器。 您會發現很多關於此技術的提及:當插入到圖形中時,您的代碼可以接收到每個處理幀的回調。 如果在回調中重新排列幀緩沖區中的像素,圖像將被鏡像。兩者都將更容易做到,因為您不必使用 DirectShow BaseClasses,后者在 2020 年眾所周知已過時。
兩者都不需要了解 DirectShow 過濾器中的數據流。 兩者以及開發完整的 DirectShow 過濾器都假定您的代碼支持以有限的一組像素格式重新排列。 例如,您可以使用 24 位 RGB 的 go 或典型的網絡攝像頭格式,例如 NV12(現在)。
如果您的像素數據重新排列做得很好,無需對代碼進行超級優化,您可以忽略性能影響——無論哪種方式,在大多數情況下都可以忽略。
我預計 Media Foundation 解決方案的集成會更加復雜,如果 Media Foundation 解決方案要真正得到很好的優化,則要復雜得多。
問題的復雜性首先是以下因素的組合。
首先,您混合了不同的解決方案:
您的方法 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.