Lernprogramm: 1-Pass-Windows Mediencodierung

Die Codierung bezieht sich auf den Prozess der Konvertierung digitaler Medien aus einem Format in ein anderes. Wenn Sie z. B. MP3-Audio in Windows Medienaudioformat konvertieren, wie durch die Advanced Systems Format (ASF)-Spezifikation definiert.

Hinweis Die ASF-Spezifikation definiert einen Containertyp für die Ausgabedatei (WMA oder WMV), die Mediendaten in jedem Format, komprimiert oder nicht komprimiert enthalten kann. Beispielsweise kann ein ASF-Container wie eine WMA-Datei Mediendaten im MP3-Format enthalten. Der Codierungsprozess konvertiert das tatsächliche Format der Daten, die in der Datei enthalten sind.

In diesem Lernprogramm wird die Codierung klarer Inhalte (nicht DRM-geschützte) Eingabequelle in Windows Medieninhalte veranschaulicht und die Daten in einer neuen ASF-Datei (.wm*) mithilfe von Pipeline Layer ASF-Komponenten geschrieben. Diese Komponenten werden verwendet, um eine teilielle Codierungstopologie zu erstellen, die von einer Instanz der Mediensitzung gesteuert wird.

In diesem Lernprogramm erstellen Sie eine Konsolenanwendung, die die Eingabe- und Ausgabedateinamen und den Codierungsmodus als Argumente verwendet. Die Eingabedatei kann in einem komprimierten oder unkomprimierten Medienformat enthalten sein. Die gültigen Codierungsmodi sind "CBR" (Konstante Bitrate) oder "VBR" (Variable Bitrate). Die Anwendung erstellt eine Medienquelle, um die vom Eingabedateinamen angegebene Quelle darzustellen, und die ASF-Datei sinken, um den codierten Inhalt der Quelldatei in eine ASF-Datei zu archivieren. Um das Szenario einfach zu implementieren, verfügt die Ausgabedatei nur über einen Audiodatenstrom und einen Videostream. Die Anwendung fügt den Windows Media Audio 9.1 Professional Codec für die Konvertierung des Audiostreams und Windows Media Video 9-Codec für den Videostream ein.

Vor Beginn der Codierung der Codierung muss der Encoder die Zielbitrate kennen, die er erreichen muss. In diesem Lernprogramm verwendet die Anwendung für den "CBR"-Modus die bitrate, die mit dem ersten Ausgabemedientyp verfügbar ist, der während der Medientypaushandlung als Zielbitrate abgerufen wird. Für die Codierung variabler Bitraten veranschaulicht dieses Lernprogramm die Codierung mit variabler Bitrate, indem Sie eine Qualitätsstufe festlegen. Die Audiostreams werden auf Qualitätsniveau von 98 und Videostreams auf Qualitätsstufe 95 codiert.

Im folgenden Verfahren werden die Schritte zum Codieren von Windows Medieninhalten in einem ASF-Container mit einem 1-Pass-Codierungsmodus zusammengefasst.

  1. Erstellen Sie eine Medienquelle für den angegebenen Wert mithilfe des Quelllösers.
  2. Enumerieren Sie die Datenströme in der Medienquelle.
  3. Erstellen Sie die ASF-Medien sinken, und fügen Sie Datenströme je nach Datenströme in der Medienquelle hinzu, die codiert werden müssen.
  4. Konfigurieren Sie das Medien sinken mit den erforderlichen Codierungseigenschaften.
  5. Erstellen Sie die Windows Media-Encoder für die Datenströme in der Ausgabedatei.
  6. Konfigurieren Sie die Encoder mit den Eigenschaften, die auf der Medien sinken.
  7. Erstellen Sie eine teilielle Codierungstopologie.
  8. Instanziieren Sie die Mediensitzung, und legen Sie die Topologie in der Mediensitzung fest.
  9. Starten Sie die Codierungssitzung, indem Sie die Mediensitzung steuern und alle relevanten Ereignisse aus der Mediensitzung abrufen.
  10. Rufen Sie für die VBR-Codierung die Codierungseigenschaftswerte aus dem Encoder ab und legen Sie sie auf der Medien sinken.
  11. Schließen und Herunterfahren der Codierungssitzung.

Dieses Lernprogramm enthält die folgenden Abschnitte:

Voraussetzungen

In diesem Tutorial wird Folgendes vorausgesetzt:

Einrichten des Project

  1. Fügen Sie die folgenden Kopfzeilen in Ihre Quelldatei ein:

    #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. Link zu den folgenden Bibliotheksdateien:

    // 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. Deklarieren Sie die Funktion SafeRelease .

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. Deklarieren Sie die ENCODING_MODE-Enumeration, um CBR- und VBR-Codierungstypen zu definieren.

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. Definieren Sie eine Konstante für pufferfenster für einen Videostream.

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

Erstellen der Medienquelle

Erstellen Sie eine Medienquelle für die Eingabequelle. Media Foundation bietet integrierte Medienquellen für verschiedene Medienformate: MP3, MP4/3GP, AVI/WAVE. Informationen zu den von Media Foundation unterstützten Formaten finden Sie unter "Unterstützte Medienformate" in Media Foundation.

