Tutorial: Einwegcodierung Windows Media

Die Codierung bezieht sich auf den Prozess der Konvertierung digitaler Medien von einem Format in ein anderes. Konvertieren Sie beispielsweise MP3-Audio in das Windows Media Audio-Format gemäß der Advanced Systems Format-Spezifikation (ASF).

Beachten Sie, dass die ASF-Spezifikation einen Containertyp für die Ausgabedatei (.wma oder .wmv) definiert, die Mediendaten in beliebigem 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 Tutorial wird die Codierung einer klaren Inhaltseingabequelle (nicht DRM-geschützt) in Windows Media-Inhalt und das Schreiben der Daten in eine neue ASF-Datei (.wm*) mithilfe von ASF-Komponenten der Pipelineschicht veranschaulicht. Diese Komponenten werden verwendet, um eine partielle Codierungstopologie zu erstellen, die von einer Instanz der Mediensitzung gesteuert wird.

In diesem Tutorial erstellen Sie eine Konsolenanwendung, die die Eingabe- und Ausgabedateinamen und den Codierungsmodus als Argumente verwendet. Die Eingabedatei kann in einem komprimierten oder nicht komprimierten Medienformat vorliegen. Die gültigen Codierungsmodi sind „CBR“ (konstante Bitrate) oder „VBR“ (variable Bitrate). Die Anwendung erstellt eine Medienquelle, um die durch den Eingabedateinamen angegebene Quelle darzustellen, und die ASF-Dateisenke, um den codierten Inhalt der Quelldatei in einer ASF-Datei zu archivieren. Damit das Szenario einfach zu implementieren ist, verfügt die Ausgabedatei nur über einen Audiostream und einen Videostream. Die Anwendung fügt den Windows Media Audio 9.1 Professional-Codec für die Konvertierung des Audiostreamformats und den Windows Media Video 9-Codec für den Videostream ein.

Für die Codierung mit konstanter Bitrate muss der Encoder vor Beginn der Codierungssitzung die Zielbitrate kennen, die er erreichen muss. In diesem Tutorial 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 vom Encoder abgerufen wird. Für die Codierung mit variabler Bitrate demonstriert dieses Tutorial die Codierung mit variabler Bitrate durch Festlegen einer Qualitätsstufe. Die Audiostreams werden mit der Qualitätsstufe 98 und die Videostreams mit der Qualitätsstufe 95 codiert.

Im folgenden Verfahren werden die Schritte zum Codieren von Windows Media-Inhalten in einem ASF-Container mithilfe eines Einweg-Codierungsmodus zusammengefasst.

  1. Erstellen Sie eine Medienquelle für den angegebenen Wert mithilfe des Source Resolver.
  2. Zählen Sie die Streams in der Medienquelle auf.
  3. Erstellen Sie die ASF-Mediensenke und fügen Sie Stream-Senken hinzu, abhängig von den Streams in der Medienquelle, die codiert werden müssen.
  4. Konfigurieren Sie die Mediensenke mit den erforderlichen Codierungseigenschaften.
  5. Erstellen Sie die Windows Media-Encoder für die Streams in der Ausgabedatei.
  6. Konfigurieren Sie die Encoder mit den Eigenschaften, die in der Mediensenke festgelegt sind.
  7. Erstellen Sie eine partielle Codierungstopologie.
  8. Instanziieren Sie die Mediensitzung, und legen Sie die Topologie für die 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 Codierungseigenschaftenwerte vom Encoder ab, und legen Sie sie in der Mediensenke fest.
  11. Schließen und beenden Sie die Codierungssitzung.

Dieses Tutorial enthält die folgenden Abschnitte:

Voraussetzungen

In diesem Tutorial wird Folgendes vorausgesetzt:

Einrichten des Projekts

  1. Fügen Sie die folgenden Kopfzeilen in die 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 SafeRelease-Funktion.

    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 das 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 Source Resolver. Dieses Objekt analysiert die URL, die für die Quelldatei angegeben wurde, und erstellt die entsprechende Medienquelle.

Führen Sie die folgenden Aufrufe durch:

Das folgende Codebeispiel zeigt eine CreateMediaSource-Funktion, 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 der ASF-Dateisenke

