Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
[Функция, связанная с этой страницей MFPlay, является устаревшей функцией. Он был заменён MediaPlayer и IMFMediaEngine. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать новый код MediaPlayer и IMFMediaEngine вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает, что существующий код, использующий устаревшие API, будет перезаписан для использования новых API, если это возможно.]
В этом руководстве представлено полное приложение, которое воспроизводит видео с помощью MFPlay. Он основан на примере пакета SDK SimplePlay .
В этом руководстве содержатся следующие разделы:
- Требования
- Файлы заголовков и библиотек
- Глобальные переменные
- Объявите класс обратного вызова
- Объявление функции SafeRelease
- Открытие файла мультимедиа
- Обработчики сообщений окна
- Реализация метода обратного вызова
- Реализуйте WinMain
- Связанные статьи
Дополнительные сведения об API MFPlay см. в статье "Начало работы с 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
открывает файл мультимедиа следующим образом:
- Если g_pPlayer имеет значение NULL, функция вызывает MFPCreateMediaPlayer для создания нового экземпляра объекта проигрывателя мультимедиа. Входные параметры MFPCreateMediaPlayer включают указатель на интерфейс обратного вызова и хэндл для окна видео.
- Чтобы открыть файл мультимедиа, функция вызывает 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 используется для отображения общего диалогового окна файла. Этот интерфейс является частью API оболочки Windows; он был представлен в Windows Vista в качестве замены старой функции GetOpenFileName . После того как пользователь выберет файл, OnFileOpen
вызывает PlayMediaFile
для запуска воспроизведения.
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. Этот метод приводит к тому, что объект проигрывателя перенастроит видео в соответствии с текущим размером окна. Обратите внимание, что UpdateVideo используется как для WM_PAINT, так и для 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();
}
}
}
Для сообщения WM_CLOSE освободите указатели IMFPMediaPlayer и IMFPMediaPlayerCallback.
void OnClose(HWND /*hwnd*/)
{
SafeRelease(&g_pPlayer);
SafeRelease(&g_pPlayerCB);
PostQuitMessage(0);
}
Реализация метода обратного вызова
Интерфейс IMFPMediaPlayerCallback определяет один метод OnMediaPlayerEvent. Этот метод уведомляет приложение при каждом возникновении события во время воспроизведения. Метод принимает один параметр, указатель на MFP_EVENT_HEADER структуру. Элемент eEventType структуры указывает событие, которое произошло.
За MFP_EVENT_HEADER структурой могут следовать дополнительные данные. Для каждого типа события определяется макрос, который приводит указатель MFP_EVENT_HEADER на структуру, зависящую от события. (См. также MFP_EVENT_TYPE.)
В этом руководстве два события являются значительными:
Мероприятие | Описание |
---|---|
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. Структура событий содержит указатель на интерфейс IMFPMediaItem , представляющий элемент мультимедиа, созданный из URL-адреса. Чтобы поставить элемент в очередь для воспроизведения, передайте этот указатель методу 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);
}
}
}
Событие MFP_EVENT_TYPE_MEDIAITEM_SET уведомляет приложение о том, что SetMediaItem завершено. Вызовите IMFPMediaPlayer::Play, чтобы начать воспроизведение:
void OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT * /*pEvent*/)
{
HRESULT hr = g_pPlayer->Play();
if (FAILED(hr))
{
ShowErrorMessage(L"IMFPMediaPlayer::Play failed.", hr);
}
}
Реализовать WinMain
В оставшейся части этого руководства нет вызовов API Media Foundation. В следующем коде показана процедура окна:
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;
}
Связанные темы