Verwenden Sie zum Erstellen der Medienquelle den Quelllöser. Dieses Objekt analysiert die URL, die für die Quelldatei angegeben wurde und die entsprechende Medienquelle erstellt.

Führen Sie die folgenden Aufrufe aus:

Das folgende Codebeispiel zeigt eine Funktion CreateMediaSource, die eine Medienquelle für die angegebene Datei erstellt.

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

Erstellen des ASF-Datei-Sinks

Erstellen Sie eine Instanz der ASF-Datei sink, die codierte Mediendaten an eine ASF-Datei am Ende der Codierungssitzung archiviert.

In diesem Lernprogramm erstellen Sie ein Aktivierungsobjekt für das ASF-Datei sinken. Die Dateisenken benötigen die folgenden Informationen, um die erforderlichen Stream-Sinken zu erstellen.

  • Die Datenströme, die codiert und in der endgültigen Datei geschrieben werden sollen.
  • Die Codierungseigenschaften, die verwendet werden, um den Medieninhalt zu codieren, z. B. Typ der Codierung, Anzahl der Codierungsübergänge und die zugehörigen Eigenschaften.
  • Die globalen Dateieigenschaften, die darauf hinweisen, dass die Mediensenken angeben, ob sie die leckigen Bucketparameter (Bitrate und Puffergröße) automatisch anpassen sollte.

Die Datenstrominformationen werden im ASF-Profilobjekt konfiguriert, und die Codierungs- und globalen Eigenschaften werden in einem Eigenschaftsspeicher festgelegt, der vom ASF ContentInfo-Objekt verwaltet wird.

Eine Übersicht über die ASF-Datei sinken finden Sie unter ASF Media Sinks.

Erstellen des ASF-Profilobjekts

Damit die ASF-Datei codierte Mediendaten in eine ASF-Datei schreibt, muss das Sinken die Anzahl der Datenströme und den Typ der Datenströme kennen, für die Datenströme erstellt werden sollen. In diesem Lernprogramm extrahieren Sie diese Informationen aus der Medienquelle und erstellen entsprechende Ausgabeströme. Beschränken Sie Ihre Ausgabedatenströme auf einen Audiodatenstrom und einen Videodatenstrom. Erstellen Sie für jeden ausgewählten Stream in der Quelle einen Zieldatenstrom, und fügen Sie ihn dem Profil hinzu.

Um diesen Schritt zu implementieren, benötigen Sie die folgenden Objekte.

  • ASF-Profil
  • Präsentationsdeskriptor für die Medienquelle
  • Streamdeskriptoren für die ausgewählten Datenströme in der Medienquelle.
  • Medientypen für die ausgewählten Datenströme.

Die folgenden Schritte beschreiben den Prozess des Erstellens des ASF-Profils und der Zieldatenströme.

So erstellen Sie das ASF-Profil

  1. Rufen Sie MFCreateASFProfile auf, um ein leeres Profilobjekt zu erstellen.

  2. Rufen Sie IMFMediaSource::CreatePresentationDescriptor auf, um das Präsentationsdeskriptor für die Medienquelle zu erstellen, die im Schritt beschrieben im Abschnitt "Medienquelle erstellen" dieses Lernprogramms erstellt wurde.

  3. Rufen Sie IMFPresentationDescriptor::GetStreamDescriptorCount auf, um die Anzahl der Datenströme in der Medienquelle abzurufen.

  4. Rufen Sie IMFPresentationDescriptor::GetStreamDescriptorByIndex für jeden Stream in der Medienquelle auf, rufen Sie den Streamdescriptor ab.

  5. Rufen Sie IMFStreamDescriptor::GetMediaTypeHandler gefolgt von IMFMediaTypeHandler::GetMediaTypeByIndex auf, und rufen Sie den ersten Medientyp für den Stream ab.

    Hinweis Um komplexe Anrufe zu vermeiden, nehmen Sie an, dass nur ein Medientyp pro Stream vorhanden ist und den ersten Medientyp des Datenstroms auswählen. Für komplexe Datenströme müssen Sie jeden Medientyp aus dem Medientyphandler aufzählen und den Medientyptyp auswählen, den Sie codieren möchten.

  6. Rufen Sie IIMFMediaType::GetMajorType auf, um den Haupttyp des Datenstroms abzurufen, um zu ermitteln, ob der Stream Audio oder Video enthält.

  7. Je nach Haupttyp des Datenstroms erstellen Sie Zielmedientypen. Diese Medientypen enthalten Formatinformationen, die der Encoder während der Codierungssitzung verwendet. In den folgenden Abschnitten dieses Lernprogramms wird der Prozess zum Erstellen der Zielmedientypen beschrieben.

    • Erstellen eines komprimierten Audiomedientyps
    • Erstellen eines komprimierten Videomedientyps
  8. Erstellen Sie einen Datenstrom basierend auf dem Zielmedientyp, konfigurieren Sie den Datenstrom entsprechend den Anforderungen der Anwendung, und fügen Sie dem Profil den Datenstrom hinzu. Weitere Informationen finden Sie unter Hinzufügen von Streaminformationen zum ASF-Datei-Sink.

    1. Rufen Sie IMFASFProfile::CreateStream auf, und übergeben Sie den Zielmedientyp, um den Ausgabedatenstrom zu erstellen. Die Methode ruft die IMFASFStreamConfig-Schnittstelle des Streamobjekts ab.
    2. Konfigurieren Des Datenstroms.
      • Rufen Sie IMFASFStreamConfig::SetStreamNumber auf, um dem Stream eine Nummer zuzuweisen. Diese Einstellung ist obligatorisch.
      • Konfigurieren Sie optional die leckigen Bucketparameter, die Nutzlasterweiterung, den gegenseitigen Ausschluss für jeden Datenstrom, indem Sie IMFASFStreamConfig-Methoden und relevante Streamkonfigurationsattribute aufrufen.
    3. Fügen Sie die Codierungseigenschaften auf Streamebene mithilfe des ASF ContentInfo-Objekts hinzu. Weitere Informationen zu diesem Schritt finden Sie im Abschnitt "Erstellen des ASF ContentInfo-Objekts" in diesem Lernprogramm.
    4. Rufen Sie IMFASFProfile::SetStream auf, um dem Profil den Datenstrom hinzuzufügen.

    Im folgenden Codebeispiel wird ein Ausgabe-Audiodatenstrom erstellt.

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

    Im folgenden Codebeispiel wird ein Ausgabevideostream erstellt.

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

