Tutorial: 1-Pass-Windows-Mediencodierung

Codierung bezieht sich auf den Prozess der Konvertierung digitaler Medien von einem Format in ein anderes. Die Konvertierung von MP3-Audio in Windows Medienaudioformat, wie in der ASF-Spezifikation (Advanced Systems Format) definiert.

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

In diesem Tutorial wird veranschaulicht, wie Sie klare Inhalte (nicht DRM-geschützt) in Windows-Medieninhalte codieren und die Daten mithilfe von ASF-Komponenten der Pipelineebene in eine neue ASF-Datei * (WM) schreiben. 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 einem unkomprimierten Medienformat vorliegen. Die gültigen Codierungsmodi sind "CBR" (konstante Bitrate) oder "VBR" (variable Bitrate). Die Anwendung erstellt eine Medienquelle zur Darstellung der durch den Eingabedateinamen angegebenen Quelle und die ASF-Dateisenke, um den codierten Inhalt der Quelldatei in einer ASF-Datei zu archivieren. Damit das Szenario einfach implementiert werden kann, enthält die Ausgabedatei nur einen Audiostream und einen Videostream. Die Anwendung fügt den Windows Media Audio 9.1-Professional-Codec für die Audiostreamformatkonvertierung und Windows Media Video 9-Codec für den Videostream ein.

Für eine 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 verfügbare Bitrate mit dem ersten Ausgabemedientyp, der während der Aushandlung des Medientyps vom Encoder abgerufen wird, als Zielbitrate. Für die Codierung variabler Bitraten veranschaulicht dieses Tutorial die Codierung mit variabler Bitrate durch Festlegen einer Qualitätsstufe. Die Audiostreams werden auf der Qualitätsstufe 98 und Videostreams mit der Qualitätsstufe 95 codiert.

Im folgenden Verfahren werden die Schritte zum Codieren Windows Medieninhalts in einem ASF-Container mithilfe eines 1-Pass-Codierungsmodus zusammengefasst.

  1. Erstellen Sie mithilfe des Quellre resolvers eine Medienquelle für die angegebene .
  2. Enumerieren Sie die Streams in der Medienquelle.
  3. Erstellen Sie die ASF-Mediensenke, und fügen Sie Abhängig von den Datenströmen in der Medienquelle, die codiert werden müssen, Streamsenken hinzu.
  4. Konfigurieren Sie die Mediensenke mit den erforderlichen Codierungseigenschaften.
  5. Erstellen Sie Windows Media Encoder für die Streams in der Ausgabedatei.
  6. Konfigurieren Sie die Encoder mit den Eigenschaften, die für die Mediensenke festgelegt sind.
  7. Erstellen sie eine Teilcodierungstopologie.
  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. Für die VBR-Codierung erhalten Sie die Codierungseigenschaftswerte vom Encoder, und legen Sie sie auf der Mediensenke fest.
  11. Schließen sie die Codierungssitzung, und fahren Sie sie herunter.

Dieses Tutorial enthält die folgenden Abschnitte:

Voraussetzungen

In diesem Tutorial wird Folgendes vorausgesetzt:

Einrichten der Project

  1. Schließen Sie die folgenden Header 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 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 stellt integrierte Medienquellen für verschiedene Medienformate zur Verfügung: 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 Quellre resolver. Dieses Objekt analysiert die URL, die für die Quelldatei angegeben wurde, und erstellt die entsprechende Medienquelle.

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 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 Streamsenken 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. Codierungstyp, Anzahl der Codierungsüberläufe und die zugehörigen Eigenschaften.
  • Die globalen Dateieigenschaften, die der Mediensenke angeben, ob die Bucketparameter für die Lecks (Bitrate und Puffergröße) automatisch angepasst werden sollen.

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

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 den Typ der Streams kennen, für die Streamsenken erstellt werden. 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 der Prozess zum Erstellen des ASF-Profils und der Zieldatenströme beschrieben.

