Udostępnij za pośrednictwem


Samouczek: pisanie pliku WMA przy użyciu obiektów WMContainer

W tym samouczku przedstawiono pisanie nowego pliku audio (.wma), wyodrębniając zawartość multimedialną z nieskompresowanego pliku audio (.wav), a następnie kompresując go w formacie ASF. Tryb kodowania używany do konwersji jest Kodowanie stałego bit rate'u (CBR). W tym trybie przed sesją kodowania aplikacja określa docelową szybkość transmisji bitów, którą musi osiągnąć koder.

W tym samouczku utworzysz aplikację konsolową, która będzie przyjmować nazwy plików wejściowych i wyjściowych jako argumenty. Aplikacja pobiera nieskompresowane przykłady multimediów z aplikacji do analizowania plików falowych, która została udostępniona w tym samouczku. Te przykłady są wysyłane do kodera w celu konwersji na format Windows Media Audio 9. Koder jest skonfigurowany do kodowania CBR i używa pierwszej szybkości transmisji bitów dostępnej podczas negocjacji typu nośnika jako docelowej szybkości transmisji bitów. Zakodowane próbki są wysyłane do multipleksera do pakietyzacji w formacie ASF. Te pakiety zostaną zapisane w strumieniu bajtowym reprezentującym obiekt danych ASF. Gdy sekcja danych będzie gotowa, utworzysz plik audio ASF i napiszesz nowy obiekt nagłówka ASF, który konsoliduje wszystkie informacje nagłówka, a następnie dołącz strumień bajtów obiektu danych ASF.

Ten samouczek zawiera następujące sekcje:

Warunki wstępne

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

  • Znasz strukturę pliku ASF i składniki udostępniane przez program Media Foundation do pracy z obiektami ASF. Składniki te obejmują obiekty ContentInfo, splitter, multiplexer i profile. Aby uzyskać więcej informacji, zobacz WMContainer ASF Components.
  • Znasz kodery Windows Media i różne typy kodowania, szczególnie CBR. Aby uzyskać więcej informacji, zobacz Windows Media Encoders .
  • Znasz multimediów i strumieni bajtów: w szczególności operacje na plikach przy użyciu strumienia bajtów i zapisywanie zawartości buforu multimediów do strumienia bajtowego.

Terminologia

Ten samouczek używa następujących terminów.

  • Typ nośnika źródłowego: obiekt typu nośnika, uwidacznia interfejs IMFMediaType, który opisuje zawartość pliku wejściowego.
  • Profil audio: obiekt profilu audio, udostępnia interfejs IMFASFProfile, który zawiera tylko strumienie audio pliku wyjściowego.
  • Przykład strumienia: próbka multimediów, uwidacznia interfejs IMFSample, reprezentuje dane multimedialne pliku wejściowego uzyskane z kodera w stanie skompresowanym.
  • Obiekt ContentInfo: obiekt ASF ContentInfo, uwidacznia interfejs IMFASFContentInfo, który reprezentuje obiekt nagłówka ASF pliku wyjściowego.
  • Strumień bajtów danych: obiekt strumienia bajtów uwidacznia interfejs IMFByteStream, który reprezentuje całą część obiektu danych ASF w pliku wyjściowym.
  • Pakiet danych: próbka multimedialna, uwidacznia interfejs IMFSample, wygenerowany przez ASF Multiplexer, reprezentuje pakiet danych ASF, który zostanie zapisany w strumieniu bajtów danych.
  • Strumień bajtów wyjściowych: obiekt strumienia bajtów uwidacznia interfejsIMFByteStream, który zawiera zawartość pliku wyjściowego.

1. Konfigurowanie projektu

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

    #include <new>
    #include <stdio.h>       // Standard I/O
    #include <windows.h>     // Windows headers
    #include <mfapi.h>       // Media Foundation platform
    #include <wmcontainer.h> // ASF interfaces
    #include <mferror.h>     // Media Foundation error codes
    
  2. Połącz z następującymi plikami biblioteki:

    • mfplat.lib
    • mf.lib
    • mfuuid.lib
  3. Zadeklaruj funkcję SafeRelease.

  4. Uwzględnij klasę CWmaEncoder w projekcie. Aby uzyskać pełny kod źródłowy tej klasy, zobacz przykładowy kod kodera.

