Partager via


Tutoriel MFPlay : Lecture vidéo

[MFPlay est disponible pour une utilisation dans les systèmes d’exploitation spécifiés dans la section Configuration requise. Il sera peut-être modifié ou indisponible dans les versions ultérieures. ]

Ce tutoriel présente une application complète qui lit la vidéo à l’aide de MFPlay. Il est basé sur l’exemple du KIT de développement logiciel (SDK) SimplePlay .

Ce didacticiel contient les sections suivantes :

Pour une présentation plus détaillée de l’API MFPlay, consultez Prise en main avec MFPlay.

Spécifications

MFPlay nécessite Windows 7.

Fichiers d’en-tête et de bibliothèque

Incluez les fichiers d’en-tête suivants dans votre projet :

#define WINVER _WIN32_WINNT_WIN7

#include <new>
#include <windows.h>
#include <windowsx.h>
#include <mfplay.h>
#include <mferror.h>
#include <shobjidl.h>   // defines IFileOpenDialog
#include <strsafe.h>
#include <Shlwapi.h>

Lien vers les bibliothèques de code suivantes :

  • mfplay.lib
  • shlwapi.lib

Variables globales

Déclarez les variables globales suivantes :

IMFPMediaPlayer         *g_pPlayer = NULL;      // The MFPlay player object.
MediaPlayerCallback     *g_pPlayerCB = NULL;    // Application callback object.

BOOL                    g_bHasVideo = FALSE;

Ces variables seront utilisées comme suit :

g_hwnd

Handle de la fenêtre d’application.

g_bVideo

Valeur booléenne qui détermine si la vidéo est en cours de lecture.

g_pPlayer

Pointeur vers l’interface IMFPMediaPlayer . Cette interface est utilisée pour contrôler la lecture.

g_pCallback

Pointeur vers l’interface IMFPMediaPlayerCallback . L’application implémente cette interface de rappel pour obtenir des notifications à partir de l’objet lecteur.

Déclarer la classe de rappel

Pour obtenir des notifications d’événements à partir de l’objet player, l’application doit implémenter l’interface IMFPMediaPlayerCallback . Le code suivant déclare une classe qui implémente l’interface. La seule variable membre est m_cRef, qui stocke le nombre de références.

Les méthodes IUnknown sont implémentées en ligne. L’implémentation de la méthode IMFPMediaPlayerCallback::OnMediaPlayerEvent s’affiche ultérieurement ; consultez Implémenter la méthode De rappel.

// Implements the callback interface for MFPlay events.

class MediaPlayerCallback : public IMFPMediaPlayerCallback
{
    long m_cRef; // Reference count

public:

    MediaPlayerCallback() : m_cRef(1)
    {
    }

    IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        static const QITAB qit[] =
        {
            QITABENT(MediaPlayerCallback, IMFPMediaPlayerCallback),
            { 0 },
        };
        return QISearch(this, qit, riid, ppv);
    }

    IFACEMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&m_cRef);
    }

    IFACEMETHODIMP_(ULONG) Release()
    {
        ULONG count = InterlockedDecrement(&m_cRef);
        if (count == 0)
        {
            delete this;
            return 0;
        }
        return count;
    }

    // IMFPMediaPlayerCallback methods
    IFACEMETHODIMP_(void) OnMediaPlayerEvent(MFP_EVENT_HEADER *pEventHeader);
};

Déclarer la fonction SafeRelease

Tout au long de ce didacticiel, la fonction SafeRelease est utilisée pour libérer les pointeurs d’interface :

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

Ouvrir un fichier multimédia

La PlayMediaFile fonction ouvre un fichier multimédia, comme suit :

  1. Si g_pPlayer a la valeur NULL, la fonction appelle MFPCreateMediaPlayer pour créer un instance de l’objet lecteur multimédia. Les paramètres d’entrée de MFPCreateMediaPlayer incluent un pointeur vers l’interface de rappel et un handle vers la fenêtre vidéo.
  2. Pour ouvrir le fichier multimédia, la fonction appelle IMFPMediaPlayer::CreateMediaItemFromURL, en passant l’URL du fichier. Cette méthode se termine de manière asynchrone. À la fin de l’opération, la méthode IMFPMediaPlayerCallback::OnMediaPlayerEvent de l’application est appelée.
HRESULT PlayMediaFile(HWND hwnd, PCWSTR pszURL)
{
    HRESULT hr = S_OK;

    // Create the MFPlayer object.
    if (g_pPlayer == NULL)
    {
        g_pPlayerCB = new (std::nothrow) MediaPlayerCallback();

        if (g_pPlayerCB == NULL)
        {
            return E_OUTOFMEMORY;
        }

        hr = MFPCreateMediaPlayer(
            NULL,
            FALSE,          // Start playback automatically?
            0,              // Flags
            g_pPlayerCB,    // Callback pointer
            hwnd,           // Video window
            &g_pPlayer
            );
    }

    // Create a new media item for this URL.

    if (SUCCEEDED(hr))
    {
        hr = g_pPlayer->CreateMediaItemFromURL(pszURL, FALSE, 0, NULL);
    }

    // The CreateMediaItemFromURL method completes asynchronously.
    // The application will receive an MFP_EVENT_TYPE_MEDIAITEM_CREATED
    // event. See MediaPlayerCallback::OnMediaPlayerEvent().

    return hr;
}

