Étape 5 : Gérer les événements de session multimédia

Cette rubrique est l’étape 5 du tutoriel Guide pratique pour lire des fichiers multimédias avec Media Foundation. Le code complet est illustré dans la rubrique Exemple de lecture de session multimédia.

Pour plus d’informations sur cette rubrique, consultez Générateurs d’événements multimédias. Cette rubrique contient les sections suivantes :

Obtention d’événements de session

Pour obtenir des événements à partir de la session multimédia, l’objet CPlayer appelle la méthode IMFMediaEventGenerator::BeginGetEvent , comme indiqué dans Étape 4 : Créer la session multimédia. Cette méthode est asynchrone, ce qui signifie qu’elle retourne immédiatement à l’appelant. Lorsque l’événement de session suivant se produit, media session appelle la méthode IMFAsyncCallback::Invoke de l’objet CPlayer.

Il est important de se rappeler qu’Invoke est appelé à partir d’un thread de travail, et non à partir du thread d’application. Par conséquent, l’implémentation d’Invoke doit être multithread-safe. Une approche consisterait à protéger les données membres avec une section critique. Toutefois, la CPlayer classe présente une autre approche :

  1. Dans la méthode Invoke , l’objet CPlayer publie un message WM_APP_PLAYER_EVENT à l’application. Le paramètre de message est un pointeur IMFMediaEvent .
  2. L’application reçoit le message WM_APP_PLAYER_EVENT .
  3. L’application appelle la CPlayer::HandleEvent méthode, en passant le pointeur IMFMediaEvent .
  4. La HandleEvent méthode répond à l’événement.

Le code suivant montre la méthode 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;
}

La méthode Invoke effectue les étapes suivantes :

  1. Appelez IMFMediaEventGenerator::EndGetEvent pour obtenir l’événement. Cette méthode retourne un pointeur vers l’interface IMFMediaEvent .
  2. Appelez IMFMediaEvent::GetType pour obtenir le code d’événement.
  3. Si le code de l’événement est MESessionClosed, appelez SetEvent pour définir l’événement m_hCloseEvent . La raison de cette étape est expliquée dans Étape 7 : Arrêter la session multimédia, ainsi que dans les commentaires de code.
  4. Pour tous les autres codes d’événement, appelez IMFMediaEventGenerator::BeginGetEvent pour demander l’événement suivant.
  5. Publiez un message WM_APP_PLAYER_EVENT dans la fenêtre.

Le code suivant montre la méthode HandleEvent, appelée lorsque l’application reçoit le message WM_APP_PLAYER_EVENT :

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;
}

Cette méthode appelle IMFMediaEvent::GetType pour obtenir le type d’événement et IMFMediaEvent::GetStatus pour obtenir la réussite du code d’échec associé à l’événement. L’action suivante dépend du code d’événement.

MESessionTopologyStatus

L’événement MESessionTopologyStatus signale un changement dans la status de la topologie. L’attribut MF_EVENT_TOPOLOGY_STATUS de l’objet événement contient le status. Pour cet exemple, la seule valeur intéressante est MF_TOPOSTATUS_READY, ce qui indique que la lecture est prête à démarrer.

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;
}

La CPlayer::StartPlayback méthode est illustrée à l’étape 6 : Lecture de contrôle.

Cet exemple appelle également MFGetService pour obtenir l’interface IMFVideoDisplayControl à partir du convertisseur vidéo amélioré (EVR). Cette interface est nécessaire pour gérer le repeint et le redimensionnement de la fenêtre vidéo, également illustré à l’étape 6 : Contrôle de lecture.

MEEndOfPresentation

L’événement MEEndOfPresentation signale que la lecture a atteint la fin du fichier. La session multimédia revient automatiquement à l’état arrêté.

//  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

L’événement MENewPresentation signale le début d’une nouvelle présentation. Les données d’événement sont un pointeur IMFPresentationDescriptor pour la nouvelle présentation.

Dans de nombreux cas, vous ne recevrez pas cet événement du tout. Si c’est le cas, utilisez le pointeur IMFPresentationDescriptor pour créer une topologie de lecture, comme indiqué dans Étape 3 : Ouvrir un fichier multimédia. Ensuite, mettez en file d’attente la nouvelle topologie sur la session multimédia.

//  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;
}

Suivant : Étape 6 : Contrôle de lecture

Lecture audio/vidéo

Guide pratique pour lire des fichiers multimédias avec Media Foundation