Im folgenden Codebeispiel werden Ausgabeströme abhängig von den Datenströme in der Quelle erstellt.

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

Erstellen eines komprimierten Audiomedientyps

Wenn Sie einen Audiodatenstrom in die Ausgabedatei einschließen möchten, erstellen Sie einen Audiotyp, indem Sie die Merkmale des codierten Typs angeben, indem Sie die erforderlichen Attribute festlegen. Um sicherzustellen, dass der Audiotyp mit dem Windows Media Audio-Encoder kompatibel ist, instanziieren Sie den Encoder MFT, legen Sie die Codierungseigenschaften fest, und rufen Sie einen Medientyp ab, indem Sie IMFTransform::GetOutputAvailableType aufrufen. Rufen Sie den erforderlichen Ausgabetyp ab, indem Sie alle verfügbaren Typen durchlaufen, die Attribute jedes Medientyps abrufen und den Typ auswählen, der den Anforderungen am nächsten ist. Rufen Sie in diesem Lernprogramm den ersten verfügbaren Typ aus der Liste der vom Encoder unterstützten Ausgabemedientypen ab.

Hinweis Für Windows 7 bietet Media Foundation eine neue Funktion, MFTranscodeGetAudioOutputAvailableTypes, die eine Liste der kompatiblen Audiotypen abruft. Diese Funktion ruft nur Medientypen für die CBR-Codierung ab.

Ein vollständiger Audiotyp muss die folgenden Attribute festlegen:

Im folgenden Codebeispiel wird ein komprimierter Audiotyp erstellt, indem ein kompatibler Typ aus dem Windows Media Audio-Encoder abgerufen wird. Die Implementierung für SetEncodingProperties wird im Abschnitt "ASF ContentInfo-Objekt erstellen" dieses Lernprogramms angezeigt.

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

Erstellen eines komprimierten Videomedientyps

Wenn Sie einen Videodatenstrom in die Ausgabedatei einschließen möchten, erstellen Sie einen vollständig codierten Videotyp. Der vollständige Medientyp muss die gewünschte Bitrate und codec private Daten enthalten.

Es gibt zwei Möglichkeiten, wie Sie einen vollständigen Videomedientyp erstellen können.

  • Erstellen Sie einen leeren Medientyp und kopieren Sie die Medientypattribute aus dem Quellvideotyp, und überschreiben Sie das MF_MT_SUBTYPE-Attribut mit der GUID-Konstante MFVideoFormat_WMV3.

    Ein vollständiger Videotyp muss die folgenden Attribute festlegen:

    • 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

    Im folgenden Codebeispiel wird ein komprimierter Videotyp vom Videotyp der Quelle erstellt.

    //-------------------------------------------------------------------
    //  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;
    }
    
    
  • Rufen Sie einen kompatiblen Medientyp aus dem Windows Media Video-Encoder ab, indem Sie die Codierungseigenschaften festlegen und dann IMFTransform::GetOutputAvailableType aufrufen. Diese Methode gibt den Teiltyp zurück. Stellen Sie sicher, dass Sie den Teiltyp in einen vollständigen Typ konvertieren, indem Sie die folgenden Informationen hinzufügen:

    Da IWMCodecPrivateData::GetPrivateData die Bitrate überprüft, bevor Sie die codec-privaten Daten zurückgeben, stellen Sie sicher, dass Sie die Bitrate festlegen, bevor Sie die codec-privaten Daten abrufen.

    Im folgenden Codebeispiel wird ein komprimierter Videotyp erstellt, indem ein kompatibler Typ aus dem Windows Media Video-Encoder abgerufen wird. Außerdem wird ein nicht komprimierter Videotyp erstellt und als Eingabe des Encoders festgelegt. Dies wird in der Hilfsfunktion CreateUncompressedVideoType implementiert. GetOutputTypeFromWMVEncoder konvertiert den zurückgegebenen Teiltyp in einen vollständigen Typ, indem Codec private Daten hinzugefügt werden. Die Implementierung für SetEncodingProperties wird im Abschnitt "ASF ContentInfo-Objekt erstellen" dieses Lernprogramms angezeigt.

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

    Im folgenden Codebeispiel wird ein nicht komprimierter Videotyp erstellt.

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

    Im folgenden Codebeispiel wird dem angegebenen Videomedientyp codec private Daten hinzugefügt.

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

