Tutoriel : Encodage Windows Media en un passage

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

Remarque : 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édia 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édia au format MP3. Le processus d’encodage convertit le format réel des données contenues dans le fichier.

Ce tutoriel montre l’encodage de contenu clair (non protégé par DRM) à partir d’une source d’entrée vers du contenu Windows Media et l’écriture des données dans un nouveau fichier ASF (.wm*) en utilisant les composants ASF de la couche Pipeline. Ces composants seront utilisés pour construire une topologie d’encodage partielle, qui sera contrôlée par une instance de la Session Média.

Dans ce tutoriel, vous allez créer une application console qui prend les noms de fichiers d’entrée et de sortie, et le mode d’encodage comme arguments. Le fichier d’entrée peut être dans un format média compressé ou non compressé. Les modes d’encodage valides sont « CBR » (vitesse de transmission constante) ou « VBR » (vitesse de transmission variable). L’application crée une source média pour représenter la source spécifiée par le nom de fichier d’entrée, et le récepteur de fichier ASF pour archiver les contenus encodés du fichier source dans un fichier ASF. Pour simplifier la mise en œuvre du scénario, le fichier de sortie aura seulement un flux audio et un flux vidéo. L’application insère le codec Windows Media Audio 9.1 Professional pour la conversion de format du flux audio et le codec Windows Media Video 9 pour le flux vidéo.

Pour l’encodage à vitesse de transmission constante, avant que la session d’encodage commence, l’encodeur doit connaître la vitesse de transmission cible qu’il doit atteindre. Dans ce tutoriel, pour le mode « CBR », l’application utilise le taux de bits disponible avec le premier type de média de sortie qui est récupéré de l’encodeur lors de la négociation du type de média comme le taux de bits cible. Pour l’encodage à vitesse de transmission variable, ce tutoriel démontre l’encodage avec une vitesse de transmission variable en réglant un niveau de qualité. Les flux audio sont encodés au niveau de qualité de 98 et les flux vidéo au niveau de qualité de 95.

La procédure suivante résume les étapes pour l’encodage du contenu Windows Media dans un conteneur ASF en utilisant un mode d’encodage en un passage.

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

Ce tutoriel contient les sections suivantes :

Prérequis

Ce didacticiel part des principes suivants :

Configurer le projet

  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. Liez aux 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 tampon pour un flux vidéo.

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

Créez la source média

Créer une source de médias pour la source d’entrée. Media Foundation fournit des sources de médias intégrées pour divers formats de médias : MP3, MP4/3GP, AVI/WAVE. Pour des informations sur les formats pris en charge par Media Foundation, veuillez consulter la rubrique Formats de médias pris en charge dans Media Foundation.

Pour créer la source de médias, 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 de médias appropriée.

Faites les appels suivants :

L’exemple de code suivant montre une fonction CreateMediaSource qui crée une source de médias 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éez le récepteur de fichier ASF

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

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

  • Les flux à encoder et à écrire dans le fichier final.
  • Les propriétés d’encodage utilisées pour encoder le contenu des médias, telles que le type d’encodage, le nombre de passages d’encodage et les propriétés associées.
  • Les propriétés globales du fichier qui indiquent au récepteur de médias s’il doit ajuster automatiquement les paramètres du seau percé (débit binaire et taille du tampon).

Les informations sur les 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 un aperçu du récepteur de fichier ASF, veuillez consulter la section Récepteur de médias ASF.

Créez l’objet profil ASF

