生成新的 ASF 数据包

ASF 多路复用器 是一个 WMContainer 层组件,适用于 ASF 数据对象 ,使应用程序能够为符合 ContentInfo 对象中定义的要求的流生成 ASF 数据包。

多路复用器有一个输入和一个输出。 它接收包含数字媒体数据的流示例,并生成一个或多个可写入 ASF 容器的数据包。

以下列表总结了生成 ASF 数据包的过程:

  1. 将输入数据传递到 IMFASFMultiplexer::P rocessSample 中的多路复用器。
  2. 通过在循环中调用 IMFASFMultiplexer::GetNextPacket 来收集数据包,直到检索到所有完整的数据包。
  3. 将输入数据转换为完整数据包后,多路复用器中可能存在一些挂起的数据, 而 GetNextPacket 未检索这些数据。 调用 IMFASFMultiplexer::Flush 将挂起的样本打包,并通过再次调用 GetNextPacket 从多路复用器收集它们。
  4. 通过调用 IMFASFMultiplexer::End 来更新关联的 ASF 标头对象,以反映多路复用器在数据包生成过程中所做的更改。

下图演示了通过多路复用器生成 ASF 文件的数据数据包。

显示 asf 文件数据包生成的示意图

ASF 数据包创建

按照创建多路复用器对象中所述创建和初始化 多路复用器后,调用 IMFASFMultiplexer::P rocessSample 将输入数据传递到多路复用器以处理数据包。 指定的输入必须位于媒体样本 (IMFSample 接口) 中,该接口 (IMFMediaBuffer 接口) 包含流数据的一个或多个媒体缓冲区。 在 ASF 到 ASF 转码的情况下,可以从创建数据包化流样本的拆分器生成输入媒体样本。 有关详细信息,请参阅 ASF 拆分器

在调用 ProcessSample 之前,请确保输入媒体样本的时间戳是有效的演示时间;否则 ,ProcessSample 将失败并返回MF_E_NO_SAMPLE_TIMESTAMP代码。

多路复用器可以通过 ProcessSample 接受作为压缩或未压缩媒体样本的输入。 多路复用器根据流的带宽使用情况为这些样本分配发送时间。 在此过程中,多路复用器会检查泄漏的存储桶参数 (比特率和缓冲区窗口利用率) ,并可以拒绝不符合这些值的样本。 输入媒体示例可能由于以下任一原因导致带宽检查失败:

  • 如果输入媒体样本由于上次分配的发送时间大于此媒体示例中的时间戳而延迟到达。 ProcessSample 失败并返回 MF_E_LATE_SAMPLE 错误代码。
  • 如果输入媒体样本上的时间戳早于分配的发送时间 (则表示缓冲区溢出) 。 如果将多路复用器配置为在多路复用器初始化期间设置 MFASF_MULTIPLEXER_AUTOADJUST_BITRATE 标志来调整比特率,则多路复用器可以忽略这种情况。 有关详细信息,请参阅创建多路复用器对象中的“ 多路复用器初始化和泄漏存储桶设置”。 如果未设置此标志,并且多路复用器遇到带宽溢出, 则 ProcessSample 将失败并返回 MF_E_BANDWIDTH_OVERRUN 错误代码。

多路复用器分配发送时间后,输入媒体样本将添加到 发送窗口,这是按发送时间排序并准备处理成数据包的输入媒体样本列表。 在数据包构造期间,将分析输入媒体示例,并将相关数据作为有效负载写入数据包。 完整的数据包可以包含来自一个或多个输入媒体样本的数据。

当新的输入媒体样本到达发送窗口时,它们将添加到队列中,直到有足够的媒体样本形成一个完整的数据包。 输入媒体样本包含的媒体缓冲区中的数据不会复制到生成的数据包。 数据包会保留对输入媒体缓冲区的引用,直到输入媒体样本已完全数据包化,并且从多路复用器收集了完整的数据包。

当完整的数据包可用时,可以通过调用 IMFASFMultiplexer::GetNextPacket 来检索它。 如果在有可供检索的完整数据包可供检索时调用 ProcessSample ,它将失败并返回 MF_E_NOTACCEPTING 错误代码。 这表示多路复用器无法接受更多输入,必须调用 GetNextPacket 来检索等待的数据包。 理想情况下,每个 ProcessSample 调用都应后跟一个或多个 GetNextPacket 调用,以获取完整的数据包。 创建完整的数据包可能需要多个输入媒体样本。 相反,一个输入媒体样本中的数据可能跨多个数据包。 因此,并非所有对 ProcessSample 的 调用都会生成输出媒体样本。

