これまでの記事

Visual Studio 2017 で DirectShow 開発環境を準備する
DirectShow フィルタグラフあれこれ
DirectShow ビデオキャプチャアプリの例

ビデオレンダラあれこれ

今回はDirectShowで映像を再生する時の出力先となるフィルタ、ビデオレンダラについて紹介します。

フィルタ名 環境 CLSID
ビデオレンダラフィルタ Windows 98以降 CLSID_VideoRenderer
Video Mixing Renderer フィルタ 7 (VMR7) Windows XP以降
DirectX 7以降
CLSID_VideoMixingRenderer
Video Mixing Renderer フィルタ 9 (VMR9) Windows XP以降
DirectX 9以降
CLSID_VideoMixingRenderer9
EnhancedVideoRenderer (EVR) Windows Vista以降 CLSID_EnhancedVideoRenderer

今となってはビデオレンダラを使う必要性は無いと思います。新しいレンダラほど高機能ですが、VMR7 の方がパフォーマンスが出る場面もあるようで、一概に新しいものを選ぶのが正解とは言えないようです。

EnhancedVideoRenderer が使えない?

Windows Vista 以降は標準で利用できていた EVR ですが、Windows 10 では標準から外れてしまっています。Media Playerと関連コンポーネントが "Media Feature Pack for N and KN versions of Windows 10" として配布されており、これをインストールする必要があります。

Media Feature Pack for N and KN versions of Windows 10

アプリケーションを開発する立場としては、EVR を採用しづらくなってしまいましたね…。

それぞれのビデオレンダラの使い方

以下、各ビデオレンダラの操作のサンプルです。

ライブラリ

lib ファイルはプロジェクト設定で追加しても問題ないです。個人的には stdafx.h に書くのが好きです。

VMR7
#include <dshow.h>
#pragma comment(lib, "strmiids.lib")

VMR7 は特に追加のヘッダーは不要です。

VMR9
#include <d3d9.h>
#include <vmr9.h>
#pragma comment(lib, "strmiids.lib")

VMR9 は d3d9.h と vmr9.h をインクルードします。

EVR
#include <evr.h>
#include <evr9.h>
#include <d3d9.h>
#pragma comment(lib, "strmiids.lib")

EVR は evr.h をインクルードします。ビデオに画像を重ねたい場合は、追加で evr9.h と d3d9.h が必要です。

初期化

何らかの寿命が長いクラスのインスタンスにて、インターフェイスポインタを保持する想定です。

今回の例は、レンダラをウィンドウレスモード(ビデオレンダラでは再生ウィンドウを用意せず、こちらが指定したウィンドウに動画を描画するモード)として初期化します。また、ビデオに画像を重ねる為に、ミキサーモードを有効にします。

VMR7
CComPtr<IBaseFilter>                m_pBaseFilter;              // IBaseFilter
CComPtr<IVMRDeinterlaceControl>     m_pDeinterlaceControl;      // IVMRDeinterlaceControl       
CComPtr<IVMRAspectRatioControl>     m_pAspectRatioControl;      // IVMRAspectRatioControl
CComPtr<IVMRWindowlessControl>      m_pWindowlessControl;       // IVMRWindowlessControl
CComPtr<IVMRMixerBitmap>            m_pMixerBitmap;             // IVMRMixerBitmap

HRESULT hr;

hr = m_pBaseFilter.CoCreateInstance(CLSID_VideoMixingRenderer);

// IBaseFilterからIVMRDeinterlaceControlを取得
hr = m_pBaseFilter.QueryInterface<IVMRDeinterlaceControl>(&m_pDeinterlaceControl);

// IBaseFilterからIVMRAspectRatioControlを取得
hr = m_pBaseFilter.QueryInterface<IVMRAspectRatioControl>(&m_pAspectRatioControl);

// IBaseFilterからIVMRFilterConfigを取得
CComPtr<IVMRFilterConfig> pVMRFilterConfig;
hr = m_pBaseFilter.QueryInterface<IVMRFilterConfig>(&pVMRFilterConfig);

// ミキサーモードを使用
hr = pVMRFilterConfig->SetNumberOfStreams(1);

