Share via


MFPlay-Tutorial: Videowiedergabe

[MFPlay ist für die Verwendung in den im Abschnitt Anforderungen angegebenen Betriebssystemen verfügbar. Es kann in nachfolgenden Versionen geändert oder entfernt werden. ]

In diesem Tutorial wird eine vollständige Anwendung vorgestellt, die Videos mithilfe von MFPlay wiedergibt. Sie basiert auf dem SimplePlay SDK-Beispiel.

Dieses Tutorial enthält die folgenden Abschnitte:

Eine ausführlichere Erläuterung der MFPlay-API finden Sie unter Erste Schritte mit MFPlay.

Anforderungen

MFPlay erfordert Windows 7.

Header- und Bibliotheksdateien

Fügen Sie die folgenden Headerdateien in Ihr Projekt ein:

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

Link zu den folgenden Codebibliotheken:

  • mfplay.lib
  • shlwapi.lib

Globale Variablen

Deklarieren Sie die folgenden globalen Variablen:

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

BOOL                    g_bHasVideo = FALSE;

Diese Variablen werden wie folgt verwendet:

g_hwnd

Ein Handle für das Anwendungsfenster.

g_bVideo

Ein boolescher Wert, der nachverfolgt, ob das Video wiedergegeben wird.

g_pPlayer

Ein Zeiger auf die IMFPMediaPlayer-Schnittstelle . Diese Schnittstelle wird verwendet, um die Wiedergabe zu steuern.

g_pCallback

Ein Zeiger auf die IMFPMediaPlayerCallback-Schnittstelle . Die Anwendung implementiert diese Rückrufschnittstelle, um Benachrichtigungen vom Playerobjekt abzurufen.

Deklarieren der Rückrufklasse

Um Ereignisbenachrichtigungen vom Player-Objekt abzurufen, muss die Anwendung die IMFPMediaPlayerCallback-Schnittstelle implementieren. Der folgende Code deklariert eine Klasse, die die -Schnittstelle implementiert. Die einzige Membervariable ist m_cRef, in der die Verweisanzahl gespeichert wird.

Die IUnknown-Methoden werden inline implementiert. Die Implementierung der IMFPMediaPlayerCallback::OnMediaPlayerEvent-Methode wird später gezeigt. Weitere Informationen finden Sie unter Implementieren der Rückrufmethode.

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

Deklarieren der SafeRelease-Funktion

In diesem Tutorial wird die SafeRelease-Funktion verwendet, um Schnittstellenzeiger freizugeben:

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

Öffnen einer Mediendatei

Die PlayMediaFile -Funktion öffnet eine Mediendatei wie folgt:

  1. Wenn g_pPlayerNULL ist, ruft die Funktion MFPCreateMediaPlayer auf, um eine neue instance des Media Player-Objekts zu erstellen. Die Eingabeparameter für MFPCreateMediaPlayer enthalten einen Zeiger auf die Rückrufschnittstelle und ein Handle auf das Videofenster.
  2. Zum Öffnen der Mediendatei ruft die Funktion IMFPMediaPlayer::CreateMediaItemFromURL auf und übergibt die URL der Datei. Diese Methode wird asynchron abgeschlossen. Nach Abschluss wird die IMFPMediaPlayerCallback::OnMediaPlayerEvent-Methode der Anwendung aufgerufen.
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;
}

Die OnFileOpen Funktion zeigt das Dialogfeld "Gemeinsame Datei" an, in dem der Benutzer eine Datei für die Wiedergabe auswählen kann. Die IFileOpenDialog-Schnittstelle wird verwendet, um das Allgemeine Dateidialogfeld anzuzeigen. Diese Schnittstelle ist Teil der Windows-Shell-APIs. es wurde in Windows Vista als Ersatz für die ältere GetOpenFileName-Funktion eingeführt. Nachdem der Benutzer eine Datei ausgewählt hat, rufen Sie PlayMediaFile auf, OnFileOpen um die Wiedergabe zu starten.

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