Erstellen Sie eine Instanz der ASF-Dateisenke, die codierte Mediendaten am Ende der Codierungssitzung in einer ASF-Datei archiviert.

In diesem Tutorial erstellen Sie ein Aktivierungsobjekt für die ASF-Dateisenke. Die Dateisenke benötigt die folgenden Informationen, um die erforderlichen Stream-Senken zu erstellen.

  • Die Streams, die codiert und in die endgültige Datei geschrieben werden sollen
  • Die Codierungseigenschaften, die zum Codieren des Medieninhalts verwendet werden, z. B. Typ der Codierung, Anzahl der Codierungsdurchläufe und die zugehörigen Eigenschaften
  • Die globalen Dateieigenschaften, die der Mediensenke angeben, ob sie die Leaky-Bucket-Parameter (Bitrate und Puffergröße) automatisch anpassen soll

Die Stream-Informationen werden im ASF-Profilobjekt konfiguriert und die Codierung und globalen Eigenschaften werden in einem vom ASF-ContentInfo-Objekt verwalteten Eigenschaftenspeicher festgelegt.

Eine Übersicht über die ASF-Dateisenke finden Sie unter ASF-Mediensenken.

Erstellen des ASF-Profilobjekts

Damit die ASF-Dateisenke codierte Mediendaten in eine ASF-Datei schreiben kann, muss die Senke die Anzahl der Streams und die Art der Streams kennen, für die Stream-Senken erstellt werden sollen. In diesem Tutorial extrahieren Sie diese Informationen aus der Medienquelle und erstellen entsprechende Ausgabestreams. Beschränken Sie Ihre Ausgabestreams auf einen Audiostream und einen Videostream. Erstellen Sie für jeden ausgewählten Stream in der Quelle einen Zielstream 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 Streams in der Medienquelle
  • Medientypen für die ausgewählten Streams

In den folgenden Schritten wird das Erstellen des ASF-Profils und der Zielstreams beschrieben.

So erstellen Sie das ASF-Profil

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

  2. Rufen Sie IMFMediaSource::CreatePresentationDescriptor auf, um den Präsentationsdeskriptor für die Medienquelle zu erstellen, die in dem im Abschnitt „Erstellen der Medienquelle“ dieses Tutorials beschriebenen Schritt erstellt wurde.

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

  4. Rufen Sie IMFPresentationDescriptor::GetStreamDescriptorByIndex für jeden Stream in der Medienquelle auf, und rufen Sie den Streamdeskriptor 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 Aufrufe zu vermeiden, gehen Sie davon aus, dass pro Stream nur ein Medientyp vorhanden ist, und wählen Sie den ersten Medientyp des Streams aus. Bei komplexen Streams müssen Sie jeden Medientyp aus dem Medientyphandler aufzählen und den Medientyp auswählen, den Sie codieren möchten.

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

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

    • Erstellen eines komprimierten Audiomedientyps
    • Erstellen eines komprimierten Videomedientyps
  8. Erstellen Sie einen Stream basierend auf dem Zielmedientyp, konfigurieren Sie den Stream gemäß den Anforderungen der Anwendung und fügen Sie den Stream dem Profil hinzu. Weitere Informationen finden Sie unter Hinzufügen von Streaminformationen zur ASF-Dateisenke.

    1. Rufen Sie IMFASFProfile::CreateStream auf, und übergeben Sie den Zielmedientyp, um den Ausgabestream zu erstellen. Die Methode ruft die IMFASFStreamConfig-Schnittstelle des Streamobjekts ab.
    2. Konfigurieren Sie den Stream.
      • Rufen Sie IMFASFStreamConfig::SetStreamNumber auf, um dem Stream eine Nummer zuzuweisen. Diese Einstellung ist obligatorisch.
      • Konfigurieren Sie optional die Leaky-Bucket-Parameter, die Nutzlasterweiterung und den gegenseitigen Ausschluss für jeden Stream durch Aufruf von IMFASFStreamConfig-Methoden und relevanten Stream-Konfigurationsattributen.
    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 Tutorial.
    4. Rufen Sie IMFASFProfile::SetStream auf, um den Stream dem Profil hinzuzufügen.

    Das folgende Codebeispiel erstellt einen Ausgabe-Audiostream.

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

    Das folgende Codebeispiel erstellt einen Ausgabe-Videostream.

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

