Bagikan melalui


Tutorial: Menyalin Aliran ASF dengan Menggunakan Objek WMContainer

Salah satu cara untuk membuat file ASF adalah dengan menyalin aliran ASF dari file yang ada. Untuk melakukan ini, Anda dapat mengambil data media dari file sumber dan menulis ke file output. Jika file sumber adalah file ASF, Anda dapat menyalin sampel aliran tanpa mendekompresi dan mengompresinya kembali.

Tutorial ini menunjukkan skenario ini dengan mengekstrak aliran audio pertama dari file audio-video ASF (.wmv) dan menyalinnya ke file audio ASF baru (.wma). Dalam tutorial ini, Anda akan membuat aplikasi konsol yang mengambil nama file input dan output sebagai argumen. Aplikasi ini menggunakan Pemisah ASF untuk mengurai sampel aliran input dan kemudian mengirimkannya ke Multiplexer ASF untuk menulis paket data ASF untuk aliran audio.

Tutorial ini berisi langkah-langkah berikut:

Prasyarat

Tutorial ini mengasumsikan hal-hal berikut:

  • Anda terbiasa dengan struktur file ASF dan komponen yang disediakan oleh Media Foundation untuk bekerja dengan Objek ASF. Komponen-komponen ini termasuk ContentInfo, pemisah, multiplexer, dan objek profil. Untuk informasi selengkapnya, lihat Komponen WMContainer ASF.
  • Anda terbiasa dengan proses penguraian Objek Header ASF dan paket data ASF dari file yang ada dan menghasilkan sampel aliran terkompresi dengan menggunakan pemisah. Untuk informasi selengkapnya lihat Tutorial: Membaca File ASF.
  • Anda terbiasa dengan buffer media dan aliran byte: Secara khusus, operasi file menggunakan aliran byte, dan menulis konten buffer media ke dalam aliran byte. (Lihat 2. Deklarasikan Fungsi Pembantu.)

Terminologi

Tutorial ini menggunakan istilah-istilah berikut:

  • Aliran byte sumber: Objek aliran byte, mengekspos antarmuka IMFByteStream , yang berisi konten file input.
  • Objek ContentInfo Sumber: Objek ContentInfo, mengekspos antarmuka IMFASFContentInfo , yang mewakili Objek Header ASF dari file input.
  • Profil audio: Objek profil, mengekspos antarmuka IMFASFProfile , yang hanya berisi aliran audio file input.
  • Sampel aliran: Sampel media, mengekspos antarmuka IMFSample , yang dihasilkan oleh pemisah mewakili data media aliran yang dipilih yang diperoleh dari file input dalam keadaan terkompresi.
  • Objek ContentInfo Output: Objek ContentInfo, mengekspos antarmuka IMFASFContentInfo , yang mewakili Objek Header ASF dari file output.
  • Aliran byte data: Objek aliran byte, mengekspos antarmuka IMFByteStream , yang mewakili seluruh bagian Objek Data ASF dari file output.
  • Paket data: Sampel media, mengekspos antarmuka IMFSample , yang dihasilkan oleh multiplexer mewakili paket data ASF yang akan ditulis ke aliran byte data.
  • Aliran byte output: Objek aliran byte, mengekspos antarmuka IMFByteStream , yang berisi konten file output.

1. Siapkan Proyek

Sertakan header berikut dalam file sumber Anda:

#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

Tautkan ke file pustaka berikut ini:

  • mfplat.lib
  • mf.lib
  • mfuuid.lib

Deklarasikan fungsi SafeRelease :

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

2. Mendeklarasikan Fungsi Pembantu

Tutorial ini menggunakan fungsi pembantu berikut untuk membaca dan menulis dari aliran byte.

Fungsi ini AllocReadFromByteStream membaca data dari aliran byte dan mengalokasikan buffer media baru untuk menyimpan data. Untuk informasi selengkapnya, lihat IMFByteStream::Read.

//-------------------------------------------------------------------
// AllocReadFromByteStream
//
// Reads data from a byte stream and returns a media buffer that
// contains the data.
//-------------------------------------------------------------------

