Udostępnij przez


Samouczek: kodowanie jednofazowe Windows Media

Kodowanie odnosi się do procesu konwertowania multimediów cyfrowych z jednego formatu na inny. Na przykład konwertowanie dźwięku MP3 na format Windows Media Audio zgodnie ze specyfikacją Advanced Systems Format (ASF).

Uwaga Specyfikacja ASF definiuje typ kontenera dla pliku wyjściowego (.wma lub .wmv), który może zawierać dane multimedialne w dowolnym formacie, skompresowanym lub nieskompresowanym. Na przykład kontener ASF, taki jak plik .wma, może zawierać dane multimedialne w formacie MP3. Proces kodowania konwertuje rzeczywisty format danych zawartych w pliku.

W tym samouczku pokazano, jak zakodować czyste źródło danych wejściowych (bez ochrony DRM) do zawartości Windows Media i zapisać dane w nowym pliku ASF (.wm*) przy użyciu komponentów warstwy Pipeline ASF. Te składniki będą używane do tworzenia topologii częściowego kodowania, która będzie kontrolowana przez wystąpienie sesji multimediów.

W tym samouczku utworzysz aplikację konsolową, która pobiera nazwy plików wejściowych i wyjściowych oraz tryb kodowania jako argumenty. Plik wejściowy może być w formacie skompresowanym lub nieskompresowanym. Prawidłowe tryby kodowania to "CBR" (stała szybkość bitów) lub "VBR" (zmienna szybkość bitów). Aplikacja tworzy źródło multimediów reprezentujące źródło określone przez nazwę pliku wejściowego i ujście pliku ASF w celu zapisania zakodowanej zawartości pliku źródłowego do formatu ASF. Aby scenariusz był prosty do zaimplementowania, plik wyjściowy będzie miał tylko jeden strumień audio i jeden strumień wideo. Aplikacja wstawia kodek Windows Media Audio 9.1 Professional do konwersji formatu strumienia audio i kodek Windows Media Video 9 dla strumienia wideo.

W przypadku kodowania stałych szybkości transmisji bitów przed rozpoczęciem sesji kodowania koder musi znać docelową szybkość transmisji bitów, którą musi osiągnąć. W tym samouczku w trybie "CBR" aplikacja używa szybkości transmisji bitów dostępnej z pierwszym typem nośnika wyjściowego pobieranego z kodera podczas negocjacji typu nośnika jako docelowej szybkości transmisji bitów. Dla kodowania ze zmienną szybkością bitów ten samouczek pokazuje, jak kodować przez ustawienie poziomu jakości. Strumienie audio są kodowane na poziomie jakości 98 i strumieni wideo na poziomie jakości 95.

Poniższa procedura zawiera podsumowanie kroków kodowania zawartości systemu Windows Media w kontenerze ASF przy użyciu trybu kodowania 1-pass.

  1. Utwórz źródło multimediów dla określonego elementu, używając narzędzia rozwiązywania źródeł.
  2. Wylicz strumienie w źródle multimediów.
  3. Utwórz odbiornik multimedialny ASF i dodaj odbiorniki strumieniowe w zależności od strumieni w źródle multimediów, które muszą zostać zakodowane.
  4. Skonfiguruj ujście multimediów z wymaganymi właściwościami kodowania.
  5. Utwórz kodery Windows Media dla strumieni w pliku wyjściowym.
  6. Skonfiguruj kodery z właściwościami ustawionymi na odbiorniku multimediów.
  7. Utwórz topologię kodowania częściowego.
  8. Utwórz wystąpienie sesji multimediów i ustaw topologię w sesji multimediów.
  9. Rozpocznij sesję kodowania, kontrolując sesję multimediów i uzyskując wszystkie odpowiednie zdarzenia z sesji multimediów.
  10. W przypadku kodowania VBR pobierz wartości właściwości kodowania z kodera i ustaw je na ujściu multimediów.
  11. Zamknij i zamknij sesję kodowania.

Ten samouczek zawiera następujące sekcje:

Warunki wstępne

W tym samouczku przyjęto założenie, że:

Konfigurowanie projektu

  1. Dołącz następujące nagłówki do pliku źródłowego:

    #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. Dołącz do następujących plików biblioteki:

    // 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. Zadeklaruj funkcję SafeRelease.

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. Zadeklaruj enumerację ENCODING_MODE w celu zdefiniowania typów kodowania CBR i VBR.

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. Zdefiniuj stałą dla okna buforu dla strumienia wideo.

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

Tworzenie źródła multimediów

Utwórz źródło multimediów dla źródła danych wejściowych. Program Media Foundation udostępnia wbudowane źródła multimediów dla różnych formatów multimediów: MP3, MP4/3GP, AVI/WAVE. Aby uzyskać informacje o formatach obsługiwanych przez program Media Foundation, zobacz Supported Media Formats in Media Foundation.

