IMediaEventEx::SetNotifyWindowを使った再生終了通知

先ほどの例では、RenderFile()を使ってMFCで作成したウィンドウでMPEGファイルを再生する方法を解説しました。 しかし、先ほどの例では、映像再生終了までWaitForCompletion()でブロックしてしまっていました。 ここでは、再生の終了などのイベントをウィンドウメッセージとして受け取る方法を説明します。 (注意)ここの例では、サンプルを簡潔にするためにエラー処理を書いていません。

とりあえず、コードを書いてみる

まず、何も考えずに以下のコードを書いてみましょう。 先ほどを同じく、MFCを使って「C:\DXSDK\Samples\media\water.mpg」というMPEGファイルを再生します。


#include <afxwin.h>

#include <dshow.h>

#define	FILENAME L"c:\\DXSDK\\Samples\\Media\\butterfly.mpg"

#define	WM_GRAPH_NOTIFY		(WM_APP + 1)

class CMainWin : public CFrameWnd
{
public:
	CMainWin();
	~CMainWin();
	DECLARE_MESSAGE_MAP()

private:
	IGraphBuilder *pGraphBuilder;
	IMediaControl *pMediaControl;
	IMediaEventEx *pMediaEventEx;
	IVideoWindow *pVideoWindow;

	bool bRunning;

protected:
	afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
	LRESULT OnGraphNotify(WPARAM wParam, LPARAM lParam);
};

CMainWin::CMainWin()
{
 Create(NULL, "Sample");

 bRunning = false;
 CoInitialize(NULL);
}

CMainWin::~CMainWin()
{
 if (pMediaEventEx != NULL) {
	pMediaEventEx->Release();
 }

 if (pMediaControl != NULL) {
	pMediaControl->Release();
 }

 if (pVideoWindow != NULL) {
	pVideoWindow->Release();
 }

 if (pGraphBuilder != NULL) {
	pGraphBuilder->Release();
 }

 CoUninitialize();
}

void
CMainWin::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 if (bRunning == false) {
	CoCreateInstance(CLSID_FilterGraph,
		NULL,
		CLSCTX_INPROC,
		IID_IGraphBuilder,
		(LPVOID *)&pGraphBuilder);

	pGraphBuilder->QueryInterface(IID_IMediaControl,
		(LPVOID *)&pMediaControl);

	pGraphBuilder->QueryInterface(IID_IMediaEventEx,
		(LPVOID *)&pMediaEventEx);
	pMediaEventEx->SetNotifyWindow((OAHWND)GetSafeHwnd(),
					WM_GRAPH_NOTIFY, NULL);

	pMediaControl->RenderFile(FILENAME);

	pGraphBuilder->QueryInterface(IID_IVideoWindow,
		(LPVOID *)&pVideoWindow);
	pVideoWindow->put_Owner((OAHWND)GetSafeHwnd());

	pVideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPSIBLINGS);

	RECT rect;
	GetClientRect(&rect);
	pVideoWindow->SetWindowPosition(0, 0,
				rect.right - rect.left,
				rect.bottom - rect.top);

	pVideoWindow->put_Visible(OATRUE);
	pVideoWindow->SetWindowForeground(OATRUE);

	pMediaControl->Run();

	bRunning = true;
 }
}

// イベントが発生すると呼び出される
LRESULT
CMainWin::OnGraphNotify(WPARAM wParam, LPARAM lParam)
{
 long evCode;
 LONG param1, param2;

 // イベントを全て取得
 while (SUCCEEDED(pMediaEventEx->GetEvent(&evCode,
					&param1, &param2, 0))) {
	pMediaEventEx->FreeEventParams(evCode, param1, param2);

	switch (evCode) {
		case EC_COMPLETE:
			// 再生終了
			MessageBox("EC_COMPLETE");
			break;
	}
 }

 return NOERROR;
}

class CApp : public CWinApp
{
public:
	BOOL InitInstance();
};

BOOL CApp::InitInstance()
{
 m_pMainWnd = new CMainWin;
 m_pMainWnd->ShowWindow(m_nCmdShow);
 m_pMainWnd->UpdateWindow();

 return true;
}

BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
	ON_WM_CHAR()
	ON_MESSAGE(WM_GRAPH_NOTIFY, OnGraphNotify)
END_MESSAGE_MAP()

CApp App;

上記コードは、まず、最初にウィンドウを表示します。 表示されたウィンドウで何らかのキーボードをたたくと、MPEGファイルの再生が始まります。 MPEGファイルの再生が終了すると、OnGraphNotify()が呼び出されます。 OnGraphNotify()は、MessageBoxで「EC_COMPLETE」と表示します。 それ以上は何も動かないので、ウィンドウ右上の×をクリックして終了してください。

このプログラムのポイントを以下に示します。

  • IMediaEventExインターフェースへのポインタを取得します。
  • ウィンドウイベントの値を定義します。上記例では、WM_GRAPH_NOTIFYというdefineをしました。
  • Notificationを送信するウィンドウ(OAHWND)をSetNotifyWindow()で設定します。
  • ON_MESSAGE()を使ってイベントハンドラを設定します。
  • IMediaEventEx::GetEvent()でイベントを取得します。

IMediaEventExはIMediaEventインターフェースを継承しています。 そのため、上記サンプルではIMediaEvent::GetEvent()をIMediaEventEx *pMediaEventExから使っています。

上記コードは、SetNotifyWindow()の利用例を示すために色々省いて書いているので、全く実用性がありません。 用途に応じて、Run()や各種ポインタの保持方法を変えてください。(^_^;)