使用索引器写入新索引

本主题演示如何为高级系统格式 (ASF) 文件编写索引。

下面是创建 ASF 索引的一般过程:

  1. 初始化 ASF 索引器对象的新实例,如 索引器创建和配置中所述。
  2. (可选)配置索引器。
  3. 将 ASF 数据包发送到索引器。
  4. 提交索引。
  5. 从索引器获取已完成的索引,并将其写入流。

配置索引器

若要使用索引器编写新的索引对象,索引器对象必须具有标志MFASF_INDEXER_WRITE_NEW_INDEX设置,并调用 IMFASFIndexer::SetFlags ,然后才能使用 IMFASFIndexer::Initialize 初始化它。

当索引器配置为写入索引时,调用方将选择要编制索引的流。 默认情况下,索引器会尝试为所有流创建索引对象。 默认时间间隔为一秒。

IMFASFIndexer::SetIndexStatus 可用于替代索引器对象的默认流和索引类型选择。

以下示例代码演示调用 SetIndexStatus 之前ASF_INDEX_DESCRIPTOR和ASF_INDEX_IDENTIFIER的初始化。

ASF_INDEX_DESCRIPTOR IndexerType;
ZeroMemory(&IndexerType, sizeof(ASF_INDEX_DESCRIPTOR));

ASF_INDEX_IDENTIFIER IndexIdentifier;
ZeroMemory(&IndexIdentifier, sizeof(ASF_INDEX_IDENTIFIER));

IndexIdentifier.guidIndexType = GUID_NULL;
IndexIdentifier.wStreamNumber = 1;

IndexerType.Identifier = IndexIdentifier;
IndexerType.cPerEntryBytes  = MFASFINDEXER_PER_ENTRY_BYTES_DYNAMIC;
IndexerType.dwInterval  = MFASFINDEXER_NO_FIXED_INTERVAL;

hr = pIndexer->SetIndexStatus((BYTE*)&IndexerType, sizeof(ASF_INDEX_DESCRIPTOR), TRUE);

索引标识符必须将其 GUID 索引类型设置为GUID_NULL,以指示索引类型将基于表示时间。 还必须使用要编制索引的 ASF 流的流号初始化索引标识符。 设置索引标识符后,使用它初始化索引描述符。

索引描述符结构具有必须在调用 SetIndexStatus 之前设置的成员。 标识符 是标识流号和索引类型的 ASF_INDEX_IDENTIFIER 结构。 cPerEntryBytes 是用于每个索引项的字节数。 如果值MFASFINDEXER_PER_ENTRY_BYTES_DYNAMIC,索引项的大小可变。 dwInterval 是索引间隔。 MFASFINDEXER_NO_FIXED_INTERVAL值指示没有固定索引间隔。

将 ASF 数据包发送到索引器

由于索引器是 WMContainer 级别对象,因此在数据包生成过程中必须与复用器结合使用。

GetNextPacket 返回的数据包可以通过调用 GenerateIndexEntries 将返回的数据包发送到索引器对象,以便在其中为每个发送的数据包创建索引条目。

以下代码演示了如何完成此操作:

HRESULT SendSampleToMux(
    IMFASFMultiplexer *pMux,
    IMFASFIndexer *pIndex,
    IMFSample *pSample,
    WORD wStream,
    IMFByteStream *pDestStream
    )
{
    IMFSample *pOutputSample = NULL;
    IMFMediaBuffer *pDataPacket = NULL;

    DWORD dwMuxStatus = ASF_STATUSFLAGS_INCOMPLETE;

    HRESULT hr = pMux->ProcessSample(wStream, pSample, 0);
    if (FAILED(hr))
    {
        goto done;
    }

    while (dwMuxStatus & ASF_STATUSFLAGS_INCOMPLETE)
    {
        hr = pMux->GetNextPacket(&dwMuxStatus, &pOutputSample);
        if (FAILED(hr))
        {
            goto done;
        }

        if (pOutputSample)
        {
            // Send the data packet to the indexer
            hr = pIndex->GenerateIndexEntries(pOutputSample);
            if (FAILED(hr))
            {
                goto done;
            }

            // Convert the sample to a contiguous buffer.
            hr = pOutputSample->ConvertToContiguousBuffer(&pDataPacket);
            if (FAILED(hr))
            {
                goto done;
            }

            // Write the buffer to the byte stream.
            hr = WriteBufferToByteStream(pDestStream, pDataPacket, NULL);
            if (FAILED(hr))
            {
                goto done;
            }
        }

        SafeRelease(&pOutputSample);
        SafeRelease(&pDataPacket);
    }

done:
    SafeRelease(&pOutputSample);
    SafeRelease(&pDataPacket);
    return hr;
}

有关详细信息,请参阅 生成新的 ASF 数据包

提交索引

最后一个数据包创建索引项后,必须提交索引。 这是通过调用 IMFASFIndexer::CommitIndex 来完成的。 CommitIndex 使用指向 ContentInfo 对象的指针,该对象描述 ASF 文件的内容。 提交索引完成索引,并使用有关文件大小和可查找性的新信息更新标头。

获取已完成索引

若要从索引器获取已完成的索引,请执行以下步骤:

  1. 调用 IMFASFIndexer::GetIndexWriteSpace 以获取索引的大小。
  2. 调用 MFCreateMemoryBuffer 创建媒体缓冲区。 可以分配足够大的缓冲区来容纳整个索引,分配较小的缓冲区,并在区块中获取索引。
  3. 调用 IMFASFIndexer::GetCompletedIndex 以获取索引数据。 在第一次调用中,将 cbOffsetWithinIndex 参数设置为零。 如果以区块为单位获取索引,则每次按上一次调用中的数据大小递增 cbOffsetWithinIndex
  4. 调用 IMFMediaBuffer::Lock 以获取指向索引数据和数据大小的指针。
  5. 将索引数据写入 ASF 文件。
  6. 调用 IMFMediaBuffer::Unlock 以解锁媒体缓冲区。
  7. 重复步骤 3-6,直到写入整个索引。

以下代码演示了这些步骤:

HRESULT WriteASFIndex(IMFASFIndexer *pIndex,IMFByteStream *pStream)
{
    const DWORD cbChunkSize = 4096;

    IMFMediaBuffer *pBuffer = NULL;

    QWORD cbIndex = 0;
    DWORD cbIndexWritten = 0;

    HRESULT hr = pIndex->GetIndexWriteSpace(&cbIndex);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = MFCreateMemoryBuffer(cbChunkSize, &pBuffer);
    if (FAILED(hr))
    {
        goto done;
    }

    while (cbIndexWritten < cbIndex)
    {
        BYTE *pData = NULL;
        DWORD cbData = 0;
        DWORD cbWritten = 0;

        hr = pIndex->GetCompletedIndex(pBuffer, cbIndexWritten);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pBuffer->Lock(&pData, NULL, &cbData);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStream->Write(pData, cbData, &cbWritten);

        (void)pBuffer->Unlock();

        if (FAILED(hr))
        {
            goto done;
        }

        cbIndexWritten += cbData;
    }

done:
    SafeRelease(&pBuffer);
    return hr;
};

ASF 索引器