Pour que le récepteur de fichier ASF écrive des données de médias encodées dans un fichier ASF, le récepteur a besoin de 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 de médias 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 mettre en œuvre cette étape, vous avez besoin des objets suivants.

  • Profil ASF
  • Descripteur de présentation pour la source de médias
  • Descripteurs de flux pour les flux sélectionnés dans la source de médias.
  • Types de mé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 de médias créée dans l’étape décrite dans la section intitulée « Créer la source de médias » de ce tutoriel.

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

  4. Appelez IMFPresentationDescriptor::GetStreamDescriptorByIndex pour chaque flux dans la source de médias, 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.

    Remarque : Pour éviter des appels complexes, supposez qu’il n’existe qu’un seul type de média par flux et sélectionnez le premier type de média du flux. Pour des flux complexes, vous devez énumérer chaque type de média du gestionnaire de types de médias et choisir le type de média que vous souhaitez encoder.

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

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

    • Créez un type de média audio compressé
    • Créez un type de média vidéo compressé
  8. Créez un flux basé sur le type de média cible, configurez le flux selon les exigences de l’application, et ajoutez le flux au profil. Pour plus d’informations, veuillez consulter la section Ajout d’informations de flux au récepteur de fichier ASF.

    1. Appelez IMFASFProfile ::CreateStream et passez le type de média cible pour créer le flux de sortie. La méthode récupère l’interface IMFASFStreamConfig du flux.
    2. Configurez le flux.
      • Appelez IMFASFStreamConfig::SetStreamNumber pour attribuer un numéro au flux. Ce paramètre est obligatoire.
      • Configurez éventuellement les paramètres du seau percé, 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 en utilisant l’objet ContentInfo ASF. Pour plus d’informations sur cette étape, veuillez consulter la section « Créer l’objet ContentInfo ASF » de ce tutoriel.
    4. Appelez IMFASFProfile::SetStream pour ajouter le flux au profil.

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

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

Le 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éez 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 encodé en définissant les attributs requis. Pour vous assurer que le type audio est compatible avec l’encodeur audio Windows Media, instanciez le MFT de l’encodeur, définissez les propriétés d’encodage et obtenez un type de support en appelant IMFTransform::GetOutputAvailableType. Obtenez le type de sortie requis en parcourant tous les types disponibles, en obtenant les attributs de chaque type de support et en sélectionnant le type qui se rapproche le plus de vos besoins. Dans ce tutoriel, obtenez le premier type disponible dans la liste des types de médias de sortie pris en charge par l’encodeur.

Remarque : pour Windows 7, Media Foundation fournit une nouvelle fonction, MFTranscodeGetAudioOutputAvailableTypes, qui récupère une liste des types audio compatibles. Cette fonction ne récupère que les types de support pour l’encodage CBR.

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

Le code suivant crée un type audio compressé en obtenant un type compatible de l’encodeur audio Windows Media. L’implémentation de SetEncodingProperties est présentée dans la section intitulée « Créer l’objet ASF ContentInfo » de ce tutoriel.

//-------------------------------------------------------------------
//  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éez 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 entièrement encodé. Le type multimédia complet doit inclure le débit binaire souhaité et les données privées du codec.

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

  • Créez un type multimédia vide et copiez les attributs du type vidéo source, puis remplacez l’attribut MF_MT_SUBTYPE par 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

    Le code suivant crée un type vidéo compressé à partir du type vidéo 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 Windows Media en définissant les propriétés d’encodage et en appelant IMFTransform::GetOutputAvailableType. Cette méthode retourne le type partiel. Assurez-vous de convertir le type partiel en un type complet en ajoutant les informations suivantes :

    Parce que IWMCodecPrivateData::GetPrivateData vérifie le débit binaire avant de renvoyer les données privées du codec, assurez-vous de définir le débit binaire avant de récupérer les données privées du codec.

    Le code suivant crée un type vidéo compressé en obtenant un type compatible de l’encodeur vidéo Windows Media. Il crée également un type vidéo non compressé et le définit en tant qu’entrée de l’encodeur. Cela est implémenté dans la fonction d’aide CreateUncompressedVideoType. GetOutputTypeFromWMVEncoder convertit le type partiel retourné en un type complet en ajoutant des données privées de codec. L’implémentation de SetEncodingProperties est présentée dans la section intitulée « Créer l’objet ASF ContentInfo » de ce tutoriel.

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

    Le code suivant crée un type 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;
    }
    

    Le code suivant ajoute des données privées de codec au type multimé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éez l’objet ContentInfo ASF

