Tutoriel : Encodage multimédia Windows 1 pass

L’encodage fait référence au processus de conversion de médias numériques d’un format en un autre. Par exemple, la conversion de l’audio MP3 en Windows format audio multimédia tel que défini par la spécification ASF (Advanced Systems Format).

Note La spécification ASF définit un type de conteneur pour le fichier de sortie (.wma ou .wmv) qui peut contenir des données multimédias dans n’importe quel format, compressé ou non compressé. Par exemple, un conteneur ASF tel qu’un fichier .wma peut contenir des données multimédias au format MP3. Le processus d’encodage convertit le format réel des données contenues dans le fichier.

Ce tutoriel illustre l’encodage de la source d’entrée de contenu clair (non protégé par DRM) dans Windows contenu multimédia et l’écriture des données dans un nouveau fichier ASF (.wm*) à l’aide des composants ASF de couche pipeline. Ces composants seront utilisés pour générer une topologie d’encodage partielle, qui sera contrôlée par une instance de la session multimédia.

Dans ce tutoriel, vous allez créer une application console qui accepte les noms de fichiers d’entrée et de sortie, ainsi que le mode d’encodage en tant qu’arguments. Le fichier d’entrée peut être dans un format multimédia compressé ou non compressé. Les modes d’encodage valides sont « CBR » (taux de bits constant) ou « VBR » (taux de bits variable). L’application crée une source multimédia pour représenter la source spécifiée par le nom de fichier d’entrée et le récepteur de fichiers ASF pour archiver le contenu encodé du fichier source dans un fichier ASF. Pour simplifier l’implémentation du scénario, le fichier de sortie n’aura qu’un seul flux audio et un seul flux vidéo. L’application insère le codec Windows Media Audio 9.1 Professional pour la conversion de format de flux audio et Windows codec Media Video 9 pour le flux vidéo.

Pour l’encodage de débit constant, avant le début de la session d’encodage, l’encodeur doit connaître le débit de bits cible qu’il doit atteindre. Dans ce tutoriel, pour le mode « CBR », l’application utilise le débit de bits disponible avec le premier type de média de sortie récupéré à partir de l’encodeur pendant la négociation de type multimédia comme taux de bits cible. Pour l’encodage de débit de bits variable, ce didacticiel illustre l’encodage avec un débit binaire variable en définissant un niveau de qualité. Les flux audio sont encodés au niveau de qualité de 98 et de flux vidéo au niveau de qualité de 95.

La procédure suivante récapitule les étapes d’encodage Windows contenu multimédia dans un conteneur ASF à l’aide d’un mode d’encodage à 1 passe.

  1. Créez une source multimédia pour l’élément spécifié à l’aide du programme de résolution source.
  2. Énumérez les flux dans la source multimédia.
  3. Créez le récepteur multimédia ASF et ajoutez des récepteurs de flux en fonction des flux de la source multimédia qui doivent être encodés.
  4. Configurez le récepteur multimédia avec les propriétés d’encodage requises.
  5. Créez les encodeurs multimédias Windows pour les flux dans le fichier de sortie.
  6. Configurez les encodeurs avec les propriétés définies sur le récepteur multimédia.
  7. Créez une topologie d’encodage partielle.
  8. Instanciez la session multimédia et définissez la topologie sur la session multimédia.
  9. Démarrez la session d’encodage en contrôlant la session multimédia et en obtenant tous les événements pertinents à partir de la session multimédia.
  10. Pour l’encodage VBR, obtenez les valeurs de propriété d’encodage de l’encodeur et définissez-les sur le récepteur multimédia.
  11. Fermez et arrêtez la session d’encodage.

Ce didacticiel contient les sections suivantes :

Prérequis

Ce didacticiel part des principes suivants :

Configurer le Project

  1. Incluez les en-têtes suivants dans votre fichier source :

    #include <new>
    #include <mfidl.h>            // Media Foundation interfaces
    #include <mfapi.h>            // Media Foundation platform APIs
    #include <mferror.h>        // Media Foundation error codes
    #include <wmcontainer.h>    // ASF-specific components
    #include <wmcodecdsp.h>        // Windows Media DSP interfaces
    #include <Dmo.h>            // DMO objects
    #include <uuids.h>            // Definition for FORMAT_VideoInfo
    #include <propvarutil.h>
    
    
  2. Lien vers les fichiers de bibliothèque suivants :

    // The required link libraries 
    #pragma comment(lib, "mfplat")
    #pragma comment(lib, "mf")
    #pragma comment(lib, "mfuuid")
    #pragma comment(lib, "msdmo")
    #pragma comment(lib, "strmiids")
    #pragma comment(lib, "propsys")
    
    
  3. Déclarez la fonction SafeRelease .

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. Déclarez l’énumération ENCODING_MODE pour définir les types d’encodage CBR et VBR.

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. Définissez une constante pour la fenêtre de mémoire tampon d’un flux vidéo.

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

Créer la source multimédia

Créez une source multimédia pour la source d’entrée. Media Foundation fournit des sources multimédias intégrées pour différents formats multimédias : MP3, MP4/3GP, AVI/WAVE. Pour plus d’informations sur les formats pris en charge par Media Foundation, consultez Formats multimédias pris en charge dans Media Foundation.

Pour créer la source multimédia, utilisez le programme de résolution de source. Cet objet analyse l’URL spécifiée pour le fichier source et crée la source multimédia appropriée.

Effectuez les appels suivants :

L’exemple de code suivant montre une fonction CreateMediaSource qui crée une source multimédia pour le fichier spécifié.

//  Create a media source from a URL.
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
{
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver* pSourceResolver = NULL;
    IUnknown* pSource = NULL;

    // Create the source resolver.
    HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
    if (FAILED(hr))
    {
        goto done;
    }

    // Use the source resolver to create the media source.

    // Note: For simplicity this sample uses the synchronous method to create 
    // the media source. However, creating a media source can take a noticeable
    // amount of time, especially for a network source. For a more responsive 
    // UI, use the asynchronous BeginCreateObjectFromURL method.

    hr = pSourceResolver->CreateObjectFromURL(
        sURL,                       // URL of the source.
        MF_RESOLUTION_MEDIASOURCE,  // Create a source object.
        NULL,                       // Optional property store.
        &ObjectType,        // Receives the created object type. 
        &pSource            // Receives a pointer to the media source.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the IMFMediaSource interface from the media source.
    hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));

done:
    SafeRelease(&pSourceResolver);
    SafeRelease(&pSource);
    return hr;
}

Créer le récepteur de fichiers ASF

Créez une instance du récepteur de fichiers ASF qui archivera les données multimédias encodées dans un fichier ASF à la fin de la session d’encodage.

Dans ce tutoriel, vous allez créer un objet d’activation pour le récepteur de fichiers ASF. Le récepteur de fichiers a besoin des informations suivantes pour créer les récepteurs de flux requis.

  • Flux à encoder et écrire dans le fichier final.
  • Propriétés d’encodage utilisées pour encoder le contenu multimédia, telles que le type d’encodage, le nombre de passes d’encodage et les propriétés associées.
  • Propriétés de fichier globales qui indiquent au récepteur multimédia s’il doit ajuster automatiquement les paramètres de compartiment fuite (débit de bits et taille de la mémoire tampon).

Les informations de flux sont configurées dans l’objet Profil ASF et les propriétés d’encodage et globales sont définies dans un magasin de propriétés géré par l’objet ContentInfo ASF.

Pour obtenir une vue d’ensemble du récepteur de fichiers ASF, consultez récepteurs de média ASF.

Créer l’objet de profil ASF

Pour que le récepteur de fichiers ASF écrive des données multimédias encodées dans un fichier ASF, le récepteur doit connaître le nombre de flux et le type de flux pour lesquels créer des récepteurs de flux. Dans ce tutoriel, vous allez extraire ces informations de la source multimédia et créer des flux de sortie correspondants. Limitez vos flux de sortie à un flux audio et un flux vidéo. Pour chaque flux sélectionné dans la source, créez un flux cible et ajoutez-le au profil.

Pour implémenter cette étape, vous avez besoin des objets suivants.

  • Profil ASF
  • Descripteur de présentation pour la source multimédia
  • Descripteurs de flux pour les flux sélectionnés dans la source multimédia.
  • Types multimédias pour les flux sélectionnés.