Das folgende Codebeispiel erstellt Ausgabestreams abhängig von den Streams in der Quelle.

    //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 Audiostream in die Ausgabedatei einbeziehen möchten, erstellen Sie einen Audiotyp, indem Sie die Merkmale des codierten Typs durch das Festlegen der erforderlichen Attribute angeben. Um sicherzustellen, dass der Audiotyp mit dem Windows Media-Audioencoder kompatibel ist, instanziieren Sie die Encoder-MFT, legen Sie die Codierungseigenschaften fest und rufen Sie einen Medientyp durch Aufruf von IMFTransform::GetOutputAvailableType ab. Rufen Sie den erforderlichen Ausgabetyp ab, indem Sie alle verfügbaren Typen durchlaufen, die Attribute jedes Medientyps abrufen und den Typ auswählen, der Ihren Anforderungen am nächsten kommt. In diesem Tutorial rufen Sie den ersten verfügbaren Typ aus der Liste der vom Encoder unterstützten Ausgabemedientypen ab.

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

Für einen vollständigen Audiotyp müssen die folgenden Attribute festgelegt sein:

Das folgende Codebeispiel erstellt einen komprimierten Audiotyp, indem ein kompatibler Typ vom Windows Media Audio-Encoder abgerufen wird. Die Implementierung für SetEncodingProperties wird im Abschnitt „Erstellen des ASF ContentInfo-Objekts“ dieses Tutorials gezeigt.

//-------------------------------------------------------------------
//  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 Videostream in die Ausgabedatei aufnehmen möchten, erstellen Sie einen vollständig codierten Videotyp. Der vollständige Medientyp muss die gewünschte Bitrate und private Codec-Daten enthalten.

Es gibt zwei Möglichkeiten, einen vollständigen Videomedientyp zu erstellen.

  • 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.

    Für einen vollständigen Videotyp müssen die folgenden Attribute festgelegt sein:

    • 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 aus dem 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 abrufen. Diese Methode gibt den partiellen Typ zurück. Stellen Sie sicher, dass Sie den partiellen Typ in einen vollständigen Typ konvertieren, indem Sie die folgenden Informationen hinzufügen:

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

    Das folgende Codebeispiel erstellt einen komprimierten Videotyp, indem ein kompatibler Typ vom 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 partiellen Typ in einen vollständigen Typ, indem private Codec-Daten hinzugefügt werden. Die Implementierung für SetEncodingProperties wird im Abschnitt „Erstellen des ASF ContentInfo-Objekts“ dieses Tutorials gezeigt.

    //-------------------------------------------------------------------
    //  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 werden dem angegebenen Videomedientyp private Codec-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-Ebenenkomponente, die hauptsächlich zum Speichern von ASF-Header-Objektinformationen konzipiert ist. Die ASF-Dateisenke implementiert das ContentInfo-Objekt, um Informationen (in einem Eigenschaftenspeicher) zu speichern, die zum Schreiben des ASF-Header-Objekts der codierten Datei verwendet werden. Dazu müssen Sie die folgenden Informationen für das 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 Eigenschaftenspeicher der Dateisenke auf Streamebene abzurufen. In diesem Aufruf müssen Sie die Stream-Nummer übergeben, die Sie dem Stream beim Erstellen des ASF-Profils zugewiesen haben.

  3. Legen Sie die Codierungseigenschaften auf Streamebene im Eigenschaftenspeicher der Dateisenke auf Streamebene fest. Weitere Informationen finden Sie unter „Stream-Codierungseigenschaften“ in Einstellungseigenschaften in der Dateisenke.

    Das folgende Codebeispiel legt die Eigenschaften auf Streamebene im Eigenschaftenspeicher der Dateisenke fest.

            //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 Codierungseigenschaften auf Streamebene 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 Eigenschaftenspeicher der Dateisenke abzurufen. In diesem Aufruf müssen Sie „0“ im ersten Parameter übergeben. Weitere Informationen finden Sie unter „Eigenschaften globaler Dateisenken“ in Festlegen von Eigenschaften in der Dateisenke.

  5. Legen Sie die MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE auf VARIANT_TRUE fest, um sicherzustellen, dass die Leaky-Bucket-Werte im ASF-Multiplexer richtig angepasst werden. Informationen zu dieser Eigenschaft finden Sie unter „Multiplexer-Initialisierung und Leaky-Bucket-Einstellungen“ in Erstellen des Multiplexer-Objekts.

    Das folgende Codebeispiel legt die Eigenschaften auf Streamebene im Eigenschaftenspeicher der Dateisenke fest.

        //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 die Dateisenke festlegen können. Weitere Informationen finden Sie unter „Konfigurieren des ContentInfo-Objekts mit Encoder-Einstellungen“ in Festlegen von Eigenschaften im ContentInfo-Objekt.

     

