Tutorial MFPlay: Pemutaran Video
[Fitur yang terkait dengan halaman ini, MFPlay, adalah fitur warisan. Ini telah digantikan oleh MediaPlayer dan IMFMediaEngine. Fitur-fitur tersebut telah dioptimalkan untuk Windows 10 dan Windows 11. Microsoft sangat menyarankan agar kode baru menggunakan MediaPlayer dan IMFMediaEngine alih-alih DirectShow, jika memungkinkan. Microsoft menyarankan agar kode yang ada yang menggunakan API warisan ditulis ulang untuk menggunakan API baru jika memungkinkan.]
Tutorial ini menyajikan aplikasi lengkap yang memutar video menggunakan MFPlay. Ini didasarkan pada sampel SimplePlay SDK.
Tutorial ini berisi bagian berikut:
- Persyaratan
- File Header dan Pustaka
- Variabel Global
- Mendeklarasikan Kelas Panggilan Balik
- Mendeklarasikan Fungsi SafeRelease
- Buka File Media
- Penangan Pesan Jendela
- Menerapkan Metode Panggilan Balik
- Menerapkan WinMain
- Topik terkait
Untuk diskusi yang lebih rinci tentang MFPlay API, lihat Memulai MFPlay.
Persyaratan
MFPlay memerlukan Windows 7.
File Header dan Pustaka
Sertakan file header berikut dalam proyek Anda:
#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>
Tautkan ke pustaka kode berikut:
- mfplay.lib
- shlwapi.lib
Variabel Global
Deklarasikan variabel global berikut:
IMFPMediaPlayer *g_pPlayer = NULL; // The MFPlay player object.
MediaPlayerCallback *g_pPlayerCB = NULL; // Application callback object.
BOOL g_bHasVideo = FALSE;
Variabel ini akan digunakan sebagai berikut:
-
g_hwnd
-
Handel ke jendela aplikasi.
-
g_bVideo
-
Nilai Boolean yang melacak apakah video diputar.
-
g_pPlayer
-
Penunjuk ke antarmuka IMFPMediaPlayer. Antarmuka ini digunakan untuk mengontrol pemutaran.
-
g_pCallback
-
Penunjuk ke antarmuka IMFPMediaPlayerCallback. Aplikasi mengimplementasikan antarmuka panggilan balik ini untuk mendapatkan pemberitahuan dari objek pemutar.
Mendeklarasikan Kelas Panggilan Balik
Untuk mendapatkan pemberitahuan peristiwa dari objek pemutar, aplikasi harus mengimplementasikan antarmuka IMFPMediaPlayerCallback . Kode berikut mendeklarasikan kelas yang mengimplementasikan antarmuka. Satu-satunya variabel anggota adalah m_cRef, yang menyimpan jumlah referensi.
Metode IUnknown diimplementasikan sebaris. Implementasi metode IMFPMediaPlayerCallback::OnMediaPlayerEvent ditampilkan nanti; lihat Menerapkan Metode Panggilan Balik.
// 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);
};
Mendeklarasikan Fungsi SafeRelease
Sepanjang tutorial ini, fungsi SafeRelease digunakan untuk merilis pointer antarmuka:
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
Buka File Media
Fungsi PlayMediaFile
membuka file media, sebagai berikut:
- Jika g_pPlayer NULL, fungsi memanggil MFPCreateMediaPlayer untuk membuat instans baru objek pemutar media. Parameter input ke MFPCreateMediaPlayer menyertakan penunjuk ke antarmuka panggilan balik dan handel ke jendela video.
- Untuk membuka file media, fungsi memanggil IMFPMediaPlayer::CreateMediaItemFromURL, meneruskan URL file. Metode ini selesai secara asinkron. Setelah selesai, metode IMFPMediaPlayerCallback::OnMediaPlayerEvent aplikasi dipanggil.
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;
}
Fungsi ini OnFileOpen
menampilkan dialog file umum, yang memungkinkan pengguna untuk memilih file untuk pemutaran. Antarmuka IFileOpenDialog digunakan untuk menampilkan dialog file umum. Antarmuka ini adalah bagian dari API Windows Shell; ini diperkenalkan di Windows Vista sebagai pengganti fungsi GetOpenFileName yang lebih lama. Setelah pengguna memilih file, OnFileOpen
panggilan PlayMediaFile
untuk memulai pemutaran.
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);
}
Penangan Pesan Jendela
Selanjutnya, deklarasikan penangan pesan untuk pesan jendela berikut:
- WM_PAINT
- WM_SIZE
- WM_CLOSE
Untuk pesan WM_PAINT, Anda harus melacak apakah video sedang diputar. Jika demikian, panggil metode IMFPMediaPlayer::UpdateVideo. Metode ini menyebabkan objek pemutar menggambar ulang bingkai video terbaru.
Jika tidak ada video, aplikasi bertanggung jawab untuk melukis jendela. Untuk tutorial ini, aplikasi hanya memanggil fungsi GDI FillRect untuk mengisi seluruh area klien.
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);
}
Untuk pesan WM_SIZE, panggil IMFPMediaPlayer::UpdateVideo. Metode ini menyebabkan objek pemutar hanya membaca video agar sesuai dengan ukuran jendela saat ini. Perhatikan bahwa UpdateVideo digunakan untuk WM_PAINT dan 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();
}
}
}
Untuk pesan WM_CLOSE, rilis pointer IMFPMediaPlayer dan IMFPMediaPlayerCallback.
void OnClose(HWND /*hwnd*/)
{
SafeRelease(&g_pPlayer);
SafeRelease(&g_pPlayerCB);
PostQuitMessage(0);
}
Menerapkan Metode Panggilan Balik
Antarmuka IMFPMediaPlayerCallback mendefinisikan satu metode, OnMediaPlayerEvent. Metode ini memberi tahu aplikasi setiap kali peristiwa terjadi selama pemutaran. Metode ini mengambil satu parameter, penunjuk ke struktur MFP_EVENT_HEADER. Anggota eEventType dari struktur menentukan peristiwa yang terjadi.
Struktur MFP_EVENT_HEADER dapat diikuti oleh data tambahan. Untuk setiap jenis peristiwa, makro didefinisikan yang melemparkan penunjuk MFP_EVENT_HEADER ke struktur khusus peristiwa. (Lihat MFP_EVENT_TYPE.)
Untuk tutorial ini, dua peristiwa signifikan:
Kejadian | Deskripsi |
---|---|
MFP_EVENT_TYPE_MEDIAITEM_CREATED | Dikirim saat CreateMediaItemFromURL selesai. |
MFP_EVENT_TYPE_MEDIAITEM_SET | Dikirim saat SetMediaItem selesai. |
Kode berikut menunjukkan cara melemparkan penunjuk MFP_EVENT_HEADER ke struktur khusus peristiwa.
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;
}
}
Peristiwa MFP_EVENT_TYPE_MEDIAITEM_CREATED memberi tahu aplikasi bahwa metode IMFPMediaPlayer::CreateMediaItemFromURL telah selesai. Struktur peristiwa berisi penunjuk ke antarmuka IMFPMediaItem , yang mewakili item media yang dibuat dari URL. Untuk mengantre item untuk pemutaran, teruskan penunjuk ini ke metode 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);
}
}
}
Peristiwa MFP_EVENT_TYPE_MEDIAITEM_SET memberi tahu aplikasi bahwa SetMediaItem telah selesai. Panggil IMFPMediaPlayer::P lay untuk memulai pemutaran:
void OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT * /*pEvent*/)
{
HRESULT hr = g_pPlayer->Play();
if (FAILED(hr))
{
ShowErrorMessage(L"IMFPMediaPlayer::Play failed.", hr);
}
}
Menerapkan WinMain
Di sisa tutorial ini, tidak ada panggilan ke API Media Foundation. Kode berikut menunjukkan prosedur jendela:
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);
}
}
Fungsi ini InitializeWindow
mendaftarkan kelas jendela aplikasi dan membuat jendela.
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;
}
Terakhir, terapkan titik masuk aplikasi:
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;
}
Topik terkait