Erstellen des ASF ContentInfo-Objekts

Das ASF ContentInfo-Objekt ist eine WMContainer-Ebene-Komponente, die hauptsächlich zum Speichern von ASF-Headerobjektinformationen konzipiert ist. Das ASF-Datei-Sink implementiert das ContentInfo-Objekt, um Informationen (in einem Eigenschaftsspeicher) zu speichern, die zum Schreiben des ASF-Headerobjekts der codierten Datei verwendet werden. Dazu müssen Sie die folgenden Informationen zum ContentInfo-Objekt erstellen und konfigurieren, bevor Sie die Codierungssitzung starten.

  1. Rufen Sie MFCreateASFContentInfo auf, um ein leeres ContentInfo-Objekt zu erstellen.

    Im folgenden Codebeispiel wird ein leeres ContentInfo-Objekt erstellt.

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. Rufen Sie IMFASFContentInfo::GetEncodingConfigurationPropertyStore auf, um den Stream-Level-Eigenschaftsspeicher des Datei-Sinks abzurufen. In diesem Aufruf müssen Sie die Datenstromnummer übergeben, die Sie beim Erstellen des ASF-Profils zugewiesen haben.

  3. Legen Sie die Eigenschaften für die Codierung auf Streamebene im Stream-Eigenschaftsspeicher des Datei sinkens fest. Weitere Informationen finden Sie unter "StreamCodierungseigenschaften" in der Einstellung von Eigenschaften im Datei sinken.

    Im folgenden Codebeispiel wird die Eigenschaften der Datenstromebene im Eigenschaftenspeicher des Dateisenken festgelegt.

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

    Das folgende Codebeispiel zeigt die Implementierung für SetEncodingProperties. Diese Funktion legt die Codierungseigenschaften auf Datenstromebene für CBR und VBR fest.

    //-------------------------------------------------------------------
    //  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. Rufen Sie IMFASFContentInfo::GetEncodingConfigurationPropertyStore auf, um den globalen Eigenschaftsspeicher des Dateisinks abzurufen. In diesem Aufruf müssen Sie 0 im ersten Parameter übergeben. Weitere Informationen finden Sie unter "Globale Datei-Sinkeigenschaften" im Festlegen von Eigenschaften im Datei sinken.

  5. Legen Sie die MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE auf VARIANT_TRUE fest, um sicherzustellen, dass die unleckigen Bucketwerte im ASF-Multiplexer ordnungsgemäß angepasst werden. Informationen zu dieser Eigenschaft finden Sie unter "Multiplexer Initialization and Leaky Bucket Einstellungen" in Creating the Multiplexer Object.

    Im folgenden Codebeispiel werden die Eigenschaften der Datenstromebene im Eigenschaftenspeicher des Dateisenkens festgelegt.

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

    Hinweis

    Es gibt weitere Eigenschaften, die Sie auf globaler Ebene für das Dateisenken festlegen können. Weitere Informationen finden Sie unter "Konfigurieren des ContentInfo-Objekts mit Encoder Einstellungen" im Festlegen von Eigenschaften im ContentInfo-Objekt.

     

Sie verwenden die konfigurierte ASF ContentInfo zum Erstellen eines Aktivierungsobjekts für die ASF-Dateisenken (im nächsten Abschnitt beschrieben).

Wenn Sie ein Out-of-Process-Dateisenken (MFCreateASFMediaSinkActivate) erstellen, das mithilfe eines Aktivierungsobjekts erfolgt, können Sie das konfigurierte ContentInfo-Objekt verwenden, um die ASF-Mediensenke instanziieren (siehe nächster Abschnitt). Wenn Sie eine In-Process-Dateisenken (MFCreateASFMediaSink) erstellen, anstatt das leere ContentInfo-Objekt wie in Schritt 1 beschrieben zu erstellen, rufen Sie einen Verweis auf die IMFASFContentInfo-Schnittstelle ab, indem Sie IMFMediaSink::QueryInterface auf dem Dateisenken aufrufen. Anschließend müssen Sie das ContentInfo-Objekt konfigurieren, wie in diesem Abschnitt beschrieben.

Erstellen des ASF-Dateisenkens

In diesem Schritt des Lernprogramms verwenden Sie die konfigurierte ASF ContentInfo, die Sie im vorherigen Schritt erstellt haben, um ein Aktivierungsobjekt für das ASF-Dateisenken zu erstellen, indem Sie die FUNKTION MFCreateASFMediaSinkActivate aufrufen. Weitere Informationen finden Sie unter Erstellen des ASF-Dateisenkens.

Im folgenden Codebeispiel wird das Aktivierungsobjekt für das Dateisenken erstellt.

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

Erstellen der Partiellen Codierungstopologie