Sie verwenden die konfigurierte ASF ContentInfo, um ein Aktivierungsobjekt für die ASF-Dateisenke zu erstellen (im nächsten Abschnitt beschrieben).

Wenn Sie eine Out-of-Process-Dateisenke (MFCreateASFMediaSinkActivate) erstellen, und zwar durch Verwendung eines Aktivierungsobjekts, können Sie das konfigurierte ContentInfo-Objekt nutzen, um die ASF-Mediensenke zu instanziieren (im nächsten Abschnitt beschrieben). Wenn Sie eine In-Process-Dateisenke (MFCreateASFMediaSink) erstellen, statt ein leeres ContentInfo-Objekt wie in Schritt 1 beschrieben, rufen Sie einen Verweis zur IMFASFContentInfo-Schnittstelle durch Aufruf von IMFMediaSink::QueryInterface in der Dateisenke ab. Anschließend müssen Sie das ContentInfo-Objekt wie in diesem Abschnitt beschrieben konfigurieren.

Erstellen der ASF-Dateisenke

In diesem Schritt des Tutorials verwenden Sie die konfigurierte ASF ContentInfo, die Sie im vorherigen Schritt erstellt haben, um ein Aktivierungsobjekt für die ASF-Dateisenke zu erstellen, indem Sie die MFCreateASFMediaSinkActivate-Funktion aufrufen. Weitere Informationen finden Sie unter Erstellen der ASF-Dateisenke.

Im folgenden Codebeispiel wird das Aktivierungsobjekt für die Dateisenke 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 die ASF-Dateisenke erstellen. Nachdem Sie die Topologieknoten hinzugefügt haben, verbinden Sie die Quell-, Transformations- und Senkenknoten. 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 in dem im Abschnitt „Erstellen der Medienquelle“ dieses Tutorials beschriebenen Schritt erstellt haben.
  • 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 Stream-Deskriptor für jeden Stream in der Medienquelle, für den Sie in dem im Abschnitt „Erstellen des ASF-Profilobjekts“ dieses Tutorials beschriebenen Schritt einen Ziel-Stream erstellt haben.

Weitere Informationen zum Erstellen von Quellknoten und Codebeispielen 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 sind weiter unten in diesem Tutorial 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 Quelltopologieknoten erstellt und zur Codierungstopologie hinzugefügt. Er verweist Zeiger auf ein früheres Topologieobjekt, die Medienquelle zum Aufzählen der Quellstreams, den Präsentationsdeskriptor der Medienquelle und den Streamdeskriptor der Medienquelle. 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 die erforderlichen Windows Media-Encoder für die Streams, die sie codieren muss, nicht automatisch ein. Die Anwendung muss die Encoder manuell hinzufügen. Zählen Sie dazu die Streams im ASF-Profil auf, das Sie in dem im Abschnitt „Erstellen des ASF-Profilobjekts“ dieses Tutorials beschriebenen Schritt erstellt haben. Instanziieren Sie für jeden Stream in der Quelle und den entsprechenden Stream im Profil die erforderlichen Encoder. Für diesen Schritt benötigen Sie einen Zeiger auf das Aktivierungsobjekt für die Dateisenke, die Sie in dem im Abschnitt „Erstellen der ASF-Dateisenke“ dieses Tutorials beschriebenen Schritt erstellt haben.

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