HRESULT AllocReadFromByteStream(
    IMFByteStream *pStream,         // Pointer to the byte stream.
    DWORD cbToRead,                 // Number of bytes to read.
    IMFMediaBuffer **ppBuffer       // Receives a pointer to the media buffer. 
    )
{
    HRESULT hr = S_OK;
    BYTE *pData = NULL;
    DWORD cbRead = 0;   // Actual amount of data read.

    IMFMediaBuffer *pBuffer = NULL;

    // Create the media buffer. 
    // This function allocates the memory for the buffer.
    hr = MFCreateMemoryBuffer(cbToRead, &pBuffer);

    // Get a pointer to the memory buffer.
    if (SUCCEEDED(hr))
    {
        hr = pBuffer->Lock(&pData, NULL, NULL);
    }

    // Read the data from the byte stream.
    if (SUCCEEDED(hr))
    {
        hr = pStream->Read(pData, cbToRead, &cbRead);
    }

    // Update the size of the valid data in the buffer.
    if (SUCCEEDED(hr))
    {
        hr = pBuffer->SetCurrentLength(cbRead);
    }

    // Return the pointer to the caller.
    if (SUCCEEDED(hr))
    {
        *ppBuffer = pBuffer;
        (*ppBuffer)->AddRef();
    }

    if (pData)
    {
        pBuffer->Unlock();
    }
    SafeRelease(&pBuffer);
    return hr;
}

Fungsi ini WriteBufferToByteStream menulis data dari buffer media ke aliran byte. Untuk informasi selengkapnya, lihat IMFByteStream::Write.

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

Fungsi menambahkan AppendToByteStream konten satu aliran byte ke aliran byte lainnya:

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

3. Buka File ASF Input

Buka file input dengan memanggil fungsi MFCreateFile . Metode mengembalikan penunjuk ke objek aliran byte yang berisi konten file. Nama file ditentukan oleh pengguna melalui argumen baris perintah aplikasi.

Contoh kode berikut mengambil nama file dan mengembalikan penunjuk ke objek aliran byte yang dapat digunakan untuk membaca file.

        // Open the file.
        hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, 
            MF_FILEFLAGS_NONE, pszFileName, &pStream);

4. Menginisialisasi Objek untuk File Input

Selanjutnya, Anda akan membuat dan menginisialisasi objek ContentInfo sumber dan pemisah untuk menghasilkan sampel aliran.

Aliran byte sumber yang dibuat di langkah 2 ini akan digunakan untuk mengurai Objek Header ASF dan mengisi objek ContentInfo sumber. Objek ini akan digunakan untuk menginisialisasi pemisah untuk memfasilitasi penguraian paket data ASF untuk aliran audio dalam file input. Anda juga akan mengambil panjang Objek Data ASF dalam file input dan offset ke paket data ASF pertama relatif terhadap awal file. Atribut ini akan digunakan oleh pemisah untuk menghasilkan sampel aliran audio.

Untuk membuat dan menginisialisasi komponen ASF untuk file input:

  1. Panggil MFCreateASFContentInfo untuk membuat objek ContentInfo. Fungsi ini mengembalikan penunjuk ke antarmuka IMFASFContentInfo .
  2. Panggil IMFASFContentInfo::P arseHeader untuk mengurai Header ASF. Untuk informasi selengkapnya tentang langkah ini, lihat Membaca Objek Header ASF dari File yang Sudah Ada.
  3. Panggil MFCreateASFSplitter untuk membuat objek pemisah ASF. Fungsi ini mengembalikan penunjuk ke antarmuka IMFASFSplitter .
  4. Panggil IMFASFSplitter::Initialize, melewati penunjuk IMFASFContentInfo. Untuk informasi selengkapnya tentang langkah ini, lihat Membuat Objek Pemisah ASF.
  5. Panggil IMFASFContentInfo::GeneratePresentationDescriptor untuk mendapatkan deskriptor presentasi untuk file ASF.
  6. Dapatkan nilai atribut MF_PD_ASF_DATA_START_OFFSET dari deskriptor presentasi. Nilai ini adalah lokasi Objek Data ASF dalam file, sebagai offset byte dari awal file.
  7. Dapatkan nilai atribut MF_PD_ASF_DATA_LENGTH dari deskriptor presentasi. Nilai ini adalah ukuran total Objek Data ASF, dalam byte. Untuk informasi selengkapnya, lihat Mendapatkan Informasi dari Objek Header ASF.