So erstellen Sie das ASF-Profil

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

  2. Rufen Sie ÜBERMEDIASOURCE::CreatePresentationDescriptor auf, um den Präsentationsdeskriptor für die Medienquelle zu erstellen, die in dem Schritt erstellt wurde, der im Abschnitt "Erstellen der Medienquelle" dieses Tutorials beschrieben wurde.

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

  4. Rufen Sie FÜR jeden Stream in der Medienquelle DIE Enskriptordatei des Streams auf, und rufen Sie DANN DEN STREAM-Deskriptor des Streams ab.

  5. Rufen Sie DANN AUFSTREAMSTREAMDescriptor::GetMediaTypeHandler gefolgt von DERMEDIATypeHandler::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 IMEDIAType::GetMajorType auf, um den Haupttyp des Streams abzurufen, um zu bestimmen, ob der Stream Audio- oder Videodaten 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 Zahl zuzuweisen. Diese Einstellung ist obligatorisch.
      • Konfigurieren Sie optional die Parameter für fehlerhafte Buckets, die Nutzlasterweiterung, den gegenseitigen Ausschluss für jeden Stream, 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 Tutorial.
    4. Rufen Sie IMFASFProfile::SetStream auf, um den Stream dem Profil hinzuzufügen.

    Im folgenden Codebeispiel wird ein Ausgabeaudiostream 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 Ausgabestreams abhängig von den Streams 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 Audiostream 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-Audioencoder kompatibel ist, instanziieren Sie den Encoder-MFT, legen Sie die Codierungseigenschaften fest, und rufen Sie einen Medientyp ab, indem Sie ÜBERTRANSFORM::GetOutputAvailableTypeaufrufen. Sie können den erforderlichen Ausgabetyp abrufen, indem Sie alle verfügbaren Typen durchlaufen, die Attribute der einzelnen Medientypen abrufen und den Typ auswählen, der Ihren Anforderungen am nächsten liegt. In diesem Tutorial erhalten Sie den ersten verfügbaren Typ aus der Liste der Ausgabemedientypen, die vom Encoder unterstützt werden.

Hinweis Für Windows 7 stellt Media Foundation die neue Funktion MFTranscodeGetAudioOutputAvailableTypes bereit, 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:

Im folgenden Codebeispiel wird ein komprimierter Audiotyp erstellt, indem ein kompatibler Typ vom Windows Media Audio Encoder erhalten 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 einschließen möchten, erstellen Sie einen vollständig codierten Videotyp. Der vollständige Medientyp muss die gewünschte Bitrate und die privaten Codecdaten enthalten.

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

  • Erstellen Sie einen leeren Medientyp, 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-HAUPTTYP _
    • MF _ _ MT-UNTERTYP
    • MF _ MT _ FRAME _ RATE
    • MF _ MT _ FRAME _ SIZE
    • MF _ MT _ INTERLACE-MODUS _
    • MF _ MT _ PIXEL _ ASPECT _ RATIO
    • MF _ MT _ AVG _ BITRATE
    • MF _ _ MT-BENUTZERDATEN _

    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 vom Windows Media-Videoencoder ab, indem Sie die Codierungseigenschaften festlegen und dann DIE CODIERUNGTRANSFORM::GetOutputAvailableTypeaufrufen. 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 Codecdaten zurückgegeben werden, stellen Sie sicher, dass Sie die Bitrate festlegen, bevor Sie die privaten Codecdaten abrufen.

    Im folgenden Codebeispiel wird ein komprimierter Videotyp erstellt, indem ein kompatibler Typ vom Windows Media Video Encoder erhalten 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 Codecdaten 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 Codecdaten 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-Komponente, die hauptsächlich zum Speichern von ASF-Headerobjektinformationen entwickelt wurde. Die ASF-Dateisenke implementiert das ContentInfo-Objekt, um Informationen (in einem Eigenschaftenspeicher) zu speichern, die zum Schreiben des ASF-Headerobjekts der codierten Datei verwendet werden. Zu diesem Zweck 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 Streamnummer übergeben, die Sie beim Erstellen des ASF-Profils für den Stream zugewiesen haben.

  3. Legen Sie die Codierungseigenschaften auf Streamebene im Streameigenschaftenspeicher der Dateisenke fest. Weitere Informationen finden Sie unter "Stream Encoding Properties" (Streamcodierungseigenschaften) unter Setting Properties in the File Sink (Festlegen von Eigenschaften in der Dateisenke).

    Im folgenden Codebeispiel werden die Eigenschaften auf Streamebene im Eigenschaftenspeicher der Dateisenke 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 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 der globalen Dateisenke" unter Festlegen von Eigenschaften in der Dateisenke.

  5. Legen Sie MFPKEY _ ASFMEDIASINK _ AUTOADJUST _ BITRATE auf VARIANT _ TRUE fest, um sicherzustellen, dass die leaky-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 auf Streamebene im Eigenschaftenspeicher der Dateisenke definiert.

        //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" unter 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, die ein Aktivierungsobjekt verwendet, können Sie das konfigurierte ContentInfo-Objekt verwenden, um die ASF-Mediensenke zu instanziieren (im nächsten Abschnitt beschrieben). Wenn Sie eine prozessintern ausgeführte Dateisenke erstellen (MFCreateASFMediaSink), anstatt das leere ContentInfo-Objekt wie in Schritt 1 beschrieben zu erstellen, rufen Sie einen Verweis auf die IMFASFContentInfo-Schnittstelle ab, indem Sie INFMediaSink::QueryInterface in der Dateisenke aufrufen. 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 Funktion MFCreateASFMediaSinkActivate 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 Topologie für partielle Codierung

Als Nächstes erstellen Sie eine Teilcodierungstopologie, 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. Vor dem Hinzufügen von Topologieknoten 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.

Um diesen Knoten zu erstellen, benötigen Sie die folgenden Verweise:

  • Ein Zeiger auf die Medienquelle, die Sie im Schritt erstellt haben, der im Abschnitt "Erstellen der Medienquelle" dieses Tutorials beschrieben wird.
  • Ein Zeiger auf den Präsentationsdeskriptor für die Medienquelle. Sie können einen Verweis auf die BENUTZEROBERFLÄCHEPresentationDescriptor-Schnittstelle der Medienquelle erhalten, indem Sie DENKMediaSource::CreatePresentationDescriptor aufrufen.
  • Ein Zeiger auf den Streamdeskriptor für jeden Stream in der Medienquelle, für den Sie im Schritt im Abschnitt "Erstellen des ASF-Profilobjekts" dieses Tutorials einen Zielstream erstellt haben.

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

Im folgenden Codebeispiel wird eine Teiltopologie 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 der Codierungstopologie hinzugefügt. Sie benötigt Zeiger auf ein zuvor topologieorientiertes Objekt, 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 nicht automatisch die erforderlichen Windows Media Encoder für die Streams ein, die sie codieren muss. Die Anwendung muss die Encoder manuell hinzufügen. Aufzählen Sie hierzu die Streams im ASF-Profil, die Sie in dem Schritt erstellt haben, der im Abschnitt "Erstellen des ASF-Profilobjekts" dieses Tutorials beschrieben wird. 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 im Schritt "Erstellen der ASF-Dateisenke" dieses Tutorials erstellt haben.

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