// レンダリングモードをウィンドウレスモードに設定
hr = pVMRFilterConfig->SetRenderingMode(VMRMode_Windowless);

// IBaseFilterからIVMRWindowlessControlを取得
hr = m_pBaseFilter.QueryInterface<IVMRWindowlessControl>(&m_pWindowlessControl);

// IBaseFilterからIVMRMixerBitmapを取得
hr = m_pBaseFilter.QueryInterface<IVMRMixerBitmap>(&m_pMixerBitmap);

// オーバレイサーフェイスを使用する
hr = pVMRFilterConfig->SetRenderingPrefs(RenderPrefs_ForceOverlays);

// ビデオを描画するウィンドウを設定
hr = m_pWindowlessControl->SetVideoClippingWindow(m_pWnd->GetSafeHwnd());

IVMRWindowlessControl はウィンドウレスモードに設定した後に取得します。

VMR9
CComPtr<IBaseFilter>                m_pBaseFilter;              // CComPtr<IVMRWindowlessControl9>      m_pWindowlessControl9;      // IVMRWindowlessControl9
CComPtr<IVMRDeinterlaceControl9>    m_pDeinterlaceControl9;     // IVMRDeinterlaceControl9
CComPtr<IVMRAspectRatioControl9>    m_pAspectRatioControl9;     // IVMRAspectRatioControl9
CComPtr<IVMRMixerBitmap9>           m_pMixerBitmap9;            // IVMRMixerBitmap9

HRESULT hr;

hr = m_pBaseFilter.CoCreateInstance(CLSID_VideoMixingRenderer9)

// IBaseFilterからIVMRDeinterlaceControl9を取得
hr = m_pBaseFilter.QueryInterface<IVMRDeinterlaceControl9>(&m_pDeinterlaceControl9);

// IBaseFilterからIVMRAspectRatioControl9を取得
hr = m_pBaseFilter.QueryInterface<IVMRAspectRatioControl9>(&m_pAspectRatioControl9);

// IBaseFilterからIVMRFilterConfig9を取得
CComPtr<IVMRFilterConfig9> pVMRFilterConfig9;
hr = m_pBaseFilter.QueryInterface<IVMRFilterConfig9>(&pVMRFilterConfig9);

// ミキサーモードを使用
hr = pVMRFilterConfig9->SetNumberOfStreams(1);

// レンダリングモードをウィンドウレスモードに設定
hr = pVMRFilterConfig9->SetRenderingMode(VMR9Mode_Windowless);

// IBaseFilterからIVMRWindowlessControl9を取得
hr = m_pBaseFilter.QueryInterface<IVMRWindowlessControl9>(&m_pWindowlessControl9);

// IBaseFilterからIVMRMixerBitmap9を取得
hr = m_pBaseFilter.QueryInterface<IVMRMixerBitmap9>(&m_pMixerBitmap9);

// ビデオを描画するウィンドウを設定
hr = m_pWindowlessControl9->SetVideoClippingWindow(m_pWnd->GetSafeHwnd());

処理の流れはほとんど VMR7 と同じです。レンダリングに使用するサーフェイスは常にオーバーレイサーフェイスなので、レンダリングサーフェイスを設定する処理がありません。

EVR
CComPtr<IBaseFilter>                m_pBaseFilter;              // CComPtr<IMFVideoDisplayControl>      m_pMFVideoDisplayControl;   // IMFVideoDisplayControl
CComPtr<IMFVideoMixerBitmap>        m_pMFVideoMixerBitmap;      // IMFVideoMixerBitmap
CComPtr<IMFVideoMixerControl>       m_pMFVideoMixerControl;     // IMFVideoMixerControl
CComPtr<IMFVideoPositionMapper>     m_pMFVideoPositionMapper;   // IMFVideoPositionMapper

HRESULT hr;

hr = m_pBaseFilter.CoCreateInstance(CLSID_EnhancedVideoRenderer);

// IBaseFilterからIMFGetServiceを取得
CComPtr<IMFGetService> pMFGetService;
hr = m_pBaseFilter.QueryInterface<IMFGetService>(&pMFGetService);