Contoh kode berikut menunjukkan fungsi yang mengonsolidasikan semua langkah. Fungsi ini mengambil penunjuk ke aliran byte sumber dan mengembalikan pointer ke objek ContentInfo sumber dan pemisah. Selain itu, ia menerima panjang dan offset ke Objek Data ASF.

//-------------------------------------------------------------------
// CreateSourceParsers
//
// Creates the ASF splitter and the ASF Content Info object for the 
// source file.
// 
// This function also calulates the offset and length of the ASF 
// Data Object.
//-------------------------------------------------------------------

HRESULT CreateSourceParsers(
    IMFByteStream *pSourceStream,
    IMFASFContentInfo **ppSourceContentInfo,
    IMFASFSplitter **ppSplitter,
    UINT64 *pcbDataOffset,
    UINT64 *pcbDataLength
    )
{
    const DWORD MIN_ASF_HEADER_SIZE = 30;

    IMFMediaBuffer *pBuffer = NULL;
    IMFPresentationDescriptor *pPD = NULL;
    IMFASFContentInfo *pSourceContentInfo = NULL;
    IMFASFSplitter *pSplitter = NULL;

    QWORD cbHeader = 0;

    /*------- Parse the ASF header. -------*/

    // Create the ASF ContentInfo object.
    HRESULT hr = MFCreateASFContentInfo(&pSourceContentInfo);
    
    // Read the first 30 bytes to find the total header size.
    if (SUCCEEDED(hr))
    {
        hr = AllocReadFromByteStream(
            pSourceStream, 
            MIN_ASF_HEADER_SIZE, 
            &pBuffer
            );
    }

    // Get the header size.
    if (SUCCEEDED(hr))
    {
        hr = pSourceContentInfo->GetHeaderSize(pBuffer, &cbHeader);
    }

    // Release the buffer; we will reuse it.
    SafeRelease(&pBuffer);
    
    // Read the entire header into a buffer.
    if (SUCCEEDED(hr))
    {
        hr = pSourceStream->SetCurrentPosition(0);
    }

    if (SUCCEEDED(hr))
    {
        hr = AllocReadFromByteStream(
            pSourceStream, 
            (DWORD)cbHeader, 
            &pBuffer
            );
    }

    // Parse the buffer and populate the header object.
    if (SUCCEEDED(hr))
    {
        hr = pSourceContentInfo->ParseHeader(pBuffer, 0);
    }

    /*------- Initialize the ASF splitter. -------*/

    // Create the splitter.
    if (SUCCEEDED(hr))
    {
        hr = MFCreateASFSplitter(&pSplitter);
    }
    
    // initialize the splitter with the ContentInfo object.
    if (SUCCEEDED(hr))
    {
        hr = pSplitter->Initialize(pSourceContentInfo);
    }


    /*------- Get the offset and size of the ASF Data Object. -------*/

    // Generate the presentation descriptor.
    if (SUCCEEDED(hr))
    {
        hr =  pSourceContentInfo->GeneratePresentationDescriptor(&pPD);
    }

    // Get the offset to the start of the Data Object.
    if (SUCCEEDED(hr))
    {
        hr = pPD->GetUINT64(MF_PD_ASF_DATA_START_OFFSET, pcbDataOffset);
    }

    // Get the length of the Data Object.
    if (SUCCEEDED(hr))
    {
        hr = pPD->GetUINT64(MF_PD_ASF_DATA_LENGTH, pcbDataLength);
    }

    // Return the pointers to the caller.
    if (SUCCEEDED(hr))
    {
        *ppSourceContentInfo = pSourceContentInfo;
        (*ppSourceContentInfo)->AddRef();

        *ppSplitter = pSplitter;
        (*ppSplitter)->AddRef();

    }

    SafeRelease(&pPD);
    SafeRelease(&pBuffer);
    SafeRelease(&pSourceContentInfo);
    SafeRelease(&pSplitter);

    return S_OK;
}