Im folgenden Verfahren werden die schritte beschrieben, die zum Instanziieren der erforderlichen Encoder erforderlich sind.

  1. Rufen Sie einen Verweis auf das ContentInfo-Objekt der Senke ab, indem Sie FÜRSTActivate::ActivateObject für die Dateisenke activate aufrufen und dann durch Aufrufen von QueryInterface nach IMFASFContentInfo aus der Dateisenke abfragen.

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

  3. Enumerieren Sie die Streams im Profil. Dazu benötigen Sie die Streamanzahl und einen Verweis auf die IMFASFStreamConfig-Schnittstelle jedes Streams.

    Rufen Sie die folgenden Methoden auf:

  4. Für jeden Stream erhalten Sie den Haupttyp und die Codierungseigenschaften des Streams aus dem ContentInfo-Objekt.

    Rufen Sie die folgenden Methoden auf:

  5. Instanziieren Sie je nach Typ des Streams, Audio oder Videos das Aktivierungsobjekt für den Encoder, indem Sie MFCreateWMAEncoderActivate oder MFCreateWMVEncoderActivate aufrufen.

    Zum Aufrufen dieser Funktionen benötigen Sie die folgenden Verweise:

  6. Aktualisieren Sie den Bucketparameter leaky für den Audiostream.

    MFCreateWMAEncoderActivate legt den Ausgabetyp für den zugrunde liegenden Encoder MFT für den Windows Medienaudiocodec fest. Nachdem der Ausgabemedientyp festgelegt wurde, ruft der Encoder die durchschnittliche Bitrate vom Ausgabemedientyp ab, berechnet die Pufferfenster-Rage-Bitrate und legt die leaky-Bucketwerte 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-Eigenschaft _ _ CORRECTED LEAKYBUCKET der Audiostreamsenke fest. Wenn Sie die aktualisierten Werte nicht bereitstellen, legt die Mediensitzung diese entsprechend fest.

    Weitere Informationen finden Sie unter The Leaky Bucket Buffer Model.

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 Creating Transform Nodes ( Erstellen von Transformationsknoten).

Im folgenden Codebeispiel wird der erforderliche Encoder erstellt und aktiviert. Sie verwendet Zeiger auf ein zuvor erstelltes Topologieobjekt, das Aktivierungsobjekt der Dateisenke und den Medientyp des Quellstreams. Sie ruft auch AddOutputNode auf (siehe nächstes Codebeispiel), das den Senkenknoten erstellt und der 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 Dateisenke

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

Um diesen Knoten zu erstellen, benötigen Sie die folgenden Verweise:

  • Ein Zeiger auf das Aktivierungsobjekt, das Sie im Schritt erstellt haben, der im Abschnitt "Erstellen der ASF-Dateisenke" dieses Tutorials beschrieben wird.
  • Die Datenstromnummern zum Identifizieren der Der Dateisenke hinzugefügten Streamsenken. Die Datenstromnummern sind mit der Identifizierung des Streams übereinstimmen, die während der Erstellung des Streams festgelegt wurde.

Weitere Informationen zum Erstellen von Ausgabeknoten und zum Codebeispiel 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 Enumeration der Streamsenke finden Sie unter "Aufzählen von Streamsenken" in Hinzufügen von Streaminformationen zur ASF-Dateisenke.

Im folgenden Codebeispiel wird der Senkenknoten erstellt und der Codierungstopologie hinzugefügt. Es werden Zeiger auf ein zuvor erstelltes Topologieobjekt, das Aktivierungsobjekt der Dateisenke und die ID des Streams verwendet. 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 Streamsenken für eine bestimmte Mediensenke aufzä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 Quell-, Transformations- und Senkenknoten