// IMFGetServiceからIMFVideoDisplayControlを取得
hr = pMFGetService->GetService(MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl, reinterpret_cast<LPVOID*>(&m_pMFVideoDisplayControl));

// IMFGetServiceからIMFVideoMixerBitmapを取得
hr = pMFGetService->GetService(MR_VIDEO_MIXER_SERVICE, IID_IMFVideoMixerBitmap, reinterpret_cast<LPVOID*>(&m_pMFVideoMixerBitmap));

// IMFGetServiceからIMFVideoMixerControlを取得
hr = pMFGetService->GetService(MR_VIDEO_MIXER_SERVICE, IID_IMFVideoMixerControl, reinterpret_cast<LPVOID*>(&m_pMFVideoMixerControl));

// IMFGetServiceからIMFVideoPositionMapperを取得
hr = pMFGetService->GetService(MR_VIDEO_RENDER_SERVICE, IID_IMFVideoPositionMapper, reinterpret_cast<LPVOID*>(&m_pMFVideoPositionMapper));

// ビデオを描画するウィンドウを設定
hr = m_pMFVideoDisplayControl->SetVideoWindow(m_pWnd->GetSafeHwnd());

IBaseFilter の QueryInterface ではなく、IMFGetService の GetService からインターフェイスを取得します。EVR は常にミキサーがロードされるので、ミキサーモードを設定する処理がありません。

再生しているビデオのサイズを取得する

ビデオレンダラが再生しているビデオストリームのサイズを取得します。フィルタグラフが再生状態でなければ取得出来ない点に注意が必要です。

VMR7
HRESULT hr;
LONG    width;
LONG    height;
CRect   rect;

hr = m_pWindowlessControl->GetNativeVideoSize(&width, &height, NULL, NULL);

if ( hr != S_OK ) rect.SetRectEmpty();

rect.SetRect(0, 0, width, height);

サンプルでは取得した width と height を CRect に設定しています。GetNativeVideoSize はビデオのアスペクト比も取得出来ますが、サンプルでは無視しています。

VMR9
HRESULT hr;
LONG    width;
LONG    height;
CRect   rect;

hr = m_pWindowlessControl9->GetNativeVideoSize(&width, &height, NULL, NULL);

if ( hr != S_OK ) rect.SetRectEmpty();

rect.SetRect(0, 0, width, height);
EVR
HRESULT hr;
SIZE    video;
CRect   rect;

hr = m_pMFVideoDisplayControl->GetNativeVideoSize(&video, NULL);

if ( hr != S_OK ) rect.SetRectEmpty();

rect.SetRect(0, 0, video.cx, video.cy);

サイズの取得に SIZE 構造体を利用する点以外は VMR と同じですね。

ビデオの転送元矩形と転送先矩形を設定する

ビデオレンダラは転送元と転送先の矩形を設定しておかないと画像が表示されません。ソースからビデオのサイズが判明している場合もありますが、定まらない場合は GetNativeVideoSize で取得します。

  1. フィルタグラフを構築
  2. フィルタグラフを再生(この時点ではビデオが表示されない)
  3. GetNativeVideoSize でソースのビデオサイズを取得
  4. SetVideoPosition で転送元矩形と転送先矩形を設定
  5. ビデオが表示される

こういう流れです。

また、描画ウィンドウのサイズが変更される度に SetVideoPosition を行う必要があります。描画ウィンドウの WM_SIZE ハンドラから SetVideoPosition を呼び出して下さい。

VMR7
hr = m_pWindowlessControl->SetVideoPosition(&srcRect, &dstRect);
VMR9
hr = m_pWindowlessControl9->SetVideoPosition(&srcRect, &dstRect);

srcRect にビデオサイズ、dstRect にウィンドウのサイズが設定されているという仮定です。それぞれの CRect に設定する値次第で、ビデオの一部を表示したり、ウィンドウの一部にビデオを表示させることができます。
ビデオの全領域をウィンドウいっぱいに描画する場合、srcRect の top と left に 0 、ritht と bottom にビデオの縦横、dstRect の top と left に 0 、ritht と bottom にウィンドウの縦横を設定します。

EVR
// ノーマライズ矩形を作成
// 各項目が1.0を超えないよう注意
MFVideoNormalizedRect srcNRect;