5. Buat Profil Audio

Selanjutnya, Anda akan membuat objek profil untuk file input dengan mendapatkannya dari objek ContentInfo sumber. Anda kemudian akan mengonfigurasi profil sehingga hanya berisi aliran audio file input. Untuk melakukan ini, hitung aliran dan hapus aliran non-audio dari profil. Objek profil audio akan digunakan nanti dalam tutorial ini untuk menginisialisasi objek ContentInfo output.

Untuk membuat profil audio

  1. Dapatkan objek profil untuk file input dari objek ContentInfo sumber dengan memanggil IMFASFContentInfo::GetProfile. Metode mengembalikan pointer ke objek profil yang berisi semua aliran dalam file input. Untuk informasi selengkapnya, lihat Membuat Profil ASF.
  2. Hapus objek pengecualian bersama dari profil. Langkah ini diperlukan karena aliran non-audio akan dihapus dari profil, yang dapat membatalkan objek pengecualian bersama.
  3. Hapus semua aliran non-audio dari profil, sebagai berikut:
  4. Simpan nomor stream aliran aliran audio pertama. Ini akan dipilih pada pemisah untuk menghasilkan sampel aliran. Jika nomor streaming adalah nol, pemanggil dapat mengasumsikan bahwa tidak ada file aliran audio.

Kode berikut langkah-langkah berikut:

//-------------------------------------------------------------------
// GetAudioProfile
//
// Gets the ASF profile from the source file and removes any video
// streams from the profile.
//-------------------------------------------------------------------