Als Nächstes erstellen Sie eine partielle Codierungstopologie, indem Sie Topologieknoten für die Medienquelle, die erforderlichen Windows Media-Encoder und den ASF-Dateisenken erstellen. Nachdem Sie die Topologieknoten hinzugefügt haben, verbinden Sie die Quelle, Transformation und die Sinkknoten. Bevor Sie Topologieknoten hinzufügen, müssen Sie ein leeres Topologieobjekt erstellen, indem Sie MFCreateTopology aufrufen.

Erstellen des Quelltopologieknotens für die Medienquelle

In diesem Schritt erstellen Sie den Quelltopologieknoten für die Medienquelle.

Zum Erstellen dieses Knotens benötigen Sie die folgenden Verweise:

  • Ein Zeiger auf die Medienquelle, die Sie im Schritt erstellt haben, der im Abschnitt "Medienquelle erstellen" dieses Lernprogramms beschrieben wurde.
  • Ein Zeiger auf den Präsentationsdeskriptor für die Medienquelle. Sie können einen Verweis auf die IMFPresentationDescriptor-Schnittstelle der Medienquelle abrufen, indem Sie IMFMediaSource::CreatePresentationDescriptor aufrufen.
  • Ein Zeiger auf den Datenstromdeskriptor für jeden Datenstrom in der Medienquelle, für den Sie einen Zieldatenstrom in dem Schritt erstellt haben, der im Abschnitt "Erstellen des ASF-Profilobjekts" dieses Lernprogramms beschrieben wird.

Weitere Informationen zum Erstellen von Quellknoten und Codebeispiel finden Sie unter Erstellen von Quellknoten.

Im folgenden Codebeispiel wird eine partielle Topologie erstellt, indem der Quellknoten und die erforderlichen Transformationsknoten hinzugefügt werden. Dieser Code ruft die Hilfsfunktionen AddSourceNode und AddTransformOutputNodes auf. Diese Funktionen werden später in diesem Lernprogramm enthalten.

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

Im folgenden Codebeispiel wird der Codierungstopologieknoten erstellt und hinzugefügt. Es benötigt Zeiger auf ein zuvor Topologieobjekt, die Medienquelle, um die Quelldatenströme, den Präsentationsdeskriptor der Medienquelle und den Streamdeskriptor der Medienquelle aufzählen zu können. Der Aufrufer empfängt einen Zeiger auf den Quelltopologieknoten.

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

Instanziieren der erforderlichen Encoder und Erstellen der Transformationsknoten

Die Media Foundation-Pipeline fügt nicht automatisch die erforderlichen Windows Media-Encoder für die Datenströme ein, die sie codieren müssen. Die Anwendung muss die Encoder manuell hinzufügen. Aufzählen Sie dazu die Datenströme im ASF-Profil, das Sie im schritt beschriebenen Schritt im Abschnitt "AsF-Profilobjekt erstellen" dieses Lernprogramms erstellt haben. Instanziieren Sie für jeden Datenstrom in der Quelle und den entsprechenden Datenstrom im Profil die erforderlichen Encoder. Für diesen Schritt benötigen Sie einen Zeiger auf das Aktivierungsobjekt für die Dateisenken, die Sie im Schritt erstellt haben, der im Abschnitt "AsF File Sink erstellen" dieses Lernprogramms beschrieben wurde.

Eine Übersicht zum Erstellen von Encodern über Aktivierungsobjekte finden Sie unter Verwenden der Aktivierungsobjekte eines Encoders.

Im folgenden Verfahren werden die erforderlichen Schritte zum Instanziieren der erforderlichen Encoder beschrieben.

  1. Rufen Sie einen Verweis auf das ContentInfo-Objekt des Sinks ab, indem Sie IMFActivate::ActivateObject auf dem Dateisenken aktivieren und dann nach IMFASFContentInfo aus dem Dateisenken abfragen, indem Sie QueryInterface aufrufen.

  2. Rufen Sie das ASF-Profil ab, das dem ContentInfo-Objekt zugeordnet ist, indem Sie IMFASFContentInfo::GetProfile aufrufen.

  3. Aufzählen der Datenströme im Profil. Dazu benötigen Sie die Datenstromanzahl und einen Verweis auf die IMFASFStreamConfig-Schnittstelle jedes Datenstroms.

    Rufen Sie die folgenden Methoden auf:

  4. Für jeden Datenstrom wird der Haupttyp und die Codierungseigenschaften des Datenstroms aus dem ContentInfo-Objekt abgerufen.

    Rufen Sie die folgenden Methoden auf:

  5. Je nach Art des Datenstroms, Audios oder Videos instanziieren Sie das Aktivierungsobjekt für den Encoder durch Aufrufen von MFCreateWMAEncoderActivate oder MFCreateWMVEncoderActivate.

    Um diese Funktionen aufzurufen, benötigen Sie die folgenden Verweise:

  6. Aktualisieren Sie den leckigen Bucketparameter für den Audiodatenstrom.

    MFCreateWMAEncoderActivate legt den Ausgabetyp für den zugrunde liegenden Encoder MFT für den Windows Media-Audiocodec fest. Nachdem der Ausgabemedientyp festgelegt wurde, ruft der Encoder die durchschnittliche Bitrate des Ausgabemedientyps ab, berechnet die Pufferfenster-Bitrate und legt die leckigen Bucketwerte fest, die während der Codierungssitzung verwendet werden. Sie können diese Werte im Dateisenken aktualisieren, indem Sie entweder den Encoder abfragen oder benutzerdefinierte Werte festlegen. Zum Aktualisieren von Werten benötigen Sie die folgenden Informationssätze:

    Erstellen Sie ein Array von DWORDs, und legen Sie den Wert in der MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET Eigenschaft des Audiodatenstromsenken fest. Wenn Sie die aktualisierten Werte nicht angeben, legt die Mediensitzung diese entsprechend fest.

    Weitere Informationen finden Sie im Leaky Bucket Buffer Model.

