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


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

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

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

Этот учебник содержит следующие действия.

Предварительные требования

В этом учебнике предполагается следующее:

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

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

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

  • Исходный поток байтов: объект потока байтов, предоставляющий интерфейс IMFByteStream , который содержит содержимое входного файла.
  • Исходный объект ContentInfo: объект ContentInfo, предоставляет интерфейс IMFASFContentInfo , который представляет объект заголовка ASF входного файла.
  • Профиль звука: объект Profile предоставляет интерфейс IMFASFProfile , который содержит только аудиопотоки входного файла.
  • Пример потока. Пример мультимедиа, предоставляющий интерфейс IMFSample , созданный разделием, представляет данные мультимедиа выбранного потока, полученные из входного файла в сжатом состоянии.
  • Выходной объект 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::P arseHeader для анализа заголовка 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, вызвав IMFASFContentInfo::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 в цикле, если он получает флаг ASF_STATUSFLAGS_INCOMPLETE в параметре pdwStatusFlags . Дополнительные сведения см. в разделе Создание примеров для пакетов данных ASF статьи Создание примеров потока из существующего объекта данных ASF.
  4. Для каждого примера мультимедиа вызовите IMFASFMultiplexer::P rocessSample , чтобы отправить образец мультимедиа в мультиплексор. Мультиплексор создает пакеты данных для объекта данных 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