手順 5: メディア セッション イベントを処理する
このトピックは、 Media Foundation を使用してメディア ファイルを再生する方法に関するチュートリアルの手順 5 です。 完全なコードは、「 メディア セッション再生の例」のトピックに示されています。
このトピックの背景については、「 Media Event Generators」を参照してください。 このトピックは、次のセクションで構成されています。
セッション イベントの取得
メディア セッションからイベントを取得するために、CPlayer オブジェクトは、「手順 4: メディア セッションを作成する」に示すように、IMFMediaEventGenerator::BeginGetEvent メソッドを呼び出します。 このメソッドは非同期です。つまり、すぐに呼び出し元に戻ります。 次のセッション イベントが発生すると、メディア セッションは CPlayer オブジェクトの IMFAsyncCallback::Invoke メソッドを呼び出します。
Invoke は、アプリケーション スレッドからではなく、ワーカー スレッドから呼び出されることを覚えておく必要があります。 したがって、 Invoke の実装はマルチスレッド セーフである必要があります。 1 つの方法は、クリティカル セクションを使用してメンバー データを保護することです。 ただし、 クラスには CPlayer
別の方法が示されています。
- Invoke メソッドでは、CPlayer オブジェクトによってWM_APP_PLAYER_EVENTメッセージがアプリケーションにポストされます。 message パラメーターは IMFMediaEvent ポインターです。
- アプリケーションは 、WM_APP_PLAYER_EVENT メッセージを受信します。
- アプリケーションは、 メソッドを
CPlayer::HandleEvent
呼び出し、 IMFMediaEvent ポインターを渡します。 - メソッドは
HandleEvent
イベントに応答します。
次のコードは 、Invoke メソッドを示しています。
// Callback for the asynchronous BeginGetEvent method.
HRESULT CPlayer::Invoke(IMFAsyncResult *pResult)
{
MediaEventType meType = MEUnknown; // Event type
IMFMediaEvent *pEvent = NULL;
// Get the event from the event queue.
HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent);
if (FAILED(hr))
{
goto done;
}
// Get the event type.
hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
if (meType == MESessionClosed)
{
// The session was closed.
// The application is waiting on the m_hCloseEvent event handle.
SetEvent(m_hCloseEvent);
}
else
{
// For all other events, get the next event in the queue.
hr = m_pSession->BeginGetEvent(this, NULL);
if (FAILED(hr))
{
goto done;
}
}
// Check the application state.
// If a call to IMFMediaSession::Close is pending, it means the
// application is waiting on the m_hCloseEvent event and
// the application's message loop is blocked.
// Otherwise, post a private window message to the application.
if (m_state != Closing)
{
// Leave a reference count on the event.
pEvent->AddRef();
PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT,
(WPARAM)pEvent, (LPARAM)meType);
}
done:
SafeRelease(&pEvent);
return S_OK;
}
Invoke メソッドは、次の手順を実行します。
- IMFMediaEventGenerator::EndGetEvent を呼び出してイベントを取得します。 このメソッドは、 IMFMediaEvent インターフェイスへのポインターを返します。
- IMFMediaEvent::GetType を呼び出してイベント コードを取得します。
- イベント コードが MESessionClosed の場合は、SetEvent を呼び出して m_hCloseEvent イベントを設定します。 この手順の理由については、「 手順 7: メディア セッションをシャットダウンする」およびコード コメントで説明されています。
- その他のすべてのイベント コードについては、 IMFMediaEventGenerator::BeginGetEvent を呼び出して、次のイベントを要求します。
- WM_APP_PLAYER_EVENT メッセージをウィンドウに投稿します。
次のコードは、アプリケーションが WM_APP_PLAYER_EVENT メッセージを受信したときに呼び出される HandleEvent メソッドを示しています。
HRESULT CPlayer::HandleEvent(UINT_PTR pEventPtr)
{
HRESULT hrStatus = S_OK;
MediaEventType meType = MEUnknown;
IMFMediaEvent *pEvent = (IMFMediaEvent*)pEventPtr;
if (pEvent == NULL)
{
return E_POINTER;
}
// Get the event type.
HRESULT hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
// Get the event status. If the operation that triggered the event
// did not succeed, the status is a failure code.
hr = pEvent->GetStatus(&hrStatus);
// Check if the async operation succeeded.
if (SUCCEEDED(hr) && FAILED(hrStatus))
{
hr = hrStatus;
}
if (FAILED(hr))
{
goto done;
}
switch(meType)
{
case MESessionTopologyStatus:
hr = OnTopologyStatus(pEvent);
break;
case MEEndOfPresentation:
hr = OnPresentationEnded(pEvent);
break;
case MENewPresentation:
hr = OnNewPresentation(pEvent);
break;
default:
hr = OnSessionEvent(pEvent, meType);
break;
}
done:
SafeRelease(&pEvent);
return hr;
}
このメソッドは 、IMFMediaEvent::GetType を呼び出してイベントの種類を取得し、 IMFMediaEvent::GetStatus を呼び出して、イベントに関連付けられているエラー コードの成功を取得します。 次に実行されるアクションは、イベント コードによって異なります。
MESessionTopologyStatus
MESessionTopologyStatus イベントは、トポロジの状態の変化を通知します。 イベント オブジェクトの MF_EVENT_TOPOLOGY_STATUS 属性には、状態が含まれています。 この例では、目的の値は MF_TOPOSTATUS_READYのみです。これは、再生を開始する準備ができていることを示します。
HRESULT CPlayer::OnTopologyStatus(IMFMediaEvent *pEvent)
{
UINT32 status;
HRESULT hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status);
if (SUCCEEDED(hr) && (status == MF_TOPOSTATUS_READY))
{
SafeRelease(&m_pVideoDisplay);
// Get the IMFVideoDisplayControl interface from EVR. This call is
// expected to fail if the media file does not have a video stream.
(void)MFGetService(m_pSession, MR_VIDEO_RENDER_SERVICE,
IID_PPV_ARGS(&m_pVideoDisplay));
hr = StartPlayback();
}
return hr;
}
メソッドは CPlayer::StartPlayback
、「 手順 6: 再生の制御」に示されています。
この例では、MFGetService を呼び出して、拡張ビデオ レンダラー (EVR) から IMFVideoDisplayControl インターフェイスを取得します。 このインターフェイスは、「 手順 6: 再生の制御」にも示されているビデオ ウィンドウの再描画とサイズ変更を処理するために必要です。
MEEndOfPresentation
MEEndOfPresentation イベントは、再生がファイルの末尾に達したことを通知します。 メディア セッションは自動的に停止状態に切り替わります。
// Handler for MEEndOfPresentation event.
HRESULT CPlayer::OnPresentationEnded(IMFMediaEvent *pEvent)
{
// The session puts itself into the stopped state automatically.
m_state = Stopped;
return S_OK;
}
MENewPresentation
MENewPresentation イベントは、新しいプレゼンテーションの開始を通知します。 イベント データは、新しいプレゼンテーション の IMFPresentationDescriptor ポインターです。
多くの場合、このイベントはまったく受け取りません。 その場合は、「手順 3: メディア ファイルを開く」に示すように、IMFPresentationDescriptor ポインターを使用して新しい再生トポロジを作成します。 次に、メディア セッションで新しいトポロジをキューに入れます。
// Handler for MENewPresentation event.
//
// This event is sent if the media source has a new presentation, which
// requires a new topology.
HRESULT CPlayer::OnNewPresentation(IMFMediaEvent *pEvent)
{
IMFPresentationDescriptor *pPD = NULL;
IMFTopology *pTopology = NULL;
// Get the presentation descriptor from the event.
HRESULT hr = GetEventObject(pEvent, &pPD);
if (FAILED(hr))
{
goto done;
}
// Create a partial topology.
hr = CreatePlaybackTopology(m_pSource, pPD, m_hwndVideo,&pTopology);
if (FAILED(hr))
{
goto done;
}
// Set the topology on the media session.
hr = m_pSession->SetTopology(0, pTopology);
if (FAILED(hr))
{
goto done;
}
m_state = OpenPending;
done:
SafeRelease(&pTopology);
SafeRelease(&pPD);
return S_OK;
}
次へ: 手順 6: 再生を制御する
関連トピック