Die in Schritt 5 erstellten Aktivierungsobjekte müssen der Topologie als Transformationstopologieknoten hinzugefügt werden. Weitere Informationen und Codebeispiel finden Sie unter "Erstellen eines Transformationsknotens aus einem Aktivierungsobjekt" in der Erstellung von Transformationsknoten.

Im folgenden Codebeispiel wird der erforderliche Encoder erstellt und hinzugefügt. Es benötigt Zeiger auf ein zuvor erstelltes Topologieobjekt, das Aktivierungsobjekt des Dateisenken und den Medientyp des Quelldatenstroms. Außerdem wird AddOutputNode (siehe nächstes Codebeispiel) aufgerufen, das den Sinkknoten zur Codierungstopologie hinzufügt. Der Aufrufer empfängt einen Zeiger auf den Quelltopologieknoten.

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

Erstellen der Ausgabetopologieknoten für die Dateisenken

In diesem Schritt erstellen Sie den Ausgabetopologieknoten für die ASF-Dateisenken.

Zum Erstellen dieses Knotens benötigen Sie die folgenden Verweise:

  • Ein Zeiger auf das Aktivierungsobjekt, das Sie im Schritt erstellt haben, der im Abschnitt "AsF File Sink" dieses Lernprogramms beschrieben wurde.
  • Die Datenstromnummern, um die Datenstromsenken zu identifizieren, die dem Dateisenken hinzugefügt wurden. Die Datenstromnummern stimmen mit der Identifizierung des Datenstroms überein, die während der Datenstromerstellung festgelegt wurde.

Weitere Informationen zum Erstellen von Ausgabeknoten und Codebeispiel finden Sie unter "Erstellen eines Ausgabeknotens aus einem Aktivierungsobjekt" in der Erstellung von Ausgabeknoten.

Wenn Sie das Aktivierungsobjekt für das Dateisenken nicht verwenden, müssen Sie die Stream-Sinken im ASF-Dateisenken aufzählen und jeden Datenstromsenken als Ausgabeknoten in der Topologie festlegen. Informationen zur Stream-Sink-Enumeration finden Sie unter "Enumerating Stream Sinks" in Add Stream Information to the ASF File Sink.

Im folgenden Codebeispiel wird der Codierungstopologie der Sinkknoten erstellt und hinzugefügt. Es dauert Zeiger auf ein zuvor erstelltes Topologieobjekt, das Aktivierungsobjekt des Dateisenken und die Identifikationsnummer des Datenstroms. Der Aufrufer empfängt einen Zeiger auf den Quelltopologieknoten.

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

Im folgenden Codebeispiel werden die Datenstromsenken für ein bestimmtes Mediensenken aufgezählt.

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

Verbinden die Quell-, Transformations- und Sinkknoten

In diesem Schritt verbinden Sie den Quellknoten mit dem Transformationsknoten, der auf die Codierung verweist, die Sie in dem Schritt erstellt haben, der im Abschnitt "Erforderliche Encoder instanziieren und die Transformationsknoten erstellen" dieses Lernprogramms beschrieben wurde. Der Transformationsknoten wird mit dem Ausgabeknoten verbunden, der das Aktivierungsobjekt für das Dateisenken enthält.

Behandeln der Codierungssitzung

Im Schritt führen Sie die folgenden Schritte aus:

  1. Rufen Sie MFCreateMediaSession auf, um eine Codierungssitzung zu erstellen.

  2. Rufen Sie IMFMediaSession::SetTopology auf, um die Codierungstopologie für die Sitzung festzulegen. Wenn der Aufruf abgeschlossen ist, wertet die Mediensitzung die Topologieknoten aus und fügt zusätzliche Transformationsobjekte ein, z. B. einen Decoder, der die angegebene komprimierte Quelle in nicht komprimierte Beispiele konvertiert, um als Eingabe an den Encoder zu feeden.

  3. Rufen Sie IMFMediaSession::GetEvent auf, um die von der Mediensitzung ausgelösten Ereignisse anzufordern.

    In der Ereignisschleife starten und schließen Sie die Codierungssitzung abhängig von den Ereignissen, die von der Mediensitzung ausgelöst werden. Das IMFMediaSession::SetTopology-Aufrufergebnis in der Mediensitzung führt dazu, dass das MESessionTopologyStatus-Ereignis mit dem MF_TOPOSTATUS_READY Flagsatz ausgelöst wird. Alle Ereignisse werden asynchron generiert und eine Anwendung kann diese Ereignisse synchron oder asynchron abrufen. Da die Anwendung in diesem Lernprogramm eine Konsolenanwendung ist und das Blockieren von Benutzeroberflächenthreads kein Problem ist, werden die Ereignisse von der Mediensitzung synchron abgerufen.

    Um die Ereignisse asynchron abzurufen, muss Ihre Anwendung die IMFAsyncCallback-Schnittstelle implementieren. Weitere Informationen und eine Beispielimplementierung dieser Schnittstelle finden Sie unter "Behandeln von Sitzungsereignissen" in der Wiedergabe von Mediendateien mit Media Foundation.