L’objet ASF ContentInfo est un composant de niveau WMContainer conçu principalement pour stocker les informations d’objet de l’en-tête ASF. Le récepteur de fichier 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 Header ASF du fichier encodé. Pour ce faire, vous devez créer et configurer l’ensemble d’informations suivant sur l’objet ContentInfo avant de démarrer la session d’encodage.

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

    Le 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 de niveau de flux du récepteur de fichier. Dans cet appel, vous devez passer le numéro de flux que vous avez attribué pour le flux lors de la création du profil ASF.

  3. Définissez les propriétés d’encodage de niveau de flux dans le magasin de propriétés de flux du récepteur de fichier. Pour plus d’informations, veuillez consulter la section « Propriétés d’encodage de flux » dans la configuration du récepteur de fichier.

    Le code suivant définit les propriétés de niveau de flux dans le magasin de propriétés du récepteur de fichier.

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

    Le code suivant montre l’implémentation de SetEncodingProperties. Cette fonction définit les propriétés d’encodage de niveau de 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 fichier. Dans cet appel, vous devez passer 0 comme premier paramètre. Pour plus d’informations, veuillez consulter la section intitulée « Propriétés globales du récepteur de fichier » dans la configuration du récepteur de fichier.

  5. Définissez MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE sur VARIANT_TRUE pour garantir que les valeurs du seau percé dans le multiplexeur ASF sont ajustées correctement. Pour obtenir des informations sur cette propriété, veuillez consulter la section intitulée « Initialisation du multiplexeur et paramètres du seau percé » dans Création de l’objet multiplexeur.

    Le code suivant définit les propriétés de niveau de flux dans le magasin de propriétés du récepteur de fichier.

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

    Remarque

    Il existe d’autres propriétés que vous pouvez définir au niveau global pour le récepteur de fichier. Pour plus d’informations, veuillez consulter la section « Configuration de l’objet ContentInfo avec les paramètres de l’encodeur » dans Définition des propriétés dans l’objet ContentInfo.

     

Vous utiliserez le ContentInfo ASF configuré pour créer un objet d’activation pour le récepteur de fichier ASF (comme décrit dans la section suivante).

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

Créez le récepteur de fichier ASF

À cette étape du tutoriel, vous utiliserez le ContentInfo ASF configuré, que vous avez créé à l’étape précédente, pour créer un objet d’activation pour le récepteur de fichier ASF en appelant la fonction MFCreateASFMediaSinkActivate. Pour plus d’informations, veuillez consulter la section Créer le récepteur de fichier ASF.

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

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

Construisez la topologie d’encodage partielle

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

Créez le nœud de topologie source pour la source média

À 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 :

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

Pour plus d’informations sur la création de nœuds source et un exemple de code, veuillez consulter la section Créer des nœuds source.

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’aide AddSourceNode et AddTransformOutputNodes. Ces fonctions sont incluses plus loin dans ce tutoriel.

//-------------------------------------------------------------------
//  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édent, la source multimédia pour énumérer les flux source, 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;
}

Instanciez les encodeurs requis et créez les nœuds de transformation

Le pipeline de Media Foundation n’insère pas automatiquement les encodeurs Windows Media requis 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 intitulée « Créer l’objet de profil ASF » de ce tutoriel. Pour chaque flux dans la source et le 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 fichier que vous avez créé à l’étape décrite dans la section intitulée « Créer le récepteur de fichier ASF » de ce tutoriel.

Pour obtenir un aperçu de la création d’encodeurs via des objets d’activation, veuillez consulter la section Utilisation des objets d’activation d’un encodeur.