Das folgende Verfahren beschreibt die Schritte, die zum Instanziieren der erforderlichen Encoder erforderlich sind.

  1. Rufen Sie einen Verweis auf das ContentInfo-Objekt der Senke ab, indem Sie IMFActivate::ActivateObject für die Aktivierung der Senke aufrufen und dann IMFASFContentInfo aus der Dateisenke durch Aufruf vonQueryInterface abfragen.

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

  3. Zählen Sie die Streams im Profil auf. Dazu benötigen Sie die Anzahl der Streams und einen Verweis auf die IMFASFStreamConfig-Schnittstelle jedes Streams.

    Rufen Sie die folgenden Methoden auf:

  4. Rufen Sie für jeden Stream den Haupttyp und die Codierungseigenschaften des Streams aus dem ContentInfo-Objekt ab.

    Rufen Sie die folgenden Methoden auf:

  5. Instanziieren Sie je nach Art des Streams (Audio oder Video) das Aktivierungsobjekt für den Encoder durch Aufruf von MFCreateWMAEncoderActivate oder MFCreateWMVEncoderActivate.

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

  6. Aktualisieren Sie den Leaky-Bucket-Parameter für den Audiostream.

    MFCreateWMAEncoderActivate legt den Ausgabetyp auf der zugrunde liegenden Encoder-MFT für den Windows Media-Audiocodec fest. Nachdem der Ausgabemedientyp festgelegt wurde, ruft der Encoder die durchschnittliche Bitrate vom Ausgabemedientyp ab, berechnet die durchschnittliche Pufferfenster-Bitrate und legt die Leaky-Bucket-Werte fest, die während der Codierungssitzung verwendet werden. Sie können diese Werte in der Dateisenke aktualisieren, indem Sie entweder den Encoder abfragen oder benutzerdefinierte Werte festlegen. Um Werte zu aktualisieren, benötigen Sie die folgenden Informationen:

    Erstellen Sie ein Array von DWORDs, und legen Sie den Wert in der MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET-Eigenschaft der Audiostream-Senke fest. Wenn Sie die aktualisierten Werte nicht bereitstellen, werden sie von der Mediensitzung entsprechend festgelegt.

    Weitere Informationen finden Sie unter Das Leaky-Bucket-Puffermodell.

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

Das folgende Codebeispiel erstellt und fügt die erforderlichen Encoder-Aktivierungen hinzu. Es verweist Zeiger auf ein zuvor erstelltes Topologieobjekt, das Aktivierungsobjekt der Dateisenke und den Medientyp des Quellstreams. Außerdem wird AddOutputNode (siehe nächstes Codebeispiel) aufgerufen, mit dem der Senkenknoten der Codierungstopologie erstellt und hinzugefügt wird. 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 Dateisenke

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

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

  • Ein Zeiger auf das Aktivierungsobjekt, das Sie in dem im Abschnitt „Erstellen der ASF-Dateisenke“ dieses Tutorials beschriebenen Schritt erstellt haben
  • Die Stream-Nummern zur Identifizierung der Stream-Senken, die der Dateisenke hinzugefügt wurden Die Stream-Nummern stimmen mit der Stream-ID überein, die bei der Stream-Erstellung festgelegt wurde

Weitere Informationen zum Erstellen von Ausgabeknoten und Codebeispiele finden Sie unter „Erstellen eines Ausgabeknotens aus einem Aktivierungsobjekt“ in Erstellen von Ausgabeknoten.

Wenn Sie kein Aktivierungsobjekt für die Dateisenke verwenden, müssen Sie die Streamsenken in der ASF-Dateisenke aufzählen und jede Streamsenke als Ausgabeknoten in der Topologie festlegen. Informationen zur Aufzählung von Stream-Senken finden Sie unter „Aufzählen von Stream-Senken“ in Hinzufügen von Streaminformationen zur ASF-Dateisenke.

Im folgenden Codebeispiel wird der Senkenknoten erstellt und zur Codierungstopologie hinzugefügt. Er verweist Zeiger auf ein zuvor erstelltes Topologieobjekt, das Aktivierungsobjekt der Dateisenke und die Identifikationsnummer des Streams. 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 Stream-Senken für eine bestimmte Mediensenke aufgelistet.

//-------------------------------------------------------------------
//  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 der Quell-, Transformations- und Senkenknoten

In diesem Schritt verbinden Sie den Quellknoten mit dem Transformationsknoten und verweisen dabei auf die Codierungsaktivierungen, die Sie in dem im Abschnitt „Instanziieren der erforderlichen Encoder und Erstellen der Transformationsknoten“ dieses Tutorials beschriebenen Schritt erstellt haben. Der Transformationsknoten wird mit dem Ausgabeknoten verbunden, der das Aktivierungsobjekt für die Dateisenke enthält.