2. Deklarowanie funkcji pomocnika

W tym samouczku wykorzystujemy następujące funkcje pomocnicze do odczytu i zapisu ze strumienia bajtów.

  • AppendToByteStream: dołącza zawartość jednego strumienia bajtów do innego strumienia bajtów.
  • WriteBufferToByteStream: zapisuje dane z buforu nośnika do strumienia bajtów.

Aby uzyskać więcej informacji, zobacz IMFByteStream::Write. Poniższy kod przedstawia następujące funkcje pomocnika:

//-------------------------------------------------------------------
// AppendToByteStream
//
// Reads the contents of pSrc and writes them to pDest.
//-------------------------------------------------------------------

HRESULT AppendToByteStream(IMFByteStream *pSrc, IMFByteStream *pDest)
{
    HRESULT hr = S_OK;

    const DWORD READ_SIZE = 1024;

    BYTE buffer[READ_SIZE];

    while (1)
    {
        ULONG cbRead;

        hr = pSrc->Read(buffer, READ_SIZE, &cbRead);

        if (FAILED(hr)) { break; }

        if (cbRead == 0)
        {
            break;
        }

        hr = pDest->Write(buffer, cbRead, &cbRead);

        if (FAILED(hr)) { break; }
    }

    return hr;
}
//-------------------------------------------------------------------
// WriteBufferToByteStream
//
// Writes data from a media buffer to a byte stream.
//-------------------------------------------------------------------

HRESULT WriteBufferToByteStream(
    IMFByteStream *pStream,   // Pointer to the byte stream.
    IMFMediaBuffer *pBuffer,  // Pointer to the media buffer.
    DWORD *pcbWritten         // Receives the number of bytes written.
    )
{
    HRESULT hr = S_OK;
    DWORD cbData = 0;
    DWORD cbWritten = 0;
    BYTE *pMem = NULL;

    hr = pBuffer->Lock(&pMem, NULL, &cbData);

    if (SUCCEEDED(hr))
    {
        hr = pStream->Write(pMem, cbData, &cbWritten);
    }

    if (SUCCEEDED(hr))
    {
        if (pcbWritten)
        {
            *pcbWritten = cbWritten;
        }
    }

    if (pMem)
    {
        pBuffer->Unlock();
    }
    return hr;
}

3. Otwieranie pliku audio

W tym samouczku założono, że aplikacja wygeneruje nieskompresowany dźwięk do kodowania. W tym celu w tym samouczku zadeklarowane są dwie funkcje:

HRESULT OpenAudioFile(PCWSTR pszURL, IMFMediaType **ppAudioFormat);
HRESULT GetNextAudioSample(BOOL *pbEOS, IMFSample **ppSample);

Implementacja tych funkcji jest pozostawiona czytelnikowi.

  • Funkcja OpenAudioFile powinna otworzyć plik multimedialny określony przez pszURL i zwrócić wskaźnik do typu nośnika opisującego strumień audio.
  • Funkcja GetNextAudioSample powinna odczytywać nieskompresowany dźwięk PCM z pliku, który został otwarty przez OpenAudioFile. Po osiągnięciu końca pliku pbEOS otrzymuje wartość TRUE. W przeciwnym razie ppSample odbiera próbkę danych multimedialnych zawierającą bufor audio.

4. Konfigurowanie kodera

Następnie utwórz koder, skonfiguruj go w celu utworzenia przykładów strumieni zakodowanych w języku CBR, a następnie negocjuj dane wejściowe i typy nośników wyjściowych.

W programie Media Foundation kodery (uwidaczniają interfejsIMFTransform) są implementowane jako Media Foundation Transforms (MFT).