srcNRect.top    = (float)srcRect.top    / (float)videoRect.Height();
srcNRect.left   = (float)srcRect.left   / (float)videoRect.Width();
srcNRect.right  = (float)srcRect.right  / (float)videoRect.Width();
srcNRect.bottom = (float)srcRect.bottom / (float)videoRect.Height();

hr = m_pMFVideoDisplayControl->SetVideoPosition(&srcNRect, &dstRect);

EVR では3つの CRect が必要となります。

  • ソースビデオサイズ
  • 描画するビデオ領域のサイズ
  • 描画先ウィンドウのサイズ

サンプルは videoRect にソースビデオサイズ、srcNRect に描画領域のサイズ、dstRect にウィンドウサイズが設定されているという仮定です。IMFVideoDisplayControl の SetVideoPosition には MFVideoNormalizedRect を渡す必要があり、各座標にソースビデオサイズと描画領域サイズの比率を 0.0 ~ 1.0 の範囲で指定しなければいけません。

アスペクト比維持モードを設定する

ビデオレンダラでは、転送元矩形と転送先矩形の比率が異なる時、画像は歪むがビデオを伸張して描画領域いっぱいに表示させるか、黒帯を表示してアスペクト比を維持する(レターボックス表示)か選択できます。フィルタグラフの再生中に切り替え可能です。

VMR7
HRESULT hr;

// アスペクト比を維持する
hr = m_pAspectRatioControl->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);

// アスペクト比を維持しない
hr = m_pAspectRatioControl->SetAspectRatioMode(VMR_ARMODE_NONE);
VMR9
HRESULT hr;

// アスペクト比を維持する
hr = m_pAspectRatioControl9->SetAspectRatioMode(VMR9ARMode_LetterBox);

// アスペクト比を維持しない
hr = m_pAspectRatioControl9->SetAspectRatioMode(VMR9ARMode_None);
EVR
HRESULT hr;

// アスペクト比を維持する
hr = m_pMFVideoDisplayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);

// アスペクト比を維持しない
hr = m_pMFVideoDisplayControl->SetAspectRatioMode(MFVideoARMode_None);

ビデオを再描画する

描画先ウィンドウで WM_PAINT イベントが発生した時、ビデオレンダラで再描画を指示する必要があります。描画先ウィンドウの WM_PAINT ハンドラから RepaintVideo を呼び出します。

VMR7
void CFrameWnd::OnPaint() 
{
    if ( // フィルタグラフを再生中の場合 // )
    {
        PAINTSTRUCT ps;
        HDC         hDC;
        HRESULT     hr;

        hDC = ::BeginPaint(GetSafeHwnd(), &ps);
        hr = m_pWindowlessControl->RepaintVideo(GetSafeHwnd(), hDC);
        ::EndPaint(GetSafeHwnd(), &ps);
    }
    else
    {
        CPaintDC dc(this);

        // 黒いブラシを作成
        CBrush backBrush(RGB(0, 0, 0));

        // 古いブラシを保存
        CBrush* pOldBrush = dc.SelectObject(&backBrush);

        // 塗りつぶし
        CRect rect;
        dc.GetClipBox(&rect);
        dc.PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);

         //ブラシを戻す
        dc.SelectObject(pOldBrush);
    }
}
VMR9
void CFrameWnd::OnPaint() 
{
    if ( // フィルタグラフを再生中の場合 // )
    {
        PAINTSTRUCT ps;
        HDC         hDC;
        HRESULT     hr;

        hDC = ::BeginPaint(GetSafeHwnd(), &ps);
        hr = m_pWindowlessControl9->RepaintVideo(GetSafeHwnd(), hDC);
        ::EndPaint(GetSafeHwnd(), &ps);
    }
    else
    {
        CPaintDC dc(this);

        // 黒いブラシを作成
        CBrush backBrush(RGB(0, 0, 0));

        // 古いブラシを保存
        CBrush* pOldBrush = dc.SelectObject(&backBrush);

        // 塗りつぶし
        CRect rect;
        dc.GetClipBox(&rect);
        dc.PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);

         //ブラシを戻す
        dc.SelectObject(pOldBrush);
    }
}
EVR
void CFrameWnd::OnPaint() 
{
    if ( // フィルタグラフを再生中の場合 // )
    {
        HRESULT hr;

        hr = m_pMFVideoDisplayControl->RepaintVideo();
    }
    else
    {
        CPaintDC dc(this);

        // 黒いブラシを作成
        CBrush backBrush(RGB(0, 0, 0));

        // 古いブラシを保存
        CBrush* pOldBrush = dc.SelectObject(&backBrush);

        // 塗りつぶし
        CRect rect;
        dc.GetClipBox(&rect);
        dc.PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);

         //ブラシを戻す
        dc.SelectObject(pOldBrush);
    }
}