Fensternachrichtenhandler

Deklarieren Sie als Nächstes Nachrichtenhandler für die folgenden Fenstermeldungen:

  • WM_PAINT
  • WM_SIZE
  • WM_CLOSE

Für die WM_PAINT Nachricht müssen Sie nachverfolgen, ob das Video gerade wiedergegeben wird. Wenn ja, rufen Sie die IMFPMediaPlayer::UpdateVideo-Methode auf. Diese Methode bewirkt, dass das Playerobjekt den neuesten Videoframe neu zeichnet.

Wenn kein Video vorhanden ist, ist die Anwendung für das Zeichnen des Fensters verantwortlich. In diesem Tutorial ruft die Anwendung einfach die GDI FillRect-Funktion auf, um den gesamten Clientbereich zu füllen.

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

Rufen Sie für die WM_SIZE Meldung IMFPMediaPlayer::UpdateVideo auf. Diese Methode bewirkt, dass das Playerobjekt das Video an die aktuelle Größe des Fensters anpassen kann. Beachten Sie, dass UpdateVideo sowohl für WM_PAINT als auch für WM_SIZE verwendet wird.

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

Geben Sie für die WM_CLOSE Nachricht die Zeiger IMFPMediaPlayer und IMFPMediaPlayerCallback frei .

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

Implementieren der Rückrufmethode

Die IMFPMediaPlayerCallback-Schnittstelle definiert eine einzelne Methode, OnMediaPlayerEvent. Diese Methode benachrichtigt die Anwendung, wenn während der Wiedergabe ein Ereignis auftritt. Die -Methode akzeptiert einen Parameter, einen Zeiger auf eine MFP_EVENT_HEADER-Struktur . Das eEventType-Element der -Struktur gibt das aufgetretene Ereignis an.

Auf die MFP_EVENT_HEADER-Struktur können zusätzliche Daten folgen. Für jeden Ereignistyp wird ein Makro definiert, mit dem der MFP_EVENT_HEADER-Zeiger in eine ereignisspezifische Struktur umgewandelt wird. (Siehe MFP_EVENT_TYPE.)

Für dieses Tutorial sind zwei Ereignisse von Bedeutung:

Ereignis Beschreibung
MFP_EVENT_TYPE_MEDIAITEM_CREATED Wird gesendet, wenn createMediaItemFromURL abgeschlossen ist.
MFP_EVENT_TYPE_MEDIAITEM_SET Wird gesendet, wenn SetMediaItem abgeschlossen ist.

 

Der folgende Code zeigt, wie sie den MFP_EVENT_HEADER-Zeiger in die ereignisspezifische Struktur umwandeln.

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

Das MFP_EVENT_TYPE_MEDIAITEM_CREATED-Ereignis benachrichtigt die Anwendung, dass die IMFPMediaPlayer::CreateMediaItemFromURL-Methode abgeschlossen wurde. Die Ereignisstruktur enthält einen Zeiger auf die IMFPMediaItem-Schnittstelle , die das aus der URL erstellte Medienelement darstellt. Um das Element für die Wiedergabe in die Warteschlange zu stellen, übergeben Sie diesen Zeiger an die IMFPMediaPlayer::SetMediaItem-Methode :

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

Das MFP_EVENT_TYPE_MEDIAITEM_SET-Ereignis benachrichtigt die Anwendung, dass SetMediaItem abgeschlossen wurde. Rufen Sie IMFPMediaPlayer::P lay auf, um die Wiedergabe zu starten:

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

Implementieren von WinMain

Im weiteren Verlauf dieses Tutorials werden keine Aufrufe von Media Foundation-APIs ausgeführt. Der folgende Code zeigt die Fensterprozedur:

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

Die InitializeWindow Funktion registriert die Fensterklasse der Anwendung und erstellt das Fenster.

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

Implementieren Sie schließlich den Anwendungseinstiegspunkt:

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

Verwenden von MFPlay für die Audio-/Videowiedergabe