Configuration d’un encodeur WMV

Pour créer un type de sortie valide pour un encodeur Windows Media Video (WMV), vous devez disposer des informations suivantes :

  • Format de la vidéo non compressée que vous allez encoder.
  • Sous-type de vidéo qui représente le format WMV encodé. Consultez GUID de sous-type de vidéo.
  • Vitesse de transmission cible pour le flux encodé.
  • Propriétés de configuration à définir sur l’encodeur.

Les propriétés de configuration sont documentées dans la documentation sur le codec audio et vidéo Windows Media et les API DSP. Pour plus d’informations, consultez « Propriétés du flux vidéo » dans Propriétés d’encodage.

Pour obtenir un type de sortie valide pour l’encodeur, procédez comme suit.

  1. Utilisez la fonction MFTEnum ou MFTEnumEx pour créer une instance de l’encodeur.

  2. Interrogez l’encodeur pour l’interface IPropertyStore .

  3. Utilisez l’interface IPropertyStore pour configurer l’encodeur.

  4. Appelez IMFTransform::SetInputType pour définir le type de vidéo non compressé sur l’encodeur.

  5. Appelez IMFTransform::GetOutputAvailableType pour obtenir la liste des formats de compression à partir de l’encodeur. Les encodeurs WMV ne retournent pas de type de média complet à partir de cette méthode. Il manque deux éléments d’information aux types de médias :

    • Débit binaire cible.
    • Données de codec privé provenant de l’encodeur.

    Avant de définir le type de sortie sur l’encodeur, vous devez ajouter ces deux éléments au type de média.

  6. Pour spécifier la vitesse de transmission cible, définissez l’attribut MF_MT_AVG_BITRATE sur le type de média.

  7. Ajoutez les données de codec privé au type de média, comme expliqué dans la section suivante.

  8. Appelez IMFTransform::SetOutputType pour définir le type de média de compression sur l’encodeur.

Données de codec privé

Les données de codec privé sont une structure de données opaque que vous devez obtenir à partir de l’encodeur WMV et ajouter au type de compression, avant de définir le type de compression sur l’encodeur. Pour obtenir les données privées, vous devez utiliser l’interface IWMCodecPrivateData , qui est documentée dans le Kit de développement logiciel (SDK) Windows Media Format 11.

Pour obtenir les données de codec privé, procédez comme suit :

  1. Appelez IMFTransform::GetOutputAvailableType pour obtenir un type de média à partir de l’encodeur. (Il s’agit de l’étape 6 de la section précédente.)
  2. Spécifiez la vitesse de transmission cible en définissant l’attribut MF_MT_AVG_BITRATE sur le type de média.
  3. Convertissez le type de média en structure DMO_MEDIA_TYPE en appelant la fonction MFInitAMMediaTypeFromMFMediaType .
  4. Interrogez l’encodeur pour l’interface IWMCodecPrivateData .
  5. Appelez la méthode IWMCodecPrivateData::SetPartialOutputType en passant la structure DMO_MEDIA_TYPE convertie.
  6. Appelez la méthode IWMCodecPrivateData::GetPrivateData deux fois, une fois pour obtenir la taille de la mémoire tampon pour les données privées et une fois pour copier les données dans la mémoire tampon.
  7. Ajoutez les données privées au type de média en définissant l’attribut MF_MT_USER_DATA sur le type.

L’exemple étendu suivant montre comment créer un format de compression WMV à partir d’un type vidéo non compressé :

#include <wmcodecdsp.h>
#include <Wmsdk.h>
#include <Dmo.h>
#include <mfapi.h>
#include <uuids.h>

#include <mfidl.h>
#include <mftransform.h>
#include <mferror.h>

#pragma comment(lib, "Msdmo")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "strmiids")
#pragma comment(lib, "propsys")

HRESULT GetEncodedVideoType(
    IMFMediaType *pTypeIn, 
    REFGUID subtype,
    UINT32 TargetBitrate, 
    IPropertyStore *pEncoderProps, 
    IMFMediaType **ppEncodingType,
    DWORD mftEnumFlags = MFT_ENUM_FLAG_SYNCMFT
    );

HRESULT CreateVideoEncoder(REFGUID subtype, DWORD mftEnumFlags, IMFTransform **ppMFT);
HRESULT AddPrivateData(IMFTransform *pMFT, IMFMediaType *pTypeOut);
HRESULT CopyPropertyStore(IPropertyStore *pSrc, IPropertyStore *pDest);

//
// GetEncodedVideoType
// Given an uncompressed video type, finds a suitable WMV type.
//