Aby utworzyć źródło multimediów, użyj narzędzia Source Resolver. Ten obiekt analizuje adres URL określony dla pliku źródłowego i tworzy odpowiednie źródło multimediów.

Wykonaj następujące wywołania:

Poniższy przykład kodu przedstawia funkcję CreateMediaSource, która tworzy źródło multimediów dla określonego pliku.

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

Tworzenie ujścia pliku ASF

Utwórz obiekt ujścia pliku ASF, który będzie archiwizować zakodowane dane multimedialne do pliku ASF na końcu sesji kodowania.

W tym samouczku utworzysz element aktywacyjny dla odbiornika pliku ASF. Ujście pliku wymaga następujących informacji, aby utworzyć wymagane ujścia strumienia.

  • Strumienie, które mają być zakodowane i zapisane w pliku końcowym.
  • Właściwości kodowania używane do kodowania zawartości multimedialnej, takie jak typ kodowania, liczba przebiegów kodowania i powiązane właściwości.
  • Globalne właściwości pliku, które wskazują odbiornikowi multimedialnemu, czy powinien automatycznie dostosować parametry modelu leaky bucket (szybkość bitów i rozmiar bufora).

Informacje o strumieniu są konfigurowane w obiekcie profilu ASF, a kodowanie i właściwości globalne są ustawiane w magazynie właściwości zarządzanym przez obiekt ASF ContentInfo.

Aby zapoznać się z omówieniem ujścia pliku ASF, zobacz ASF Media Sinks.

Tworzenie obiektu profilu ASF

Aby urządzenie ujścia pliku ASF mogło zapisywać zakodowane dane multimedialne do pliku ASF, musi znać liczbę strumieni oraz typy strumieni, dla których będą tworzone ujścia strumieni. W tym samouczku wyodrębnisz te informacje ze źródła multimediów i utworzysz odpowiednie strumienie wyjściowe. Ogranicz strumienie wyjściowe do jednego strumienia audio i jednego strumienia wideo. Dla każdego wybranego strumienia w źródle utwórz strumień docelowy i dodaj go do profilu.

Aby zaimplementować ten krok, potrzebne są następujące obiekty.

  • Profil ASF
  • Deskryptor prezentacji dla źródła multimediów
  • Deskryptory strumieni dla wybranych strumieni w źródle multimediów.
  • Typy multimediów dla wybranych strumieni.

W poniższych krokach opisano proces tworzenia profilu ASF oraz strumieni docelowych.

Aby utworzyć profil ASF

  1. Wywołaj MFCreateASFProfile, aby utworzyć pusty obiekt profilu.

  2. Wywołaj IMFMediaSource::CreatePresentationDescriptor, aby utworzyć opis prezentacji dla źródła multimediów utworzonego w kroku opisanym w sekcji "Tworzenie źródła multimediów" w tym samouczku.

  3. Wywołaj IMFPresentationDescriptor::GetStreamDescriptorCount, aby uzyskać liczbę strumieni w źródle multimediów.

  4. Wywołaj IMFPresentationDescriptor::GetStreamDescriptorByIndex dla każdego strumienia w źródle multimediów, pobierz deskryptor strumienia.

  5. Wywołaj IMFStreamDescriptor::GetMediaTypeHandler, a następnie IMFMediaTypeHandler::GetMediaTypeByIndex, aby pobrać pierwszy typ nośnika dla strumienia.

    Uwaga Aby uniknąć złożonych wywołań, załóżmy, że istnieje tylko jeden typ nośnika na strumień i wybierz pierwszy typ nośnika w strumieniu. W przypadku złożonych strumieni należy wyliczyć każdy typ nośnika z programu obsługi typu nośnika i wybrać typ nośnika, który chcesz zakodować.

  6. Wywołaj metodę IIMFMediaType::GetMajorType, aby uzyskać główny typ strumienia, aby określić, czy strumień zawiera dźwięk, czy wideo.

  7. W zależności od głównego typu strumienia utwórz docelowe typy multimediów. Te typy multimediów będą zawierać informacje o formacie, które będą używane przez koder podczas sesji kodowania. W poniższych sekcjach tego samouczka opisano proces tworzenia typów nośników docelowych.

    • Tworzenie skompresowanego typu nośnika audio
    • Tworzenie skompresowanego typu nośnika wideo
  8. Utwórz strumień na podstawie typu nośnika docelowego, skonfiguruj strumień zgodnie z wymaganiami aplikacji i dodaj strumień do profilu. Aby uzyskać więcej informacji, zobacz Dodawanie informacji o strumieniu do ujścia pliku ASF.

    1. Wywołaj IMFASFProfile::CreateStream i przekaż docelowy typ nośnika, aby utworzyć strumień wyjściowy. Metoda pobiera interfejs IMFASFStreamConfig obiektu strumienia.
    2. Skonfiguruj strumień.
      • Wywołaj IMFASFStreamConfig::SetStreamNumber, aby przypisać numer do strumienia. To ustawienie jest obowiązkowe.
      • Opcjonalnie skonfiguruj parametry wiadra przeciekowego, rozszerzenie pakietu oraz wzajemne wykluczenie dla każdego strumienia, wywołując metody IMFASFStreamConfig i odpowiednie atrybuty konfiguracji strumienia.
    3. Dodaj właściwości kodowania na poziomie strumienia przy użyciu obiektu ASF ContentInfo. Aby uzyskać więcej informacji na temat tego kroku, zobacz sekcję "Tworzenie obiektu ASF ContentInfo" w tym samouczku.
    4. Wywołaj IMFASFProfile::SetStream, aby dodać strumień do profilu.

    Poniższy przykład kodu tworzy wyjściowy strumień audio.

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

    Poniższy przykład kodu tworzy wyjściowy strumień wideo.

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