Les étapes suivantes décrivent le processus de création du profil ASF et des flux cibles.

Pour créer le profil ASF

  1. Appelez MFCreateASFProfile pour créer un objet de profil vide.

  2. Appelez IMFMediaSource::CreatePresentationDescriptor pour créer le descripteur de présentation pour la source multimédia créée à l’étape décrite dans la section « Créer la source multimédia » de ce didacticiel.

  3. Appelez IMFPresentationDescriptor::GetStreamDescriptorCount pour obtenir le nombre de flux dans la source multimédia.

  4. Appelez IMFPresentationDescriptor::GetStreamDescriptorByIndex pour chaque flux dans la source multimédia, obtenez le descripteur de flux du flux.

  5. Appelez IMFStreamDescriptor::GetMediaTypeHandler suivi de IMFMediaTypeHandler::GetMediaTypeByIndex et obtenez le premier type de média pour le flux.

    Note Pour éviter les appels complexes, supposons qu’un seul type de média existe par flux et sélectionnez le premier type de média du flux. Pour les flux complexes, vous devez énumérer chaque type de média à partir du gestionnaire de type multimédia et choisir le type de média que vous souhaitez encoder.

  6. Appelez IIMFMediaType::GetMajorType pour obtenir le type principal du flux afin de déterminer si le flux contient de l’audio ou de la vidéo.

  7. Selon le type principal du flux, créez des types de médias cibles. Ces types multimédias contiennent des informations de format que l’encodeur utilisera pendant la session d’encodage. Les sections suivantes de ce didacticiel décrivent le processus de création des types de médias cibles.

    • Créer un type de média audio compressé
    • Créer un type de média vidéo compressé
  8. Créez un flux en fonction du type de média cible, configurez le flux en fonction des exigences de l’application et ajoutez le flux au profil. Pour plus d’informations, consultez Ajout d’informations de flux au récepteur de fichiers ASF.

    1. Appelez IMFASFProfile::CreateStream et transmettez le type de média cible pour créer le flux de sortie. La méthode récupère l’interface IMFASFStreamConfig de l’objet stream.
    2. Configurez le flux.
      • Appelez IMFASFStreamConfig::SetStreamNumber pour affecter un numéro au flux. Ce paramètre est obligatoire.
      • Configurez éventuellement les paramètres de compartiment fuite, l’extension de charge utile, l’exclusion mutuelle sur chaque flux en appelant les méthodes IMFASFStreamConfig et les attributs de configuration de flux pertinents.
    3. Ajoutez les propriétés d’encodage au niveau du flux à l’aide de l’objet ContentInfo ASF. Pour plus d’informations sur cette étape, consultez la section « Créer l’objet ContentInfo ASF » dans ce tutoriel.
    4. Appelez IMFASFProfile::SetStream pour ajouter le flux au profil.

    L’exemple de code suivant crée un flux audio de sortie.

    //-------------------------------------------------------------------
    //  CreateAudioStream
    //  Create an audio stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //-------------------------------------------------------------------
    
    HRESULT CreateAudioStream(IMFASFProfile* pProfile, WORD wStreamNumber)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        IMFMediaType* pAudioType = NULL;
        IMFASFStreamConfig* pAudioStream = NULL;
    
        //Create an output type from the encoder
        HRESULT hr = GetOutputTypeFromWMAEncoder(&pAudioType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the audio type
        hr = pProfile->CreateStream(pAudioType, &pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set stream number
        hr = pAudioStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Audio Stream created. Stream Number: %d.\n", wStreamNumber);
    
    done:
        SafeRelease(&pAudioStream);
        SafeRelease(&pAudioType);
        return hr;
    }
    

    L’exemple de code suivant crée un flux vidéo de sortie.

    //-------------------------------------------------------------------
    //  CreateVideoStream
    //  Create an video stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //    pType: A pointer to the source's video media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateVideoStream(IMFASFProfile* pProfile, WORD wStreamNumber, IMFMediaType* pType)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        HRESULT hr = S_OK;
    
    
        IMFMediaType* pVideoType = NULL;
        IMFASFStreamConfig* pVideoStream = NULL;
    
        UINT32 dwBitRate = 0;
    
        //Create a new video type from the source type
        hr = CreateCompressedVideoType(pType, &pVideoType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the video type
        hr = pProfile->CreateStream(pVideoType, &pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
        //Set a valid stream number
        hr = pVideoStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Video Stream created. Stream Number: %d .\n", wStreamNumber);
    
    done:
    
        SafeRelease(&pVideoStream);
        SafeRelease(&pVideoType);
    
        return hr;
    }
    

L’exemple de code suivant crée des flux de sortie en fonction des flux dans la source.

    //For each stream in the source, add target stream information in the profile
    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        //Get the media type handler
        hr = pStreamDesc->GetMediaTypeHandler(&pHandler);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the first media type 
        hr = pHandler->GetMediaTypeByIndex(0, &pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the major type
        hr = pMediaType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        // If this is a video stream, create the target video stream
        if (guidMajor == MFMediaType_Video)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateVideoStream(pProfile, wStreamID, pMediaType);

            if (FAILED(hr))
            {
                goto done;
            }
        }
        
        // If this is an audio stream, create the target audio stream
        if (guidMajor == MFMediaType_Audio)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateAudioStream(pProfile, wStreamID);

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

        //Get stream's encoding property
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }

        //Set the stream-level encoding properties
        hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
        if (FAILED(hr))
        {
            goto done;
        }


        SafeRelease(&pMediaType);
        SafeRelease(&pStreamDesc);
        SafeRelease(&pContentInfoProps);
        wStreamID++;
    }

Créer un type de média audio compressé

Si vous souhaitez inclure un flux audio dans le fichier de sortie, créez un type audio en spécifiant les caractéristiques du type codé en définissant les attributs requis. Pour vous assurer que le type audio est compatible avec l’encodeur audio Windows Media, instanciez l’encodeur MFT, définissez les propriétés d’encodage et obtenez un type multimédia en appelant IMFTransform::GetOutputAvailableType. Obtenez le type de sortie requis en boucle dans tous les types disponibles, en obtenant les attributs de chaque type de média et en sélectionnant le type le plus proche de vos besoins. Dans ce tutoriel, obtenez le premier type disponible dans la liste des types de supports de sortie pris en charge par l’encodeur.

Note Pour Windows 7, Media Foundation fournit une nouvelle fonction, MFTranscodeGetAudioOutputAvailableTypes qui récupère une liste des types audio compatibles. Cette fonction obtient uniquement les types multimédias pour l’encodage CBR.

Un type audio complet doit avoir les attributs suivants définis :

L’exemple de code suivant crée un type audio compressé en obtenant un type compatible à partir de l’encodeur audio multimédia Windows. L’implémentation de SetEncodingProperties s’affiche dans la section « Créer l’objet ContentInfo ASF » de ce didacticiel.

//-------------------------------------------------------------------
//  GetOutputTypeFromWMAEncoder
//  Gets a compatible output type from the Windows Media audio encoder.
//
//  ppAudioType: Receives a pointer to the media type.
//-------------------------------------------------------------------

HRESULT GetOutputTypeFromWMAEncoder(IMFMediaType** ppAudioType)
{
    if (!ppAudioType)
    {
        return E_POINTER;
    }

    IMFTransform* pMFT = NULL;
    IMFMediaType* pType = NULL;
    IPropertyStore* pProps = NULL;

    //We need to find a suitable output media type
    //We need to create the encoder to get the available output types
    //and discard the instance.

    CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
    UINT32 cCLSID = 0;            // Size of the array.

    MFT_REGISTER_TYPE_INFO tinfo;
    
    tinfo.guidMajorType = MFMediaType_Audio;
    tinfo.guidSubtype = MFAudioFormat_WMAudioV9;

    // Look for an encoder.
    HRESULT hr = MFTEnum(
        MFT_CATEGORY_AUDIO_ENCODER,
        0,                  // Reserved
        NULL,                // Input type to match. None.
        &tinfo,             // WMV encoded type.
        NULL,               // Attributes to match. (None.)
        &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
        &cCLSID             // Receives the size of the array.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // MFTEnum can return zero matches.
    if (cCLSID == 0)
    {
        hr = MF_E_TOPO_CODEC_NOT_FOUND;
        goto done;
    }
    else
    {
        // Create the MFT decoder
        hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));

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

    }

    // Get the encoder's property store

    hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
    if (FAILED(hr))
    {
        goto done;
    }
    
    //Set encoding properties
    hr = SetEncodingProperties(MFMediaType_Audio, pProps);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get the first output type
    //You can loop through the available output types to choose 
    //the one that meets your target requirements
    hr = pMFT->GetOutputAvailableType(0, 0, &pType);
    if (FAILED(hr))
    {
        goto done;
    }

    //Return to the caller
    *ppAudioType = pType;
    (*ppAudioType)->AddRef();
    
