MFPlay 入门
[可在“要求”部分中指定的操作系统中使用 MFPlay。 在后续版本中,它可能会被修改,也可能无法使用。 ]
MFPlay 是一个 API,通过它可使用 C++ 创建媒体播放应用程序。
本主题包含以下各节:
要求
MFPlay 需要 Windows 7。
关于 MFPlay
MFPlay 具有简单的编程模型,同时提供大多数播放应用程序所需的核心功能集。 应用程序会创建用于控制播放的 player 对象。 若要播放媒体文件,player 对象会创建一个媒体项,该媒体项可用于获取媒体文件内容的相关信息。 应用程序通过 player 对象的 IMFPMediaPlayer 接口上的方法控制播放。 (可选)应用程序可通过回调接口获取事件通知。下图说明了此过程。
播放媒体文件
若要播放媒体文件,请调用 MFPCreateMediaPlayer 函数。
// Global variables
IMFPMediaPlayer *g_pPlayer = NULL;
const WCHAR *sURL = L"C:\\Users\\Public\\Videos\\example.wmv";
HRESULT PlayVideo(HWND hwnd, const WCHAR* sURL)
{
// Create the player object and play a video file.
return MFPCreateMediaPlayer(
sURL,
TRUE, // Start playback automatically?
0, // Flags.
NULL, // Callback pointer.
hwnd,
&g_pPlayer
);
}
MFPCreateMediaPlayer 函数会创建 MFPlay player 对象的新实例。 该函数采用以下参数:
- 第一个参数是要打开的文件的 URL。 这可以是本地文件,也可以是媒体服务器上的文件。
- 第二个参数指定是否自动启动播放。 通过将此参数设置为 TRUE,文件将在 MFPlay 加载它后立即播放。
- 第三个参数设置各种选项。 对于默认选项,传递零 (0)。
- 第四个参数是指向可选回调接口的指针。 此参数可以为 NULL,如下所示。 有关回调,请参阅从播放器接收事件部分。
- 第五个参数是应用程序窗口的句柄。 如果媒体文件包含视频流,视频将显示在此窗口的客户端区域内。 对于仅音频播放,此参数可以为 NULL。
最后一个参数接收指向 player 对象的 IMFPMediaPlayer 接口的指针。
在应用程序关闭之前,请务必释放 IMFPMediaPlayer 指针。 可以在 WM_CLOSE 消息处理程序中执行此操作。
void OnClose(HWND /*hwnd*/)
{
SafeRelease(&g_pPlayer);
SafeRelease(&g_pPlayerCB);
PostQuitMessage(0);
}
注意
此示例使用 SafeRelease 函数释放接口指针。
对于简单的视频播放,这就是你需要的所有代码。 本教程的其余部分介绍如何添加更多功能,从如何控制播放开始。
控制播放
上一部分中显示的代码将播放媒体文件,直到文件结束。 可通过在 IMFPMediaPlayer 接口调用以下方法来停止和启动播放:
- IMFPMediaPlayer::Pause 可暂停播放。 暂停播放时,将显示最新的视频帧,并且音频静音。
- IMFPMediaPlayer::Stop 可停止播放。 不会显示视频,并且播放位置将重置为文件的开头。
- IMFPMediaPlayer::Play 可在停止或暂停后恢复播放。
按空格键时,以下代码将暂停或恢复播放。
//-------------------------------------------------------------------
// OnKeyDown
//
// Handles the WM_KEYDOWN message.
//-------------------------------------------------------------------
void OnKeyDown(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
{
HRESULT hr = S_OK;
switch (vk)
{
case VK_SPACE:
// Toggle between playback and paused/stopped.
if (g_pPlayer)
{
MFP_MEDIAPLAYER_STATE state = MFP_MEDIAPLAYER_STATE_EMPTY;
g_pPlayer->GetState(&state);
if (state == MFP_MEDIAPLAYER_STATE_PAUSED ||
state == MFP_MEDIAPLAYER_STATE_STOPPED)
{
g_pPlayer->Play();
}
else if (state == MFP_MEDIAPLAYER_STATE_PLAYING)
{
g_pPlayer->Pause();
}
}
break;
}
}
此示例会调用 IMFPMediaPlayer::GetState 方法以获取当前播放状态(“已暂停”、“已停止”或“正在播放”),并相应地进行暂停或恢复。
从播放器接收事件
MFPlay 使用回调接口将事件发送到应用程序。 进行此回调有两个原因:
- 播放发生在单独的线程上。 在播放期间,可能会发生某些事件,例如到达文件的末尾。 MFPlay 使用回调将事件通知给应用程序。
- 许多 IMFPMediaPlayer 方法都是异步的,这意味着它们在操作完成之前返回。 使用异步方法可以从 UI 线程启动操作,该操作可能需要很长时间才能完成,而不会导致 UI 被阻止。 操作完成后,MFPlay 会发出回调信号。
若要接收回调通知,请实现 IMFPMediaPlayerCallback 接口。 此接口会继承 IUnknown 并定义单个方法 OnMediaPlayerEvent。 若要设置回调,请在 MFPCreateMediaPlayer 函数的 pCallback 参数中传递指向 IMFPMediaPlayerCallback 实现的指针。
下面是本教程中的第一个示例,已修改为包含回调。
// Global variables.
IMFPMediaPlayer *g_pPlayer = NULL;
IMFPMediaPlayerCallback *g_pCallback = NULL;
// Call an application-defined function to create the callback object.
hr = CreateMyCallback(&g_pCallback);
// Create the player object and play a video file.
const WCHAR *sURL = L"C:\\Users\\Public\\Videos\\example.wmv";
if (SUCCEEDED(hr))
{
hr = MFPCreateMediaPlayer(
sURL,
TRUE, // Start playback automatically?
0, // Flags.
g_pCallback, // Callback pointer.
hwnd,
&g_pPlayer
);
}
OnMediaPlayerEvent 方法具有单个参数,它是指向 MFP_EVENT_HEADER 结构的指针。 通过此结构的 eEventType 成员,可知道发生了哪个事件。 例如,播放启动时,MFPlay 会发送 MFP_EVENT_TYPE_PLAY 事件。
每个事件类型都有一个相应的数据结构,其中包含该事件的信息。 其中每个结构都以 MFP_EVENT_HEADER 结构开头。 在回调中,将 MFP_EVENT_HEADER 指针强制转换为特定于事件的数据结构。 例如,如果事件类型为 MFP_PLAY_EVENT,则数据结构为 MFP_PLAY_EVENT。 以下代码显示如何在回调中处理此事件。
void STDMETHODCALLTYPE MediaPlayerCallback::OnMediaPlayerEvent(
MFP_EVENT_HEADER *pEventHeader
)
{
switch (pEventHeader->eEventType)
{
case MFP_EVENT_TYPE_PLAY:
OnPlay(MFP_GET_PLAY_EVENT(pEventHeader));
break;
// Other event types (not shown).
}
}
// Function to handle the event.
void OnPlay(MFP_PLAY_EVENT *pEvent)
{
if (FAILED(pEvent->header.hrEvent))
{
// Error occurred during playback.
}
}
此示例使用 MFP_GET_PLAY_EVENT 事件强制转换指向 MFP_PLAY_EVENT 结构的 pEventHeade 指针。 触发事件的操作的 HRESULT 存储在结构的 hrEvent 字段中。
有关所有事件类型和相应数据结构的列表,请参阅 MFP_EVENT_TYPE。
有关线程的注意事项:默认情况下,MFPlay 从调用 MFPCreateMediaPlayer 的同一线程调用回调。 此线程必须具有消息循环。 或者,可以在 MFPCreateMediaPlayer 的 creationOptions 参数中传递 MFP_OPTION_FREE_THREADED_CALLBACK 标志。 此标志会导致 MFPlay 从单独的线程调用回调。 任一选项对应用程序都有影响。 如果是默认选项,则回调无法执行任何在消息循环上等待的任何操作,例如等待 UI 操作,因为这会阻止窗口程序。 如果是自由线程选项,则需要使用关键部分来保护在回调中访问的任何数据。 在大多数情况下,默认选项最简单。
获取有关媒体文件的信息
在 MFPlay 中打开媒体文件时,播放器将创建一个名为“媒体项”的对象,该对象表示媒体文件。 此对象公开 IMFPMediaItem 接口,它可用于获取媒体文件的相关信息。 许多 MFPlay 事件结构都包含指向当前媒体项的指针。 还可通过在播放器上调用 IMFPMediaPlayer::GetMediaItem 来获取当前媒体项。
两种特别有用的方法是 IMFPMediaItem::HasVideo 和 IMFPMediaItem::HasAudio。 这些方法会查询媒体源是否包含视频或音频。
例如,以下代码会测试当前媒体文件是否包含视频流。
IMFPMediaItem *pItem;
BOOL bHasVideo = FALSE;
BOOL bIsSelected = FALSE;
hr = g_pPlayer->GetMediaItem(TRUE, &pItem);
if (SUCCEEDED(hr))
{
hr = pItem->HasVideo(&bHasVideo, &bIsSelected);
pItem->Release();
}
如果源文件包含选择要播放的视频流,则 bHasVideo 和 bIsSelected 都设置为 TRUE。
管理视频播放
MFPlay 播放视频文件时,它会在 MFPCreateMediaPlayer 函数中指定的窗口中绘制视频。 这发生在 Microsoft 媒体基础播放管道拥有的单独线程上。 在大多数情况下,应用程序不需要管理此过程。 但是,在下面两种情况下,应用程序必须通知 MFPlay 更新视频。
- 如果播放已暂停或停止,则每当应用程序的视频窗口收到 WM_PAINT 消息时,都必须通知 MFPlay。 这样,MFPlay 就能重新绘制窗口。
- 如果调整窗口大小,必须通知 MFPlay,以便它可缩放视频以匹配新的窗口大小。
IMFPMediaPlayer::UpdateVideo 方法可处理这两种情况。 在视频窗口的 WM_PAINT 和 WM_SIZE 消息处理程序中调用此方法。
重要
在调用 UpdateVideo 之前,调用 GDI BeginPaint 函数。
IMFPMediaPlayer *g_pPlayer; // MFPlay player object
void OnSize(HWND hwnd, UINT state, int cx, int cy)
{
HDC hdc;
PAINTSTRUCT ps;
if (g_pPlayer && (state == SIZE_RESTORED))
{
hdc = BeginPaint(hwnd, &ps);
g_pPlayer->UpdateVideo();
EndPaint(hwnd, &ps);
}
}
void OnPaint(HWND hwnd)
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if (g_pPlayer)
{
g_pPlayer->UpdateVideo();
}
EndPaint(hwnd, &ps);
}
除非另行指定,否则 MFPlay 会根据需要加边框来按正确的纵横比显示视频。 如果不想保留纵横比,请使用 MFVideoARMode_None 标志调用 IMFPMediaPlayer::SetAspectRatioMode。 这将导致 MFPlay 拉伸视频来填充整个矩形,无需加边框。 通常,应使用默认设置,让 MFPlay 对视频加边框。 默认的边框颜色为黑色,但可通过调用 IMFPMediaPlayer::SetBorderColor 来更改此颜色。
MFPlay 的限制
当前版本的 MFPlay 具有以下限制:
- MFPlay 不支持受 DRM 保护的内容。
- 默认情况下,MFPlay 不支持网络流式处理协议。 有关详细信息,请参阅 IMFPMediaPlayer::CreateMediaItemFromURL。
- MFPlay 不支持服务器端播放列表 (SSPL) 或播放多个段的其他类型的源。 从技术上讲,MFPlay 在第一次呈现后停止播放,并忽略媒体源中的任何 MENewPresentation 事件。
- MFPlay 不支持媒体源之间的无缝转换。
- MFPlay 不支持混合多个视频流。
- MFPlay 仅支持媒体基础中本机支持的媒体格式。 (这包括系统上可能安装的第三方媒体基础组件。)
相关主题