如何创建播放列表
本主题介绍如何使用序列源播放一系列文件。
概述
若要按顺序播放媒体文件,应用程序必须按顺序添加拓扑以创建播放列表,并在媒体会话中将这些拓扑排队以供播放。
在媒体会话开始播放当前拓扑之前,序列器源通过初始化和加载下一个拓扑来确保无缝播放。 这使应用程序能够在需要时快速启动下一个拓扑。
媒体会话负责将数据馈送到接收器,并在序列源中播放拓扑。 此外,媒体会话还管理段的演示时间。
有关 Sequencer 源如何管理拓扑的详细信息,请参阅 关于 Sequencer 源。
本演练包含以下步骤:
显示本主题的代码示例摘自主题 Sequencer Source Example Code,其中包含完整的示例代码。
先决条件
在开始本演练之前,请先熟悉以下媒体基础概念:
另请阅读 如何使用 Media Foundation 播放媒体文件,因为此处的示例代码 shwon 扩展了该主题中的代码。
初始化媒体基础
在使用任何 Media Foundation 接口或方法之前,请通过调用 MFStartup 函数初始化 Media Foundation。 有关详细信息,请参阅 初始化媒体基础。
hr = MFStartup(MF_VERSION);
创建媒体基础对象
接下来,创建以下 Media Foundation 对象:
- 媒体会话。 此对象公开 IMFMediaSession 接口,该接口提供播放、暂停和停止当前拓扑的方法。
- Sequencer 源。 此对象公开 IMFSequencerSource 接口,该接口提供按顺序添加、更新和删除拓扑的方法。
- 调用 MFCreateMediaSession 函数以创建媒体会话。
- 调用 IMFMediaEventQueue::BeginGetEvent 以从媒体会话请求第一个事件。
- 调用 MFCreateSequencerSource 函数来创建 sequencer 源。
以下代码创建媒体会话并请求第一个事件:
// Create a new instance of the media session.
HRESULT CPlayer::CreateSession()
{
// Close the old session, if any.
HRESULT hr = CloseSession();
if (FAILED(hr))
{
goto done;
}
assert(m_state == Closed);
// Create the media session.
hr = MFCreateMediaSession(NULL, &m_pSession);
if (FAILED(hr))
{
goto done;
}
// Start pulling events from the media session
hr = m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL);
if (FAILED(hr))
{
goto done;
}
m_state = Ready;
done:
return hr;
}
创建媒体源
接下来,为第一个播放列表段创建媒体源。 使用 源冲突解决程序 根据 URL 创建媒体源。 为此,请调用 MFCreateSourceResolver 函数来创建源解析程序,然后调用 IMFSourceResolver::CreateObjectFromURL 方法来创建媒体源。
有关媒体源的信息,请参阅 媒体源。
创建部分拓扑
sequencer 源中的每个段都有自己的部分拓扑。 接下来,为媒体源创建部分拓扑。 对于部分拓扑,拓扑源节点直接连接到输出节点,而无需指定任何中间转换。 媒体会话使用拓扑加载程序对象来解析拓扑。 解析拓扑后,将添加所需的解码器和其他转换节点。 sequencer 源还可以包含完整的拓扑。
若要创建拓扑对象,请使用 MFCreateTopology 函数,然后使用 IMFTopologyNode 接口创建流节点。
有关使用这些编程元素创建拓扑的完整说明,请参阅 创建播放拓扑。
应用程序可以通过配置源节点来播放本机源的选定部分。 为此,请在 MF_TOPOLOGY_SOURCESTREAM_NODE 拓扑节点上设置 MF_TOPONODE_MEDIASTART 属性和MF_TOPONODE_MEDIASTOP 属性。 将媒体启动时间和媒体停止时间相对于本机源的开始时间指定为 UINT64 类型。
将拓扑添加到 Sequencer 源
接下来,将创建的部分拓扑添加到 sequencer 源。 每个序列元素(称为 段)都分配有一个 MFSequencerElementId 标识符。 有关 Sequencer 源如何管理拓扑的详细信息,请参阅 关于 Sequencer 源。
将所有拓扑添加到 sequencer 源后,应用程序必须标记序列中的最后一个段,以结束在管道中的播放。 如果没有此标志,排序程序源需要添加更多拓扑。
调用 IMFSequencerSource::AppendTopology 方法,将特定拓扑添加到 sequencer 源。
hr = m_pSequencerSource->AppendTopology( pTopology, SequencerTopologyFlags_Last, &SegmentId );
AppendTopology 将指定的拓扑添加到序列。 此方法返回 pdwId 参数中的段标识符。
如果拓扑是排序程序源中的最后一个拓扑,请在 dwFlags 参数中传递SequencerTopologyFlags_Last。 此值在 MFSequencerTopologyFlags 枚举中定义。
调用 IMFSequencerSource::UpdateTopologyFlags 以更新与输入列表中的段标识符关联的拓扑的标志。 在这种情况下,调用指示指定的段是排序器中的最后一段。 (如果在 AppendTopology 调用中指定了最后一个拓扑,则此调用是可选的)
BOOL bFirstSegment = (NumSegments() == 0); if (!bFirstSegment) { // Remove the "last segment" flag from the last segment. hr = m_pSequencerSource->UpdateTopologyFlags(LastSegment(), 0); if (FAILED(hr)) { goto done; } }
应用程序可以通过调用 IMFSequencerSource::UpdateTopology 并在 pTopology 中传递新拓扑,将段的拓扑替换为另一个拓扑。 如果新拓扑中存在新的本机源,则会将源添加到源缓存中。 预滚动列表也会刷新。
在媒体会话上设置第一个拓扑
接下来,在媒体会话上将序列源中的第一个拓扑排队。 若要从 sequencer 源获取第一个拓扑,应用程序必须调用 IMFMediaSourceTopologyProvider::GetMediaSourceTopology 方法。 此方法返回部分拓扑,该拓扑由媒体会话解析。
有关部分拓扑的信息,请参阅 关于拓扑。
检索序列源的第一个拓扑的本机媒体源。
通过调用 IMFMediaSource::CreatePresentationDescriptor 方法为媒体源创建演示文稿描述符。
通过调用 IMFMediaSourceTopologyProvider::GetMediaSourceTopology 方法检索演示文稿的关联拓扑。
通过调用 IMFMediaSession::SetTopology 在媒体会话上设置第一个拓扑。
调用 SetTopology ,并将 dwSetTopologyFlags 参数设置为 NULL。 这会指示媒体会话在当前拓扑完成时启动指定的拓扑。 因为在这种情况下,指定的拓扑是第一个拓扑,没有当前演示文稿,因此媒体会话会立即启动新演示文稿。
NULL 值还指示媒体会话必须解析拓扑,因为拓扑提供程序返回的拓扑始终是部分拓扑。
// Queues the next topology on the session.
HRESULT CPlaylist::QueueNextSegment(IMFPresentationDescriptor *pPD)
{
IMFMediaSourceTopologyProvider *pTopoProvider = NULL;
IMFTopology *pTopology = NULL;
//Get the topology for the presentation descriptor
HRESULT hr = m_pSequencerSource->QueryInterface(IID_PPV_ARGS(&pTopoProvider));
if (FAILED(hr))
{
goto done;
}
hr = pTopoProvider->GetMediaSourceTopology(pPD, &pTopology);
if (FAILED(hr))
{
goto done;
}
//Set the topology on the media session
m_pSession->SetTopology(NULL, pTopology);
done:
SafeRelease(&pTopoProvider);
SafeRelease(&pTopology);
return hr;
}
在媒体会话上排队下一个拓扑
接下来,应用程序需要处理 MENewPresentation 事件。
当媒体会话开始播放其后有另一段段的段时,Sequencer 源将引发 MENewPresentation 。 此事件通过为预卷列表中的下一段提供表示描述符,通知应用程序序列源中的下一个拓扑。 应用程序必须使用拓扑提供程序检索关联的拓扑,并在媒体会话中将其排队。 然后,sequencer 源将预生成此拓扑,以确保在演示文稿之间无缝转换。
当应用程序跨段查找时,当 sequencer 源刷新预滚动列表并设置正确的拓扑时,应用程序会收到多个 MENewPresentation 事件。 应用程序必须处理每个事件,并在媒体会话上对事件数据中返回的拓扑进行排队。 有关跳过段的信息,请参阅 使用 Sequencer 源。
有关获取 sequencer 源通知的信息,请参阅 Sequencer 源事件。
在 MENewPresentation 事件处理程序中,从事件数据中检索下一段的表示描述符。
通过调用 IMFMediaSourceTopologyProvider::GetMediaSourceTopology 方法获取演示文稿的关联拓扑。
通过调用 IMFMediaSession::SetTopology 方法在媒体会话上设置拓扑。
当当前演示文稿完成时,媒体会话将启动新演示文稿。
HRESULT CPlaylist::OnNewPresentation(IMFMediaEvent *pEvent)
{
IMFPresentationDescriptor *pPD = NULL;
HRESULT hr = GetEventObject(pEvent, &pPD);
if (SUCCEEDED(hr))
{
// Queue the next segment on the media session
hr = QueueNextSegment(pPD);
}
SafeRelease(&pPD);
return hr;
}
释放 Sequencer 源
最后,关闭 Sequencer 源。 为此,请在排序器源上调用 IMFMediaSource::Shutdown 方法。 此调用将关闭排序器源中的所有基础本机媒体源。
释放 Sequencer 源后,应用程序应按该顺序调用 IMFMediaSession::Close 和 IMFMediaSession::Shutdown 来关闭和关闭媒体会话。
为了避免内存泄漏,应用程序必须在不再需要媒体基础接口时释放指向这些接口的指针。
后续步骤
本演练演示了如何使用 Sequencer 源创建基本播放列表。 创建播放列表后,可能需要添加高级功能,例如跳过段、更改播放状态以及在段内查找。 以下列表提供了指向相关主题的链接:
相关主题