La procédure suivante décrit les étapes nécessaires 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 faisant un requête pour 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. En fonction du type de flux, audio ou 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 seau percé pour le flux audio.

    MFCreateWMAEncoderActivate définit le type de sortie sur le MFT d’encodeur sous-jacent pour le codec audio Windows Media. Une fois le type de support de sortie défini, l’encodeur obtient le débit binaire moyen à partir du type de support de sortie, calcule la plage de fenêtre de tampon et définit les valeurs de seau percé qui seront utilisées pendant la session d’encodage. Vous pouvez mettre à jour ces valeurs dans le récepteur de fichier 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 :

    • Débit binaire moyen : obtenez le débit binaire moyen à partir de l’attribut MF_MT_AUDIO_AVG_BYTES_PER_SECOND défini sur le type de support de sortie sélectionné lors de la négociation du type de support.
    • Fenêtre de tampon : vous pouvez l’obtenir en appelant IWMCodecLeakyBucket::GetBufferSizeBits.
    • Taille de tampon initiale : réglée sur 0.

    Créez un tableau de DWORDs 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, veuillez consulter la section Le modèle de tampon de seau percé.

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 un exemple de code, veuillez consulter la section intitulée « Créer un nœud de transformation à partir d’un objet d’activation » dans Créer des nœuds de transformation.

L’exemple de code suivant crée et ajoute les objets d’activation d’encodeur requis. Il prend des pointeurs vers un objet de topologie précédemment créé, l’objet d’activation du récepteur de fichier et le type de support du flux source. Il appelle également AddOutputNode (voir exemple de code suivant) qui crée et ajoute le nœud de récepteur à la topologie d’encodage. L’appelant 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éez 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 fichier ASF.

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

  • Un pointeur vers l’objet d’activation que vous avez créé à l’étape décrite dans la section intitulée « Créer le récepteur de fichier ASF » de ce tutoriel.
  • Les numéros de flux pour identifier les récepteurs de flux ajoutés au récepteur de fichier. 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 un exemple de code, veuillez consulter la section intitulée « Création d’un nœud de sortie à partir d’un objet d’activation » dans Créer des nœuds de sortie.

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

L’exemple de code suivant crée et ajoute le nœud de récepteur à la topologie d’encodage. Il prend des pointeurs vers un objet de topologie précédemment créé, l’objet d’activation du récepteur de fichier et le numéro d’identification du flux. L’appelant 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 de support 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;
}

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

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

Gestion de la session d’encodage

Dans cette étape, vous appliquerez la procédure suivante :

  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 échantillons non compressés à fournir en entrée à l’encodeur.

  3. Appelez IMFMediaSession::GetEvent pour demander les événements générés par la session média.

    Dans la boucle d’événement, vous démarrerez et fermerez la session d’encodage en fonction des événements générés par la session média. L’appel à IMFMediaSession::SetTopology entraîne la génération 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 tutoriel est une application console et que le blocage des threads de l’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, veuillez consulter la section intitulée « Gestion des événements de session » dans Comment lire des fichiers multimédias avec Media Foundation.

Dans la boucle d’événement pour obtenir les événements de la session multimédia, attendez l’événement MESessionTopologyStatus qui est déclenché lorsque IMFMediaSession::SetTopology est terminé et que la topologie est résolue. Après avoir reçu 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 discuté dans la prochaine section intitulée « Mise à jour des propriétés d’encodage sur le récepteur de fichier pour l’encodage VBR » de ce tutoriel.

La session multimédia génère l’objet d’en-tête ASF et finalise le fichier lorsque la session d’encodage est 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. Après la fermeture de la session d’encodage, 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 actuelle doit être fermée et libérée et la nouvelle topologie doit être définie sur une nouvelle session multimédia 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 la 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;
}

Mise à jour des propriétés d’encodage dans le récepteur de fichiers

Certaines propriétés d’encodage telles que le débit binaire d’encodage et les valeurs précises du seau percé ne sont pas connues avant que l’encodage soit 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 du seau percé 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 nécessaires pour parcourir les nœuds dans la topologie d’encodage pour obtenir le nœud du récepteur de fichier et définir les propriétés du seau percé requises.

