Bagikan melalui


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:

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:

  1. 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.
  2. 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;
}

Menggunakan MFPlay untuk Pemutaran Audio/Video