Utilisation de l’indexeur pour écrire un nouvel index

Cette rubrique montre comment écrire un index pour un fichier ASF (Advanced Systems Format).

Voici la procédure générale de création d’un index ASF :

  1. Initialisez une nouvelle instance de l’objet indexeur ASF, comme décrit dans Création et configuration de l’indexeur.
  2. Si vous le souhaitez, configurez l’indexeur.
  3. Envoyez des paquets de données ASF à l’indexeur.
  4. Commitez l’index.
  5. Obtenez l’index terminé à partir de l’indexeur et écrivez-le dans un flux.

Configurer l’indexeur

Pour utiliser l’indexeur pour écrire un nouvel objet d’index, l’objet indexeur doit avoir l’indicateur MFASF_INDEXER_WRITE_NEW_INDEX défini avec un appel à IMFASFIndexer::SetFlags avant d’être initialisé avec IMFASFIndexer::Initialize.

Lorsque l’indexeur est configuré pour écrire un index, l’appelant choisit les flux à indexer. Par défaut, l’indexeur tente de créer un objet Index pour tous les flux. L’intervalle de temps par défaut est d’une seconde.

IMFASFIndexer::SetIndexStatus peut être utilisé pour remplacer le choix par défaut des flux et des types d’index de l’objet indexeur.

L’exemple de code suivant montre l’initialisation d’un ASF_INDEX_DESCRIPTOR et d’un ASF_INDEX_IDENTIFIER avant un appel à SetIndexStatus.

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);

L’identificateur d’index doit avoir son type d’index GUID défini sur GUID_NULL pour indiquer que le type d’index sera basé sur le temps de présentation. L’identificateur d’index doit également être initialisé avec le numéro de flux du flux ASF à indexer. Une fois l’identificateur d’index défini, utilisez-le pour initialiser le descripteur d’index.

La structure de descripteur d’index a des membres qui doivent être définis avant l’appel à SetIndexStatus. Identificateur est une structure ASF_INDEX_IDENTIFIER qui identifie le numéro de flux et le type d’index. cPerEntryBytes est le nombre d’octets utilisés pour chaque entrée d’index. Si la valeur est MFASFINDEXER_PER_ENTRY_BYTES_DYNAMIC, les entrées d’index ont une taille variable. dwInterval est l’intervalle d’indexation. La valeur MFASFINDEXER_NO_FIXED_INTERVAL indique qu’il n’existe aucun intervalle d’indexation fixe.

Envoyer des paquets de données ASF à l’indexeur

Étant donné que l’indexeur est un objet de niveau WMContainer, il doit être utilisé conjointement avec le multiplexeur lors de la génération de paquets.

Les paquets retournés par GetNextPacket peuvent être envoyés à l’objet indexeur par le biais d’appels à GenerateIndexEntries , où il crée des entrées d’index pour chaque paquet envoyé.

Le code suivant montre comment procéder :

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;
}

Pour plus d’informations, consultez Génération de nouveaux paquets de données ASF.

Valider l’index

Une fois qu’une entrée d’index a été créée pour le dernier paquet, l’index doit être validée. Pour ce faire, appelez IMFASFIndexer::CommitIndex. CommitIndex prend un pointeur vers l’objet ContentInfo qui décrit le contenu du fichier ASF. La validation de l’index termine l’indexation et met à jour l’en-tête avec de nouvelles informations sur la taille et la recherche du fichier.

Obtenir l’index terminé

Pour obtenir l’index terminé à partir de l’indexeur, effectuez les étapes suivantes :

  1. Appelez IMFASFIndexer::GetIndexWriteSpace pour obtenir la taille de l’index.
  2. Appelez MFCreateMemoryBuffer pour créer une mémoire tampon multimédia. Vous pouvez soit allouer une mémoire tampon suffisamment grande pour contenir l’index entier, soit allouer une mémoire tampon plus petite et obtenir l’index en blocs.
  3. Appelez IMFASFIndexer::GetCompletedIndex pour obtenir les données d’index. Lors du premier appel, définissez le paramètre cbOffsetWithinIndex sur zéro. Si vous obtenez l’index en blocs, incrémentez cbOffsetWithinIndex à chaque fois en fonction de la taille des données de l’appel précédent.
  4. Appelez IMFMediaBuffer::Lock pour obtenir un pointeur vers les données d’index et la taille des données.
  5. Écrivez les données d’index dans le fichier ASF.
  6. Appelez IMFMediaBuffer::Unlock pour déverrouiller la mémoire tampon multimédia.
  7. Répétez les étapes 3 à 6 jusqu’à ce que vous ayez écrit l’index entier.

Le code suivant illustre ces étapes :

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;
};

Indexeur ASF