从现有 ASF 数据对象生成流示例
ASF 拆分器 对象是一个 WMContainer 层组件,用于分析高级系统格式的 ASF 数据对象 (ASF) 文件。
在将数据包传递给拆分器之前,应用程序必须在拆分器上初始化、配置和选择流,以便为分析过程做好准备。 有关信息,请参阅 创建 ASF 拆分器对象 和 配置 ASF 拆分器对象。
分析 ASF 数据对象所需的方法是:
- IMFASFSplitter::P arseData ,通过将包含数据包的缓冲区推送到拆分器来启动分析过程。
- IMFASFSplitter::GetNextSample ,用于收集从传递给拆分器的缓冲区生成的流样本。
查找数据偏移量
在开始分析过程之前,应用程序必须在 ASF 文件中找到数据对象。 可通过两种方法从文件开头获取数据对象的偏移量:
在初始化 ContentInfo 对象之前,可以调用 IMFASFContentInfo::GetHeaderSize 方法。 此方法需要包含 ASF 标头的前 30 个字节的缓冲区。 它返回指示第一个数据包的偏移量的整个标头的大小。 此值还包括 50 字节的数据对象标头大小。
初始化 ContentInfo 对象后,可以通过调用 IMFASFContentInfo::GeneratePresentationDescriptor 来获取演示文稿描述符,然后查询 MF_PD_ASF_DATA_START_OFFSET 属性的演示文稿描述符。 此属性的值是标头大小。
注意
表示描述符上的 MF_PD_ASF_DATA_LENGTH 属性指定 ASF 数据对象的长度。
在这两种情况下,返回的值都是标头对象的大小加上数据对象的标头部分的大小。 因此,生成的值是 ASF 数据对象中数据包开始的偏移量。 开始向拆分器发送数据时,数据必须从 ASF 文件开头的此偏移量开始。
偏移值作为参数传递给启动分析过程的 ParseData 。
数据对象划分为数据包。 每个数据包都包含一个数据包标头,该标头提供数据包分析信息和有效负载数据-实际数字媒体数据。 在查找方案中,应用程序可能希望拆分器开始分析特定数据包。 为此,可以使用 ASF 索引器来检索偏移量。 索引器返回从数据包边界开始的偏移值。 如果不使用索引器,请确保偏移量从数据包标头的开头开始。 如果向拆分器传递了无效的偏移量,例如值未指向数据包边界,则 ParseHeader 和 GetNextSample 调用成功,但 GetNextSample 不会检索任何样本,并且 pSample 参数中收到 NULL。
如果拆分器配置为反向分析,则拆分器始终在传递给 ParseData 的媒体缓冲区的末尾开始分析。 因此,对于 对 ParseData 的调用中的反向分析,请传递 cbLength 参数中的偏移量,该参数指定数据的长度并将 cbBufferOffset 设置为零。
为 ASF 数据包生成示例
应用程序通过将数据包传递给拆分器来启动分析过程。 拆分器输入是一系列包含数据对象的整个 或 片段的媒体缓冲区。 拆分器输出是一系列包含数据包数据的媒体示例。
若要将输入数据传递给拆分器,请创建一个媒体缓冲区,并使用 ASF 文件的“数据对象”部分中的数据填充该缓冲区。 (有关媒体缓冲区的详细信息,请参阅 Media Buffers.) 然后将媒体缓冲区传递给 IMFASFSplitter::P arseData 方法。 您还可以指定:
- 拆分器应开始分析的缓冲区的偏移量。 如果偏移量为零,则分析从缓冲区的开头开始。 有关设置数据偏移量的信息,请参阅本主题中的“查找数据偏移量”部分。
- 要分析的数据量。 如果此值为零,拆分器将分析,直到它到达 由 IMFMediaBuffer::GetCurrentLength 方法指定的缓冲区末尾。
拆分器通过引用媒体缓冲区中的数据来生成媒体样本。 客户端可以通过在循环中调用 IMFASFSplitter::GetNextSample 来检索输出样本,直到没有更多要分析的数据。 如果 GetNextSample 返回 pdwStatusFlags 参数中的ASF_STATUSFLAGS_INCOMPLETE标志,则意味着还有更多示例要检索,应用程序可以再次调用 GetNextSample 。 否则,请调用 ParseData 将更多数据传递给拆分器。 对于生成的示例,拆分器设置以下信息:
- 拆分器为其生成的所有样本设置时间戳。 示例时间表示演示时间,不包括预滚动时间。 应用程序可以调用 IMFSample::GetSampleTime 来获取呈现时间(以 100 纳秒为单位)。
- 如果在样本生成期间发生中断,拆分器在中断后对第一个样本设置 MFSampleExtension_Discontinuity 属性。 中断通常是由网络连接上的数据包丢弃、文件数据损坏或拆分器从一个源流切换到另一个源流引起的。
- 对于视频,拆分器会检查示例是否包含关键帧。 如果存在,拆分器将设置示例上的 MFSampleExtension_CleanPoint 属性。
如果拆分器正在分析从媒体服务器接收的数据包,则数据包长度可能可变。 在这种情况下,客户端必须为每个数据包调用 ParseData ,并在发送到拆分器的每个缓冲区上设置 MFASFSPLITTER_PACKET_BOUNDARY 属性。 此属性向拆分器指示媒体缓冲区是否包含 ASF 数据包的开头。 如果缓冲区包含新数据包的开始,请将 属性设置为 TRUE 。 如果缓冲区包含上一个数据包的延续,请将 属性设置为 FALSE。 缓冲区不能跨越多个数据包。
在将新的媒体缓冲区传递给拆分器之前,应用程序必须调用 IMFASFSplitter::Flush。 此方法重置拆分器并清除等待完成的任何部分帧。 这在查找偏移量位于不同位置的方案中非常有用。
示例
下面的代码示例演示如何分析数据包。 此示例分析从数据对象的开头到流的末尾,并显示有关包含关键帧的示例的信息。 有关使用此代码的完整示例,请参阅 教程:读取 ASF 文件。
// Parse the video stream and display information about the video samples.
//
// The current read position of the byte stream must be at the start of the ASF
// Data Object.
HRESULT DisplayKeyFrames(IMFByteStream *pStream, IMFASFSplitter *pSplitter)
{
const DWORD cbReadSize = 2048; // Read size (arbitrary value)
IMFMediaBuffer *pBuffer = NULL;
IMFSample *pSample = NULL;
HRESULT hr = S_OK;
while (SUCCEEDED(hr))
{
// The parser must get a newly allocated buffer each time.
hr = MFCreateMemoryBuffer(cbReadSize, &pBuffer);
if (FAILED(hr))
{
break;
}
// Read data into the buffer.
hr = ReadFromByteStream(pStream, pBuffer, cbReadSize);
if (FAILED(hr))
{
break;
}
// Get the amound of data that was read.
DWORD cbData;
hr = pBuffer->GetCurrentLength(&cbData);
if (FAILED(hr))
{
break;
}
if (cbData == 0)
{
break; // End of file.
}
// Send the data to the ASF splitter.
hr = pSplitter->ParseData(pBuffer, 0, 0);
SafeRelease(&pBuffer);
if (FAILED(hr))
{
break;
}
// Pull samples from the splitter.
DWORD parsingStatus = 0;
do
{
WORD streamID;
hr = pSplitter->GetNextSample(&parsingStatus, &streamID, &pSample);
if (FAILED(hr))
{
break;
}
if (pSample == NULL)
{
// No samples yet. Parse more data.
break;
}
if (IsRandomAccessPoint(pSample))
{
DisplayKeyFrame(pSample);
}
SafeRelease(&pSample);
} while (parsingStatus & ASF_STATUSFLAGS_INCOMPLETE);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
相关主题