Pour mettre à jour les valeurs des propriétés après l’encodage sur le récepteur de fichier ASF

  1. Appelez IMFTopology::GetOutputNodeCollection pour obtenir la collection de nœuds de sortie 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. Interrogez l’interface IMFStreamSink sur le pointeur IUnknown retourné par IMFTopologyNode::GetObject.
  3. Pour chaque récepteur de flux, obtenez le nœud aval (encodeur) en appelant IMFTopologyNode::GetInput.
  4. Interrogez le nœud pour obtenir le pointeur IMFTransform à partir du nœud de l’encodeur.
  5. Interrogez l’encodeur pour obtenir le pointeur IPropertyStore pour obtenir le magasin de propriétés d’encodage de l’encodeur.
  6. Interrogez le récepteur de flux pour obtenir 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 copiez-les dans le magasin de propriétés du récepteur de flux en appelant IPropertyStore::SetValue.

Le tableau suivant montre les valeurs des propriétés après l’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 la propriété (SetValue)
Encodage de débit binaire 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 des propriétés après l’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émentez 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;
}

Testez le fichier de sortie

La liste suivante décrit une liste de contrôle pour tester le fichier encodé. Ces valeurs peuvent être vérifiées dans la boîte de dialogue des propriétés du fichier que vous pouvez afficher en faisant un clic droit sur le fichier encodé et en sélectionnant Propriétés dans le menu contextuel.

  • Le chemin du fichier encodé est correct.
  • 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 intitulée « Créer l’objet de profil ASF ».
  • Pour le flux audio, le débit binaire doit être proche de la valeur spécifiée sur le type de support cible.
  • Ouvrez le fichier dans le Windows Media Player 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é sur ce site Web de Microsoft.

Codes d’erreur courants et astuces de débogage

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

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

    Assurez-vous d’avoir initialisé la plate-forme Media Foundation en appelant MFStartup. Cette fonction configure la plate-forme asynchrone utilisée par toutes les méthodes qui démarrent des opérations asynchrones, telles que IMFSourceResolver::CreateObjectFromURL, en interne.

  • IMFSourceResolver::CreateObjectFromURL renvoie HRESULT 0x80070002 « Le système ne peut pas trouver le fichier spécifié ».

    Assurez-vous 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 de IMFTransform renvoient MF_E_INVALIDMEDIATYPE.

    Assurez-vous 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 de supports que l’encodeur prend en charge.
    • Les types de supports que vous avez spécifiés sont complets. Pour que les types de supports soient complets, consultez les attributs requis dans les sections intitulée « Créer un type de support audio compressé » et « Créer un type de support vidéo compressé » de ce tutoriel.
    • Assurez-vous d’avoir défini le débit binaire cible sur le type de support partiel auquel vous ajoutez les données privées du codec.
  • La session multimédia renvoie MF_E_UNSUPPORTED_D3D_TYPE dans l’état de l’événement.

    Cette erreur est renvoyée lorsque le type de support de la source indique un mode entrelacé mixte qui n’est pas pris en charge par l’encodeur Windows Media Video. Si votre type de support vidéo compressé est défini pour utiliser le mode progressif, le pipeline doit utiliser une transformation de désentrelacement. Parce que le pipeline est incapable de trouver une correspondance (indiquée par ce code d’erreur), vous devez insérer manuellement un désentrelaceur (processeur vidéo de transcodage) entre les nœuds de décodage et d’encodage.

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

    Cette erreur est renvoyée lorsque les attributs du type de support de la source ne sont pas compatibles avec les attributs du type de support de sortie définis sur l’encodeur Windows Media.

  • IWMCodecPrivateData::GetPrivateData renvoie HRESULT 0x80040203 « Une erreur de syntaxe s’est produite en essayant d’évaluer une chaîne de requête »

    Assurez-vous que le type d’entrée est défini sur l’encodeur MFT.

Composants ASF de couche de pipeline