次の方法で共有


手順 5: メディア セッション イベントを処理する

このトピックは、 Media Foundation を使用してメディア ファイルを再生する方法に関するチュートリアルの手順 5 です。 完全なコードは、「 メディア セッション再生の例」のトピックに示されています。

このトピックの背景については、「 Media Event Generators」を参照してください。 このトピックは、次のセクションで構成されています。

セッション イベントの取得

メディア セッションからイベントを取得するために、CPlayer オブジェクトは、「手順 4: メディア セッションを作成する」に示すように、IMFMediaEventGenerator::BeginGetEvent メソッドを呼び出します。 このメソッドは非同期です。つまり、すぐに呼び出し元に戻ります。 次のセッション イベントが発生すると、メディア セッションは CPlayer オブジェクトの IMFAsyncCallback::Invoke メソッドを呼び出します。

Invoke は、アプリケーション スレッドからではなく、ワーカー スレッドから呼び出されることを覚えておく必要があります。 したがって、 Invoke の実装はマルチスレッド セーフである必要があります。 1 つの方法は、クリティカル セクションを使用してメンバー データを保護することです。 ただし、 クラスには CPlayer 別の方法が示されています。

  1. Invoke メソッドでは、CPlayer オブジェクトによってWM_APP_PLAYER_EVENTメッセージがアプリケーションにポストされます。 message パラメーターは IMFMediaEvent ポインターです。
  2. アプリケーションは 、WM_APP_PLAYER_EVENT メッセージを受信します。
  3. アプリケーションは、 メソッドを CPlayer::HandleEvent 呼び出し、 IMFMediaEvent ポインターを渡します。
  4. メソッドは 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 メソッドは、次の手順を実行します。

  1. IMFMediaEventGenerator::EndGetEvent を呼び出してイベントを取得します。 このメソッドは、 IMFMediaEvent インターフェイスへのポインターを返します。
  2. IMFMediaEvent::GetType を呼び出してイベント コードを取得します。
  3. イベント コードが MESessionClosed の場合は、SetEvent を呼び出して m_hCloseEvent イベントを設定します。 この手順の理由については、「 手順 7: メディア セッションをシャットダウンする」およびコード コメントで説明されています。
  4. その他のすべてのイベント コードについては、 IMFMediaEventGenerator::BeginGetEvent を呼び出して、次のイベントを要求します。
  5. 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: 再生を制御する

オーディオ/ビデオの再生

Media Foundation でメディア ファイルを再生する方法