Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
[Funkcja skojarzona z tą stroną, MFPlay, jest starszą funkcją. Został zastąpiony przez MediaPlayer i IMFMediaEngine. Te funkcje zostały zoptymalizowane pod kątem systemów Windows 10 i Windows 11. Firma Microsoft zdecydowanie zaleca, aby nowy kod używał MediaPlayer i IMFMediaEngine zamiast DirectShow, jeśli to możliwe. Firma Microsoft sugeruje, że istniejący kod, który używa starszych interfejsów API, należy przepisać go do korzystania z nowych interfejsów API, jeśli to możliwe.]
W tym samouczku przedstawiono pełną aplikację, która odtwarza wideo, korzystając z MFPlay. Jest oparty na przykładzie SDK-u SimplePlay.
Ten samouczek zawiera następujące sekcje:
- Wymagania
- Pliki nagłówkowe i biblioteczne
- Zmienne globalne
- Deklarowanie klasy wywołania zwrotnego
- Deklarowanie funkcji SafeRelease
- otwórz plik multimedialny
- Programy obsługi komunikatów okien
- Zaimplementować metodę wywołania zwrotnego
- Zaimplementuj WinMain
- Tematy pokrewne
Aby uzyskać bardziej szczegółowe omówienie interfejsu API MFPlay, zobacz Wprowadzenie do interfejsu MFPlay.
Wymagania
Funkcja MFPlay wymaga systemu Windows 7.
Pliki nagłówków i bibliotek
Uwzględnij następujące pliki nagłówkowe w projekcie:
#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>
Połącz z następującymi bibliotekami kodu:
- mfplay.lib
- shlwapi.lib
Zmienne globalne
Zadeklaruj następujące zmienne globalne:
IMFPMediaPlayer *g_pPlayer = NULL; // The MFPlay player object.
MediaPlayerCallback *g_pPlayerCB = NULL; // Application callback object.
BOOL g_bHasVideo = FALSE;
Te zmienne będą używane w następujący sposób:
-
g_hwnd
-
Uchwyt do okna aplikacji.
-
g_bVideo
-
Wartość logiczna, która śledzi, czy wideo jest odtwarzane.
-
g_pPlayer
-
Wskaźnik do interfejsu IMFPMediaPlayer. Ten interfejs służy do sterowania odtwarzaniem.
-
g_pCallback
-
Wskaźnik do interfejsu IMFPMediaPlayerCallback. Aplikacja implementuje ten interfejs wywołania zwrotnego, aby otrzymywać powiadomienia z obiektu odtwarzacza multimediów.
Deklarowanie klasy wywołania zwrotnego
Aby uzyskać powiadomienia o zdarzeniach z obiektu odtwarzacza, aplikacja musi zaimplementować interfejs IMFPMediaPlayerCallback. Poniższy kod deklaruje klasę, która implementuje interfejs. Jedyną zmienną składową jest m_cRef, która przechowuje liczbę odwołań.
Metody IUnknown są implementowane inline. Implementacja metody IMFPMediaPlayerCallback::OnMediaPlayerEvent jest pokazana później; zobacz Implementacja metody wywołania zwrotnego.
// 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);
};
Deklarowanie funkcji SafeRelease
W tym samouczku funkcja SafeRelease jest używana do zwalniania wskaźników interfejsu:
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
Otwieranie pliku multimedialnego
Funkcja PlayMediaFile otwiera plik multimedialny w następujący sposób:
- Jeśli g_pPlayer ma wartość null, funkcja wywołuje MFPCreateMediaPlayer w celu utworzenia nowego wystąpienia obiektu odtwarzacza multimediów. Parametry wejściowe do MFPCreateMediaPlayer zawierają wskaźnik do interfejsu wywołania zwrotnego i uchwyt do okna wideo.
- Aby otworzyć plik multimedialny, funkcja wywołuje IMFPMediaPlayer::CreateMediaItemFromURL, przekazując adres URL pliku. Ta metoda jest zakończona asynchronicznie. Po zakończeniu działania wywoływana jest metoda IMFPMediaPlayerCallback::OnMediaPlayerEvent.
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;
}
Funkcja OnFileOpen wyświetla typowe okno dialogowe pliku, które umożliwia użytkownikowi wybranie pliku do odtwarzania. Interfejs IFileOpenDialog służy do wyświetlania wspólnego okna dialogowego pliku. Ten interfejs jest częścią interfejsów API powłoki systemu Windows; Wprowadzono go w systemie Windows Vista jako zamiennik starszej funkcji GetOpenFileName. Gdy użytkownik wybierze plik, OnFileOpen wywołuje PlayMediaFile, aby rozpocząć odtwarzanie.
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);
}
Programy obsługi komunikatów okien
Następnie zadeklaruj programy obsługi komunikatów dla następujących komunikatów okna:
- WM_PAINT
- WM_SIZE
- WM_CLOSE
W przypadku komunikatu WM_PAINT należy śledzić, czy wideo jest obecnie odtwarzane. Jeśli tak, wywołaj metodę IMFPMediaPlayer::UpdateVideo. Ta metoda powoduje, że obiekt odtwarzacza ponownie wyrysuje najnowszą ramkę wideo.
Jeśli nie ma wideo, aplikacja jest odpowiedzialna za malowanie okna. Na potrzeby tego samouczka aplikacja po prostu wywołuje funkcję GDI FillRect, aby wypełnić cały obszar klienta.
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);
}
W przypadku komunikatu WM_SIZE wywołaj IMFPMediaPlayer::UpdateVideo. Ta metoda powoduje, że obiekt odtwarzacza dopasowuje wideo do bieżącego rozmiaru okna. Należy pamiętać, że UpdateVideo jest używana zarówno dla WM_PAINT, jak i 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();
}
}
}
W przypadku komunikatu WM_CLOSE wydaj wskaźniki IMFPMediaPlayer i IMFPMediaPlayerCallback.
void OnClose(HWND /*hwnd*/)
{
SafeRelease(&g_pPlayer);
SafeRelease(&g_pPlayerCB);
PostQuitMessage(0);
}
Implementowanie metody wywołania zwrotnego
InterfejsIMFPMediaPlayerCallback definiuje jedną metodę, OnMediaPlayerEvent. Ta metoda powiadamia aplikację za każdym razem, gdy wystąpi zdarzenie podczas odtwarzania. Metoda przyjmuje jeden parametr, wskaźnik do struktury MFP_EVENT_HEADER. Element eEventType w strukturze określa zdarzenie, które miało miejsce.
Po strukturze MFP_EVENT_HEADER mogą znajdować się dodatkowe dane. Dla każdego typu zdarzenia definiowane jest makro, które rzutuje wskaźnik MFP_EVENT_HEADER na strukturę specyficzną dla zdarzenia. (Zobacz MFP_EVENT_TYPE.)
Ten samouczek jest związany z dwoma istotnymi zdarzeniami:
| Zdarzenie | Opis |
|---|---|
| MFP_EVENT_TYPE_MEDIAITEM_CREATED | Wysłane po zakończeniu CreateMediaItemFromURL. |
| MFP_EVENT_TYPE_MEDIAITEM_SET | Wysłane, gdy SetMediaItem zostanie zakończone. |
Poniższy kod pokazuje, jak rzutować wskaźnik MFP_EVENT_HEADER na strukturę specyficzną dla zdarzenia.
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;
}
}
Zdarzenie MFP_EVENT_TYPE_MEDIAITEM_CREATED powiadamia aplikację, że IMFPMediaPlayer::CreateMediaItemFromURL metoda została ukończona. Struktura zdarzeń zawiera wskaźnik do interfejsu IMFPMediaItem, który reprezentuje element multimedialny utworzony z adresu URL. Aby ustawić element w kolejce do odtwarzania, przekaż ten wskaźnik do metody 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);
}
}
}
Aplikacja otrzymuje powiadomienie, że zdarzenie MFP_EVENT_TYPE_MEDIAITEM_SET zakończyło się zakończeniem SetMediaItem. Wywołaj IMFPMediaPlayer::Play, aby rozpocząć odtwarzanie:
void OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT * /*pEvent*/)
{
HRESULT hr = g_pPlayer->Play();
if (FAILED(hr))
{
ShowErrorMessage(L"IMFPMediaPlayer::Play failed.", hr);
}
}
Zaimplementuj WinMain
W pozostałej części tego samouczka nie ma wywołań interfejsów API programu Media Foundation. Poniższy kod przedstawia procedurę okna:
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);
}
}
Funkcja InitializeWindow rejestruje klasę okna aplikacji i tworzy okno.
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;
}
Na koniec zaimplementuj punkt wejścia aplikacji:
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;
}
Tematy pokrewne