done:
    SafeRelease(&pProps);
    SafeRelease(&pType);
    SafeRelease(&pMFT);
    CoTaskMemFree(pMFTCLSIDs);
    return hr;
}

Créer un type de média vidéo compressé

Si vous souhaitez inclure un flux vidéo dans le fichier de sortie, créez un type vidéo encodé complet. Le type de média complet doit inclure le débit de bits et les données privées de codec souhaitées.

Il existe deux façons de créer un type de média vidéo complet.

  • Créez un type multimédia vide et copiez les attributs de type multimédia à partir du type vidéo source et remplacez l’attribut MF_MT_SUBTYPE avec la constante GUID MFVideoFormat_WMV3 .

    Un type vidéo complet doit avoir les attributs suivants définis :

    • MF_MT_MAJOR_TYPE
    • MF_MT_SUBTYPE
    • MF_MT_FRAME_RATE
    • MF_MT_FRAME_SIZE
    • MF_MT_INTERLACE_MODE
    • MF_MT_PIXEL_ASPECT_RATIO
    • MF_MT_AVG_BITRATE
    • MF_MT_USER_DATA

    L’exemple de code suivant crée un type vidéo compressé à partir du type vidéo de la source.

    //-------------------------------------------------------------------
    //  CreateCompressedVideoType
    //  Creates an output type from source's video media type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateCompressedVideoType(
            IMFMediaType* pType, 
            IMFMediaType** ppVideoType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        HRESULT hr = S_OK;
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 fSamplesIndependent = 0;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->CopyAllItems(pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Fill the missing attributes
        //1. Frame rate
        hr = MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
            hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////2. Frame size
        hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
            hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////3. Interlace mode
    
        if (FAILED(pMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &interlace)))
        {
            hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        ////4. Pixel aspect Ratio
        hr = MFGetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            par.Numerator = par.Denominator = 1;
            hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32)par.Numerator, (UINT32)par.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //6. bit rate
        if (FAILED(pMediaType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate)))
        {
            hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, 1000000);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppVideoType = pMediaType;
        (*ppVideoType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    
    
  • Obtenez un type multimédia compatible à partir de l’encodeur vidéo media Windows en définissant les propriétés d’encodage, puis en appelant IMFTransform::GetOutputAvailableType. Cette méthode retourne le type partiel. Veillez à convertir le type partiel en type complet en ajoutant les informations suivantes :

    Étant donné que IWMCodecPrivateData::GetPrivateData vérifie le taux de bits avant de retourner les données privées de codec, assurez-vous de définir le débit avant d’obtenir les données privées de codec.

    L’exemple de code suivant crée un type vidéo compressé en obtenant un type compatible à partir de l’encodeur Windows Media Video. Il crée également un type de vidéo non compressé et le définit comme entrée de l’encodeur. Cette opération est implémentée dans la fonction d’assistance CreateUn compresséVideoType. GetOutputTypeFromWMVEncoder convertit le type partiel retourné en type complet en ajoutant des données privées de codec. L’implémentation de SetEncodingProperties s’affiche dans la section « Créer l’objet ContentInfo ASF » de ce didacticiel.

    //-------------------------------------------------------------------
    //  GetOutputTypeFromWMVEncoder
    //  Gets a compatible output type from the Windows Media video encoder.
    //
    //  pType: A pointer to the source video stream's media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT GetOutputTypeFromWMVEncoder(IMFMediaType* pType, IMFMediaType** ppVideoType)
    {
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        IMFTransform* pMFT = NULL;
        IPropertyStore* pProps = NULL;
        IMFMediaType* pInputType = NULL;
        IMFMediaType* pOutputType = NULL;
    
        UINT32 cBitrate = 0;
    
        //We need to find a suitable output media type
        //We need to create the encoder to get the available output types
        //and discard the instance.
    
        CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
        UINT32 cCLSID = 0;          // Size of the array.
    
        MFT_REGISTER_TYPE_INFO tinfo;
    
        tinfo.guidMajorType = MFMediaType_Video;
        tinfo.guidSubtype = MFVideoFormat_WMV3;
    
        // Look for an encoder.
        HRESULT hr = MFTEnum(
            MFT_CATEGORY_VIDEO_ENCODER,
            0,                  // Reserved
            NULL,               // Input type to match. None.
            &tinfo,             // WMV encoded type.
            NULL,               // Attributes to match. (None.)
            &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
            &cCLSID             // Receives the size of the array.
            );
        if (FAILED(hr))
        {
            goto done;
        }
    
        // MFTEnum can return zero matches.
        if (cCLSID == 0)
        {
            hr = MF_E_TOPO_CODEC_NOT_FOUND;
            goto done;
        }
        else
        {
            //Create the MFT decoder
            hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
                CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //Get the video encoder property store
        hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set encoding properties
        hr = SetEncodingProperties(MFMediaType_Video, pProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = CreateUncompressedVideoType(pType, &pInputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMFT->SetInputType(0, pInputType, 0);
    
        //Get the first output type
        //You can loop through the available output types to choose 
        //the one that meets your target requirements
        hr =  pMFT->GetOutputAvailableType(0, 0, &pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Now set the bit rate
        hr = pOutputType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = AddPrivateData(pMFT, pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Return to the caller
        *ppVideoType = pOutputType;
        (*ppVideoType)->AddRef();
    
    done:
        SafeRelease(&pProps);
        SafeRelease(&pOutputType);
        SafeRelease(&pInputType);
        SafeRelease(&pMFT);
        CoTaskMemFree(pMFTCLSIDs);
        return hr;
    }
    

    L’exemple de code suivant crée un type de vidéo non compressé.

    //-------------------------------------------------------------------
    //  CreateUncompressedVideoType
    //  Creates an uncompressed video type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateUncompressedVideoType(IMFMediaType* pType, IMFMediaType** ppMediaType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppMediaType)
        {
            return E_POINTER;
        }
    
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        HRESULT hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
        }
        hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
        }
    
        interlace = MFGetAttributeUINT32(pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    
        hr = MFGetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (FAILED(hr))
        {
            par.Numerator = par.Denominator = 1;
        }
    
        cBitrate = MFGetAttributeUINT32(pType, MF_MT_AVG_BITRATE, 1000000);
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, 2);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppMediaType = pMediaType;
        (*ppMediaType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    

    L’exemple de code suivant ajoute des données privées de codec au type de média vidéo spécifié.

    //
    // AddPrivateData
    // Appends the private codec data to a media type.
    //
    // pMFT: The video encoder
    // pTypeOut: A video type from the encoder's type list.
    //
    // The function modifies pTypeOut by adding the codec data.
    //
    
    HRESULT AddPrivateData(IMFTransform *pMFT, IMFMediaType *pTypeOut)
    {
        HRESULT hr = S_OK;
        ULONG cbData = 0;
        BYTE *pData = NULL;
    
        IWMCodecPrivateData *pPrivData = NULL;
    
        DMO_MEDIA_TYPE mtOut = { 0 };
    
        // Convert the type to a DMO type.
        hr = MFInitAMMediaTypeFromMFMediaType(
            pTypeOut, 
            FORMAT_VideoInfo, 
            (AM_MEDIA_TYPE*)&mtOut
            );
    
        if (SUCCEEDED(hr))
        {
            hr = pMFT->QueryInterface(IID_PPV_ARGS(&pPrivData));
        }
    
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->SetPartialOutputType(&mtOut);
        }
    
        //
        // Get the private codec data
        //
    
        // First get the buffer size.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(NULL, &cbData);
        }
    
        if (SUCCEEDED(hr))
        {
            pData = new BYTE[cbData];
    
            if (pData == NULL)
            {
                hr = E_OUTOFMEMORY;
            }
        }
    
        // Now get the data.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(pData, &cbData);
        }
    
        // Add the data to the media type.
        if (SUCCEEDED(hr))
        {
            hr = pTypeOut->SetBlob(MF_MT_USER_DATA, pData, cbData);
        }
    
        delete [] pData;
        MoFreeMediaType(&mtOut);
        SafeRelease(&pPrivData);
        return hr;
    }
    

Créer l’objet ContentInfo ASF

L’objet ContentInfo ASF est un composant de niveau WMContainer qui est principalement conçu pour stocker les informations de l’objet d’en-tête ASF. Le récepteur de fichiers ASF implémente l’objet ContentInfo afin de stocker des informations (dans un magasin de propriétés) qui seront utilisées pour écrire l’objet d’en-tête ASF du fichier encodé. Pour ce faire, vous devez créer et configurer l’ensemble d’informations suivant sur l’objet ContentInfo avant de commencer la session d’encodage.

  1. Appelez MFCreateASFContentInfo pour créer un objet ContentInfo vide.

    L’exemple de code suivant crée un objet ContentInfo vide.

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. Appelez IMFASFContentInfo::GetEncodingConfigurationPropertyStore pour obtenir le magasin de propriétés au niveau du flux du récepteur de fichiers. Dans cet appel, vous devez transmettre le numéro de flux que vous avez affecté pour le flux lors de la création du profil ASF.

  3. Définissez les propriétés d’encodage au niveau du flux dans le magasin de propriétés de flux du récepteur de fichiers. Pour plus d’informations, consultez « Propriétés d’encodage de flux » dans la définition des propriétés dans le récepteur de fichiers.

    L’exemple de code suivant définit les propriétés de niveau de flux dans le magasin de propriétés du récepteur de fichiers.

            //Get stream's encoding property
            hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
            if (FAILED(hr))
            {
                goto done;
            }
    
            //Set the stream-level encoding properties
            hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
            if (FAILED(hr))
            {
                goto done;
            }
    
    
    

    L’exemple de code suivant montre l’implémentation de SetEncodingProperties. Cette fonction définit les propriétés d’encodage au niveau du flux pour CBR et VBR.

    //-------------------------------------------------------------------
    //  SetEncodingProperties
    //  Create a media source from a URL.
    //
    //  guidMT:  Major type of the stream, audio or video
    //  pProps:  A pointer to the property store in which 
    //           to set the required encoding properties.
    //-------------------------------------------------------------------
    
    HRESULT SetEncodingProperties (const GUID guidMT, IPropertyStore* pProps)
    {
        if (!pProps)
        {
            return E_INVALIDARG;
        }
    
        if (EncodingMode == NONE)
        {
            return MF_E_NOT_INITIALIZED;
        }
    
        HRESULT hr = S_OK;
    
        PROPVARIANT var;
    
        switch (EncodingMode)
        {
            case CBR:
                // Set VBR to false.
                hr = InitPropVariantFromBoolean(FALSE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the video buffer window.
                if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromInt32(VIDEO_WINDOW_MSEC, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VIDEOWINDOW, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            case VBR:
                //Set VBR to true.
                hr = InitPropVariantFromBoolean(TRUE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Number of encoding passes is 1.
    
                hr = InitPropVariantFromInt32(1, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_PASSESUSED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the quality level.
    
                if (guidMT == MFMediaType_Audio)
                {
                    hr = InitPropVariantFromUInt32(98, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_DESIRED_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                else if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromUInt32(95, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            default:
                hr = E_UNEXPECTED;
                break;
        }    
    
    done:
        PropVariantClear(&var);
        return hr;
    }
    
  4. Appelez IMFASFContentInfo::GetEncodingConfigurationPropertyStore pour obtenir le magasin de propriétés global du récepteur de fichiers. Dans cet appel, vous devez passer 0 dans le premier paramètre. Pour plus d’informations, consultez « Propriétés globales du récepteur de fichiers » dans la définition des propriétés dans le récepteur de fichiers.

  5. Définissez la MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE sur VARIANT_TRUE pour vous assurer que les valeurs de compartiment fuite dans le multiplexeur ASF sont ajustées correctement. Pour plus d’informations sur cette propriété, consultez « Multiplexer Initialization and Leaky Bucket Paramètres » dans Creating the Multiplexer Object.

    L’exemple de code suivant définit les propriétés au niveau du flux dans le magasin de propriétés du récepteur de fichiers.

        //Now we need to set global properties that will be set on the media sink
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(0, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Auto adjust Bitrate
        var.vt = VT_BOOL;
        var.boolVal = VARIANT_TRUE;
    
        hr = pContentInfoProps->SetValue(MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, var);
    
        //Initialize with the profile
        hr = pContentInfo->SetProfile(pProfile);
        if (FAILED(hr))
        {
            goto done;
        }
    

    Notes

    Il existe d’autres propriétés que vous pouvez définir au niveau global pour le récepteur de fichiers. Pour plus d’informations, consultez « Configuration de l’objet ContentInfo avec encoder Paramètres » dans la définition des propriétés dans l’objet ContentInfo.

     

Vous allez utiliser l’asF ContentInfo configuré pour créer un objet d’activation pour le récepteur de fichiers ASF (décrit dans la section suivante).

Si vous créez un récepteur de fichiers hors processus (MFCreateASFMediaSinkActivate), c’est-à-dire à l’aide d’un objet d’activation, vous pouvez utiliser l’objet ContentInfo configuré pour instancier le récepteur multimédia ASF (décrit dans la section suivante). Si vous créez un récepteur de fichiers in-process (MFCreateASFMediaSink), au lieu de créer l’objet ContentInfo vide comme décrit à l’étape 1, obtenez une référence à l’interface IMFASFContentInfo en appelant IMFMediaSink::QueryInterface sur le récepteur de fichiers. Vous devez ensuite configurer l’objet ContentInfo comme décrit dans cette section.

Créer le récepteur de fichiers ASF

Dans cette étape du tutoriel, vous allez utiliser l’ASF ContentInfo configuré que vous avez créé à l’étape précédente pour créer un objet d’activation pour le récepteur de fichiers ASF en appelant la fonction MFCreateASFMediaSinkActivate . Pour plus d’informations, consultez Création du récepteur de fichiers ASF.

L’exemple de code suivant crée l’objet d’activation pour le récepteur de fichiers.

    //Create the activation object for the  file sink
    hr = MFCreateASFMediaSinkActivate(sURL, pContentInfo, &pActivate);
    if (FAILED(hr))
    {
        goto done;
    }

Générer la topologie d’encodage partiel

Ensuite, vous allez créer une topologie d’encodage partielle en créant des nœuds de topologie pour la source multimédia, les Windows encodeurs multimédias requis et le récepteur de fichiers ASF. Après avoir ajouté les nœuds de topologie, vous connecterez la source, la transformation et les nœuds récepteurs. Avant d’ajouter des nœuds de topologie, vous devez créer un objet de topologie vide en appelant MFCreateTopology.

Créer le nœud de topologie source pour la source multimédia

Dans cette étape, vous allez créer le nœud de topologie source pour la source multimédia.

Pour créer ce nœud, vous avez besoin des références suivantes :

  • Pointeur vers la source multimédia que vous avez créée à l’étape décrite dans la section « Créer la source multimédia » de ce didacticiel.
  • Pointeur vers le descripteur de présentation pour la source multimédia. Vous pouvez obtenir une référence à l’interface IMFPresentationDescriptor de la source multimédia en appelant IMFMediaSource::CreatePresentationDescriptor.
  • Pointeur vers le descripteur de flux pour chaque flux de la source multimédia pour laquelle vous avez créé un flux cible à l’étape décrite dans la section « Créer l’objet de profil ASF » de ce didacticiel.

Pour plus d’informations sur la création de nœuds sources et l’exemple de code, consultez Création de nœuds sources.

L’exemple de code suivant crée une topologie partielle en ajoutant le nœud source et les nœuds de transformation requis. Ce code appelle les fonctions d’assistance AddSourceNode et AddTransformOutputNodes. Ces fonctions sont incluses plus loin dans ce didacticiel.

//-------------------------------------------------------------------
//  BuildPartialTopology
//  Create a partial encoding topology by adding the source and the sink.
//
//  pSource:  A pointer to the media source to enumerate the source streams.
//  pSinkActivate: A pointer to the activation object for ASF file sink.
//  ppTopology:  Receives a pointer to the topology.
//-------------------------------------------------------------------

HRESULT BuildPartialTopology(
       IMFMediaSource *pSource, 
       IMFActivate* pSinkActivate,
       IMFTopology** ppTopology)
{
    if (!pSource || !pSinkActivate)
    {
        return E_INVALIDARG;
    }
    if (!ppTopology)
    {
        return E_POINTER;
    }

    HRESULT hr = S_OK;

    IMFPresentationDescriptor* pPD = NULL;
    IMFStreamDescriptor *pStreamDesc = NULL;
    IMFMediaTypeHandler* pMediaTypeHandler = NULL;
    IMFMediaType* pSrcType = NULL;

    IMFTopology* pTopology = NULL;
    IMFTopologyNode* pSrcNode = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;


    DWORD cElems = 0;
    DWORD dwSrcStream = 0;
    DWORD StreamID = 0;
    GUID guidMajor = GUID_NULL;
    BOOL fSelected = FALSE;


    //Create the topology that represents the encoding pipeline
    hr = MFCreateTopology (&pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

        
    hr = pSource->CreatePresentationDescriptor(&pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pPD->GetStreamDescriptorCount(&dwSrcStream);
    if (FAILED(hr))
    {
        goto done;
    }

    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        hr = AddSourceNode(pTopology, pSource, pPD, pStreamDesc, &pSrcNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamDesc->GetMediaTypeHandler (&pMediaTypeHandler);
        if (FAILED(hr))
        {
            goto done;
        }
        
        hr = pStreamDesc->GetStreamIdentifier(&StreamID);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pMediaTypeHandler->GetMediaTypeByIndex(0, &pSrcType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSrcType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = AddTransformOutputNodes(pTopology, pSinkActivate, pSrcType, &pEncoderNode);
        if (FAILED(hr))
        {
            goto done;
        }

        //now we have the transform node, connect it to the source node
        hr = pSrcNode->ConnectOutput(0, pEncoderNode, 0);
        if (FAILED(hr))
        {
            goto done;
        }
                

        SafeRelease(&pStreamDesc);
        SafeRelease(&pMediaTypeHandler);
        SafeRelease(&pSrcType);
        SafeRelease(&pEncoderNode);
        SafeRelease(&pOutputNode);
        guidMajor = GUID_NULL;
    }

    *ppTopology = pTopology;
   (*ppTopology)->AddRef();


    wprintf_s(L"Partial Topology Built.\n");

done:
    SafeRelease(&pStreamDesc);
    SafeRelease(&pMediaTypeHandler);
    SafeRelease(&pSrcType);
    SafeRelease(&pEncoderNode);
    SafeRelease(&pOutputNode);
    SafeRelease(&pTopology);

    return hr;
}

L’exemple de code suivant crée et ajoute le nœud de topologie source à la topologie d’encodage. Il prend des pointeurs vers un objet de topologie précédemment, la source multimédia pour énumérer les flux sources, le descripteur de présentation de la source multimédia et le descripteur de flux de la source multimédia. L’appelant reçoit un pointeur vers le nœud de topologie source.

// Add a source node to a topology.
HRESULT AddSourceNode(
    IMFTopology *pTopology,           // Topology.
    IMFMediaSource *pSource,          // Media source.
    IMFPresentationDescriptor *pPD,   // Presentation descriptor.
    IMFStreamDescriptor *pSD,         // Stream descriptor.
    IMFTopologyNode **ppNode)         // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    // Create the node.
    HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the attributes.
    hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the node to the topology.
    hr = pTopology->AddNode(pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

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

Instancier les encodeurs requis et créer les nœuds de transformation

Le pipeline Media Foundation n’insère pas automatiquement les encodeurs media requis Windows pour les flux qu’il doit encoder. L’application doit ajouter manuellement les encodeurs. Pour ce faire, énumérez les flux dans le profil ASF que vous avez créé à l’étape décrite dans la section « Créer l’objet de profil ASF » de ce didacticiel. Pour chaque flux de la source et du flux correspondant dans le profil, instanciez les encodeurs requis. Pour cette étape, vous avez besoin d’un pointeur vers l’objet d’activation pour le récepteur de fichiers que vous avez créé à l’étape décrite dans la section « Créer le récepteur de fichiers ASF » de ce didacticiel.

Pour obtenir une vue d’ensemble de la création d’encodeurs via des objets d’activation, consultez Utilisation des objets d’activation d’un encodeur.

La procédure suivante décrit les étapes requises pour instancier les encodeurs requis.

  1. Obtenez une référence à l’objet ContentInfo du récepteur en appelant IMFActivate::ActivateObject sur le récepteur de fichiers activé, puis en interrogeant IMFASFContentInfo à partir du récepteur de fichiers en appelant QueryInterface.

  2. Obtenez le profil ASF associé à l’objet ContentInfo en appelant IMFASFContentInfo::GetProfile.

  3. Énumérez les flux dans le profil. Pour cela, vous aurez besoin du nombre de flux et d’une référence à l’interface IMFASFStreamConfig de chaque flux.

    Appelez les méthodes suivantes :

  4. Pour chaque flux, obtenez le type principal et les propriétés d’encodage du flux à partir de l’objet ContentInfo.

    Appelez les méthodes suivantes :

  5. Selon le type du flux, de l’audio ou de la vidéo, instanciez l’objet d’activation pour l’encodeur en appelant MFCreateWMAEncoderActivate ou MFCreateWMVEncoderActivate.

    Pour appeler ces fonctions, vous avez besoin des références suivantes :

  6. Mettez à jour le paramètre de compartiment fuite pour le flux audio.

    MFCreateWMAEncoderActivate définit le type de sortie sur l’encodeur sous-jacent MFT pour le codec audio Windows Media. Une fois le type de média de sortie défini, l’encodeur obtient le taux de bits moyen du type de média de sortie, calcule le débit de rage de la fenêtre de mémoire tampon et définit les valeurs de compartiment fuite qui seront utilisées pendant la session d’encodage. Vous pouvez mettre à jour ces valeurs dans le récepteur de fichiers en interrogeant l’encodeur ou en définissant des valeurs personnalisées. Pour mettre à jour les valeurs, vous avez besoin de l’ensemble d’informations suivant :

    • Taux de bits moyen : obtenez le taux de bits moyen à partir de l’attribut MF_MT_AUDIO_AVG_BYTES_PER_SECOND défini sur le type de média de sortie sélectionné lors de la négociation de type multimédia.
    • Fenêtre de mémoire tampon : vous pouvez la récupérer en appelant IWMCodecLeakyBucket::GetBufferSizeBits.
    • Taille initiale de la mémoire tampon : définie sur 0.

    Créez un tableau de DWORD et définissez la valeur dans la propriété MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET du récepteur de flux audio. Si vous ne fournissez pas les valeurs mises à jour, la session multimédia les définit de manière appropriée.

    Pour plus d’informations, consultez le modèle de mémoire tampon de compartiment fuite.

Les objets d’activation créés à l’étape 5 doivent être ajoutés à la topologie en tant que nœuds de topologie de transformation. Pour plus d’informations et d’exemple de code, consultez « Création d’un nœud de transformation à partir d’un objet d’activation » dans Création de nœuds de transformation.

L’exemple de code suivant crée et ajoute l’encodeur requis s’active. Il prend des pointeurs vers un objet de topologie créé précédemment, l’objet d’activation du récepteur de fichiers et le type de média du flux source. Il appelle également AddOutputNode (voir l’exemple de code suivant) qui crée et ajoute le nœud récepteur à la topologie d’encodage. Il reçoit un pointeur vers le nœud de topologie source.

//-------------------------------------------------------------------
//  AddTransformOutputNodes
//  Creates and adds the sink node to the encoding topology.
//  Creates and adds the required encoder activates.

//  pTopology:  A pointer to the topology.
//  pSinkActivate:  A pointer to the file sink's activation object.
//  pSourceType: A pointer to the source stream's media type.
//  ppNode:  Receives a pointer to the topology node.
//-------------------------------------------------------------------

HRESULT AddTransformOutputNodes(
    IMFTopology* pTopology,
    IMFActivate* pSinkActivate,
    IMFMediaType* pSourceType,
    IMFTopologyNode **ppNode    // Receives the node pointer.
    )
{
    if (!pTopology || !pSinkActivate || !pSourceType)
    {
        return E_INVALIDARG;
    }

    IMFTopologyNode* pEncNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;
    IMFASFContentInfo* pContentInfo = NULL;
    IMFASFProfile* pProfile = NULL;
    IMFASFStreamConfig* pStream = NULL;
    IMFMediaType* pMediaType = NULL;
    IPropertyStore* pProps = NULL;
    IMFActivate *pEncoderActivate = NULL;
    IMFMediaSink *pSink = NULL; 

    GUID guidMT = GUID_NULL;
    GUID guidMajor = GUID_NULL;

    DWORD cStreams = 0;
    WORD wStreamNumber = 0;

    HRESULT hr = S_OK;
        
    hr = pSourceType->GetMajorType(&guidMajor);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create the node.
    hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &pEncNode);
    if (FAILED(hr))
    {
        goto done;
    }
    
    //Activate the sink
    hr = pSinkActivate->ActivateObject(__uuidof(IMFMediaSink), (void**)&pSink);
    if (FAILED(hr))
    {
        goto done;
    }
    //find the media type in the sink
    //Get content info from the sink
    hr = pSink->QueryInterface(__uuidof(IMFASFContentInfo), (void**)&pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }
    

    hr = pContentInfo->GetProfile(&pProfile);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pProfile->GetStreamCount(&cStreams);
    if (FAILED(hr))
    {
        goto done;
    }

    for(DWORD index = 0; index < cStreams ; index++)
    {
        hr = pProfile->GetStream(index, &wStreamNumber, &pStream);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pStream->GetMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->GetMajorType(&guidMT);
        if (FAILED(hr))
        {
            goto done;
        }
        if (guidMT!=guidMajor)
        {
            SafeRelease(&pStream);
            SafeRelease(&pMediaType);
            guidMT = GUID_NULL;

            continue;
        }
        //We need to activate the encoder
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamNumber, &pProps);
        if (FAILED(hr))
        {
            goto done;
        }

        if (guidMT == MFMediaType_Audio)
        {
             hr = MFCreateWMAEncoderActivate(pMediaType, pProps, &pEncoderActivate);
            if (FAILED(hr))
            {
                goto done;
            }
            wprintf_s(L"Audio Encoder created. Stream Number: %d .\n", wStreamNumber);

            break;
        }
        if (guidMT == MFMediaType_Video)
        {
            hr = MFCreateWMVEncoderActivate(pMediaType, pProps, &pEncoderActivate);
            if (FAILED(hr))
            {
                goto done;
            }
            wprintf_s(L"Video Encoder created. Stream Number: %d .\n", wStreamNumber);

            break;
        }
    }

    // Set the object pointer.
    hr = pEncNode->SetObject(pEncoderActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the node to the topology.
    hr = pTopology->AddNode(pEncNode);
    if (FAILED(hr))
    {
        goto done;
    }

    //Add the output node to this node.
    hr = AddOutputNode(pTopology, pSinkActivate, wStreamNumber, &pOutputNode);
    if (FAILED(hr))
    {
        goto done;
    }

    //now we have the output node, connect it to the transform node
    hr = pEncNode->ConnectOutput(0, pOutputNode, 0);
    if (FAILED(hr))
    {
        goto done;
    }

   // Return the pointer to the caller.
    *ppNode = pEncNode;
    (*ppNode)->AddRef();


done:
    SafeRelease(&pEncNode);
    SafeRelease(&pOutputNode);
    SafeRelease(&pEncoderActivate);
    SafeRelease(&pMediaType);
    SafeRelease(&pProps);
    SafeRelease(&pStream);
    SafeRelease(&pProfile);
    SafeRelease(&pContentInfo);
    SafeRelease(&pSink);
    return hr;
}

Créer les nœuds de topologie de sortie pour le récepteur de fichiers

Dans cette étape, vous allez créer le nœud de topologie de sortie pour le récepteur de fichiers ASF.

Pour créer ce nœud, vous avez besoin des références suivantes :

  • Pointeur vers l’objet d’activation que vous avez créé à l’étape décrite dans la section « Créer le récepteur de fichiers ASF » de ce didacticiel.
  • Numéros de flux permettant d’identifier les récepteurs de flux ajoutés au récepteur de fichiers. Les numéros de flux correspondent à l’identification du flux qui a été définie lors de la création du flux.

Pour plus d’informations sur la création de nœuds de sortie et l’exemple de code, consultez « Création d’un nœud de sortie à partir d’un objet d’activation » dans Création de nœuds de sortie.

Si vous n’utilisez pas d’objet d’activation pour le récepteur de fichiers, vous devez énumérer les récepteurs de flux dans le récepteur de fichiers ASF et définir chaque récepteur de flux comme nœud de sortie dans la topologie. Pour plus d’informations sur l’énumération du récepteur de flux, consultez « Énumération des récepteurs de flux » dans Ajout d’informations de flux au récepteur de fichiers ASF.

L’exemple de code suivant crée et ajoute le nœud récepteur à la topologie d’encodage. Il prend des pointeurs vers un objet de topologie créé précédemment, l’objet d’activation du récepteur de fichiers et le numéro d’identification du flux. Il reçoit un pointeur vers le nœud de topologie source.

// Add an output node to a topology.
HRESULT AddOutputNode(
    IMFTopology *pTopology,     // Topology.
    IMFActivate *pActivate,     // Media sink activation object.
    DWORD dwId,                 // Identifier of the stream sink.
    IMFTopologyNode **ppNode)   // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    // Create the node.
    HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the object pointer.
    hr = pNode->SetObject(pActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the stream sink ID attribute.
    hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the node to the topology.
    hr = pTopology->AddNode(pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

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

L’exemple de code suivant énumère les récepteurs de flux pour un récepteur multimédia donné.

//-------------------------------------------------------------------
//  EnumerateStreamSinks
//  Enumerates the stream sinks within the specified media sink.
//
//  pSink:  A pointer to the media sink.
//-------------------------------------------------------------------
HRESULT EnumerateStreamSinks (IMFMediaSink* pSink)
{
    if (!pSink)
    {
        return E_INVALIDARG;
    }
    
    IMFStreamSink* pStreamSink = NULL;
    DWORD cStreamSinks = 0;

    HRESULT hr = pSink->GetStreamSinkCount(&cStreamSinks);
    if (FAILED(hr))
    {
        goto done;
    }

    for(DWORD index = 0; index < cStreamSinks; index++)
    {
        hr = pSink->GetStreamSinkByIndex (index, &pStreamSink);
        if (FAILED(hr))
        {
            goto done;
        }

        //Use the stream sink
        //Not shown.
    }
done:
    SafeRelease(&pStreamSink);

    return hr;
}

Connecter les nœuds source, transformation et récepteur

Dans cette étape, vous allez connecter le nœud source au nœud de transformation référençant l’encodage que vous avez créé à l’étape décrite dans la section « Instancier les encodeurs requis et créer les nœuds de transformation » de ce didacticiel. Le nœud de transformation est connecté au nœud de sortie contenant l’objet d’activation pour le récepteur de fichiers.

Gestion de la session d’encodage

À l’étape, vous allez effectuer les étapes suivantes :

  1. Appelez MFCreateMediaSession pour créer une session d’encodage.

  2. Appelez IMFMediaSession::SetTopology pour définir la topologie d’encodage sur la session. Si l’appel se termine, la session multimédia évalue les nœuds de topologie et insère des objets de transformation supplémentaires tels qu’un décodeur qui convertit la source compressée spécifiée en exemples non compressés en flux comme entrée dans l’encodeur.

  3. Appelez IMFMediaSession::GetEvent pour demander les événements déclenchés par la session des médias.

    Dans la boucle d’événements, vous démarrez et fermez la session d’encodage en fonction des événements déclenchés par la session multimédia. L’appel IMFMediaSession::SetTopology entraîne la levée de l’événement MESessionTopologyStatus avec l’indicateur MF_TOPOSTATUS_READY défini. Tous les événements sont générés de manière asynchrone et une application peut récupérer ces événements de manière synchrone ou asynchrone. Étant donné que l’application de ce didacticiel est une application console et que le blocage des threads d’interface utilisateur n’est pas un problème, nous obtiendrons les événements de la session multimédia de manière synchrone.

    Pour obtenir les événements de manière asynchrone, votre application doit implémenter l’interface IMFAsyncCallback . Pour plus d’informations et un exemple d’implémentation de cette interface, consultez « Gestion des événements de session » dans How to Play Media Files with Media Foundation.

Dans la boucle d’événements permettant d’obtenir des événements Media Session, attendez l’événement MESessionTopologyStatus qui est déclenché lorsque le IMFMediaSession::SetTopology se termine et que la topologie est résolue. Lors de l’obtention de l’événement MESessionTopologyStatus, démarrez la session d’encodage en appelant IMFMediaSession::Start. La session multimédia génère l’événement MEEndOfPresentation lorsque toutes les opérations d’encodage sont terminées. Cet événement doit être géré pour l’encodage VBR et est abordé dans la section suivante « Mettre à jour les propriétés d’encodage sur le récepteur de fichiers pour l’encodage VBR » de ce didacticiel.

La session multimédia génère l’objet d’en-tête ASF et finalise le fichier une fois la session d’encodage terminée, puis déclenche l’événement MESessionClosed . Cet événement doit être géré en effectuant des opérations d’arrêt appropriées sur la session multimédia. Pour commencer les opérations d’arrêt, appelez IMFMediaSession::Shutdown. Une fois la session d’encodage fermée, vous ne pouvez pas définir une autre topologie pour l’encodage sur la même instance de session multimédia. Pour encoder un autre fichier, la session multimédia active doit être fermée et publiée et la nouvelle topologie doit être définie sur une session multimédia nouvellement créée. L’exemple de code suivant crée la session multimédia, définit la topologie d’encodage et gère les événements de session multimédia.

L’exemple de code suivant crée la session multimédia, définit la topologie d’encodage et contrôle la session d’encodage en gérant les événements de la session multimédia.

//-------------------------------------------------------------------
//  Encode
//  Controls the encoding session and handles events from the media session.
//
//  pTopology:  A pointer to the encoding topology.
//-------------------------------------------------------------------

HRESULT Encode(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }
    
    IMFMediaSession *pSession = NULL;
    IMFMediaEvent* pEvent = NULL;
    IMFTopology* pFullTopology = NULL;
    IUnknown* pTopoUnk = NULL;


    MediaEventType meType = MEUnknown;  // Event type

    HRESULT hr = S_OK;
    HRESULT hrStatus = S_OK;            // Event status
                
    MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.    
        

    hr = MFCreateMediaSession(NULL, &pSession);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSession->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get media session events synchronously
    while (1)
    {
        hr = pSession->GetEvent(0, &pEvent);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetType(&meType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetStatus(&hrStatus);
        if (FAILED(hr))
        {
            goto done;
        }
        if (FAILED(hrStatus))
        {
            hr = hrStatus;
            goto done;
        }

       switch(meType)
        {
            case MESessionTopologyStatus:
                {
                    // Get the status code.
                    MF_TOPOSTATUS status = (MF_TOPOSTATUS)MFGetAttributeUINT32(
                                             pEvent, MF_EVENT_TOPOLOGY_STATUS, MF_TOPOSTATUS_INVALID);

                    if (status == MF_TOPOSTATUS_READY)
                    {
                        PROPVARIANT var;
                        PropVariantInit(&var);
                        wprintf_s(L"Topology resolved and set on the media session.\n");

                        hr = pSession->Start(NULL, &var);
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                    }
                    if (status == MF_TOPOSTATUS_STARTED_SOURCE)
                    {
                        wprintf_s(L"Encoding started.\n");
                        break;
                    }
                    if (status == MF_TOPOSTATUS_ENDED)
                    {
                        wprintf_s(L"Encoding complete.\n");
                        hr = pSession->Close();
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                        break;
                    }
                }
                break;


            case MESessionEnded:
                wprintf_s(L"Encoding complete.\n");
                hr = pSession->Close();
                if (FAILED(hr))
                {
                    goto done;
                }
                break;

            case MEEndOfPresentation:
                {
                    if (EncodingMode == VBR)
                    {
                        hr = pSession->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        hr = PostEncodingUpdate(pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        wprintf_s(L"Updated sinks for VBR. \n");
                    }
                }
                break;

            case MESessionClosed:
                wprintf_s(L"Encoding session closed.\n");

                hr = pSession->Shutdown();
                goto done;
        }
        if (FAILED(hr))
        {
            goto done;
        }

        SafeRelease(&pEvent);

    }
done:
    SafeRelease(&pEvent);
    SafeRelease(&pSession);
    SafeRelease(&pFullTopology);
    SafeRelease(&pTopoUnk);
    return hr;
}

Mettre à jour les propriétés d’encodage dans le récepteur de fichiers

Certaines propriétés d’encodage telles que le taux de bits d’encodage et les valeurs précises de compartiment fuite ne sont pas connues tant que l’encodage n’est pas terminé, en particulier pour l’encodage VBR. Pour obtenir les valeurs correctes, l’application doit attendre l’événement MEEndOfPresentation qui indique que la session d’encodage est terminée. Les valeurs de compartiment fuite doivent être mises à jour dans le récepteur afin que l’objet d’en-tête ASF puisse refléter les valeurs précises.

La procédure suivante décrit les étapes requises pour parcourir les nœuds de la topologie d’encodage pour obtenir le nœud récepteur de fichiers et définir les propriétés de compartiment fuite requises.

Pour mettre à jour les valeurs de propriété post-encodage sur le récepteur de fichiers ASF

  1. Appelez IMFTopology::GetOutputNodeCollection pour obtenir la collection de nœuds de sortie à partir de la topologie d’encodage.
  2. Pour chaque nœud, obtenez un pointeur vers le récepteur de flux dans le nœud en appelant IMFTopologyNode::GetObject. Requête de l’interface IMFStreamSink sur le pointeur IUnknown retourné par IMFTopologyNode::GetObject.
  3. Pour chaque récepteur de flux, obtenez le nœud en aval (encodeur) en appelant IMFTopologyNode::GetInput.
  4. Interrogez le nœud pour obtenir le pointeur IMFTransform à partir du nœud d’encodeur.
  5. Interrogez l’encodeur pour le pointeur IPropertyStore pour obtenir le magasin de propriétés d’encodage à partir de l’encodeur.
  6. Interrogez le récepteur de flux pour le pointeur IPropertyStore pour obtenir le magasin de propriétés du récepteur de flux.
  7. Appelez IPropertyStore::GetValue pour obtenir les valeurs de propriété requises à partir du magasin de propriétés de l’encodeur et les copier dans le magasin de propriétés du récepteur de flux en appelant IPropertyStore::SetValue.

Le tableau suivant montre les valeurs de propriété post-encodage qui doivent être définies sur le récepteur de flux pour le flux vidéo.

Type d’encodage Nom de la propriété (GetValue) Nom de propriété (SetValue)
Encodage de débit constant MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Encodage de débit variable basé sur la qualité MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

L’exemple de code suivant définit les valeurs de propriété post-encodage.

//-------------------------------------------------------------------
//  PostEncodingUpdate
//  Updates the file sink with encoding properties set on the encoder
//  during the encoding session.
    //1. Get the output nodes
    //2. For each node, get the downstream node
    //3. For the downstream node, get the MFT
    //4. Get the property store
    //5. Get the required values
    //6. Set them on the stream sink
//
//  pTopology: A pointer to the full topology retrieved from the media session.
//-------------------------------------------------------------------

HRESULT PostEncodingUpdate(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;

    IMFCollection* pOutputColl = NULL;
    IUnknown* pNodeUnk = NULL;
    IMFMediaType* pType = NULL;
    IMFTopologyNode* pNode = NULL;
    IUnknown* pSinkUnk = NULL;
    IMFStreamSink* pStreamSink = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IUnknown* pEncoderUnk = NULL;
    IMFTransform* pEncoder = NULL;
    IPropertyStore* pStreamSinkProps = NULL;
    IPropertyStore* pEncoderProps = NULL;

    GUID guidMajorType = GUID_NULL;

    PROPVARIANT var;
    PropVariantInit( &var );

    DWORD cElements = 0;

    hr = pTopology->GetOutputNodeCollection( &pOutputColl);
    if (FAILED(hr))
    {
        goto done;
    }


    hr = pOutputColl->GetElementCount(&cElements);
    if (FAILED(hr))
    {
        goto done;
    }


    for(DWORD index = 0; index < cElements; index++)
    {
        hr = pOutputColl->GetElement(index, &pNodeUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**)&pNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInputPrefType(0, &pType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pType->GetMajorType( &guidMajorType );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetObject(&pSinkUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSinkUnk->QueryInterface(IID_IMFStreamSink, (void**)&pStreamSink);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInput( 0, &pEncoderNode, NULL );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderNode->GetObject(&pEncoderUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderUnk->QueryInterface(IID_IMFTransform, (void**)&pEncoder);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamSink->QueryInterface(IID_IPropertyStore, (void**)&pStreamSinkProps);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoder->QueryInterface(IID_IPropertyStore, (void**)&pEncoderProps);
        if (FAILED(hr))
        {
            goto done;
        }

        if( guidMajorType == MFMediaType_Video )
        {            
            hr = pEncoderProps->GetValue( MFPKEY_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_BMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }
        }
        else if( guidMajorType == MFMediaType_Audio )
        {      
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BMAX, &var);
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var );         
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, var );         
            if (FAILED(hr))
            {
                goto done;
            }
        } 
        PropVariantClear( &var ); 
   }
done:
    SafeRelease (&pOutputColl);
    SafeRelease (&pNodeUnk);
    SafeRelease (&pType);
    SafeRelease (&pNode);
    SafeRelease (&pSinkUnk);
    SafeRelease (&pStreamSink);
    SafeRelease (&pEncoderNode);
    SafeRelease (&pEncoderUnk);
    SafeRelease (&pEncoder);
    SafeRelease (&pStreamSinkProps);
    SafeRelease (&pEncoderProps);

    return hr;
   
}

Implémenter main

L’exemple de code suivant montre la fonction principale de votre application console.

int wmain(int argc, wchar_t* argv[])
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    if (argc != 4)
    {
        wprintf_s(L"Usage: %s inputaudio.mp3, %s output.wm*, %Encoding Type: CBR, VBR\n");
        return 0;
    }


    HRESULT             hr = S_OK;

    IMFMediaSource* pSource = NULL;
    IMFTopology* pTopology = NULL;
    IMFActivate* pFileSinkActivate = NULL;

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr))
    {
        goto done;
    }

    //Set the requested encoding mode
    if (wcscmp(argv[3], L"CBR")==0)
    {
        EncodingMode = CBR;
    }
    else if (wcscmp(argv[3], L"VBR")==0)
    {
        EncodingMode = VBR;
    }
    else
    {
        EncodingMode = CBR;
    }

    // Start up Media Foundation platform.
    hr = MFStartup(MF_VERSION);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the media source
    hr = CreateMediaSource(argv[1], &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the file sink activate
    hr = CreateMediaSink(argv[2], pSource, &pFileSinkActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    //Build the encoding topology.
    hr = BuildPartialTopology(pSource, pFileSinkActivate, &pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Instantiate the media session and start encoding
    hr = Encode(pTopology);
    if (FAILED(hr))
    {
        goto done;
    }


done:
    // Clean up.
    SafeRelease(&pSource);
    SafeRelease(&pTopology);
    SafeRelease(&pFileSinkActivate);

    MFShutdown();
    CoUninitialize();

    if (FAILED(hr))
    {
        wprintf_s(L"Could not create the output file due to 0x%X\n", hr);
    }
    return 0;
}

Tester le fichier de sortie

La liste suivante décrit une liste de vérification pour tester le fichier encodé. Ces valeurs peuvent être cochées dans la boîte de dialogue propriétés du fichier que vous pouvez afficher en cliquant avec le bouton droit sur le fichier encodé et en sélectionnant Propriétés dans le menu contextuel.

  • Le chemin d’accès du fichier encodé est exact.
  • La taille du fichier est supérieure à zéro Ko et la durée de lecture correspond à la durée du fichier source.
  • Pour le flux vidéo, vérifiez la largeur et la hauteur de l’image, la fréquence d’images. Ces valeurs doivent correspondre aux valeurs que vous avez spécifiées dans le profil ASF que vous avez créé à l’étape décrite dans la section « Créer l’objet de profil ASF ».
  • Pour le flux audio, le débit binaire doit être proche de la valeur que vous avez spécifiée sur le type de média cible.
  • Ouvrez le fichier dans Lecteur multimédia Windows et vérifiez la qualité de l’encodage.
  • Ouvrez le fichier ASF dans ASFViewer pour voir la structure d’un fichier ASF. Cet outil peut être téléchargé à partir de ce site web Microsoft.

Codes d’erreur courants et Astuces de débogage

La liste suivante décrit les codes d’erreur courants que vous pouvez recevoir et les conseils de débogage.

  • L’appel à IMFSourceResolver::CreateObjectFromURL bloque l’application.

    Vérifiez que vous avez initialisé la plateforme Media Foundation en appelant MFStartup. Cette fonction configure la plateforme asynchrone utilisée par toutes les méthodes qui démarrent des opérations asynchrones, telles que IMFSourceResolver::CreateObjectFromURL, en interne.

  • IMFSourceResolver::CreateObjectFromURL retourne HRESULT 0x80070002 « Le système ne trouve pas le fichier spécifié.

    Vérifiez que le nom de fichier d’entrée spécifié par l’utilisateur dans le premier argument existe.

  • HRESULT 0x80070020 « Le processus ne peut pas accéder au fichier, car il est utilisé par un autre processus. "

    Assurez-vous que les fichiers d’entrée et de sortie ne sont pas actuellement utilisés par une autre ressource dans le système.

  • Les appels aux méthodes IMFTransform retournent MF_E_INVALIDMEDIATYPE.

    Vérifiez que les conditions suivantes sont remplies :

    • Le type d’entrée ou le type de sortie que vous avez spécifié est compatible avec les types multimédias pris en charge par l’encodeur.
    • Les types de média que vous avez spécifiés sont terminés. Pour que les types de médias soient complets, consultez les attributs requis dans les sections « Créer un type de média audio compressé » et « Créer un type de média vidéo compressé » de ce didacticiel.
    • Assurez-vous que vous avez défini le débit cible sur le type de média partiel auquel vous ajoutez les données privées de codec.
  • La session multimédia retourne MF_E_UNSUPPORTED_D3D_TYPE dans l’état de l’événement.

    Cette erreur est retournée lorsque le type de média de la source indique un mode entrelacé mixte qui n’est pas pris en charge par Windows encodeur Media Video. Si votre type de média vidéo compressé est défini pour utiliser le mode progressif, le pipeline doit utiliser une transformation de délacement. Étant donné que le pipeline ne parvient pas à trouver une correspondance (indiquée par ce code d’erreur), vous devez insérer manuellement un délaceur (processeur vidéo transcodeur) entre le décodeur et les nœuds d’encodeur.

  • La session multimédia retourne E_INVALIDARG dans l’état de l’événement.

    Cette erreur est retournée lorsque les attributs de type média de la source ne sont pas compatibles avec les attributs du type de média de sortie défini sur l’encodeur multimédia Windows.

  • IWMCodecPrivateData::GetPrivateData retourne HRESULT 0x80040203 « Une erreur de syntaxe s’est produite lors de la tentative d’évaluation d’une chaîne de requête »

    Vérifiez que le type d’entrée est défini sur l’encodeur MFT.

Composants ASF de couche de pipeline