次の方法で共有


MFPlay チュートリアル: ビデオ再生

[このページに関連付けられている機能である MFPlay はレガシ機能です。 これは MediaPlayer および IMFMediaEngine に置き換えられました。 これらの機能は、Windows 10 および Windows 11 用に最適化されています。 Microsoftでは、新しいコードで可能な限り DirectShow ではなく MediaPlayerIMFMediaEngine を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存コードを、新しい API を使用するように可能であれば書き直すことを提案しています。]

このチュートリアルでは、MFPlay を使用してビデオを再生する完全なアプリケーションについて説明します。 これは、SimplePlay SDK サンプルに基づいています。

このチュートリアルは、次のセクションで構成されています。

MFPlay API の詳細については、「 MFPlay の概要」を参照してください。

要件

MFPlay には Windows 7 が必要です。

ヘッダー ファイルとライブラリ ファイル

プロジェクトに次のヘッダー ファイルを含めます。

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

次のコード ライブラリへのリンク:

  • mfplay.lib
  • shlwapi.lib

グローバル変数

以下のグローバル変数を宣言します。

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

BOOL                    g_bHasVideo = FALSE;

これらの変数は、次のように使用されます。

g_hwnd

アプリケーション ウィンドウへのハンドル。

g_bVideo

ビデオが再生されているかどうかを追跡するブール値。

g_pPlayer

IMFPMediaPlayer インターフェイスのアドレスへのポインター。 このインターフェイスは、再生を制御するために使用されます。

g_pCallback

IMFPMediaPlayerCallback インターフェイスのアドレスへのポインター。 アプリケーションは、プレーヤー オブジェクトから通知を取得するために、このコールバック インターフェイスを実装します。

コールバック クラスを宣言する

プレイヤー オブジェクトからイベント通知を取得するには、アプリケーションで IMFPMediaPlayerCallback インターフェイスを実装する必要があります。 次のコードは、 インターフェイスを実装するクラスを宣言します。 唯一のメンバー変数は、参照カウントを格納する m_cRefです。

IUnknown メソッドはインラインで実装されます。 IMFPMediaPlayerCallback::OnMediaPlayerEvent メソッドの実装については、後で示します。「コールバック メソッドの実装」を参照してください。

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

SafeRelease 関数を宣言します。

このチュートリアルでは、SafeRelease 関数を使用してインターフェイス ポインターを解放します。

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

メディア ファイルを開く

PlayMediaFile 関数は、次のようにメディア ファイルを開きます。

  1. g_pPlayerNULL の場合、関数は MFPCreateMediaPlayer を呼び出して、メディア プレーヤー オブジェクトの新しいインスタンスを作成します。 MFPCreateMediaPlayer への入力パラメーターには、コールバック インターフェイスへのポインターとビデオ ウィンドウへのハンドルが含まれます。
  2. メディア ファイルを開くために、関数は IMFPMediaPlayer::CreateMediaItemFromURL を呼び出し、ファイルの URL を渡します。 このメソッドは非同期的に完了します。 完了すると、アプリケーションの 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;
}

OnFileOpen 関数は、ユーザーが再生するファイルを選択できる共通のファイル ダイアログを表示します。 IFileOpenDialog インターフェイスは、共通のファイル ダイアログを表示するために使用されます。 このインターフェイスは、Windows シェル API の一部です。これは、古い GetOpenFileName 関数の代わりとして Windows Vista で導入されました。 ユーザーがファイルを選択した後、OnFileOpenPlayMediaFileを呼び出して再生を開始します。

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

ウィンドウ メッセージ ハンドラー

次に、次のウィンドウ メッセージのメッセージ ハンドラーを宣言します。

  • WM_PAINT
  • WM_SIZE
  • WM_CLOSE

WM_PAINT メッセージでは、ビデオが現在再生されているかどうかを追跡する必要があります。 その場合は、IMFPMediaPlayer::UpdateVideo メソッドを呼び出します。 このメソッドにより、プレーヤー オブジェクトは最新のビデオ フレームを再描画します。

ビデオがない場合、アプリケーションはウィンドウの描画を担当します。 このチュートリアルでは、アプリケーションは GDI FillRect 関数を呼び出して、クライアント領域全体を埋めます。

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

WM_SIZE メッセージの場合は、IMFPMediaPlayer::UpdateVideo を呼び出します。 このメソッドにより、プレーヤー オブジェクトは、ウィンドウの現在のサイズと一致するようにビデオを再調整します。 UpdateVideoWM_PAINTWM_SIZE の両方で使用されることに注意してください。

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

WM_CLOSE メッセージの場合は、IMFPMediaPlayer ポインターと IMFPMediaPlayerCallback ポインターを解放します。

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

コールバック メソッドを実装する

IMFPMediaPlayerCallback インターフェイスは、単一のメソッド OnMediaPlayerEvent を定義します。 このメソッドは、再生中にイベントが発生するたびにアプリケーションに通知します。 このメソッドは、MFP_EVENT_HEADER 構造体へのポインターである 1 つのパラメーターを受け取ります。 構造体の eEventType メンバーは、発生したイベントを指定します。

MFP_EVENT_HEADER 構造体の後に追加のデータが続く場合があります。 イベントの種類ごとに、MFP_EVENT_HEADER ポインターをイベント固有の構造体にキャストするマクロが定義されます。 (MFP_EVENT_TYPE を参照してください。)

このチュートリアルでは、次の 2 つのイベントが重要です。

イベント 説明
MFP_EVENT_TYPE_MEDIAITEM_CREATED CreateMediaItemFromURL が完了したときに送信されます。
MFP_EVENT_TYPE_MEDIAITEM_SET SetMediaItem が完了したときに送信されます。

 

次のコードは、MFP_EVENT_HEADER ポインターをイベント固有の構造体にキャストする方法を示しています。

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

MFP_EVENT_TYPE_MEDIAITEM_CREATED イベントは、IMFPMediaPlayer::CreateMediaItemFromURL メソッドが完了したことをアプリケーションに通知します。 イベント構造体には、URL から作成されたメディア項目を表す IMFPMediaItem インターフェイスへのポインターが含まれています。 このポインターを 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);
        }
   }
}

MFP_EVENT_TYPE_MEDIAITEM_SET イベントは、SetMediaItem が完了したことをアプリケーションに通知します。 IMFPMediaPlayer::P lay を呼び出して再生を開始します。

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

WinMain を実装する

このチュートリアルの残りの部分では、Media Foundation API の呼び出しはありません。 次のコードは、ウィンドウ プロシージャを示しています。

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

InitializeWindow 関数は、アプリケーションのウィンドウ クラスを登録し、ウィンドウを作成します。

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

最後に、アプリケーション エントリ ポイントを実装します。

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

オーディオとビデオの再生に MFPlay を使用する