Poniższy przykład kodu tworzy strumienie wyjściowe w zależności od strumieni w źródle.

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

Tworzenie skompresowanego typu nośnika audio

Jeśli chcesz dołączyć strumień audio do pliku wyjściowego, utwórz typ dźwięku, określając cechy zakodowanego typu, ustawiając wymagane atrybuty. Aby upewnić się, że typ dźwięku jest zgodny z koderem audio Windows Media, utwórz wystąpienie kodera MFT, ustaw właściwości kodowania i pobierz typ multimediów przez wywołanie IMFTransform::GetOutputAvailableType. Pobierz wymagany typ danych wyjściowych, przechodząc w pętli przez wszystkie dostępne typy, uzyskując atrybuty każdego typu nośnika i wybierając typ, który jest najbliższy twoim wymaganiom. W tym samouczku uzyskaj pierwszy dostępny typ z listy typów nośników wyjściowych obsługiwanych przez koder.

Uwaga dla systemu Windows 7, Media Foundation udostępnia nową funkcję, MFTranscodeGetAudioOutputAvailableTypes, która pobiera listę zgodnych typów audio. Ta funkcja pobiera tylko typy multimediów dla kodowania CBR.

Pełny typ dźwięku musi mieć następujące atrybuty:

Poniższy przykład kodu tworzy skompresowany typ dźwięku przez pobranie zgodnego typu z kodera Windows Media Audio. Implementacja dla SetEncodingProperties jest przedstawiona w sekcji "Tworzenie obiektu ASF ContentInfo" tego samouczka.

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

Tworzenie skompresowanego typu nośnika wideo

Jeśli chcesz dołączyć strumień wideo do pliku wyjściowego, utwórz całkowicie zakodowany typ wideo. Kompletny typ nośnika musi zawierać żądaną przepływność bitową i dane prywatne kodeka.

Istnieją dwa sposoby tworzenia kompletnego typu multimediów wideo.

  • Utwórz pusty typ nośnika i skopiuj atrybuty z typu wideo źródłowego, a następnie zastąp atrybut MF_MT_SUBTYPE stałą GUID MFVideoFormat_WMV3.

    Pełny typ wideo musi mieć następujące atrybuty:

    • 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

    Poniższy przykład kodu tworzy skompresowany typ wideo na podstawie typu wideo źródłowego.

    //-------------------------------------------------------------------
    //  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;
    }
    
    
  • Pobierz zgodny typ nośnika z kodera wideo Windows Media, ustawiając właściwości kodowania, a następnie wywołując IMFTransform::GetOutputAvailableType. Ta metoda zwraca typ częściowy. Upewnij się, że typ częściowy został przekonwertowany na kompletny, dodając następujące informacje:

    Ponieważ IWMCodecPrivateData::GetPrivateData sprawdza przepływność bitową przed zwróceniem danych prywatnych kodeku, upewnij się, że ustawiono przepływność bitową przed pobraniem danych prywatnych kodeku.

    Poniższy przykład kodu tworzy skompresowany typ wideo, uzyskując zgodny typ z kodera Windows Media Video. Tworzy również nieskompresowany typ wideo i ustawia go jako dane wejściowe kodera. Jest to zaimplementowane w funkcji pomocnika CreateUncompressedVideoType. GetOutputTypeFromWMVEncoder konwertuje zwracany częściowy typ na pełny typ, dodając dane prywatne kodeka. Implementacja właściwości SetEncodingProperties jest pokazana w sekcji "Tworzenie obiektu ASF ContentInfo" tego samouczka.

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

    Poniższy przykład kodu tworzy nieskompresowany typ wideo.

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

    Poniższy przykład kodu dodaje dane prywatne kodera do określonego typu nośnika wideo.

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

Tworzenie obiektu ASF ContentInfo