La OnFileOpen fonction affiche la boîte de dialogue de fichier commune, qui permet à l’utilisateur de sélectionner un fichier à lire. L’interface IFileOpenDialog est utilisée pour afficher la boîte de dialogue de fichier commun. Cette interface fait partie des API Windows Shell ; il a été introduit dans Windows Vista en remplacement de l’ancienne fonction GetOpenFileName . Une fois que l’utilisateur a sélectionné un fichier, OnFileOpen appelle PlayMediaFile pour démarrer la lecture.

void OnFileOpen(HWND hwnd)
{
    IFileOpenDialog *pFileOpen = NULL;
    IShellItem *pItem = NULL;
    PWSTR pwszFilePath = NULL;

    // Create the FileOpenDialog object.
    HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL,
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
    if (SUCCEEDED(hr))
    {
        hr = pFileOpen->SetTitle(L"Select a File to Play");
    }

    // Show the file-open dialog.
    if (SUCCEEDED(hr))
    {
        hr = pFileOpen->Show(hwnd);
    }

    if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
    {
        // User canceled.
        SafeRelease(&pFileOpen);
        return;
    }

    // Get the file name from the dialog.
    if (SUCCEEDED(hr))
    {
        hr = pFileOpen->GetResult(&pItem);
    }

    if (SUCCEEDED(hr))
    {
       hr = pItem->GetDisplayName(SIGDN_URL, &pwszFilePath);
    }

    // Open the media file.
    if (SUCCEEDED(hr))
    {
        hr = PlayMediaFile(hwnd, pwszFilePath);
    }

    if (FAILED(hr))
    {
        ShowErrorMessage(L"Could not open file.", hr);
    }

    CoTaskMemFree(pwszFilePath);

    SafeRelease(&pItem);
    SafeRelease(&pFileOpen);
}

Gestionnaires de messages de fenêtre

Ensuite, déclarez les gestionnaires de messages pour les messages de fenêtre suivants :

  • WM_PAINT
  • WM_SIZE
  • WM_CLOSE

Pour le message WM_PAINT , vous devez vérifier si la vidéo est en cours de lecture. Si c’est le cas, appelez la méthode IMFPMediaPlayer::UpdateVideo . Cette méthode permet à l’objet lecteur de redessiner l’image vidéo la plus récente.

S’il n’y a pas de vidéo, l’application est responsable de la peinture de la fenêtre. Pour ce tutoriel, l’application appelle simplement la fonction FillRect GDI pour remplir l’ensemble de la zone cliente.

void OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    if (g_pPlayer && g_bHasVideo)
    {
        // Playback has started and there is video.

        // Do not draw the window background, because the video
        // frame fills the entire client area.

        g_pPlayer->UpdateVideo();
    }
    else
    {
        // There is no video stream, or playback has not started.
        // Paint the entire client area.

        FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
    }

    EndPaint(hwnd, &ps);
}

Pour obtenir le message WM_SIZE , appelez IMFPMediaPlayer::UpdateVideo. Cette méthode permet à l’objet lecteur de réajuster la vidéo pour qu’elle corresponde à la taille actuelle de la fenêtre. Notez qu’UpdateVideo est utilisé pour les WM_PAINT et les WM_SIZE.

void OnSize(HWND /*hwnd*/, UINT state, int /*cx*/, int /*cy*/)
{
    if (state == SIZE_RESTORED)
    {
        if (g_pPlayer)
        {
            // Resize the video.
            g_pPlayer->UpdateVideo();
        }
    }
}

Pour le message WM_CLOSE , relâchez les pointeurs IMFPMediaPlayer et IMFPMediaPlayerCallback .

void OnClose(HWND /*hwnd*/)
{
    SafeRelease(&g_pPlayer);
    SafeRelease(&g_pPlayerCB);
    PostQuitMessage(0);
}

Implémenter la méthode Callback

L’interface IMFPMediaPlayerCallback définit une méthode unique, OnMediaPlayerEvent. Cette méthode avertit l’application chaque fois qu’un événement se produit pendant la lecture. La méthode prend un paramètre, un pointeur vers une structure MFP_EVENT_HEADER . Le membre eEventType de la structure spécifie l’événement qui s’est produit.