W tym samouczku koder jest implementowany w klasie CWmaEncoder, która zapewnia nakładkę dla MFT. Aby uzyskać pełny kod źródłowy tej klasy, zobacz kod przykładu kodera .

Notatka

Opcjonalnie można określić typ kodowania jako CBR. Domyślnie koder jest skonfigurowany do używania kodowania CBR. Aby uzyskać więcej informacji, zobacz kodowanie stałych szybkości bitów. Dodatkowe właściwości można ustawić w zależności od typu kodowania, aby uzyskać informacje o właściwościach specyficznych dla trybu kodowania, zobacz Quality-Based kodowanie zmiennej szybkości bitów, niekonsekwowane kodowanie zmiennej szybkości bitówi Peak-Constrained kodowanie zmiennej szybkości bitów.

 

    CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.

    hr = OpenAudioFile(sInputFileName, &pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Initialize the WMA encoder wrapper.

    pEncoder = new (std::nothrow) CWmaEncoder();
    if (pEncoder == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto done;
    }

    hr = pEncoder->Initialize();
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetEncodingType(EncodeMode_CBR);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetInputType(pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

5. Utwórz obiekt ASF ContentInfo.

Obiekt ASF ContentInfo zawiera informacje o różnych obiektach nagłówka pliku wyjściowego.

Najpierw utwórz profil ASF dla strumienia audio:

  1. Wywołaj MFCreateASFProfile, aby utworzyć pusty obiekt profilu ASF. Profil ASF uwidacznia interfejs IMFASFProfile. Aby uzyskać więcej informacji, zobacz Tworzenie i konfigurowanie strumieni ASF.
  2. Pobierz zakodowany format audio z obiektu CWmaEncoder.
  3. Wywołaj IMFASFProfile::CreateStream, aby utworzyć nowy strumień dla profilu ASF. Przekaż wskaźnik do interfejsu IMFMediaType, który reprezentuje format strumienia.
  4. Wywołaj IMFASFStreamConfig::SetStreamNumber, aby przypisać identyfikator strumienia.
  5. Ustaw parametry "leaky bucket", ustawiając atrybut MF_ASFSTREAMCONFIG_LEAKYBUCKET1 na obiekcie strumienia.
  6. Wywołaj IMFASFProfile::SetStream, aby dodać nowy strumień do profilu.

Teraz utwórz obiekt ASF ContentInfo w następujący sposób:

  1. Wywołaj MFCreateASFContentInfo, aby utworzyć pusty obiekt ContentInfo.
  2. Wywołaj IMFASFContentInfo::SetProfile, aby ustawić profil ASF.

Poniższy kod przedstawia następujące kroki:

HRESULT CreateASFContentInfo(
    CWmaEncoder* pEncoder,
    IMFASFContentInfo** ppContentInfo
    )
{
    HRESULT hr = S_OK;
    
    IMFASFProfile* pProfile = NULL;
    IMFMediaType* pMediaType = NULL;
    IMFASFStreamConfig* pStream = NULL;
    IMFASFContentInfo* pContentInfo = NULL;

    // Create the ASF profile object.

    hr = MFCreateASFProfile(&pProfile); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Create a stream description for the encoded audio.

    hr = pEncoder->GetOutputType(&pMediaType); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pProfile->CreateStream(pMediaType, &pStream); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pStream->SetStreamNumber(DEFAULT_STREAM_NUMBER); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Set "leaky bucket" values.

    LeakyBucket bucket;

    hr = pEncoder->GetLeakyBucket1(&bucket);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pStream->SetBlob(
        MF_ASFSTREAMCONFIG_LEAKYBUCKET1, 
        (UINT8*)&bucket, 
        sizeof(bucket)
        );

    if (FAILED(hr))
    {
        goto done;
    }

    //Add the stream to the profile

    hr = pProfile->SetStream(pStream);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create the ASF ContentInfo object.

    hr = MFCreateASFContentInfo(&pContentInfo); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pContentInfo->SetProfile(pProfile); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.

    *ppContentInfo = pContentInfo;
    (*ppContentInfo)->AddRef();

done:
    SafeRelease(&pProfile);
    SafeRelease(&pStream);
    SafeRelease(&pMediaType);
    SafeRelease(&pContentInfo);
    return hr;
}

6. Tworzenie multipleksera ASF

ASF Multiplexer generuje pakiety danych ASF.

  1. Wywołaj MFCreateASFMultiplexer, aby utworzyć multiplekser ASF.
  2. Wywołaj IMFASFMultiplexer::Initialize, aby zainicjować multiplekser. Przekaż wskaźnik do obiektu ASF informacje o zawartości, utworzony w poprzedniej sekcji.
  3. Wywołaj IMFASFMultiplexer::SetFlags, aby ustawić flagę MFASF_MULTIPLEXER_AUTOADJUST_BITRATE. Gdy to ustawienie jest używane, multiplekser automatycznie reguluje szybkość bitową zawartości ASF, aby dopasować ją do charakterystyki multipleksowanych strumieni.
HRESULT CreateASFMux( 
    IMFASFContentInfo* pContentInfo,
    IMFASFMultiplexer** ppMultiplexer
    )
{
    HRESULT hr = S_OK;
    
    IMFMediaType* pMediaType = NULL;
    IMFASFMultiplexer *pMultiplexer = NULL;

    // Create and initialize the ASF Multiplexer object.

    hr = MFCreateASFMultiplexer(&pMultiplexer);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pMultiplexer->Initialize(pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }

    // Enable automatic bit-rate adjustment.

    hr = pMultiplexer->SetFlags(MFASF_MULTIPLEXER_AUTOADJUST_BITRATE);
    if (FAILED(hr))
    {
        goto done;
    }

    *ppMultiplexer = pMultiplexer;
    (*ppMultiplexer)->AddRef();

done:
    SafeRelease(&pMultiplexer);
    return hr;
}

7. Generowanie nowych pakietów danych ASF

Następnie wygeneruj pakiety danych ASF dla nowego pliku. Te pakiety danych będą stanowić końcowy obiekt danych ASF dla nowego pliku. Aby wygenerować nowe pakiety danych ASF:

  1. Wywołaj MFCreateTempFile, aby utworzyć tymczasowy strumień bajtów do przechowywania pakietów danych ASF.
  2. Wywołaj funkcję GetNextAudioSample zdefiniowaną przez aplikację, aby pobrać nieskompresowane dane audio dla kodera.
  3. Przekaż nieskompresowany dźwięk do kodera w celu kompresji. Aby uzyskać więcej informacji, zobacz Przetwarzanie danych w koderze
  4. Wywołaj IMFASFMultiplexer::ProcessSample, aby wysłać skompresowane próbki audio do multipleksera ASF w celu ich zapakietowania.
  5. Pobierz pakiety ASF z multipleksera i zapisz je w tymczasowym strumieniu bajtów. Aby uzyskać więcej informacji, zobacz Generowanie nowych pakietów danych ASF.
  6. Po osiągnięciu końca strumienia źródłowego opróżnij koder i pobierz pozostałe skompresowane próbki z kodera. Aby uzyskać więcej informacji na temat opróżniania MFT, zobacz Basic MFT Processing Model.
  7. Po wysłaniu wszystkich próbek do multipleksera wywołaj IMFASFMultiplexer::Flush i ściągnij pozostałe pakiety ASF z multipleksera.
  8. Wywołaj IMFASFMultiplexer::End.

Poniższy kod generuje pakiety danych ASF. Funkcja zwraca wskaźnik do strumienia bajtów zawierającego obiekt danych ASF.

HRESULT EncodeData(
    CWmaEncoder* pEncoder, 
    IMFASFContentInfo* pContentInfo,
    IMFASFMultiplexer* pMux, 
    IMFByteStream** ppDataStream) 
{
    HRESULT hr = S_OK;

    IMFByteStream* pStream = NULL;
    IMFSample* pInputSample = NULL;
    IMFSample* pWmaSample = NULL;

    BOOL bEOF = FALSE;

   // Create a temporary file to hold the data stream.
   hr = MFCreateTempFile(
        MF_ACCESSMODE_READWRITE, 
        MF_OPENMODE_DELETE_IF_EXIST,
        MF_FILEFLAGS_NONE,
        &pStream
        );

   if (FAILED(hr))
   {
       goto done;
   }

    BOOL bNeedInput = TRUE;

    while (TRUE)
    {
        if (bNeedInput)
        {
            hr = GetNextAudioSample(&bEOF, &pInputSample);
            if (FAILED(hr))
            {
                goto done;
            }

            if (bEOF)
            {
                // Reached the end of the input file.
                break;
            }

            // Encode the uncompressed audio sample.
            hr = pEncoder->ProcessInput(pInputSample);
            if (FAILED(hr))
            {
                goto done;
            }

            bNeedInput = FALSE;
        }

        if (bNeedInput == FALSE)
        {
            // Get data from the encoder.

            hr = pEncoder->ProcessOutput(&pWmaSample);
            if (FAILED(hr))
            {
                goto done;
            }

            // pWmaSample can be NULL if the encoder needs more input.

            if (pWmaSample)
            {
                hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
                if (FAILED(hr))
                {
                    goto done;
                }

                //Collect the data packets and write them to a stream
                hr = GenerateASFDataPackets(pMux, pStream);
                if (FAILED(hr))
                {
                    goto done;
                }
            }
            else
            {
                bNeedInput = TRUE;
            }
        }
        
        SafeRelease(&pInputSample);
        SafeRelease(&pWmaSample);
    }

    // Drain the MFT and pull any remaining samples from the encoder.

    hr = pEncoder->Drain();
    if (FAILED(hr))
    {
        goto done;
    }

    while (TRUE)
    {
        hr = pEncoder->ProcessOutput(&pWmaSample);
        if (FAILED(hr))
        {
            goto done;
        }

        if (pWmaSample == NULL)
        {
            break;
        }

        hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
        if (FAILED(hr))
        {
            goto done;
        }

        //Collect the data packets and write them to a stream
        hr = GenerateASFDataPackets(pMux, pStream);
        if (FAILED(hr))
        {
            goto done;
        }

        SafeRelease(&pWmaSample);
    }

    // Flush the mux and get any pending ASF data packets.
    hr = pMux->Flush();
    if (FAILED(hr))
    {
        goto done;
    }

    hr = GenerateASFDataPackets(pMux, pStream);
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Update the ContentInfo object
    hr = pMux->End(pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }

    //Return stream to the caller that contains the ASF encoded data.
    *ppDataStream = pStream;
    (*ppDataStream)->AddRef();

done:
    SafeRelease(&pStream);
    SafeRelease(&pInputSample);
    SafeRelease(&pWmaSample);
    return hr;
}

Kod funkcji GenerateASFDataPackets jest wyświetlany w temacie Generowanie nowych pakietów danych ASF.

8. Napisz plik ASF

Następnie zapisz nagłówek ASF w buforze multimediów, wywołując IMFASFContentInfo::GenerateHeader. Ta metoda konwertuje dane przechowywane w obiekcie ContentInfo na dane binarne w formacie obiektu nagłówka ASF. Aby uzyskać więcej informacji, zobacz Generowanie nowego obiektu nagłówka ASF.

Po wygenerowaniu nowego obiektu nagłówka ASF utwórz strumień bajtów dla pliku wyjściowego. Najpierw zapisz obiekt nagłówka w strumieniu bajtów wyjściowych. Postępuj zgodnie z obiektem nagłówka obiektem danych zawartym w strumieniu bajtów danych.

HRESULT WriteASFFile( 
     IMFASFContentInfo *pContentInfo, 
     IMFByteStream *pDataStream,
     PCWSTR pszFile
     )
{
    HRESULT hr = S_OK;
    
    IMFMediaBuffer* pHeaderBuffer = NULL;
    IMFByteStream* pWmaStream = NULL;

    DWORD cbHeaderSize = 0;
    DWORD cbWritten = 0;

    //Create output file
    hr = MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST,
        MF_FILEFLAGS_NONE, pszFile, &pWmaStream);

    if (FAILED(hr))
    {
        goto done;
    }


    // Get the size of the ASF Header Object.
    hr = pContentInfo->GenerateHeader (NULL, &cbHeaderSize);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create a media buffer.
    hr = MFCreateMemoryBuffer(cbHeaderSize, &pHeaderBuffer);
    if (FAILED(hr))
    {
        goto done;
    }

    // Populate the media buffer with the ASF Header Object.
    hr = pContentInfo->GenerateHeader(pHeaderBuffer, &cbHeaderSize);
    if (FAILED(hr))
    {
        goto done;
    }

    // Write the ASF header to the output file.
    hr = WriteBufferToByteStream(pWmaStream, pHeaderBuffer, &cbWritten);
    if (FAILED(hr))
    {
        goto done;
    }

    // Append the data stream to the file.

    hr = pDataStream->SetCurrentPosition(0);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = AppendToByteStream(pDataStream, pWmaStream);

done:
    SafeRelease(&pHeaderBuffer);
    SafeRelease(&pWmaStream);
    return hr;
}

9. Zdefiniuj funkcję Entry-Point

Teraz możesz połączyć poprzednie kroki w pełną aplikację. Przed użyciem dowolnego z obiektów Programu Media Foundation zainicjuj platformę Media Foundation, wywołując MFStartup. Po zakończeniu wywołaj MFShutdown. Aby uzyskać więcej informacji, zobacz Inicjowanie programu Media Foundation.

Poniższy kod przedstawia kompletną aplikację konsolową. Argument wiersza polecenia określa nazwę pliku do konwersji i nazwę nowego pliku audio.

int wmain(int argc, WCHAR* argv[])
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    if (argc != 3)
    {
        wprintf_s(L"Usage: %s input.wmv, %s output.wma");
        return 0;
    }

    const WCHAR* sInputFileName = argv[1];    // Source file name
    const WCHAR* sOutputFileName = argv[2];  // Output file name
    
    IMFMediaType* pInputType = NULL;
    IMFASFContentInfo* pContentInfo = NULL;
    IMFASFMultiplexer* pMux = NULL;
    IMFByteStream* pDataStream = NULL;

    CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.

    HRESULT hr = CoInitializeEx(
        NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if (FAILED(hr))
    {
        goto done;
    }

    hr = MFStartup(MF_VERSION);
    if (FAILED(hr))
    {
        goto done;
    }

    CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.

    hr = OpenAudioFile(sInputFileName, &pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Initialize the WMA encoder wrapper.

    pEncoder = new (std::nothrow) CWmaEncoder();
    if (pEncoder == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto done;
    }

    hr = pEncoder->Initialize();
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetEncodingType(EncodeMode_CBR);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pEncoder->SetInputType(pInputType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create the WMContainer objects.
    hr = CreateASFContentInfo(pEncoder, &pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = CreateASFMux(pContentInfo, &pMux);
    if (FAILED(hr))
    {
        goto done;
    }

    // Convert uncompressed data to ASF format.
    hr = EncodeData(pEncoder, pContentInfo, pMux, &pDataStream);
    if (FAILED(hr))
    {
        goto done;
    }

    // Write the ASF objects to the output file.
    hr = WriteASFFile(pContentInfo, pDataStream, sOutputFileName);

done:
    SafeRelease(&pInputType);
    SafeRelease(&pContentInfo);
    SafeRelease(&pMux);
    SafeRelease(&pDataStream);
    delete pEncoder;

    MFShutdown();
    CoUninitialize();

    if (FAILED(hr))
    {
        wprintf_s(L"Error: 0x%X\n", hr);
    }

    return 0;
} 

komponentów ASF WMContainer

ASF obsługa w Media Foundation