Behandeln der Codierungssitzung

In diesem 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 unkomprimierte Beispiele umwandelt, um sie als Eingabe an den Encoder weiterzuleiten.

  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 von der Mediensitzung ausgelösten Ereignissen. Der Aufruf IMFMediaSession::SetTopology führt dazu, dass die Mediensitzung das MESessionTopologyStatus-Ereignis mit dem festgelegten MF_TOPOSTATUS_READY-Flag auslöst. Alle Ereignisse werden asynchron generiert und eine Anwendung kann diese Ereignisse synchron oder asynchron abrufen. Da es sich bei der Anwendung in diesem Tutorial um eine Konsolenanwendung handelt und das Blockieren von Benutzeroberflächen-Threads kein Problem darstellt, werden die Ereignisse von Mediensitzungen 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 Wiedergeben von Mediendateien mit Media Foundation.

Warten Sie in der Ereignisschleife zum Abrufen von Mediensitzungsereignissen auf das MESessionTopologyStatus-Ereignis, das ausgelöst wird, wenn IMFMediaSession::SetTopology abgeschlossen ist und die Topologie aufgelöst wird. 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 „Aktualisieren der Codierungseigenschaften in der Dateisenke für die VBR-Codierung“ dieses Tutorials erläutert.

Die Mediensitzung generiert das ASF-Header-Objekt und schließt die Datei ab, wenn die Codierungssitzung abgeschlossen ist, und löst dann das MESessionClosed-Ereignis aus. Dieses Ereignis muss durch ordnungsgemäße Vorgänge zum Herunterfahren für die Mediensitzung behandelt werden. Rufen Sie zum Starten der Vorgänge zum Herunterfahren IMFMediaSession::Shutdown auf. Nachdem die Codierungssitzung geschlossen wurde, können Sie keine andere Topologie für die Codierung auf derselben Mediensitzungsinstanz festlegen. Um eine andere Datei zu codieren, muss die aktuelle Mediensitzung geschlossen und freigegeben werden und die neue Topologie muss für eine neu erstellte Mediensitzung festgelegt werden. Das folgende Codebeispiel erstellt die Mediensitzung, legt die Codierungstopologie fest und verarbeitet die Mediensitzungsereignisse.

Das folgende Codebeispiel erstellt die Mediensitzung, legt die Codierungstopologie fest und steuert die Codierungssitzung durch die Verarbeitung von Ereignissen aus der Mediensitzung.

//-------------------------------------------------------------------
//  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 von Codierungseigenschaften in Dateisenken

Bestimmte Codierungseigenschaften wie die Codierungsbitrate und die genauen Leaky-Bucket-Werte sind erst nach Abschluss der Codierung bekannt, insbesondere bei der VBR-Codierung. Um die richtigen Werte abzurufen, muss die Anwendung auf das MEEndOfPresentation-Ereignis warten, das angibt, dass die Codierungssitzung abgeschlossen ist. Die Leaky-Bucket-Werte müssen in der Senke aktualisiert werden, damit das ASF-Header-Objekt die genauen Werte widerspiegeln kann.

Das folgende Verfahren beschreibt die Schritte, die zum Durchlaufen der Knoten in der Codierungstopologie erforderlich sind, um den Dateisenkenknoten abzurufen und die erforderlichen Leaky-Bucket-Eigenschaften festzulegen.

So aktualisieren Sie die Werte der Postcodierungseigenschaft in der ASF-Dateisenke

  1. Rufen Sie IMFTopology::GetOutputNodeCollection auf, um die Ausgabeknotenauflistung aus der Codierungstopologie abzurufen.
  2. Rufen Sie für jeden Knoten einen Zeiger auf die Streamsenke im Knoten ab, indem Sie IMFTopologyNode::GetObject aufrufen. Abfrage der IMFStreamSink-Schnittstelle im IUnknown-Zeiger, der von IMFTopologyNode::GetObject zurückgegeben wird.
  3. Rufen Sie für jede Stream-Senke den Downstream-Knoten (Encoder) durch Aufruf von IMFTopologyNode::GetInput ab.
  4. Fragen Sie den Knoten ab, um den IMFTransform-Zeiger vom Encoderknoten abzurufen.
  5. Fragen Sie den Encoder für den IPropertyStore-Zeiger ab, um den Codierungseigenschaftenspeicher vom Encoder abzurufen.
  6. Fragen Sie die Stream-Senke für den IPropertyStore-Zeiger ab, um den Eigenschaftenspeicher der Streamsenke abzurufen.
  7. Rufen Sie IPropertyStore::GetValue auf, um die erforderlichen Eigenschaftswerte aus dem Eigenschaftenspeicher des Encoders abzurufen und sie durch Aufrufen von IPropertyStore::SetValue in den Eigenschaftenspeicher der Stream-Senke zu kopieren.