Obiekt ASF ContentInfo jest komponentem poziomu WMContainer, zaprojektowanym głównie do przechowywania informacji o obiekcie nagłówka ASF. Ujście pliku ASF implementuje obiekt ContentInfo w celu przechowywania informacji (w magazynie właściwości), które będą używane do zapisu obiektu nagłówka ASF zakodowanego pliku. W tym celu należy utworzyć i skonfigurować następujący zestaw informacji na obiekcie ContentInfo przed rozpoczęciem sesji kodowania.

  1. Wywołaj MFCreateASFContentInfo, aby utworzyć pusty obiekt ContentInfo.

    Poniższy przykład kodu tworzy pusty obiekt ContentInfo.

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. Wywołaj IMFASFContentInfo::GetEncodingConfigurationPropertyStore, aby pobrać magazyn właściwości na poziomie kanału wyjściowego pliku. W tym wywołaniu należy przekazać numer, który przypisałeś do strumienia podczas tworzenia profilu ASF.

  3. Ustaw właściwości kodowania na poziomie strumienia w magazynie właściwości strumienia ujścia pliku. Aby uzyskać więcej informacji, zobacz "Właściwości kodowania strumienia" w Właściwości ustawienia w ujściu pliku.

    Poniższy przykład kodu ustawia właściwości poziomu strumienia w magazynie właściwości ujścia pliku.

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

    Poniższy przykład kodu przedstawia implementację właściwości SetEncodingProperties. Ta funkcja ustawia właściwości kodowania na poziomie strumienia dla CBR i VBR.

    //-------------------------------------------------------------------
    //  SetEncodingProperties
    //  Create a media source from a URL.
    //
    //  guidMT:  Major type of the stream, audio or video
    //  pProps:  A pointer to the property store in which 
    //           to set the required encoding properties.
    //-------------------------------------------------------------------
    
    HRESULT SetEncodingProperties (const GUID guidMT, IPropertyStore* pProps)
    {
        if (!pProps)
        {
            return E_INVALIDARG;
        }
    
        if (EncodingMode == NONE)
        {
            return MF_E_NOT_INITIALIZED;
        }
    
        HRESULT hr = S_OK;
    
        PROPVARIANT var;
    
        switch (EncodingMode)
        {
            case CBR:
                // Set VBR to false.
                hr = InitPropVariantFromBoolean(FALSE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the video buffer window.
                if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromInt32(VIDEO_WINDOW_MSEC, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VIDEOWINDOW, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            case VBR:
                //Set VBR to true.
                hr = InitPropVariantFromBoolean(TRUE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Number of encoding passes is 1.
    
                hr = InitPropVariantFromInt32(1, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_PASSESUSED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the quality level.
    
                if (guidMT == MFMediaType_Audio)
                {
                    hr = InitPropVariantFromUInt32(98, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_DESIRED_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                else if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromUInt32(95, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            default:
                hr = E_UNEXPECTED;
                break;
        }    
    
    done:
        PropVariantClear(&var);
        return hr;
    }
    
  4. Wywołaj IMFASFContentInfo::GetEncodingConfigurationPropertyStore, aby uzyskać globalny magazyn właściwości ujścia pliku. W tym wywołaniu należy przekazać wartość 0 w pierwszym parametrze. Aby uzyskać więcej informacji, zobacz "Globalne właściwości ujścia plików" w Ustawianie właściwości w ujściu pliku.

  5. Ustaw MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE na VARIANT_TRUE, aby upewnić się, że wartości przecieku w multiplekserze ASF są prawidłowo dostosowywane. Aby uzyskać informacje o tej właściwości, zobacz "Multiplexer Initialization and Leaky Bucket Settings" w Creating the Multiplexer Object.

    Poniższy przykład kodu ustawia właściwości poziomu strumienia w magazynie właściwości ujścia pliku.

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

    Notatka

    Istnieją inne właściwości, które można ustawić na poziomie globalnym odbiornika pliku. Aby uzyskać więcej informacji, zobacz "Konfigurowanie obiektu ContentInfo z ustawieniami kodera" w Ustawianie właściwości w obiekcie ContentInfo.

     

Będziesz używać skonfigurowanych informacji ASF ContentInfo do utworzenia obiektu aktywacyjnego dla odbiornika pliku ASF (opisanego w następnej sekcji).

Jeśli tworzysz ujście pliku poza procesem (MFCreateASFMediaSinkActivate), czyli przy użyciu obiektu aktywacji, możesz użyć skonfigurowanego obiektu ContentInfo, aby utworzyć wystąpienie ujścia multimediów ASF (opisane w następnej sekcji). Jeśli tworzysz ujście pliku w procesie (MFCreateASFMediaSink), zamiast tworzyć pusty obiekt ContentInfo zgodnie z opisem w kroku 1, uzyskaj odwołanie do interfejsu IMFASFContentInfo, wywołując IMFMediaSink::QueryInterface ujścia pliku. Następnie należy skonfigurować obiekt ContentInfo zgodnie z opisem w tej sekcji.

Tworzenie odbiornika pliku ASF

W tym kroku samouczka użyjesz skonfigurowanego środowiska ASF ContentInfo utworzonego w poprzednim kroku, aby utworzyć obiekt aktywacji dla ujścia pliku ASF przez wywołanie funkcji MFCreateASFMediaSinkActivate. Aby uzyskać więcej informacji, zobacz Tworzenie miejsca docelowego pliku ASF.

Poniższy przykład kodu tworzy obiekt aktywacji dla odbiornika plików.

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

Tworzenie topologii kodowania częściowego

Następnie zbudujesz częściową topologię kodowania, tworząc węzły topologii dla źródła multimediów, wymaganych koderów Windows Media oraz odbiornika plików ASF. Po dodaniu węzłów topologii połączysz źródło, przekształcenie i węzły ujścia. Przed dodaniem węzłów topologii należy utworzyć pusty obiekt topologii, wywołując MFCreateTopology.

Tworzenie węzła topologii źródłowej dla źródła multimediów

W tym kroku utworzysz węzeł topologii źródłowej dla źródła multimediów.

Aby utworzyć ten węzeł, potrzebne są następujące odwołania:

  • Wskaźnik do źródła multimediów utworzonego w kroku opisanym w sekcji "Tworzenie źródła multimediów" w tym samouczku.
  • Wskaźnik do deskryptora prezentacji dla źródła multimediów. Możesz uzyskać odwołanie do interfejsu IMFPresentationDescriptor źródła multimediów, wywołując IMFMediaSource::CreatePresentationDescriptor.
  • Wskaźnik do deskryptora strumienia dla każdego strumienia w źródle multimediów, dla którego utworzyłeś strumień docelowy zgodnie z krokiem opisanym w sekcji „Tworzenie obiektu profilu ASF” tego samouczka.

Aby uzyskać więcej informacji na temat tworzenia węzłów źródłowych i przykładu kodu, zobacz Creating Source Nodes (Tworzenie węzłów źródłowych).

Poniższy przykład kodu tworzy częściową topologię przez dodanie węzła źródłowego i wymaganych węzłów przekształcania. Ten kod wywołuje funkcje pomocnika AddSourceNode i AddTransformOutputNodes. Te funkcje zostaną uwzględnione w dalszej części tego samouczka.

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

Poniższy przykład kodu tworzy i dodaje węzeł topologii źródłowej do topologii kodowania. Pobiera wskaźniki do wcześniej istniejącego obiektu topologii, źródła multimediów, aby wyliczyć strumienie źródłowe, deskryptory prezentacji i strumienia źródła multimediów. Obiekt wywołujący odbiera wskaźnik do węzła topologii źródłowej.

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

Inicjalizuj wymagane kodery i utwórz węzły przekształcania.

Potok programu Media Foundation nie wstawia automatycznie wymaganych koderów usługi Windows Media dla strumieni, które musi zakodować. Aplikacja musi ręcznie dodać kodery. W tym celu należy wyliczyć strumienie w profilu ASF utworzonym w kroku opisanym w sekcji "Tworzenie obiektu profilu ASF" w tym samouczku. Dla każdego strumienia w źródle i odpowiadającego mu strumienia w profilu utwórz wymagane kodery. W tym kroku potrzebujesz wskaźnika do obiektu aktywacji dla ujścia pliku, które utworzyłeś w kroku opisanym w sekcji "Tworzenie ujścia pliku ASF" w tym samouczku.

Aby zapoznać się z omówieniem tworzenia koderów za pośrednictwem obiektów aktywacji, zobacz Korzystanie z obiektów aktywacji kodera.

Poniższa procedura opisuje kroki wymagane do zainicjowania wymaganych koderów.

  1. Uzyskaj odwołanie do obiektu ContentInfo ujścia, wywołując IMFActivate::ActivateObject dotyczącą aktywacji ujścia pliku, a następnie zapytaj o IMFASFContentInfo z ujścia pliku przez wywołanie QueryInterface.

  2. Pobierz profil ASF skojarzony z obiektem ContentInfo, wywołując IMFASFContentInfo::GetProfile.

  3. Wyliczanie strumieni w profilu. W tym celu będzie potrzebna liczba strumieni oraz odwołanie do interfejsu "IMFASFStreamConfig" każdego strumienia.

    Wywołaj następujące metody:

  4. Dla każdego strumienia pobierz typ główny i właściwości kodowania strumienia z obiektu ContentInfo.

    Wywołaj następujące metody:

  5. W zależności od typu strumienia, dźwięku lub wideo, utwórz instancję obiektu aktywacji dla kodera, wywołując MFCreateWMAEncoderActivate lub MFCreateWMVEncoderActivate.

    Aby wywołać te funkcje, potrzebne są następujące odwołania:

  6. Zaktualizuj parametr algorytmu leaky bucket dla strumienia audio.

    MFCreateWMAEncoderActivate ustawia typ danych wyjściowych na podstawowym koderze MFT dla kodera audio Windows Media. Po ustawieniu typu nośnika wyjściowego koder pobiera średnią szybkość transmisji bitów z typu nośnika wyjściowego, oblicza szybkość transmisji bitów okna buforu i ustawia nieszczelne wartości zasobnika, które będą używane podczas sesji kodowania. Te wartości można zaktualizować w ujściu pliku, wykonując zapytanie względem kodera lub ustawiając wartości niestandardowe. Aby zaktualizować wartości, potrzebny jest następujący zestaw informacji:

    Utwórz tablicę DWORDów i ustaw wartość we właściwości MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET do ujścia strumienia audio. Jeśli nie podasz zaktualizowanych wartości, sesja multimediów ustawia je odpowiednio.

    Aby uzyskać więcej informacji, zobacz Model buforu wycieku.

Obiekty aktywacji utworzone w kroku 5 muszą zostać dodane do topologii jako węzły topologii przekształcania. Aby uzyskać więcej informacji i przykład kodu, zobacz "Creating a Transform Node from an Activation Object" (Tworzenie węzła przekształcenia na podstawie obiektu aktywacji) w Creating Transform Nodes.

Poniższy przykład kodu tworzy i dodaje wymagany koder aktywowany. Pobiera wskaźniki do wcześniej utworzonego obiektu topologii, obiektu aktywacyjnego ujścia pliku i formatu mediów strumienia źródłowego. Wywołuje również metodę AddOutputNode (zobacz następny przykład kodu), która tworzy węzeł odbiorczy i dodaje go do topologii kodowania. Obiekt wywołujący odbiera wskaźnik do węzła topologii źródłowej.

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

Tworzenie węzłów topologii danych wyjściowych dla ujścia pliku

W tym kroku utworzysz węzeł topologii danych wyjściowych dla ujścia pliku ASF.

Aby utworzyć ten węzeł, potrzebne są następujące odwołania:

  • Wskaźnik do obiektu aktywacji, który utworzyłeś w kroku opisanym w sekcji "Tworzenie ujścia plików ASF" w tym samouczku.
  • Numery strumieni są używane do identyfikacji wyjść strumieni dodanych do wyjścia plikowego. Numery strumieni są zgodne z identyfikacją strumienia ustawioną podczas tworzenia strumienia.

Aby uzyskać więcej informacji na temat tworzenia węzłów wyjściowych i przykładu kodu, zobacz "Tworzenie węzła wyjściowego na podstawie obiektu aktywacji" w Tworzenie węzłów wyjściowych.

Jeśli nie używasz obiektu aktywacji dla ujścia pliku, musisz wyliczyć ujścia strumienia w ujściu pliku ASF i ustawić każdy ujście strumienia jako węzeł wyjściowy w topologii. Aby uzyskać informacje o wyliczaniu ujść strumieni, zobacz "Wyliczanie ujść strumieni" w sekcji Dodawanie informacji o strumieniach do ujścia pliku ASF.

Poniższy przykład kodu tworzy i dodaje węzeł wyjściowy do topologii kodowania. Pobiera wskaźniki do wcześniej utworzonego obiektu topologii, obiektu aktywacji ujścia pliku i numeru identyfikacyjnego strumienia. Wywołujący odbiera wskaźnik do węzła topologii źródłowej.

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

Poniższy przykład kodu wylicza ujścia strumienia dla danego ujścia multimediów.

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

Łączenie węzłów źródła, przekształcania i ujścia

W tym kroku połączysz węzeł źródłowy z węzłem przekształcania, powołującym się na aktywowane kodowania, które utworzyłeś w kroku opisanym w sekcji „Tworzenie wymaganych koderów i węzłów przekształcania” w tym samouczku. Węzeł przekształcania zostanie połączony z węzłem wyjściowym, zawierającym obiekt aktywacyjny dla zrzutu pliku.

Obsługa sesji kodowania

W tym kroku wykonasz następujące kroki:

  1. Wywołaj MFCreateMediaSession, aby utworzyć sesję kodowania.

  2. Wywołaj IMFMediaSession::SetTopology, aby ustawić topologię kodowania w sesji. Jeśli wywołanie zostanie zakończone, Sesja Media ocenia węzły topologii i wstawia dodatkowe obiekty przekształcania, takie jak dekoder, który konwertuje określony skompresowany sygnał źródłowy na nieskompresowane sygnały będące danymi wejściowymi dla kodera.

  3. Aby zażądać zdarzeń zgłoszonych przez Sesję Medialną, wywołaj IMFMediaSession::GetEvent.

    W pętli zdarzeń rozpoczniesz i zamkniesz sesję kodowania w zależności od zdarzeń zgłaszanych przez sesję multimediów. Wywołanie IMFMediaSession::SetTopology skutkuje tym, że sesja multimediów generuje zdarzenie MESessionTopologyStatus z ustawioną flagą MF_TOPOSTATUS_READY. Wszystkie zdarzenia są generowane asynchronicznie, a aplikacja może pobierać te zdarzenia synchronicznie lub asynchronicznie. Ponieważ aplikacja w tym samouczku jest aplikacją konsolową i blokowanie wątków interfejsu użytkownika nie jest problemem, będziemy otrzymywać zdarzenia z Sesji Mediów synchronicznie.

    Aby uzyskać zdarzenia asynchronicznie, aplikacja musi zaimplementować interfejs IMFAsyncCallback. Aby uzyskać więcej informacji i przykładową implementację tego interfejsu, zobacz sekcję "Obsługa zdarzeń sesji" w How to Play Media Files with Media Foundation.

W pętli zdarzeń dla odbierania zdarzeń sesji multimediów poczekaj na zdarzenie MESessionTopologyStatus, które jest zgłaszane, gdy IMFMediaSession::SetTopology zostanie zakończone i topologia zostanie rozwiązana. Po otrzymaniu zdarzenia MESessionTopologyStatus uruchom sesję kodowania, wywołując IMFMediaSession::Start. Sesja multimediów generuje zdarzenie MEEndOfPresentation po zakończeniu wszystkich operacji kodowania. To zdarzenie musi być obsługiwane dla kodowania VBR i zostało omówione w następnej sekcji "Aktualizuj właściwości kodowania w ujściu pliku dla kodowania VBR" w tym samouczku.

Sesja multimediów generuje obiekt nagłówka ASF i finalizuje plik po zakończeniu sesji kodowania, a następnie zgłasza zdarzenie MESessionClosed. To zdarzenie musi być obsługiwane przez wykonanie odpowiednich operacji zamykania w sesji multimedialnej. Aby rozpocząć operacje zamykania, wywołaj IMFMediaSession::Shutdown. Po zamknięciu sesji kodowania nie można ustawić innej topologii kodowania w tym samym wystąpieniu sesji multimediów. Aby zakodować inny plik, bieżąca sesja multimediów musi zostać zamknięta i wydana, a nowa topologia musi być ustawiona na nowo utworzonej sesji multimediów. Poniższy przykład kodu tworzy sesję multimediów, ustawia topologię kodowania i obsługuje zdarzenia sesji multimediów.

Poniższy przykład kodu tworzy sesję multimediów, ustawia topologię kodowania i kontroluje sesję kodowania przez obsługę zdarzeń z sesji multimediów.

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

Aktualizowanie właściwości kodowania w ujściu pliku

Niektóre właściwości kodowania, takie jak szybkość bitowa kodowania i dokładne wartości funkcji 'leaky bucket', nie są znane aż do zakończenia kodowania, zwłaszcza w przypadku kodowania VBR. Aby uzyskać poprawne wartości, aplikacja musi poczekać na zdarzenie MEEndOfPresentation wskazujące, że sesja kodowania została ukończona. Nieszczelne wartości zasobnika muszą zostać zaktualizowane w ujściu, aby obiekt nagłówka ASF mógł odzwierciedlać dokładne wartości.

Poniższa procedura opisuje kroki wymagane do przechodzenia przez węzły w topologii kodowania, aby znaleźć węzeł ujścia pliku i ustawić wymagane właściwości algorytmu leaky bucket.

Aby zaktualizować wartości właściwości kodowania w składnicy plików ASF

  1. Wywołaj IMFTopology::GetOutputNodeCollection, aby pobrać kolekcję węzłów wyjściowych z topologii kodowania.
  2. Dla każdego węzła pobierz wskaźnik do ujścia strumienia w węźle, wywołując IMFTopologyNode::GetObject. Zapytanie o interfejs IMFStreamSink na wskaźniku IUnknown zwróconym przez funkcję IMFTopologyNode::GetObject.
  3. Dla każdego ujścia strumienia pobierz węzeł podrzędny (koder) przez wywołanie IMFTopologyNode::GetInput.
  4. Wykonaj zapytanie względem węzła, aby uzyskać wskaźnik IMFTransform z węzła kodera.
  5. Wykonaj zapytanie względem kodera dla wskaźnika IPropertyStore, aby pobrać magazyn właściwości kodowania z kodera.
  6. Wykonaj zapytanie dotyczące ujścia strumienia dla wskaźnika IPropertyStore, aby pobrać magazyn właściwości ujścia strumienia.
  7. Wywołaj IPropertyStore::GetValue, aby pobrać wymagane wartości właściwości z magazynu właściwości kodera i skopiować je do magazynu właściwości ujścia strumienia, wywołując IPropertyStore::SetValue.

W poniższej tabeli przedstawiono wartości właściwości post kodowania, które należy ustawić w ujściu strumienia dla strumienia wideo.

Typ kodowania Nazwa właściwości (GetValue) Nazwa właściwości (SetValue)
kodowanie przy stałej prędkości bitów MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Quality-Based kodowanie zmiennej szybkości bitów MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

Poniższy przykład kodu ustawia wartości właściwości po kodowaniu.

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

Implementacja funkcji main

Poniższy przykład kodu przedstawia główną funkcję aplikacji konsolowej.

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

Testowanie pliku wyjściowego

Na poniższej liście opisano listę kontrolną do testowania zakodowanego pliku. Te wartości można zaewidencjonować w oknie dialogowym właściwości pliku, które można wyświetlić, klikając prawym przyciskiem myszy zakodowany plik i wybierając Właściwości z menu kontekstowego.

  • Ścieżka zakodowanego pliku jest dokładna.
  • Rozmiar pliku jest większy niż zero KB, a czas trwania odtwarzania jest zgodny z czasem trwania pliku źródłowego.
  • W przypadku strumienia wideo sprawdź szerokość i wysokość ramki, szybkość klatek. Te wartości powinny być zgodne z wartościami określonymi w profilu ASF utworzonym w kroku opisanym w sekcji "Tworzenie obiektu profilu ASF".
  • W przypadku strumienia audio szybkość transmisji bitów musi być zbliżona do wartości określonej w typie nośnika docelowego.
  • Otwórz plik w programie Windows Media Player i sprawdź jakość kodowania.
  • Otwórz plik ASF w programie ASFViewer, aby zobaczyć strukturę pliku ASF. To narzędzie można pobrać z tej witryny internetowej firmy Microsoft .

Typowe kody błędów i porady dotyczące debugowania

Na poniższej liście opisano typowe kody błędów, które mogą zostać odebrane, oraz wskazówki dotyczące debugowania.

  • Wywołanie IMFSourceResolver::CreateObjectFromURL blokuje działanie aplikacji.

    Upewnij się, że zainicjowano platformę Media Foundation, wywołując MFStartup. Ta funkcja konfiguruje platformę asynchroniczną używaną przez wszystkie metody, które uruchamiają operacje asynchroniczne, takie jak IMFSourceResolver::CreateObjectFromURL, wewnętrznie.

  • IMFSourceResolver::CreateObjectFromURL zwraca 0x80070002 HRESULT "System nie może odnaleźć określonego pliku.

    Upewnij się, że istnieje nazwa pliku wejściowego określona przez użytkownika w pierwszym argumencie.

  • HRESULT 0x80070020 "Proces nie może uzyskać dostępu do pliku, ponieważ jest używany przez inny proces. "

    Upewnij się, że pliki wejściowe i wyjściowe nie są obecnie używane przez inny zasób w systemie.

  • Wywołania metod IMFTransform zwracają MF_E_INVALIDMEDIATYPE.

    Upewnij się, że spełnione są następujące warunki:

    • Typ danych wejściowych lub określony typ danych wyjściowych jest zgodny z typami multimediów obsługiwanymi przez koder.
    • Określone typy multimediów są kompletne. Aby typy multimediów były pełne, zobacz wymagane atrybuty w sekcjach "Create a Compressed Audio Media Type" (Tworzenie skompresowanego typu audio) i "Create a Compressed Video Media Type" (Tworzenie skompresowanego typu wideo) w tym samouczku.
    • Upewnij się, że ustawiłeś docelową szybkość transmisji bitów na typie częściowego nośnika, do którego dodawane są dane prywatne kodeka.
  • Sesja multimediów zwraca MF_E_UNSUPPORTED_D3D_TYPE w stanie zdarzenia.

    Ten błąd jest zwracany, gdy typ nośnika źródła wskazuje mieszany tryb przeplotu, który nie jest obsługiwany przez koder Windows Media Video. Jeśli skompresowany typ nośnika wideo jest ustawiony na użycie w trybie progresywnym, potok danych musi używać funkcji deinterlacingu. Ponieważ potok nie może odnaleźć dopasowania (wskazanego przez ten kod błędu), należy ręcznie wstawić rozdzielacz (transkodowanie procesora wideo) między węzłami dekodera i kodera.

  • Sesja multimediów zwraca E_INVALIDARG w statusie zdarzenia.

    Ten błąd jest zwracany, gdy atrybuty typu nośnika źródłowego nie są zgodne z atrybutami typu nośnika wyjściowego ustawionego w koderze Windows Media.

  • IWMCodecPrivateData::GetPrivateData zwraca 0x80040203 HRESULT "Wystąpił błąd składni podczas próby oceny ciągu zapytania"

    Upewnij się, że typ danych wejściowych jest ustawiony na koderze MFT.

Składniki ASF warstwy kanału