Поделиться через


Руководство по копированию потоков ASF с помощью объектов WMContainer

Одним из способов создания ASF-файла является копирование потоков ASF из существующего файла. Для этого можно получить данные мультимедиа из исходного файла и записать в выходной файл. Если исходный файл является файлом ASF, можно скопировать примеры потоков без распаковки и повторного сжатия.

В этом руководстве показано, как извлечь первый аудиопоток из аудиофайла ASF (.wmv) и скопировать его в новый звуковой файл ASF (.wma). В этом руководстве вы создадите консольное приложение, которое принимает входные и выходные имена файлов в качестве аргументов. Приложение использует разделитель ASF для синтаксического анализа входных потоков, а затем отправляет их в мультиплексер ASF для записи пакетов данных ASF для аудиопотока.

В этом руководстве содержатся следующие действия.

Необходимые условия

В этом руководстве предполагается следующее:

  • Вы знакомы со структурой ASF-файла и компонентами, предоставляемыми Media Foundation для работы с объектами ASF. К этим компонентам относятся ContentInfo, разделитель, мультиплексор и объекты профиля. Дополнительную информацию см. в разделе компоненты ASF WMContainer.
  • Вы знакомы с процессом синтаксического анализа объекта заголовка ASF и пакетов данных ASF существующего файла и создания сжатых примеров потоков с помощью разбиения. Дополнительные сведения см. в руководстве "Чтение файла ASF".
  • Вы знакомы с буферами мультимедиа и потоками байтов: в частности, операции с файлами с помощью потока байтов и запись содержимого буфера мультимедиа в байтовый поток. (См. 2. Объявление вспомогательных функций.)

Терминология

В этом руководстве используются следующие термины:

  • Исходный поток байтов: объект потока байтов, предоставляющий интерфейс IMFByteStream, который содержит содержимое входного файла.
  • Объект Source ContentInfo: объект ContentInfo предоставляет интерфейс IMFASFContentInfo, который представляет заголовочный объект ASF для входного файла.
  • Звуковой профиль: объект профиля, предоставляет интерфейс IMFASFProfile, который содержит только звуковые потоки входного файла.
  • Пример потока: мультимедийный образец предоставляет интерфейс IMFSample, созданный сплиттером, который представляет собой данные мультимедиа выбранного потока, извлеченные из входного файла в сжатом состоянии.
  • Объект Output ContentInfo: Объект ContentInfo предоставляет интерфейс IMFASFContentInfo, представляющий объект заголовка ASF выходного файла.
  • Поток данных в байтах: объект байтового потока предоставляет интерфейс IMFByteStream, который представляет всю часть объекта данных ASF в выходном файле.
  • Пакет данных: пример мультимедиа предоставляет интерфейс IMFSample, созданный мультиплексером, представляет пакет данных ASF, который будет записан в поток данных байтов.
  • Выходной поток байтов: объект потока байтов предоставляет интерфейс IMFByteStream, содержащий содержимое выходного файла.

1. Настройка проекта

Включите следующие заголовки в исходный файл:

#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

Ссылка на следующие файлы библиотеки:

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

Объявите функцию SafeRelease:

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

2. Объявление вспомогательных функций

В этом руководстве используются следующие вспомогательные функции для чтения и записи из потока байтов.

Функция AllocReadFromByteStream считывает данные из потока байтов и выделяет новый буфер мультимедиа для хранения данных. Дополнительные сведения см. в разделе 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;
}

Функция WriteBufferToByteStream записывает данные из буфера мультимедиа в поток байтов. Дополнительные сведения см. в разделе 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;
}

Функция AppendToByteStream добавляет содержимое одного потока байтов к другому:

//-------------------------------------------------------------------
// 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. Открытие входного ASF-файла

Откройте входной файл, вызвав функцию MFCreateFile. Метод возвращает указатель на объект потока байтов, содержащий содержимое файла. Имя файла указывается пользователем с помощью аргументов командной строки приложения.

Следующий пример кода принимает имя файла и возвращает указатель на объект потока байтов, который можно использовать для чтения файла.

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