Die folgende Tabelle zeigt die Postcodierungs-Eigenschaftswerte, die in der Stream-Senke für den Videostream festgelegt werden müssen.

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

 

Das folgende Codebeispiel legt die Eigenschaftswerte nach der Codierung fest.

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

Implementieren der Hauptfunktion

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“ überprüft werden, das Sie anzeigen können, indem Sie mit der rechten Maustaste auf die codierte Datei klicken und Eigenschaften aus dem Kontextmenü auswählen.

  • Der Pfad der codierten Datei ist korrekt.
  • Die Größe der Datei beträgt mehr als null KB, und die Wiedergabedauer entspricht der Dauer der Quelldatei.
  • Überprüfen Sie für den Videostream die Framebreite und -höhe sowie die Framerate. Diese Werte sollten mit den Werten übereinstimmen, die Sie im ASF-Profil angegeben haben, das Sie in dem im Abschnitt „Erstellen des ASF-Profilobjekts“ beschriebenen Schritt erstellt haben.
  • Für den Audiostream muss die Bitrate nahe an dem Wert liegen, den Sie für den Zielmedientyp angegeben haben.
  • Öffnen Sie die Datei im Windows Media Player, 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 von dieser Microsoft-Website heruntergeladen werden.

Häufige Fehlercodes und Debugging-Tipps

In der folgenden Liste werden die häufigsten Fehlercodes beschrieben, die Sie möglicherweise erhalten, sowie Tipps zur Problembehandlung.

  • Der Aufruf von IMFSourceResolver::CreateObjectFromURL unterbricht 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 intern asynchrone Vorgänge starten, z. B. intern IMFSourceResolver::CreateObjectFromURL.

  • IMFSourceResolver::CreateObjectFromURL gibt Folgendes zurück: 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 sie bereits von einem anderen Prozess verwendet wird. "

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

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

    Stellen Sie sicher, dass die folgenden Bedingungen erfüllt sind:

    • Der von Ihnen angegebene Eingabetyp oder Ausgabetyp ist mit den vom Encoder unterstützten Medientypen kompatibel.
    • Die von Ihnen angegebenen Medientypen sind abgeschlossen. Zur Vervollständigung der Medientypen sehen Sie sich die erforderlichen Attribute in den Abschnitten „Erstellen eines komprimierten Audio-Medientyps“ und „Erstellen eines komprimierten Video-Medientyps“ dieses Tutorials an.
    • Stellen Sie sicher, dass Sie die Zielbitrate für den partiellen Medientyp festgelegt haben, dem Sie die privaten Codec-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 Interlace-Modus angibt, der vom Windows Media Video-Encoder nicht unterstützt wird. Wenn Ihr komprimierter Videomedientyp auf die Verwendung des progressiven Modus eingestellt ist, muss die Pipeline eine De-Interlacing-Transformation verwenden. Da die Pipeline keine Übereinstimmung finden kann (angezeigt durch diesen Fehlercode), müssen Sie manuell einen De-Interlacer (Transcode-Videoprozessor) zwischen den Decoder- und Encoder-Knoten 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 im Windows Media-Encoder festgelegten Ausgabemedientyps kompatibel sind.

  • IWMCodecPrivateData::GetPrivateData gibt Folgendes zurück: HRESULT 0x80040203 „Es ist ein Syntaxfehler aufgetreten, der versucht hat, eine Abfragezeichenfolge auszuwerten.“

    Stellen Sie sicher, dass der Eingabetyp auf die MFT des Encoders festgelegt ist.

ASF-Komponenten der Pipelineschicht