Share via


Tutorial de MFPlay: Reproducción de vídeo

[MFPlay está disponible para su uso en los sistemas operativos especificados en la sección Requisitos. En versiones posteriores podría modificarse o no estar disponible. ]

En este tutorial se presenta una aplicación completa que reproduce vídeo con MFPlay. Se basa en el ejemplo del SDK de SimplePlay .

Este tutorial contiene las secciones siguientes:

Para obtener una explicación más detallada de la API MFPlay, consulte Introducción con MFPlay.

Requisitos

MFPlay requiere Windows 7.

Archivos de encabezado y biblioteca

Incluya los siguientes archivos de encabezado en el proyecto:

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

Vincule a las bibliotecas de código siguientes:

  • mfplay.lib
  • shlwapi.lib

Variables globales

Declare las siguientes variables globales:

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

BOOL                    g_bHasVideo = FALSE;

Estas variables se usarán de la siguiente manera:

g_hwnd

Identificador de la ventana de la aplicación.

g_bVideo

Valor booleano que realiza un seguimiento de si se está reproduciendo vídeo.

g_pPlayer

Puntero a la interfaz IMFPMediaPlayer . Esta interfaz se usa para controlar la reproducción.

g_pCallback

Puntero a la interfaz IMFPMediaPlayerCallback . La aplicación implementa esta interfaz de devolución de llamada para obtener notificaciones del objeto player.

Declarar la clase callback

Para obtener notificaciones de eventos del objeto player, la aplicación debe implementar la interfaz IMFPMediaPlayerCallback . El código siguiente declara una clase que implementa la interfaz . La única variable miembro es m_cRef, que almacena el recuento de referencias.

Los métodos IUnknown se implementan insertados. La implementación del método IMFPMediaPlayerCallback::OnMediaPlayerEvent se muestra más adelante; consulte Implementación del método de devolución de llamada.

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

Declarar la función SafeRelease

A lo largo de este tutorial, la función SafeRelease se usa para liberar punteros de interfaz:

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

Abrir un archivo multimedia

La PlayMediaFile función abre un archivo multimedia, como se indica a continuación:

  1. Si g_pPlayer es NULL, la función llama a MFPCreateMediaPlayer para crear una nueva instancia del objeto del reproductor multimedia. Los parámetros de entrada para MFPCreateMediaPlayer incluyen un puntero a la interfaz de devolución de llamada y un identificador para la ventana de vídeo.
  2. Para abrir el archivo multimedia, la función llama a IMFPMediaPlayer::CreateMediaItemFromURL, pasando la dirección URL del archivo. Este método se completa de forma asincrónica. Cuando se completa, se llama al método IMFPMediaPlayerCallback::OnMediaPlayerEvent de la aplicación.
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 función muestra el cuadro de diálogo de archivo común, que permite al usuario seleccionar un archivo para su reproducción. La interfaz IFileOpenDialog se usa para mostrar el cuadro de diálogo de archivo común. Esta interfaz forma parte de las API de Windows Shell; se introdujo en Windows Vista como sustituto de la antigua función GetOpenFileName . Después de que el usuario seleccione un archivo, OnFileOpen llama PlayMediaFile a para iniciar la reproducción.

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

Controladores de mensajes de ventana

A continuación, declare controladores de mensajes para los siguientes mensajes de ventana:

  • WM_PAINT
  • WM_SIZE
  • WM_CLOSE

Para el WM_PAINT mensaje, debe realizar un seguimiento de si el vídeo se está reproduciendo actualmente. Si es así, llame al método IMFPMediaPlayer::UpdateVideo . Este método hace que el objeto del reproductor vuelva a dibujar el fotograma de vídeo más reciente.

Si no hay ningún vídeo, la aplicación es responsable de pintar la ventana. En este tutorial, la aplicación simplemente llama a la función FillRect de GDI para rellenar todo el área de 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);
}

Para el mensaje de WM_SIZE , llame a IMFPMediaPlayer::UpdateVideo. Este método hace que el objeto del reproductor reajuste el vídeo para que coincida con el tamaño actual de la ventana. Tenga en cuenta que UpdateVideo se usa tanto para WM_PAINT como para 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();
        }
    }
}

Para el mensaje WM_CLOSE , libere los punteros IMFPMediaPlayer y IMFPMediaPlayerCallback .

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

Implementar el método de devolución de llamada

La interfaz IMFPMediaPlayerCallback define un único método, OnMediaPlayerEvent. Este método notifica a la aplicación cada vez que se produce un evento durante la reproducción. El método toma un parámetro, un puntero a una estructura MFP_EVENT_HEADER . El miembro eEventType de la estructura especifica el evento que se produjo.

La estructura de MFP_EVENT_HEADER puede seguir datos adicionales. Para cada tipo de evento, se define una macro que convierte el puntero MFP_EVENT_HEADER a una estructura específica del evento. (Consulte MFP_EVENT_TYPE).

Para este tutorial, dos eventos son significativos:

Evento Descripción
MFP_EVENT_TYPE_MEDIAITEM_CREATED Se envía cuando se completa CreateMediaItemFromURL .
MFP_EVENT_TYPE_MEDIAITEM_SET Se envía cuando se completa SetMediaItem .

 

En el código siguiente se muestra cómo convertir el puntero MFP_EVENT_HEADER a la estructura específica del evento.

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

El evento MFP_EVENT_TYPE_MEDIAITEM_CREATED notifica a la aplicación que se ha completado el método IMFPMediaPlayer::CreateMediaItemFromURL . La estructura de eventos contiene un puntero a la interfaz IMFPMediaItem , que representa el elemento multimedia creado a partir de la dirección URL. Para poner en cola el elemento para su reproducción, pase este puntero al método 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);
        }
   }
}

El evento MFP_EVENT_TYPE_MEDIAITEM_SET notifica a la aplicación que SetMediaItem ha completado. Llama a IMFPMediaPlayer::P lay para iniciar la reproducción:

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

Implementación de WinMain

En el resto de este tutorial, no hay llamadas a las API de Media Foundation. El código siguiente muestra el procedimiento de ventana:

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 función registra la clase de ventana de la aplicación y crea la ventana.

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

Por último, implemente el punto de entrada de la aplicación:

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

Uso de MFPlay para la reproducción de audio y vídeo