EVR は再描画にデバイスコンテキストを必要としません。

ディスプレイモード変更を通知する

VMR7 と VMR9 では、フィルタグラフをウィンドウレスモードで再生中にディスプレイの設定が変更された時、ビデオレンダラにディスプレイモード変更を通知する必要があります。EVR ではこの処理は不要です。描画先ウィンドウなどの WM_DISPLAYCHANGE ハンドラから DisplayModeChanged を呼び出します。

VMR7
LRESULT CFrameWnd::OnDisplayChange(WPARAM wParam, LPARAM lParam)
{
    if ( // フィルタグラフを再生中の場合 // )
    {
        HRESULT hr;

        hr = m_pWindowlessControl->DisplayModeChanged();
    }
}
VMR9
LRESULT CFrameWnd::OnDisplayChange(WPARAM wParam, LPARAM lParam)
{
    if ( // フィルタグラフを再生中の場合 // )
    {
        HRESULT hr;

        hr = m_pWindowlessControl9->DisplayModeChanged();
    }
}

現在描画しているイメージを取得する

いわゆるスクリーンショットです。ディスプレイに表示されている通りのイメージを取得しますので、複数のビデオストリームをミキシングしていたり、画像を重ね合わせている場合、反映された状態となっています。

サンプルでは取得したイメージを GDI+ を使用して JPG 形式に変換し、保存しています。取得できるイメージのフォーマットはディスプレイモードに依存します。サイズは不定として、色数については今時はまずまず 32bit だと思われますが、サンプルでは 24bit と 32bit に対応しています。

VMR7
LPBYTE  pBuffer = NULL;
HRESULT hr;

hr = m_pWindowlessControl->GetCurrentImage(&pBuffer);

BITMAPINFOHEADER* pBih = reinterpret_cast<BITMAPINFOHEADER*>(pBuffer);

// ピクセルフォーマットを取得
PixelFormat pixelFormat;

if ( pBih->biBitCount == 24 )
{
    pixelFormat = PixelFormat24bppRGB;
}
else if ( pBih->biBitCount == 32 )
{
    pixelFormat = PixelFormat32bppRGB; 
}
else
{
    ::CoTaskMemFree(pBuffer);
    return; 
}

// ビットマップを作成
Bitmap bitmap(pBih->biWidth, pBih->biHeight, pixelFormat);

// ビットマップをロック
BitmapData      bmpData;
Gdiplus::Rect   rect;
rect.X      = 0;
rect.Y      = 0;
rect.Width  = pBih->biWidth;
rect.Height = pBih->biHeight;

bitmap.LockBits(&rect, ImageLockModeRead | ImageLockModeWrite, pixelFormat, &bmpData);

// ポインタを取得
byte* pDst = reinterpret_cast<byte*>(bmpData.Scan0);

// データをコピー
memcpy(pDst, pBuffer + sizeof(BITMAPINFOHEADER), (pBih->biHeight * pBih->biWidth * pBih->biBitCount / 8));

// ロックを解除
bitmap.UnlockBits(&bmpData);

// 上下反転
bitmap.RotateFlip(Rotate180FlipX);

// メモリ解放
::CoTaskMemFree(pBuffer);

// エンコーダを取得
CLSID encoderClsid;
GetEncoderClsid(_T("jpg"), encoderClsid);

bitmap.Save(_T("C:\Sample.jpg"), &encoderClsid, NULL); 
VMR9
LPBYTE  pBuffer = NULL;
HRESULT hr;

hr = m_pWindowlessControl9->GetCurrentImage(&pBuffer);

BITMAPINFOHEADER* pBih = reinterpret_cast<BITMAPINFOHEADER*>(pBuffer);

// ピクセルフォーマットを取得
PixelFormat pixelFormat;

if ( pBih->biBitCount == 24 )
{
    pixelFormat = PixelFormat24bppRGB;
}
else if ( pBih->biBitCount == 32 )
{
    pixelFormat = PixelFormat32bppRGB; 
}
else
{
    ::CoTaskMemFree(pBuffer);
    return; 
}

// ビットマップを作成
Bitmap bitmap(pBih->biWidth, pBih->biHeight, pixelFormat);

// ビットマップをロック
BitmapData      bmpData;
Gdiplus::Rect   rect;
rect.X      = 0;
rect.Y      = 0;
rect.Width  = pBih->biWidth;
rect.Height = pBih->biHeight;

bitmap.LockBits(&rect, ImageLockModeRead | ImageLockModeWrite, pixelFormat, &bmpData);

// ポインタを取得
byte* pDst = reinterpret_cast<byte*>(bmpData.Scan0);

// データをコピー
memcpy(pDst, pBuffer + sizeof(BITMAPINFOHEADER), (pBih->biHeight * pBih->biWidth * pBih->biBitCount / 8));

// ロックを解除
bitmap.UnlockBits(&bmpData);

// 上下反転
bitmap.RotateFlip(Rotate180FlipX);

// メモリ解放
::CoTaskMemFree(pBuffer);

// エンコーダを取得
CLSID encoderClsid;
GetEncoderClsid(_T("jpg"), encoderClsid);

bitmap.Save(_T("C:\Sample.jpg"), &encoderClsid, NULL); 

GetCurrentImage で DIBitmap のメモリ配列を取得できますので、後はそれをファイルに書き出すだけです。ここで確保されたメモリは CoTaskMemFree で解放しなければいけません。メモリの先頭が BITMAPINFOHEADER 構造体になっていますので、キャストして DIB の情報を取得し、GDI+ の Bitmap オブジェクトのインスタンスを生成、JPG に変換して保存しています。

EVR
LPBYTE              pBuffer = NULL;
BITMAPINFOHEADER    bih;
DWORD               size;
LONGLONG            timestamp;
HRESULT             hr;

::ZeroMemory(&bih, sizeof(bih));
bih.biSize = sizeof(BITMAPINFOHEADER);

hr = m_pMFVideoDisplayControl->GetCurrentImage(&bih, &pBuffer, &size, &timestamp);

BITMAPINFOHEADER* pBih = reinterpret_cast<BITMAPINFOHEADER*>(pBuffer);

// ピクセルフォーマットを取得
PixelFormat pixelFormat;

if ( pBih->biBitCount == 24 )
{
    pixelFormat = PixelFormat24bppRGB;
}
else if ( pBih->biBitCount == 32 )
{
    pixelFormat = PixelFormat32bppRGB; 
}
else
{
    ::CoTaskMemFree(pBuffer);
    return; 
}

// ビットマップを作成
Bitmap bitmap(pBih->biWidth, pBih->biHeight, pixelFormat);

// ビットマップをロック
BitmapData      bmpData;
Gdiplus::Rect   rect;
rect.X      = 0;
rect.Y      = 0;
rect.Width  = pBih->biWidth;
rect.Height = pBih->biHeight;

bitmap.LockBits(&rect, ImageLockModeRead | ImageLockModeWrite, pixelFormat, &bmpData);

// ポインタを取得
byte* pDst = reinterpret_cast<byte*>(bmpData.Scan0);

// データをコピー
memcpy(pDst, pBuffer + sizeof(BITMAPINFOHEADER), (pBih->biHeight * pBih->biWidth * pBih->biBitCount / 8));

// ロックを解除
bitmap.UnlockBits(&bmpData);

// 上下反転
bitmap.RotateFlip(Rotate180FlipX);

// メモリ解放
::CoTaskMemFree(pBuffer);

// エンコーダを取得
CLSID encoderClsid;
GetEncoderClsid(_T("jpg"), encoderClsid);

bitmap.Save(_T("C:\Sample.jpg"), &encoderClsid, NULL); 

ビデオに画像を重ねて表示する

VMR や EVR はビデオに特定の画像を重ね合わせてレンダリングすることが可能です。再生中、常に表示されるイメージやロゴマークに使われます。重ね合わせる時、アルファブレンディング値によって透明度を設定できます。

サンプルでは画像ファイルの処理に GDI+ を使用しています。重ねる画像の原点座標の色をカラーキーとし、透明度はゼロにしています。

VMR7
// ファイルを開く
Bitmap bitmap(_T("C:\Sample.jpg");

// 描画ウィンドウのGraphicsを取得
Graphics graphics(AfxGetMainWnd()->GetSafeHwnd());

// Graphicsから互換DCを取得
CDC* pDC = CDC::FromHandle(::CreateCompatibleDC(graphics.GetHDC()));

// HBITMAPを生成
HBITMAP hBitmap;
bitmap.GetHBITMAP(0, &hBitmap);

// ビットマップの情報を取得
BITMAP bm;
::ZeroMemory(&bm, sizeof(bm));

if ( ::GetObject(hBitmap, sizeof(bm), &bm) == 0 )
{
    pDC->DeleteDC();
    return;
}

// HDCでビットマップを選択、現在のHBITMAPを保存しておく
HBITMAP hOldBitmap = static_cast<HBITMAP>(::SelectObject(pDC->m_hDC, hBitmap));

// 画像原点からカラーキーを取得
Color color;
bitmap.GetPixel(0, 0, &color);

COLORREF colorKey;
colorKey = RGB(color.GetR(), color.GetG(), color.GetB());

HRESULT         hr;
RECT            rcBitmap;
NORMALIZEDRECT  rcDest;

rcBitmap.top    = 0;
rcBitmap.left   = 0;
rcBitmap.right  = bm.bmWidth;
rcBitmap.bottom = bm.bmHeight;

rcDest.top      = 0;
rcDest.left     = 0;
rcDest.right    = 1.0f;
rcDest.bottom   = 1.0f;

VMRALPHABITMAP alphaBitmap;
::ZeroMemory(&alphaBitmap, sizeof(alphaBitmap));

alphaBitmap.dwFlags     = VMRBITMAP_HDC | VMRBITMAP_SRCCOLORKEY;
alphaBitmap.hdc         = pDC->m_hDC;
alphaBitmap.pDDS        = NULL;
alphaBitmap.rSrc        = rcBitmap;
alphaBitmap.rDest       = rcDest;
alphaBitmap.fAlpha      = 1.0f;
alphaBitmap.clrSrcKey   = colorKey;

hr = m_pMixerBitmap->SetAlphaBitmap(&alphaBitmap);

::SelectObject(pDC->m_hDC, hOldBitmap);
::DeleteObject(hBitmap);
pDC->DeleteDC();
VMR9
// ファイルを開く
Bitmap bitmap(_T("C:\Sample.jpg");

// 描画ウィンドウのGraphicsを取得
Graphics graphics(AfxGetMainWnd()->GetSafeHwnd());

// Graphicsから互換DCを取得
CDC* pDC = CDC::FromHandle(::CreateCompatibleDC(graphics.GetHDC()));

// HBITMAPを生成
HBITMAP hBitmap;
bitmap.GetHBITMAP(0, &hBitmap);

// ビットマップの情報を取得
BITMAP bm;
::ZeroMemory(&bm, sizeof(bm));

if ( ::GetObject(hBitmap, sizeof(bm), &bm) == 0 )
{
    pDC->DeleteDC();
    return;
}

// HDCでビットマップを選択、現在のHBITMAPを保存しておく
HBITMAP hOldBitmap = static_cast<HBITMAP>(::SelectObject(pDC->m_hDC, hBitmap));

// 画像原点からカラーキーを取得
Color color;
bitmap.GetPixel(0, 0, &color);

COLORREF colorKey;
colorKey = RGB(color.GetR(), color.GetG(), color.GetB());

HRESULT             hr;
RECT                rcBitmap;
VMR9NormalizedRect  rcDest;

rcBitmap.top    = 0;
rcBitmap.left   = 0;
rcBitmap.right  = bm.bmWidth;
rcBitmap.bottom = bm.bmHeight;

rcDest.top      = 0;
rcDest.left     = 0;
rcDest.right    = 1.0f;
rcDest.bottom   = 1.0f;

VMR9AlphaBitmap alphaBitmap;
::ZeroMemory(&alphaBitmap, sizeof(alphaBitmap));

alphaBitmap.dwFlags         = VMR9AlphaBitmap_hDC | VMR9AlphaBitmap_SrcColorKey;
alphaBitmap.hdc             = pDC->m_hDC;
alphaBitmap.pDDS            = NULL;
alphaBitmap.rSrc            = rcBitmap;
alphaBitmap.rDest           = rcDest;
alphaBitmap.fAlpha          = 1.0f;
alphaBitmap.clrSrcKey       = colorKey;
alphaBitmap.dwFilterMode    = 0;

hr = m_pMixerBitmap9->SetAlphaBitmap(&alphaBitmap);

::SelectObject(pDC->m_hDC, hOldBitmap);
::DeleteObject(hBitmap);
pDC->DeleteDC();
EVR
// ファイルを開く
Bitmap bitmap(_T("C:\Sample.jpg");

// 描画ウィンドウのGraphicsを取得
Graphics graphics(AfxGetMainWnd()->GetSafeHwnd());

// Graphicsから互換DCを取得
CDC* pDC = CDC::FromHandle(::CreateCompatibleDC(graphics.GetHDC()));

// HBITMAPを生成
HBITMAP hBitmap;
bitmap.GetHBITMAP(0, &hBitmap);

// ビットマップの情報を取得
BITMAP bm;
::ZeroMemory(&bm, sizeof(bm));

if ( ::GetObject(hBitmap, sizeof(bm), &bm) == 0 )
{
    pDC->DeleteDC();
    return;
}

// HDCでビットマップを選択、現在のHBITMAPを保存しておく
HBITMAP hOldBitmap = static_cast<HBITMAP>(::SelectObject(pDC->m_hDC, hBitmap));

// 画像原点からカラーキーを取得
Color color;
bitmap.GetPixel(0, 0, &color);

COLORREF colorKey;
colorKey = RGB(color.GetR(), color.GetG(), color.GetB());

HRESULT             hr;
RECT                rcBitmap;
MFVideoNormalizedRect   rcDest;

rcBitmap.top    = 0;
rcBitmap.left   = 0;
rcBitmap.right  = bm.bmWidth;
rcBitmap.bottom = bm.bmHeight;

rcDest.top      = 0;
rcDest.left     = 0;
rcDest.right    = 1.0f;
rcDest.bottom   = 1.0f;

MFVideoAlphaBitmap alphaBitmap;
::ZeroMemory(&alphaBitmap, sizeof(alphaBitmap));

alphaBitmap.GetBitmapFromDC     = true;
alphaBitmap.bitmap.hdc          = pDC->m_hDC;
alphaBitmap.params.dwFlags      = MFVideoAlphaBitmap_Alpha | MFVideoAlphaBitmap_DestRect | MFVideoAlphaBitmap_SrcColorKey;
alphaBitmap.params.rcSrc        = rcBitmap;
alphaBitmap.params.nrcDest      = rcDest;
alphaBitmap.params.fAlpha       = 1.0f;
alphaBitmap.params.clrSrcKey    = colorKey;
alphaBitmap.params.dwFilterMode = 0;

hr = m_pMFVideoMixerBitmap->SetAlphaBitmap(&alphaBitmap);

::SelectObject(pDC->m_hDC, hOldBitmap);
::DeleteObject(hBitmap);
pDC->DeleteDC();

画像の重ね合わせについてはどのレンダラもほぼ同じ処理になります。

というわけで

VMR7、VMR9、EVR の各ビデオレンダラについて、基本的な使い方をサンプルとして説明させていただきました。これを足がかりにインターフェイスやメソッド、構造体について調べてもらえると、理解しやすいのではないでしょうか。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.