Warten Sie in der Ereignisschleife zum Abrufen von Mediensitzungsereignissen auf das MeSessionTopologyStatus-Ereignis , das ausgelöst wird, wenn die IMFMediaSession::SetTopology abgeschlossen ist, und die Topologie wird aufgelöst. Starten Sie nach dem Abrufen des MESessionTopologyStatus-Ereignisses die Codierungssitzung, indem Sie IMFMediaSession::Start aufrufen. Die Mediensitzung generiert das MEEndOfPresentation-Ereignis , wenn alle Codierungsvorgänge abgeschlossen sind. Dieses Ereignis muss für die VBR-Codierung behandelt werden und wird im nächsten Abschnitt "Update Encoding Properties on the File Sink for VBR Encoding" dieses Lernprogramms erläutert.

Die Mediensitzung generiert das ASF Header-Objekt und beendet die Datei, wenn die Codierungssitzung abgeschlossen ist, und löst dann das MESessionClosed-Ereignis aus . Dieses Ereignis muss behandelt werden, indem ordnungsgemäß heruntergefahrene Vorgänge in der Mediensitzung ausgeführt werden. Rufen Sie zum Starten der Herunterfahren-Vorgänge IMFMediaSession::Herunterfahren auf. Nachdem die Codierungssitzung geschlossen wurde, können Sie keine weitere Topologie für die Codierung auf derselben Mediensitzungsinstanz festlegen. Um eine andere Datei zu codieren, muss die aktuelle Mediensitzung geschlossen und veröffentlicht werden, und die neue Topologie muss auf einer neu erstellten Mediensitzung festgelegt werden. Im folgenden Codebeispiel wird die Mediensitzung erstellt, die Codierungstopologie festgelegt und die Mediensitzungsereignisse behandelt.

Im folgenden Codebeispiel wird die Mediensitzung erstellt, die Codierungstopologie festgelegt und die Codierungssitzung gesteuert, indem Ereignisse aus der Mediensitzung behandelt werden.

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

Aktualisieren der Codierungseigenschaften im Datei sinken

Bestimmte Codierungseigenschaften wie die Codierungsbitrate und die genauen leckigen Bucketwerte werden erst bekannt, wenn die Codierung abgeschlossen ist, insbesondere für DIE VBR-Codierung. Um die richtigen Werte abzurufen, muss die Anwendung auf das MEEndOfPresentation-Ereignis warten, das angibt, dass die Codierungssitzung abgeschlossen ist. Die leckigen Bucketwerte müssen im Sinken aktualisiert werden, sodass das ASF Header-Objekt die genauen Werte widerspiegeln kann.

Im folgenden Verfahren werden die Schritte beschrieben, die erforderlich sind, um die Knoten in der Codierungstopologie zu durchlaufen, um den Datei-Sinkknoten abzurufen und die erforderlichen leckigen Bucketeigenschaften festzulegen.

So aktualisieren Sie die Werte der Postcodierungseigenschaft im ASF-Dateisenken

  1. Rufen Sie IMFTopologie::GetOutputNodeCollection auf, um die Ausgabeknotensammlung aus der Codierungstopologie abzurufen.
  2. Rufen Sie für jeden Knoten einen Zeiger auf das Stream-Sinken im Knoten ab, indem Sie IMFTopologyNode::GetObject aufrufen. Abfrage für die IMFStreamSink-Schnittstelle auf dem IUnknown-Zeiger , der von IMFTopologyNode zurückgegeben wird::GetObject.
  3. Bei jedem Stream-Sink wird der nachgelagerte Knoten (Encoder) abgerufen, indem IMFTopologyNode::GetInput aufgerufen wird.
  4. Abfrage des Knotens, um den IMFTransform-Zeiger vom Encoderknoten abzurufen.
  5. Abfragen des Encoders für den IPropertyStore-Zeiger , um den Codierungseigenschaftsspeicher aus dem Encoder abzurufen.
  6. Abfragen des Stream-Sinks für den IPropertyStore-Zeiger , um den Eigenschaftsspeicher des Stream-Sinks abzurufen.
  7. Rufen Sie IPropertyStore auf:GetValue , um die erforderlichen Eigenschaftswerte aus dem Eigenschaftsspeicher des Encoders abzurufen und sie in den Eigenschaftenspeicher des Stream-Sinks zu kopieren, indem Sie IPropertyStore::SetValue aufrufen.

Die folgende Tabelle zeigt die Werte der Postcodierungseigenschaft, die für den Videostream festgelegt werden müssen.