HRESULT GetEncodedVideoType(
    IMFMediaType *pTypeIn,          // Uncompressed format
    REFGUID subtype,                // Compression format
    UINT32 TargetBitrate,           // Target bit rate
    IPropertyStore *pEncoderProps,  // Encoder properties (can be NULL)
    IMFMediaType **ppEncodingType,  // Receives the WMV type.
    DWORD mftEnumFlags              // MFTEnumEx flags
    )
{
    HRESULT hr = S_OK;

    IMFTransform *pMFT = NULL;
    IPropertyStore *pPropStore = NULL;
    IMFMediaType *pTypeOut = NULL;

    // Instantiate the encoder
    hr = CreateVideoEncoder(subtype, mftEnumFlags, &pMFT);

    // Copy the properties to the encoder.

    if (SUCCEEDED(hr))
    {
        if (pEncoderProps)
        {
            hr = pMFT->QueryInterface(IID_PPV_ARGS(&pPropStore));

            if (SUCCEEDED(hr))
            {
                hr = CopyPropertyStore(pEncoderProps, pPropStore);
            }
        }
    }

    // Set the uncompressed type.
    if (SUCCEEDED(hr))
    {
        hr = pMFT->SetInputType(0, pTypeIn, 0);
    }

    // Get the partial output type
    if (SUCCEEDED(hr))
    {
        hr = pMFT->GetOutputAvailableType(0, 0, &pTypeOut);
    }

    // Set the bit rate.
    // You must set this before getting the codec private data.

    if (SUCCEEDED(hr))
    {
        hr = pTypeOut->SetUINT32(MF_MT_AVG_BITRATE, TargetBitrate);   
    }

    if (SUCCEEDED(hr))
    {
        hr = AddPrivateData(pMFT, pTypeOut);
    }

    if (SUCCEEDED(hr))
    {
        hr = pMFT->SetOutputType(0, pTypeOut, 0);
    }

    if (SUCCEEDED(hr))
    {
        *ppEncodingType = pTypeOut;
        (*ppEncodingType)->AddRef();
    }

    SafeRelease(&pMFT);
    SafeRelease(&pPropStore);
    SafeRelease(&pTypeOut);
    return hr;
}

La fonction CreateVideoEncoder crée un encodeur vidéo pour un sous-type vidéo spécifié, par exemple MFVideoFormat_WMV3 :

//
// CreateVideoEncoder
// Creates a video encoder for a specified video subtype.
//

HRESULT CreateVideoEncoder(
    REFGUID subtype,            // Encoding subtype.
    DWORD mftEnumFlags,         // Flags for MFTEnumEx
    IMFTransform **ppMFT        // Receives a pointer to the encoder.
    )
{
    HRESULT hr = S_OK;
    IMFTransform *pMFT = NULL;

    MFT_REGISTER_TYPE_INFO info;
    info.guidMajorType = MFMediaType_Video;
    info.guidSubtype = subtype;

    IMFActivate **ppActivates = NULL;    
    UINT32 count = 0;

    hr = MFTEnumEx(
        MFT_CATEGORY_VIDEO_ENCODER,
        mftEnumFlags | MFT_ENUM_FLAG_SORTANDFILTER,
        NULL,
        &info,
        &ppActivates,
        &count
        );

    if (count == 0)
    {
        hr = E_FAIL;
    }

    if (SUCCEEDED(hr))
    {
        hr = ppActivates[0]->ActivateObject(
            __uuidof(IMFTransform),
            (void**)&pMFT
            );
    }

    if (SUCCEEDED(hr))
    {
        *ppMFT = pMFT;
        (*ppMFT)->AddRef();
    }

    // Clean up

    for (DWORD i = 0; i < count; i++)
    {
        ppActivates[i]->Release();
    }
    CoTaskMemFree(ppActivates);
    SafeRelease(&pMFT);
    return hr;
}

La fonction AddPrivateData ajoute les données du codec privé au type de compression :

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

La fonction CopyPropertyStore est une fonction d’assistance qui copie les propriétés d’un magasin de propriétés vers un autre :

//
// CopyPropertyStore
// Helper function to copy properties from one property
// store to another property store.
//

HRESULT CopyPropertyStore(IPropertyStore *pSrc, IPropertyStore *pDest)
{
    HRESULT hr = S_OK;
    DWORD cProps = 0;

    PROPERTYKEY key;
    PROPVARIANT var;

    PropVariantInit(&var);

    hr = pSrc->GetCount(&cProps);

    if (SUCCEEDED(hr))
    {
        for (DWORD i = 0; i < cProps; i++)
        {
            hr = pSrc->GetAt(i, &key);

            if (FAILED(hr)) { break; }

            hr = pSrc->GetValue(key, &var);

            if (FAILED(hr)) { break; }

            hr = pDest->SetValue(key, var);

            if (FAILED(hr)) { break; }

            PropVariantClear(&var);
        }
    }

    PropVariantClear(&var);
    return hr;
}

Instanciation d’un encodeur MFT

Encodeurs Windows Media