如果输入媒体示例包含 由 MFSampleExtension_CleanPoint 属性指示的关键帧,则多路复用器会将 属性复制到数据包。

获取 ASF 数据包

若要收集多路复用器生成的完整数据包的输出媒体样本,请在循环中调用 IMFASFMultiplexer::GetNextPacket ,直到没有为数据包留下更多输出媒体样本。 下面列出了成功案例:

  • 如果有可用的完整数据包,GetNextPacket 会在 pdwStatusFlags 参数中接收ASF_STATUS_FLAGS_INCOMPLETE标志;ppIPacket 参数接收指向第一个数据包的指针。 必须调用此方法,只要它收到此标志。 每次迭代时, ppIPacket 都指向队列中的下一个数据包。
  • 如果只有一个数据包,则 ppIPacket 指向该数据包,并且 pdwStatusFlags 中不会收到ASF_STATUS_FLAGS_INCOMPLETE标志。
  • 如果多路复用器仍在数据包化和添加数据包的过程中,则 GetNextPacket 可以成功,而不会生成任何数据包。 在本例中, ppIPacket 指向 NULL。 若要继续,必须通过调用 ProcessSample 为多路复用器提供更多输入媒体样本。

以下示例代码演示使用多路复用器生成数据包的函数。 生成的数据包内容将写入调用方分配的数据字节流。

//-------------------------------------------------------------------
// GenerateASFDataPackets
// 
// Gets data packets from the mux. This function is called after 
// calling IMFASFMultiplexer::ProcessSample. 
//-------------------------------------------------------------------

HRESULT GenerateASFDataPackets( 
    IMFASFMultiplexer *pMux, 
    IMFByteStream *pDataStream
    )
{
    HRESULT hr = S_OK;

    IMFSample *pOutputSample = NULL;
    IMFMediaBuffer *pDataPacketBuffer = NULL;

    DWORD dwMuxStatus = ASF_STATUSFLAGS_INCOMPLETE;

    while (dwMuxStatus & ASF_STATUSFLAGS_INCOMPLETE)
    {
        hr = pMux->GetNextPacket(&dwMuxStatus, &pOutputSample);

        if (FAILED(hr))
        {
            break;
        }

        if (pOutputSample)
        {
            //Convert to contiguous buffer
            hr = pOutputSample->ConvertToContiguousBuffer(&pDataPacketBuffer);
            
            if (FAILED(hr))
            {
                break;
            }

            //Write buffer to byte stream
            hr = WriteBufferToByteStream(pDataStream, pDataPacketBuffer, NULL);

            if (FAILED(hr))
            {
                break;
            }
        }

        SafeRelease(&pDataPacketBuffer);
        SafeRelease(&pOutputSample);
    }

    SafeRelease(&pOutputSample);
    SafeRelease(&pDataPacketBuffer);
    return hr;
}

函数 WriteBufferToByteStream 显示在主题 IMFByteStream::Write 中。

若要查看使用此代码示例的完整应用程序,请参阅 教程:将 ASF 流从一个文件复制到另一个文件

发布Packet-Generation呼叫

若要确保多路复用器中没有等待的完整数据包,请调用 IMFASFMultiplexer::Flush。 这会强制多路复用器对正在进行的所有媒体样本进行数据包处理。 应用程序可以在循环中通过 GetNextPacket 以媒体样本的形式收集这些数据包,直到没有剩余的数据包要检索为止。

生成所有媒体样本后,调用 IMFASFMultiplexer::End 以更新与这些数据包关联的 ASF 标头对象。 通过传递用于初始化多路复用器的 ContentInfo 对象来指定标头对象。 此调用会更新各种标头对象,以反映多路复用器在数据包生成期间所做的更改。 此信息包括所有流的数据包计数、发送持续时间、播放持续时间和流数。 总体标头大小也会更新。

必须确保在检索完所有数据包后调用 End 。 如果多路复用器中有任何数据包等待, 则 End 将失败并返回 MF_E_FLUSH_NEEDED 错误代码。 在这种情况下,通过循环调用 FlushGetNextPacket 来获取等待的数据包。

注意

对于 VBR 编码,调用 End 后,必须在 ContentInfo 对象的编码属性中设置编码统计信息。 有关此过程的信息,请参阅在 ContentInfo 对象中设置属性中的“使用编码器设置配置 ContentInfo 对象”。 以下列表显示了要设置的特定属性:

 

ASF 多路复用器

教程:将 ASF 流从一个文件复制到另一个文件

教程:使用 CBR 编码编写 WMA 文件