MFPlay-Tutorial: Videowiedergabe
[Bei dem auf dieser Seite gezeigten Feature MFPlay handelt es sich um ein Legacyfeature. Es wurde durch MediaPlayer und IMFMediaEngine abgelöst. Diese Features wurden für Windows 10 und Windows 11 optimiert. Microsoft empfiehlt dringend, dass in neuem Code wenn möglich MediaPlayer und IMFMediaEngine anstelle von DirectShow verwendet wird. Microsoft schlägt vor, dass vorhandener Code, der die Legacy-APIs verwendet, wenn möglich umgeschrieben wird, um die neuen APIs zu verwenden.]
In diesem Tutorial wird eine vollständige Anwendung vorgestellt, die Videos mithilfe von MFPlay wiedergibt. Sie basiert auf dem SDK-Beispiel SimplePlay.
Dieses Tutorial enthält die folgenden Abschnitte:
- Anforderungen
- Header- und Bibliotheksdateien
- Globale Variablen
- Deklarieren der Rückrufklasse
- Deklarieren der SafeRelease-Funktion
- Öffnen einer Mediendatei
- Fenstermeldungshandler
- Implementieren der Rückrufmethode
- Implementieren von WinMain
- Verwandte Themen
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>
Verknüpfen Sie die 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 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 Playerobjekt abzurufen, muss die Anwendung die IMFPMediaPlayerCallback-Schnittstelle implementieren. Der folgende Code deklariert eine Klasse, welche 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:
- Wenn g_pPlayer NULL ist, ruft die Funktion MFPCreateMediaPlayer auf, um eine neue Instanz des Media Player-Objekts zu erstellen. Die Eingabeparameter für MFPCreateMediaPlayer enthalten einen Zeiger auf die Rückrufschnittstelle und ein Handle für das Videofenster.
- 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 standardmäßige Dateidialogfeld an, in dem Benutzer eine Datei für die Wiedergabe auswählen können. Die IFileOpenDialog-Schnittstelle wird verwendet, um das standardmäßige Dateidialogfeld anzuzeigen. Diese Schnittstelle ist Teil der Windows-Shell-APIs und wurde in Windows Vista als Ersatz für die ältere Funktion GetOpenFileName eingeführt. Nachdem der Benutzer eine Datei ausgewählt hat, ruft wird PlayMediaFile
von OnFileOpen
aufgerufen, 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);
}
Fenstermeldungshandler
Als Nächstes deklarieren Sie Meldungshandler für die folgenden Fenstermeldungen:
- WM_PAINT
- WM_SIZE
- WM_CLOSE
Für die WM_PAINT-Meldung müssen Sie nachverfolgen, ob derzeit Video wiedergegeben wird. Wenn ja, rufen Sie die IMFPMediaPlayer::UpdateVideo-Methode auf. Diese Methode bewirkt, dass das Playerobjekt den aktuellen Videoframe neu gezeichnet.
Wenn kein Video vorhanden ist, ist die Anwendung für das Zeichnen des Fensters verantwortlich. In diesem Tutorial ruft die Anwendung einfach die GDI-Funktion FillRect 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 IMFPMediaPlayer::UpdateVideo für die WM_SIZE-Meldung auf. Mit dieser Methode wird das Playerobjekt so angepasst, dass das Video mit der aktuellen Größe des Fensters übereinstimmt. 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-Meldung die IMFPMediaPlayer- und IMFPMediaPlayerCallback-Zeiger 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. Mit dieser Methode wird die Anwendung benachrichtigt, wenn während der Wiedergabe ein Ereignis auftritt. Die Methode verwendet einen Parameter, einen Zeiger auf eine MFP_EVENT_HEADER-Struktur. Der eEventType-Member 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::Play 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 Einstiegspunkt der Anwendung:
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;
}
Zugehörige Themen