In diesem Schritt verbinden Sie den Quellknoten mit dem Transformationsknoten, der auf die Codierungsaktivierung verweisen, die Sie im Schritt erstellt haben, der im Abschnitt "Instanziieren der erforderlichen Encoder und Erstellen der Transformationsknoten" dieses Tutorials beschrieben ist. 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 DIE CODIERUNGMediaSession::SetTopology auf, um die Codierungstopologie für die Sitzung festzugeben. 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 konvertiert, um sie als Eingabe an den Encoder zu senden.

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

    In der Ereignisschleife starten und schließen Sie die Codierungssitzung abhängig von den Ereignissen, die von der Mediensitzung ausgelöst werden. Der AUFRUF VON DURCHGEFMediaSession::SetTopology führt dazu, dass media Session das MESessionTopologyStatus-Ereignis mit dem MF _ TOPOSTATUS _ READY-Flag ausgelöst wird. Alle Ereignisse werden asynchron generiert, und eine Anwendung kann diese Ereignisse synchron oder asynchron abrufen. Da die Anwendung in diesem Tutorial eine Konsolenanwendung ist und das Blockieren von Benutzeroberflächenthreads kein Problem ist, erhalten wir die Ereignisse synchron aus der Mediensitzung.

    Um die Ereignisse asynchron zu erhalten, muss Ihre Anwendung die ASYNCHRONOUSAsyncCallback-Schnittstelle implementieren. Weitere Informationen und eine Beispielimplementierung dieser Schnittstelle finden Sie unter "Behandeln von Sitzungsereignissen" in How to Play Media Files with Media Foundation.

Warten Sie in der Ereignisschleife zum Abrufen von Mediensitzungsereignissen auf das MESessionTopologyStatus-Ereignis, das ausgelöst wird, wenn DIE 1.000-schleife abgeschlossen ist und die Topologie aufgelöst wird. Starten Sie nach dem Abrufen des MESessionTopologyStatus-Ereignisses die Codierungssitzung, indem Sie DENKMediaSession::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 von Codierungseigenschaften für die Dateisenke für VBR-Codierung" dieses Tutorials erläutert.

Die Mediensitzung generiert das ASF-Headerobjekt 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 Herunterfahren in der Mediensitzung behandelt werden. Rufen Sie zum Starten der Vorgänge zum Herunterfahren DIEMEDIASESSION::Shutdown auf. Nachdem die Codierungssitzung geschlossen wurde, können Sie keine weitere Topologie für die Codierung auf derselben Media Session-Instanz 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 behandelt die Media Session-Ereignisse.

Das folgende Codebeispiel erstellt die Mediensitzung, legt die Codierungstopologie fest und steuert die Codierungssitzung durch Behandeln 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 der Dateisenke

Bestimmte Codierungseigenschaften, z. B. die Codierungsbitrate und die genauen Leaky Bucket-Werte, sind erst bekannt, wenn die Codierung abgeschlossen ist, insbesondere bei der VBR-Codierung. Um die richtigen Werte zu erhalten, muss die Anwendung auf das MEEndOfPresentation-Ereignis warten, das angibt, dass die Codierungssitzung abgeschlossen ist. Die Bucketwerte für die Lecks müssen in der Senke aktualisiert werden, damit das ASF-Headerobjekt 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 Dateisenkenknoten zu erhalten und die erforderlichen Eigenschaften des Buckets mit Datenlecks festlegen zu können.

So aktualisieren Sie die Eigenschaftswerte nach der Codierung in der ASF-Dateisenke

  1. Rufen Sie DIE TOPOLOGIE::GetOutputNodeCollection auf, um die Ausgabeknotensammlung aus der Codierungstopologie zu erhalten.
  2. Rufen Sie für jeden Knoten einen Zeiger auf die Streamsenke im Knoten ab, indem Sie DIE TOPOLOGIENode::GetObject aufrufen. Fragen Sie die BENUTZEROBERFLÄCHEStreamSink-Schnittstelle für den IUnknown-Zeiger ab, der von DER TOPOLOGIENode::GetObject zurückgegeben wird.
  3. Rufen Sie für jede Streamsenke den Downstreamknoten (Encoder) durch Aufrufen vonTOPOLOGYTopologyNode::GetInput ab.
  4. Fragen Sie den Knoten ab, um den ZEIGER FÜR DIE Übertragung vom Encoderknoten zu erhalten.
  5. Fragen Sie den Encoder nach dem IPropertyStore-Zeiger ab, um den Codierungseigenschaftenspeicher vom Encoder zu erhalten.
  6. Fragen Sie die Streamsenke nach dem IPropertyStore-Zeiger ab, um den Eigenschaftenspeicher der Streamsenke zu erhalten.
  7. Rufen Sie IPropertyStore::GetValue auf, um die erforderlichen Eigenschaftswerte aus dem Eigenschaftenspeicher des Encoders zu erhalten, und kopieren Sie sie in den Eigenschaftenspeicher der Streamsenke, indem Sie IPropertyStore::SetValue aufrufen.