4. Инициализация объектов для входного файла

Затем вы создадите и инициализируете исходный объект ContentInfo и разделитель для создания примеров потоков.

Этот исходный поток байтов, созданный на шаге 2, будет использоваться для анализа объекта заголовка ASF и заполнения исходного объекта ContentInfo. Этот объект будет использоваться для инициализации разделителя для упрощения синтаксического анализа пакетов данных ASF для аудиопотока во входном файле. Вы также получите длину объекта данных ASF в входном файле и смещение к первому пакету данных ASF относительно начала файла. Эти атрибуты будут использоваться разделителем для создания образцов аудиопотока.

Чтобы создать и инициализировать компоненты ASF для входного файла:

  1. Вызовите MFCreateASFContentInfo для создания объекта ContentInfo. Эта функция возвращает указатель на интерфейс IMFASFContentInfo.
  2. Вызовите IMFASFContentInfo::ParseHeader для анализа заголовка ASF. Дополнительные сведения об этом шаге см. в Чтение объекта заголовка ASF существующего файла.
  3. Вызовите MFCreateASFSplitter для создания объекта разделения ASF. Эта функция возвращает указатель на интерфейс IMFASFSplitter.
  4. Вызов IMFASFSplitter::Initialize, передавая указатель IMFASFContentInfo. Дополнительные сведения об этом шаге см. в разделе Создание объекта-разделителя ASF.
  5. Вызовите IMFASFContentInfo::GeneratePresentationDescriptor, чтобы получить дескриптор презентации для файла ASF.
  6. Получите значение атрибута MF_PD_ASF_DATA_START_OFFSET из дескриптора презентации. Это значение является расположением объекта данных ASF в файле в виде смещения в байтах от начала файла.
  7. Получите значение атрибута MF_PD_ASF_DATA_LENGTH из дескриптора презентации. Это значение — общий размер объекта данных ASF в байтах. Дополнительные сведения см. в разделе Получение сведений из объектов заголовков ASF.

В следующем примере кода показана функция, которая объединяет все шаги. Эта функция принимает указатель на исходный поток байтов и возвращает указатели на исходный объект ContentInfo и разделитель. Кроме того, он получает длину и смещение для объекта данных 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. Создание профиля звука

Затем вы создадите объект профиля для входного файла, получив его из исходного объекта ContentInfo. Затем вы настроите профиль таким образом, чтобы он содержал только звуковые потоки входного файла. Для этого перечислите потоки и удалите потоки, отличные от звука, из профиля. Объект профиля звука будет использоваться далее в этом руководстве для инициализации выходного объекта ContentInfo.

Чтобы создать звуковой профиль

  1. Получите объект профиля для входного файла из исходного объекта ContentInfo, вызвав IMFASFContentInfo::GetProfile. Метод возвращает указатель на объект профиля, содержащий все потоки во входном файле. Дополнительные сведения см. в создании профиля ASF.
  2. Удалите все объекты взаимного исключения из профиля. Этот шаг необходим, так как потоки, отличные от звука, будут удалены из профиля, что может привести к недопустимости объектов взаимного исключения.
  3. Удалите все потоки, отличные от звука, из профиля, как показано ниже.
  4. Сохраните номер потока первого аудиопотока. Этот параметр будет выбран в разделителе для создания образцов потоков. Если номер потока равен нулю, вызывающий может предположить, что не было файла аудиопотоков.

Следующий код содержит следующие шаги:

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

Функция RemoveMutexes удаляет все объекты взаимного исключения из профиля:

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. Инициализация объектов для выходного файла

Затем вы создадите выходной объект ContentInfo и мультиплексер для создания пакетов данных для выходного файла.

Звуковой профиль, созданный на шаге 4, будет использоваться для заполнения выходного объекта ContentInfo. Этот объект содержит такие сведения, как глобальные атрибуты файла и свойства потока. Выходной объект ContentInfo будет использоваться для инициализации мультиплексера, который создаст пакеты данных для выходного файла. После создания пакетов данных объект ContentInfo должен быть обновлен, чтобы отразить новые значения.