Codierungstyp Eigenschaftsname (GetValue) Eigenschaftsname (SetValue)
Codierung konstanter Bitrate MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Qualitätsbasierte Variable Bitrate-Codierung MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

Im folgenden Codebeispiel werden die Werte für die postcodierungseigenschaft festgelegt.

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

Haupt implementieren

Das folgende Codebeispiel zeigt die Hauptfunktion Ihrer Konsolenanwendung.

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

Testen der Ausgabedatei

In der folgenden Liste wird eine Prüfliste zum Testen der codierten Datei beschrieben. Diese Werte können im Dialogfeld "Dateieigenschaften" aktiviert werden, das Sie anzeigen können, indem Sie mit der rechten Maustaste auf die codierte Datei klicken und Eigenschaften im Kontextmenü auswählen.

  • Der Pfad der codierten Datei ist genau.
  • Die Größe der Datei beträgt mehr als 0 KB, und die Wiedergabedauer entspricht der Dauer der Quelldatei.
  • Überprüfen Sie für den Videodatenstrom die Framebreite und -höhe, die Framerate. Diese Werte sollten den Werten entsprechen, die Sie im ASF-Profil angegeben haben, das Sie im Abschnitt "AsF-Profilobjekt erstellen" erstellt haben.
  • Für den Audiodatenstrom muss die Bitrate nahe dem wert sein, den Sie im Zielmedientyp angegeben haben.
  • Öffnen Sie die Datei in Windows Medienwiedergabe, und überprüfen Sie die Qualität der Codierung.
  • Öffnen Sie die ASF-Datei in ASFViewer, um die Struktur einer ASF-Datei anzuzeigen. Dieses Tool kann auf dieser Microsoft-Website heruntergeladen werden.

Häufige Fehlercodes und Debuggen Tipps

In der folgenden Liste werden die allgemeinen Fehlercodes beschrieben, die möglicherweise empfangen werden, und die Debugtipps.

  • Der Aufruf von IMFSourceResolver::CreateObjectFromURL verzögert die Anwendung.

    Stellen Sie sicher, dass Sie die Media Foundation-Plattform initialisiert haben, indem Sie MFStartup aufrufen. Diese Funktion richtet die asynchrone Plattform ein, die von allen Methoden verwendet wird, die asynchrone Vorgänge starten, z. B. IMFSourceResolver::CreateObjectFromURL, intern.

  • IMFSourceResolver::CreateObjectFromURL gibt HRESULT 0x80070002 "Das System kann die angegebene Datei nicht finden.

    Stellen Sie sicher, dass der vom Benutzer im ersten Argument angegebene Eingabedateiname vorhanden ist.

  • HRESULT 0x80070020 "Der Prozess kann nicht auf die Datei zugreifen, da es von einem anderen Prozess verwendet wird. "

    Stellen Sie sicher, dass die Eingabe und die Ausgabedateien derzeit nicht von einer anderen Ressource im System verwendet werden.

  • Aufrufe an IMFTransform-Methoden geben MF_E_INVALIDMEDIATYPE zurück.

    Stellen Sie sicher, dass die folgenden Bedingungen wahr sind:

    • Der Eingabetyp oder der von Ihnen angegebene Ausgabetyp ist mit Medientypen kompatibel, die der Encoder unterstützt.
    • Die von Ihnen angegebenen Medientypen sind abgeschlossen. Informationen zum Abschließen von Medientypen finden Sie in den erforderlichen Attributen im Abschnitt "Erstellen eines komprimierten Audiomedientyps" und "Erstellen eines komprimierten Videomedientyps" in diesem Lernprogramm.
    • Stellen Sie sicher, dass Sie die Zielbitrate für den teiliellen Medientyp festgelegt haben, zu dem Sie die Codec-privaten Daten hinzufügen.
  • Die Mediensitzung gibt MF_E_UNSUPPORTED_D3D_TYPE im Ereignisstatus zurück.

    Dieser Fehler wird zurückgegeben, wenn der Medientyp der Quelle einen gemischten Interlacemodus angibt, der von Windows Media Video-Encoder nicht unterstützt wird. Wenn Der komprimierte Videomedientyp auf die Verwendung des progressiven Modus festgelegt ist, muss die Pipeline eine De-Interlacing-Transformation verwenden. Da die Pipeline keine Übereinstimmung finden kann (die durch diesen Fehlercode angegeben ist), müssen Sie einen De-Interlacer (Transcode-Videoprozessor) zwischen dem Decoder und encoderknoten manuell einfügen.

  • Die Mediensitzung gibt E_INVALIDARG im Ereignisstatus zurück.

    Dieser Fehler wird zurückgegeben, wenn die Medientypattribute der Quelle nicht mit den Attributen des Ausgabemedientyps kompatibel sind, der auf dem Windows Media-Encoder festgelegt ist.

  • IWMCodecPrivateData::GetPrivateData gibt HRESULT 0x80040203 "Ein Syntaxfehler aufgetreten, der versucht, eine Abfragezeichenfolge auszuwerten" zurück.

    Stellen Sie sicher, dass der Eingabetyp auf dem Encoder MFT festgelegt ist.

Pipeline layer ASF-Komponenten