Die folgende Tabelle zeigt die Eigenschaftswerte nach der Codierung, die für die Streamsenke für den Videostream festgelegt werden müssen.

Codierungstyp Eigenschaftenname (GetValue) Eigenschaftenname (SetValue)
Codierung der konstanten Bitrate MFPKEY _ BAVG
MFPKEY _ RAVG
MFPKEY _ STAT _ BAVG
MFPKEY _ STAT _ RAVG
Qualitätsbasierte Codierung mit variabler Bitrate 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 Eigenschaftswerte nach der Codierung definiert.

//-------------------------------------------------------------------
//  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 von Main

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 im Kontextmenü Eigenschaften auswählen.

  • Der Pfad der codierten Datei ist genau.
  • Die Größe der Datei ist größer als 0 KB, und die Wiedergabedauer entspricht der Dauer der Quelldatei.
  • Überprüfen Sie für den Videostream die Framebreite und -höhe sowie die Bildfrequenz. Diese Werte sollten mit den Werten übereinstimmen, die Sie im ASF-Profil angegeben haben, das Sie im Schritt "Erstellen des ASF-Profilobjekts" erstellt haben.
  • Für den Audiostream muss die Bitrate in der Nähe des Werts sein, den Sie für den Zielmedientyp angegeben haben.
  • Öffnen Sie die Datei in 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 zu sehen. Dieses Tool kann von dieser Microsoft-Website heruntergeladen werden.

Allgemeine Fehlercodes und debuggende Tipps

In der folgenden Liste werden die allgemeinen Fehlercodes beschrieben, die Möglicherweise empfangen werden, und die Tipps zum Debuggen.

  • Durch den Aufruf von VERALSOURCEResolver::CreateObjectFromURL wird die Anwendung nicht mehr unterstützt.

    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. INTERN DURCHSOURCEResolver::CreateObjectFromURL.

  • FALSESourceResolver::CreateObjectFromURL gibt HRESULT zurück 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 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 METHODEN vom Typ "VERFORMTransform" 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 vollständig. Informationen zum Abschließen von Medientypen finden Sie in den Abschnitten "Erstellen eines komprimierten Audiomedientyps" und "Erstellen eines komprimierten Videomedientyps" dieses Tutorials.
    • Stellen Sie sicher, dass Sie die Zielbitrate für den partiellen Medientyp festgelegt haben, dem Sie die privaten Codecdaten hinzufügen.
  • Die Mediensitzung gibt MF _ E _ UNSUPPORTED _ D3D _ TYPE im Ereignisstatus zurück.

    Dieser Fehler wird zurückgegeben, wenn der Medientyp der Quelle auf einen gemischten Interlace-Modus hinweist, der nicht von Windows Media Video Encoder unterstützt wird. Wenn ihr komprimierter Videomedientyp für den progressiven Modus festgelegt ist, muss die Pipeline eine De-Interlacing-Transformation verwenden. Da die Pipeline (durch diesen Fehlercode angegeben) keine Übereinstimmung finden kann, müssen Sie manuell einen De-Interlacer (Transcodierungsvideoprozessor) zwischen dem Decoder und den Encoderknoten 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, die auf dem medienencoder festgelegten Windows sind.

  • IWMCodecPrivateData::GetPrivateData gibt HRESULT zurück 0x80040203 "Beim Auswerten einer Abfragezeichenfolge ist ein Syntaxfehler aufgetreten".

    Stellen Sie sicher, dass der Eingabetyp für den Encoder MFT festgelegt ist.

ASF-Komponenten auf Pipelineebene