Создание и инициализация компонентов ASF для выходного файла

  1. Создайте пустой объект ContentInfo, вызвав MFCreateASFContentInfo и заполните его сведениями из звукового профиля, созданного на шаге 3, путем вызова МВФASFContentInfo::SetProfile. Дополнительные сведения см. в инициализации объекта ContentInfo нового ASF-файла.
  2. Создайте и инициализировать объект мультиплексера с помощью выходного объекта ContentInfo. Более подробную информацию см. в разделе "Создание объекта мультиплексера".

В следующем примере кода показана функция, которая объединяет шаги. Эта функция принимает указатель на объект профиля и возвращает указатели на выходной объект ContentInfo и мультиплексер.

//-------------------------------------------------------------------
// 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. Создание новых пакетов данных ASF

Затем вы создадите примеры аудиопотока из исходного потока байтов с помощью разделителя и отправите их в мультиплексор для создания пакетов данных ASF. Эти пакеты данных будут представлять собой окончательный объект данных ASF для нового файла.

Для создания образцов аудиопотоков

  1. Выберите первый аудиопоток в разделителе, вызвав метод IMFASFSplitter::SelectStreams.
  2. Чтение блоков данных мультимедиа фиксированного размера из исходного потока байтов в буфер мультимедиа.
  3. Собирайте примеры потока как мультимедийные образцы из разделителя, циклически вызывая IMFASFSplitter::GetNextSample, пока в параметре pdwStatusFlags поступает флаг состояния ASF_STATUSFLAGS_INCOMPLETE. Дополнительные сведения см. в разделе "Генерация образцов для пакетов данных ASF" в "Генерация образцов потоков из существующего объекта данных ASF".
  4. Для каждого медиа-образца вызовите IMFASFMultiplexer::ProcessSample, чтобы отправить его в мультиплексер. Мультиплексер создает пакеты данных для объекта данных ASF.
  5. Запишите пакет данных, созданный мультиплексером, в поток байтов данных.
  6. После создания всех пакетов данных вызовите IMFASFMultiplexer::End для обновления выходного объекта ContentInfo с информацией, собираемой во время создания пакетов данных ASF.

В следующем примере кода генерируются семплы потока из ASF-разделителя и отправляются в мультиплексор. Мультиплексер создает пакеты данных ASF и записывает его в поток.

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

Чтобы получить пакеты из разделителя ASF, предыдущий код вызывает функцию GetPacketsFromSplitter, показанную здесь:

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

Функция GenerateDataPackets получает пакеты данных из мультиплексера. Дополнительные сведения см. в разделе Получение пакетов данных 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. Запишите объекты ASF в новый файл

Затем вы напишете содержимое выходного объекта ContentInfo в буфер мультимедиа, вызвав IMFASFContentInfo::GenerateHeader. Этот метод преобразует данные, хранящиеся в объекте ContentInfo, в двоичные данные в формате объекта заголовка ASF. Дополнительные сведения см. в статье Создание нового объекта заголовка ASF.

После создания нового объекта заголовка ASF запишите выходной файл, сначала записав объект заголовка в выходной поток байтов, созданный на шаге 2, вызвав вспомогательную функцию WriteBufferToByteStream. Следом за объектом заголовка идёт объект данных, содержащийся в потоке данных. В примере кода показана функция, которая передает содержимое потока байтов данных в выходной поток байтов.

//-------------------------------------------------------------------
// 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 Напишите функцию Entry-Point

Теперь вы можете объединить предыдущие шаги в полное приложение. Перед использованием любого из объектов Media Foundation инициализировать платформу Media Foundation путем вызова MFStartup. По завершении вызовите MFShutdown. Дополнительные сведения по теме см. в инициализация Media Foundation.

В следующем коде показано полное консольное приложение. Аргумент командной строки указывает имя файла для преобразования и имя нового звукового файла.

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

компонентов ASF WMContainer

поддержка ASF в Media Foundation