HRESULT GetAudioProfile(
    IMFASFContentInfo *pSourceContentInfo, 
    IMFASFProfile **ppAudioProfile, 
    WORD *pwSelectStreamNumber
    )
{
    IMFASFStreamConfig *pStream = NULL;
    IMFASFProfile *pProfile = NULL;

    DWORD dwTotalStreams = 0;
    WORD  wStreamNumber = 0; 
    GUID guidMajorType = GUID_NULL;
    
    // Get the profile object from the source ContentInfo object.
    HRESULT hr = pSourceContentInfo->GetProfile(&pProfile);

    // Remove mutexes from the profile
    if (SUCCEEDED(hr))
    {
        hr = RemoveMutexes(pProfile);
    }

    // Get the total number of streams on the profile.
    if (SUCCEEDED(hr))
    {
        hr = pProfile->GetStreamCount(&dwTotalStreams);
    }

    // Enumerate the streams and remove the non-audio streams.
    if (SUCCEEDED(hr))
    {
        for (DWORD index = 0; index < dwTotalStreams; )
        {
            hr = pProfile->GetStream(index, &wStreamNumber, &pStream);

            if (FAILED(hr)) { break; }

            hr = pStream->GetStreamType(&guidMajorType);

            SafeRelease(&pStream);

            if (FAILED(hr)) { break; }

            if (guidMajorType != MFMediaType_Audio)
            {
                hr = pProfile->RemoveStream(wStreamNumber);
    
                if (FAILED(hr)) { break; }

                index = 0;
                dwTotalStreams--;
            }
            else
            {
                // Store the first audio stream number. 
                // This will be selected on the splitter.

                if (*pwSelectStreamNumber == 0)
                {
                    *pwSelectStreamNumber = wStreamNumber;
                }

                index++;
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        *ppAudioProfile = pProfile;
        (*ppAudioProfile)->AddRef();
    }

    SafeRelease(&pStream);
    SafeRelease(&pProfile);

    return S_OK;
}

Fungsi ini RemoveMutexes menghapus objek pengecualian timbal balik dari profil:

HRESULT RemoveMutexes(IMFASFProfile *pProfile)
{
    DWORD cMutex = 0;
    HRESULT hr = pProfile->GetMutualExclusionCount(&cMutex);

    if (SUCCEEDED(hr))
    {
        for (DWORD i = 0; i < cMutex; i++)
        {
            hr = pProfile->RemoveMutualExclusion(0);

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

    return hr;
}

6. Inisialisasi Objek untuk File Output

Selanjutnya, Anda akan membuat objek ContentInfo output dan multiplexer untuk menghasilkan paket data untuk file output.

Profil audio yang dibuat di langkah 4 akan digunakan untuk mengisi objek ContentInfo output. Objek ini berisi informasi seperti atribut file global dan properti aliran. Objek ContentInfo output akan digunakan untuk menginisialisasi multiplexer yang akan menghasilkan paket data untuk file output. Setelah paket data dibuat, objek ContentInfo harus diperbarui untuk mencerminkan nilai baru.

Untuk membuat dan menginisialisasi komponen ASF untuk file output

  1. Buat objek ContentInfo kosong dengan memanggil MFCreateASFContentInfo dan isi dengan informasi dari profil audio yang dibuat di langkah 3 dengan memanggil IMFASFContentInfo::SetProfile. Untuk informasi selengkapnya, lihat Menginisialisasi Objek ContentInfo dari File ASF Baru.
  2. Buat dan inisialisasi objek multiplexer dengan menggunakan objek ContentInfo output. Untuk informasi selengkapnya, lihat Membuat Objek Multiplexer.

Contoh kode berikut menunjukkan fungsi yang mengonsolidasikan langkah-langkahnya. Fungsi ini membawa penunjuk ke objek profil dan mengembalikan penunjuk ke objek ContentInfo output dan multiplexer.

//-------------------------------------------------------------------
// CreateOutputGenerators
//
// Creates the ASF mux and the ASF Content Info object for the 
// output file.
//-------------------------------------------------------------------

HRESULT CreateOutputGenerators(
    IMFASFProfile *pProfile, 
    IMFASFContentInfo **ppContentInfo, 
    IMFASFMultiplexer **ppMux
    )
{
    IMFASFContentInfo *pContentInfo = NULL;
    IMFASFMultiplexer *pMux = NULL;

    // Use the ASF profile to create the ContentInfo object.
    HRESULT hr = MFCreateASFContentInfo(&pContentInfo);

    if (SUCCEEDED(hr))
    {
        hr = pContentInfo->SetProfile(pProfile);
    }

    // Create the ASF Multiplexer object.
    if (SUCCEEDED(hr))
    {
        hr = MFCreateASFMultiplexer(&pMux);
    }
    
    // Initialize it using the new ContentInfo object.
    if (SUCCEEDED(hr))
    {
        hr = pMux->Initialize(pContentInfo);
    }

    // Return the pointers to the caller.
    if (SUCCEEDED(hr))
    {
        *ppContentInfo = pContentInfo;
        (*ppContentInfo)->AddRef();

        *ppMux = pMux;
        (*ppMux)->AddRef();
    }

    SafeRelease(&pContentInfo);
    SafeRelease(&pMux);

    return hr;
}

7. Hasilkan Paket Data ASF Baru

Selanjutnya, Anda akan menghasilkan sampel aliran audio dari aliran byte sumber dengan menggunakan pemisah dan mengirimkannya ke multiplexer untuk membuat paket data ASF. Paket data ini akan merupakan Objek Data ASF akhir untuk file baru.

Untuk menghasilkan sampel aliran audio

  1. Pilih aliran audio pertama pada pemisah dengan memanggil IMFASFSplitter::SelectStreams.
  2. Baca blok data media ukuran tetap dari aliran byte sumber ke buffer media.
  3. Kumpulkan sampel aliran sebagai sampel media dari pemisah dengan memanggil IMFASFSplitter::GetNextSample dalam perulangan selama menerima bendera ASF_STATUSFLAGS_INCOMPLETE dalam parameter pdwStatusFlags . Untuk informasi selengkapnya, lihat Membuat Sampel untuk Paket Data ASF" dalam Menghasilkan Sampel Aliran dari Objek Data ASF yang Ada.
  4. Untuk setiap sampel media, panggil IMFASFMultiplexer::P rocessSample untuk mengirim sampel media ke multiplexer. Multiplexer menghasilkan paket data untuk Objek Data ASF.
  5. Tulis paket data yang dihasilkan oleh multiplexer ke aliran byte data.
  6. Setelah semua paket data dibuat, panggil IMFASFMultiplexer::End untuk memperbarui objek ContentInfo output dengan informasi yang dikumpulkan selama pembuatan paket data ASF.

Contoh kode berikut menghasilkan sampel aliran dari pemisah ASF dan mengirimkannya ke multiplexer. Multiplexer menghasilkan paket data ASF dan menulisnya ke aliran.

//-------------------------------------------------------------------
// GenerateASFDataObject
// 
// Creates a byte stream that contains the ASF Data Object for the
// output file.
//-------------------------------------------------------------------

HRESULT GenerateASFDataObject(
    IMFByteStream *pSourceStream, 
    IMFASFSplitter *pSplitter, 
    IMFASFMultiplexer *pMux, 
    UINT64   cbDataOffset,
    UINT64   cbDataLength,
    IMFByteStream **ppDataStream
    )
{
    IMFMediaBuffer *pBuffer = NULL;
    IMFByteStream *pDataStream = NULL;
    
    const DWORD READ_SIZE = 1024 * 4;

    // Flush the splitter to remove any pending samples.
    HRESULT hr = pSplitter->Flush();

    if (SUCCEEDED(hr))
    {
        hr = MFCreateTempFile(
            MF_ACCESSMODE_READWRITE, 
            MF_OPENMODE_DELETE_IF_EXIST,
            MF_FILEFLAGS_NONE,
            &pDataStream
            );
    }

    if (SUCCEEDED(hr))
    {
        hr = pSourceStream->SetCurrentPosition(cbDataOffset);
    }

    if (SUCCEEDED(hr))
    {
        while (cbDataLength > 0)
        {
            DWORD cbRead = min(READ_SIZE, (DWORD)cbDataLength);

            hr = AllocReadFromByteStream(
                pSourceStream, 
                cbRead, 
                &pBuffer
                );

            if (FAILED(hr)) 
            { 
                break; 
            }

            cbDataLength -= cbRead;

            // Push data on the splitter.
            hr =  pSplitter->ParseData(pBuffer, 0, 0);

            if (FAILED(hr)) 
            { 
                break; 
            }

            // Get ASF packets from the splitter and feed them to the mux.
            hr = GetPacketsFromSplitter(pSplitter, pMux, pDataStream);

            if (FAILED(hr)) 
            { 
                break; 
            }

            SafeRelease(&pBuffer);
        }
    }

    // Flush the mux and generate any remaining samples.
    if (SUCCEEDED(hr))
    {
        hr = pMux->Flush();
    }

    if (SUCCEEDED(hr))
    {
        hr = GenerateASFDataPackets(pMux, pDataStream);
    }

     // Return the pointer to the caller.
    if (SUCCEEDED(hr))
    {
        *ppDataStream = pDataStream;
        (*ppDataStream)->AddRef();
    }

    SafeRelease(&pBuffer);
    SafeRelease(&pDataStream);
    return hr;
}

Untuk mendapatkan paket dari pemisah ASF, kode sebelumnya memanggil fungsi , yang GetPacketsFromSplitter ditunjukkan di sini:

//-------------------------------------------------------------------
// GetPacketsFromSplitter
//
// Gets samples from the ASF splitter.
//
// This function is called after calling IMFASFSplitter::ParseData.
//-------------------------------------------------------------------

HRESULT GetPacketsFromSplitter(
    IMFASFSplitter *pSplitter,
    IMFASFMultiplexer *pMux,
    IMFByteStream *pDataStream
    )
{
    HRESULT hr = S_OK;
    DWORD   dwStatus = ASF_STATUSFLAGS_INCOMPLETE;
    WORD    wStreamNum = 0;

    IMFSample *pSample = NULL;

    while (dwStatus & ASF_STATUSFLAGS_INCOMPLETE) 
    {
        hr = pSplitter->GetNextSample(&dwStatus, &wStreamNum, &pSample);

        if (FAILED(hr))
        {
            break;
        }

        if (pSample)
        {
            //Send to the multiplexer to convert it into ASF format
            hr = pMux->ProcessSample(wStreamNum, pSample, 0);

            if (FAILED(hr)) 
            { 
                break; 
            }

            hr = GenerateASFDataPackets(pMux, pDataStream);

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

        SafeRelease(&pSample);
    }

    SafeRelease(&pSample);
    return hr;
}

Fungsi ini GenerateDataPackets mendapatkan paket data dari multiplexer. Untuk informasi selengkapnya, lihat Mendapatkan Paket Data ASF.

//-------------------------------------------------------------------
// GenerateASFDataPackets
// 
// Gets data packets from the mux. This function is called after 
// calling IMFASFMultiplexer::ProcessSample. 
//-------------------------------------------------------------------

HRESULT GenerateASFDataPackets( 
    IMFASFMultiplexer *pMux, 
    IMFByteStream *pDataStream
    )
{
    HRESULT hr = S_OK;

    IMFSample *pOutputSample = NULL;
    IMFMediaBuffer *pDataPacketBuffer = NULL;

    DWORD dwMuxStatus = ASF_STATUSFLAGS_INCOMPLETE;

    while (dwMuxStatus & ASF_STATUSFLAGS_INCOMPLETE)
    {
        hr = pMux->GetNextPacket(&dwMuxStatus, &pOutputSample);

        if (FAILED(hr))
        {
            break;
        }

        if (pOutputSample)
        {
            //Convert to contiguous buffer
            hr = pOutputSample->ConvertToContiguousBuffer(&pDataPacketBuffer);
            
            if (FAILED(hr))
            {
                break;
            }

            //Write buffer to byte stream
            hr = WriteBufferToByteStream(pDataStream, pDataPacketBuffer, NULL);

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

        SafeRelease(&pDataPacketBuffer);
        SafeRelease(&pOutputSample);
    }

    SafeRelease(&pOutputSample);
    SafeRelease(&pDataPacketBuffer);
    return hr;
}

8. Tulis Objek ASF dalam File Baru

Selanjutnya, Anda akan menulis konten objek ContentInfo output ke buffer media dengan memanggil IMFASFContentInfo::GenerateHeader. Metode ini mengonversi data yang disimpan dalam objek ContentInfo menjadi data biner dalam format Objek Header ASF. Untuk informasi selengkapnya, lihat Membuat Objek Header ASF Baru.

Setelah Objek Header ASF baru dibuat, tulis file output dengan terlebih dahulu menulis Objek Header ke aliran byte output yang dibuat di langkah 2 dengan memanggil fungsi pembantu WriteBufferToByteStream. Ikuti Objek Header dengan Objek Data yang terkandung dalam aliran byte data. Contoh kode menunjukkan fungsi yang mentransfer konten aliran byte data ke aliran byte output.

//-------------------------------------------------------------------
// WriteASFFile
//
// Writes the complete ASF file.
//-------------------------------------------------------------------

HRESULT WriteASFFile( 
    IMFASFContentInfo *pContentInfo, // ASF Content Info for the output file.
    IMFByteStream *pDataStream,      // Data stream.
    PCWSTR pszFile                   // Output file name.
    )
{
    
    IMFMediaBuffer *pHeaderBuffer = NULL;
    IMFByteStream *pWmaStream = NULL;

    DWORD cbHeaderSize = 0;
    DWORD cbWritten = 0;

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

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

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

    // Populate the media buffer with the ASF Header Object.
    if (SUCCEEDED(hr))
    {
        hr = pContentInfo->GenerateHeader(pHeaderBuffer, &cbHeaderSize);
    }
 
    // Write the header contents to the byte stream for the output file.
    if (SUCCEEDED(hr))
    {
        hr = WriteBufferToByteStream(pWmaStream, pHeaderBuffer, &cbWritten);
    }

    if (SUCCEEDED(hr))
    {
        hr = pDataStream->SetCurrentPosition(0);
    }

    // Append the data stream to the file.

    if (SUCCEEDED(hr))
    {
        hr = AppendToByteStream(pDataStream, pWmaStream);
    }

    SafeRelease(&pHeaderBuffer);
    SafeRelease(&pWmaStream);

    return hr;
}

9 Tulis Fungsi Entry-Point

Sekarang Anda dapat menggabungkan langkah-langkah sebelumnya ke dalam aplikasi lengkap. Sebelum menggunakan salah satu objek Media Foundation, inisialisasi platform Media Foundation dengan memanggil MFStartup. Setelah selesai, hubungi MFShutdown. Untuk informasi selengkapnya, lihat Menginisialisasi Media Foundation.

Kode berikut menunjukkan aplikasi konsol lengkap. Argumen baris perintah menentukan nama file yang akan dikonversi dan nama file audio baru.

int wmain(int argc, WCHAR* argv[])
{
    if (argc != 3)
    {
        wprintf_s(L"Usage: %s input.wmv, %s output.wma\n");
        return 0;
    }

    HRESULT hr = MFStartup(MF_VERSION);

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

    PCWSTR pszInputFile = argv[1];      
    PCWSTR pszOutputFile = argv[2];     
    
    IMFByteStream      *pSourceStream = NULL;       
    IMFASFContentInfo  *pSourceContentInfo = NULL;  
    IMFASFProfile      *pAudioProfile = NULL;       
    IMFASFContentInfo  *pOutputContentInfo = NULL;  
    IMFByteStream      *pDataStream = NULL;         
    IMFASFSplitter     *pSplitter = NULL;           
    IMFASFMultiplexer  *pMux = NULL;                

    UINT64  cbDataOffset = 0;           
    UINT64  cbDataLength = 0;           
    WORD    wSelectStreamNumber = 0;    

    // Open the input file.

    hr = OpenFile(pszInputFile, &pSourceStream);

    // Initialize the objects that will parse the source file.

    if (SUCCEEDED(hr))
    {
        hr = CreateSourceParsers(
            pSourceStream, 
            &pSourceContentInfo,    // ASF Header for the source file.
            &pSplitter,             // Generates audio samples.
            &cbDataOffset,          // Offset to the first data packet.
            &cbDataLength           // Length of the ASF Data Object.
            );
    }

    // Create a profile object for the audio streams in the source file.

    if (SUCCEEDED(hr))
    {
        hr = GetAudioProfile(
            pSourceContentInfo, 
            &pAudioProfile,         // ASF profile for the audio stream.
            &wSelectStreamNumber    // Stream number of the first audio stream.
            );
    }

    // Initialize the objects that will generate the output data.

    if (SUCCEEDED(hr))
    {
        hr = CreateOutputGenerators(
            pAudioProfile, 
            &pOutputContentInfo,    // ASF Header for the output file.
            &pMux                   // Generates ASF data packets.
            );
    }

    // Set up the splitter to generate samples for the first
    // audio stream in the source media.

    if (SUCCEEDED(hr))
    {
        hr = pSplitter->SelectStreams(&wSelectStreamNumber, 1);
    }
    
    // Generate ASF Data Packets and store them in a byte stream.

    if (SUCCEEDED(hr))
    {
        hr = GenerateASFDataObject(
               pSourceStream, 
               pSplitter, 
               pMux, 
               cbDataOffset, 
               cbDataLength, 
               &pDataStream    // Byte stream for the ASF data packets.    
               );
    }

    // Update the header with new information if any.

    if (SUCCEEDED(hr))
    {
        hr = pMux->End(pOutputContentInfo);
    }

    //Write the ASF objects to the output file
    if (SUCCEEDED(hr))
    {
        hr = WriteASFFile(pOutputContentInfo, pDataStream, pszOutputFile);
    }

    // Clean up.
    SafeRelease(&pMux);
    SafeRelease(&pSplitter);
    SafeRelease(&pDataStream);
    SafeRelease(&pOutputContentInfo);
    SafeRelease(&pAudioProfile);
    SafeRelease(&pSourceContentInfo);
    SafeRelease(&pSourceStream);

    MFShutdown();

    if (FAILED(hr))
    {
        wprintf_s(L"Could not create the audio file: 0x%X\n", hr);
    }

    return 0;
}

Komponen WMContainer ASF

Dukungan ASF di Media Foundation