La structure MFP_EVENT_HEADER peut être suivie de données supplémentaires. Pour chaque type d’événement, une macro est définie qui caste le pointeur MFP_EVENT_HEADER vers une structure spécifique à l’événement. (Voir MFP_EVENT_TYPE.)

Pour ce tutoriel, deux événements sont significatifs :

Événement Description
MFP_EVENT_TYPE_MEDIAITEM_CREATED Envoyé lorsque CreateMediaItemFromURL se termine.
MFP_EVENT_TYPE_MEDIAITEM_SET Envoyé lorsque SetMediaItem se termine.

 

Le code suivant montre comment caster le pointeur MFP_EVENT_HEADER vers la structure spécifique à l’événement.

void MediaPlayerCallback::OnMediaPlayerEvent(MFP_EVENT_HEADER * pEventHeader)
{
    if (FAILED(pEventHeader->hrEvent))
    {
        ShowErrorMessage(L"Playback error", pEventHeader->hrEvent);
        return;
    }

    switch (pEventHeader->eEventType)
    {
    case MFP_EVENT_TYPE_MEDIAITEM_CREATED:
        OnMediaItemCreated(MFP_GET_MEDIAITEM_CREATED_EVENT(pEventHeader));
        break;

    case MFP_EVENT_TYPE_MEDIAITEM_SET:
        OnMediaItemSet(MFP_GET_MEDIAITEM_SET_EVENT(pEventHeader));
        break;
    }
}

L’événement MFP_EVENT_TYPE_MEDIAITEM_CREATED avertit l’application que la méthode IMFPMediaPlayer::CreateMediaItemFromURL a terminé. La structure d’événements contient un pointeur vers l’interface IMFPMediaItem , qui représente l’élément multimédia créé à partir de l’URL. Pour mettre en file d’attente l’élément en vue de la lecture, passez ce pointeur à la méthode IMFPMediaPlayer::SetMediaItem :

void OnMediaItemCreated(MFP_MEDIAITEM_CREATED_EVENT *pEvent)
{
    // The media item was created successfully.

    if (g_pPlayer)
    {
        BOOL    bHasVideo = FALSE;
        BOOL    bIsSelected = FALSE;

        // Check if the media item contains video.
        HRESULT hr = pEvent->pMediaItem->HasVideo(&bHasVideo, &bIsSelected);
        if (SUCCEEDED(hr))
        {
            g_bHasVideo = bHasVideo && bIsSelected;

            // Set the media item on the player. This method completes
            // asynchronously.
            hr = g_pPlayer->SetMediaItem(pEvent->pMediaItem);
        }

        if (FAILED(hr))
        {
            ShowErrorMessage(L"Error playing this file.", hr);
        }
   }
}

L’événement MFP_EVENT_TYPE_MEDIAITEM_SET avertit l’application que SetMediaItem a terminé. Appelez IMFPMediaPlayer::P lay pour démarrer la lecture :

void OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT * /*pEvent*/)
{
    HRESULT hr = g_pPlayer->Play();
    if (FAILED(hr))
    {
        ShowErrorMessage(L"IMFPMediaPlayer::Play failed.", hr);
    }
}

Implémenter WinMain

Dans le reste de ce tutoriel, il n’y a aucun appel aux API Media Foundation. Le code suivant illustre la procédure de fenêtre :

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        HANDLE_MSG(hwnd, WM_CLOSE,   OnClose);
        HANDLE_MSG(hwnd, WM_PAINT,   OnPaint);
        HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
        HANDLE_MSG(hwnd, WM_SIZE,    OnSize);

    case WM_ERASEBKGND:
        return 1;

    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

La InitializeWindow fonction inscrit la classe window de l’application et crée la fenêtre.

BOOL InitializeWindow(HWND *pHwnd)
{
    const wchar_t CLASS_NAME[]  = L"MFPlay Window Class";
    const wchar_t WINDOW_NAME[] = L"MFPlay Sample Application";

    WNDCLASS wc = {};

    wc.lpfnWndProc   = WindowProc;
    wc.hInstance     = GetModuleHandle(NULL);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = CLASS_NAME;
    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);

    RegisterClass(&wc);

    HWND hwnd = CreateWindow(
        CLASS_NAME, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, GetModuleHandle(NULL), NULL);

    if (!hwnd)
    {
        return FALSE;
    }

    ShowWindow(hwnd, SW_SHOWDEFAULT);
    UpdateWindow(hwnd);

    *pHwnd = hwnd;

    return TRUE;
}

Enfin, implémentez le point d’entrée de l’application :

int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    HRESULT hr = CoInitializeEx(NULL,
        COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if (FAILED(hr))
    {
        return 0;
    }

    HWND hwnd = NULL;
    if (InitializeWindow(&hwnd))
    {
        // Message loop
        MSG msg = {};
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        DestroyWindow(hwnd);
    }
    CoUninitialize();

    return 0;
}

Utilisation de MFPlay pour la lecture audio/vidéo