簡體   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