Condividi tramite


Esercitazione: Copia di flussi ASF usando oggetti WMContainer

Un modo per creare un file ASF consiste nel copiare i flussi ASF da un file esistente. A tale scopo, è possibile recuperare i dati multimediali dal file di origine e scrivere nel file di output. Se il file di origine è un file ASF, è possibile copiare esempi di flusso senza decompressione e ricompressione.

Questa esercitazione illustra questo scenario estraendo il primo flusso audio da un file audio-video asF (con estensione wmv) e copiandolo in un nuovo file audio ASF (con estensione wma). In questa esercitazione verrà creata un'applicazione console che accetta i nomi di file di input e output come argomenti. L'applicazione usa il splitter ASF per analizzare gli esempi di flusso di input e quindi li invia a ASF Multiplexer per scrivere i pacchetti di dati ASF per il flusso audio.

Questa esercitazione include i passaggi seguenti:

Prerequisiti

Nell’esercitazione si presuppongono le condizioni seguenti:

  • Si ha familiarità con la struttura di un file ASF e i componenti forniti da Media Foundation per lavorare con gli oggetti ASF. Questi componenti includono ContentInfo, splitter, multiplexer e oggetti profile. Per altre informazioni, vedere Componenti ASF WMContainer.
  • Si ha familiarità con il processo di analisi dell'oggetto intestazione ASF e i pacchetti di dati ASF di un file esistente e la generazione di esempi di flusso compressi usando il splitter. Per altre informazioni, vedere Esercitazione: Lettura di un file ASF.
  • Si ha familiarità con i buffer multimediali e i flussi di byte: in particolare, le operazioni di file usando un flusso di byte e la scrittura del contenuto di un buffer multimediale in un flusso di byte. (Vedere 2. Dichiarare funzioni helper.

Terminologia

Questa esercitazione usa i termini seguenti:

  • Flusso di byte di origine: l'oggetto flusso Byte espone l'interfaccia FMByteStream che contiene il contenuto del file di input.
  • Oggetto ContentInfo di origine: oggetto ContentInfo espone l'interfaccia IMFASFContentInfo , che rappresenta l'oggetto intestazione ASF del file di input.
  • Profilo audio: l'oggetto Profile espone l'interfaccia IMFASFProfile , che contiene solo flussi audio del file di input.
  • Esempio di flusso: l'esempio multimediale espone l'interfaccia IMFSample , generata dal splitter rappresenta i dati multimediali del flusso selezionato ottenuti dal file di input in stato compresso.
  • Oggetto ContentInfo di output: oggetto ContentInfo espone l'interfaccia IMFASFContentInfo , che rappresenta l'oggetto intestazione ASF del file di output.
  • Flusso di byte di dati: l'oggetto flusso Byte espone l'interfaccia FMByteStream , che rappresenta l'intera parte dell'oggetto dati ASF del file di output.
  • Pacchetto di dati: l'esempio multimediale espone l'interfaccia IMFSample , generata dal multiplexer rappresenta un pacchetto di dati ASF che verrà scritto nel flusso di byte dei dati.
  • Flusso di byte di output: l'oggetto flusso byte espone l'interfaccia FMByteStream che contiene il contenuto del file di output.

1. Configurare il progetto

Includere le intestazioni seguenti nel file di origine:

#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

Collegamento ai file di libreria seguenti:

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

Dichiarare la funzione SafeRelease :

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

2. Dichiarare funzioni helper

Questa esercitazione usa le funzioni helper seguenti per leggere e scrivere da un flusso di byte.

La AllocReadFromByteStream funzione legge i dati da un flusso di byte e alloca un nuovo buffer multimediale per contenere i dati. Per altre informazioni, vedere FMByteStream::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;
}

La WriteBufferToByteStream funzione scrive i dati da un buffer multimediale a un flusso di byte. Per altre informazioni, vedere FMByteStream::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;
}

La AppendToByteStream funzione aggiunge il contenuto di un flusso di byte a un altro:

//-------------------------------------------------------------------
// 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. Aprire il file ASF di input

Aprire il file di input chiamando la funzione MFCreateFile . Il metodo restituisce un puntatore all'oggetto flusso di byte che contiene il contenuto del file. Il nome del file viene specificato dall'utente tramite argomenti della riga di comando dell'applicazione.

Il codice di esempio seguente accetta un nome file e restituisce un puntatore a un oggetto flusso di byte che può essere usato per leggere il file.

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

4. Inizializzare gli oggetti per il file di input

Verrà quindi creato e inizializzato l'oggetto ContentInfo di origine e il splitter per la generazione di esempi di flusso.

Questo flusso di byte di origine creato nel passaggio 2 verrà usato per analizzare l'oggetto Intestazione ASF e popolare l'oggetto ContentInfo di origine. Questo oggetto verrà usato per inizializzare il splitter per facilitare l'analisi dei pacchetti di dati ASF per il flusso audio nel file di input. Si recupera anche la lunghezza dell'oggetto dati ASF nel file di input e l'offset al primo pacchetto di dati ASF rispetto all'inizio del file. Questi attributi verranno usati dal splitter per generare esempi di flusso audio.

Per creare e inizializzare i componenti ASF per il file di input:

  1. Chiamare MFCreateASFContentInfo per creare l'oggetto ContentInfo. Questa funzione restituisce un puntatore all'interfaccia FMASFContentInfo .
  2. Chiamare FMASFContentInfo::P arseHeader per analizzare l'intestazione ASF. Per altre informazioni su questo passaggio, vedere Lettura dell'oggetto intestazione ASF di un file esistente.
  3. Chiamare MFCreateASFSplitter per creare l'oggetto splitter ASF. Questa funzione restituisce un puntatore all'interfaccia FMASFSplitter .
  4. Chiamare FMASFSplitter::Initialize, passando il puntatore IMFASFContentInfo. Per altre informazioni su questo passaggio, vedere Creazione dell'oggetto Splitter ASF.
  5. Chiamare FMASFContentInfo::GeneratePresentationDescriptor per ottenere un descrittore di presentazione per il file ASF.
  6. Ottenere il valore dell'attributo MF_PD_ASF_DATA_START_OFFSET dal descrittore di presentazione. Questo valore è il percorso dell'oggetto dati ASF nel file, come offset di byte dall'inizio del file.
  7. Ottenere il valore dell'attributo MF_PD_ASF_DATA_LENGTH dal descrittore di presentazione. Questo valore è la dimensione totale dell'oggetto dati ASF, in byte. Per altre informazioni, vedere Recupero di informazioni dagli oggetti intestazione ASF.

Il codice di esempio seguente mostra una funzione che consolida tutti i passaggi. Questa funzione accetta un puntatore al flusso di byte di origine e restituisce puntatori all'oggetto ContentInfo di origine e al splitter. Riceve inoltre la lunghezza e gli offset nell'oggetto dati 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. Creare un profilo audio

Verrà quindi creato un oggetto profilo per il file di input ottenendolo dall'oggetto ContentInfo di origine. Verrà quindi configurato il profilo in modo che contenga solo i flussi audio del file di input. A tale scopo, enumerare i flussi e rimuovere flussi non audio dal profilo. L'oggetto profilo audio verrà usato più avanti in questa esercitazione per inizializzare l'oggetto ContentInfo di output.

Per creare un profilo audio

  1. Ottenere l'oggetto profile per il file di input dall'oggetto ContentInfo di origine chiamando FMASFContentInfo::GetProfile. Il metodo restituisce un puntatore a un oggetto profile che contiene tutti i flussi nel file di input. Per altre informazioni, vedere Creazione di un profilo ASF.
  2. Rimuovere tutti gli oggetti di esclusione reciproca dal profilo. Questo passaggio è obbligatorio perché i flussi non audio verranno rimossi dal profilo, che potrebbe invalidare gli oggetti di esclusione reciproca.
  3. Rimuovere tutti i flussi non audio dal profilo, come segue:
  4. Archiviare il numero di flusso del primo flusso audio. Questa opzione verrà selezionata nella barra di divisione per generare esempi di flusso. Se il numero di flusso è zero, il chiamante può presupporre che non vi fosse alcun file di flussi audio.

Il codice seguente segue:

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

La RemoveMutexes funzione rimuove tutti gli oggetti di esclusione reciproca dal profilo:

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. Inizializzare gli oggetti per il file di output

Successivamente, si creerà l'oggetto ContentInfo di output e il multiplexer per la generazione di pacchetti di dati per il file di output.

Il profilo audio creato nel passaggio 4 verrà usato per popolare l'oggetto ContentInfo di output. Questo oggetto contiene informazioni quali attributi di file globali e proprietà del flusso. L'oggetto ContentInfo di output verrà utilizzato per inizializzare il multiplexer che genererà pacchetti di dati per il file di output. Dopo la generazione dei pacchetti di dati, l'oggetto ContentInfo deve essere aggiornato per riflettere i nuovi valori.

Per creare e inizializzare componenti ASF per il file di output

  1. Creare un oggetto ContentInfo vuoto chiamando MFCreateASFContentInfo e popolarlo con le informazioni del profilo audio creato nel passaggio 3 chiamando IMFASFContentInfo::SetProfile. Per altre informazioni, vedere Inizializzazione dell'oggetto ContentInfo di un nuovo file ASF.
  2. Creare e inizializzare l'oggetto multiplexer usando l'oggetto ContentInfo di output. Per altre informazioni, vedere Creazione dell'oggetto Multiplexer.

Il codice di esempio seguente mostra una funzione che consolida i passaggi. Questa funzione accetta un puntatore a un oggetto profilo e restituisce puntatori all'oggetto ContentInfo di output e al 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. Generare nuovi pacchetti di dati ASF

Successivamente, si genereranno esempi di flusso audio dal flusso di byte di origine usando il splitter e li si invierà al multiplexer per creare pacchetti di dati ASF. Questi pacchetti di dati costituiscono l'oggetto dati ASF finale per il nuovo file.

Per generare esempi di flusso audio

  1. Selezionare il primo flusso audio nel separatore chiamando IMFASFSplitter::SelectStreams.
  2. Legge blocchi a dimensione fissa di dati multimediali dal flusso di byte di origine in un buffer multimediale.
  3. Raccogliere gli esempi di flusso come campioni multimediali dal separatore chiamando IMFASFSplitter::GetNextSample in un ciclo purché riceva il flag ASF_STATUSFLAGS_INCOMPLETE nel parametro pdwStatusFlags . Per altre informazioni, vedere Generazione di esempi per pacchetti di dati ASF" in Generazione di esempi di flusso da un oggetto dati ASF esistente.
  4. Per ogni esempio multimediale, chiamare IMFASFMultiplexer::P rocessSample per inviare l'esempio multimediale al multiplexer. Il multiplexer genera i pacchetti di dati per l'oggetto dati ASF.
  5. Scrivere il pacchetto di dati generato dal multiplexer nel flusso di byte dei dati.
  6. Dopo aver generato tutti i pacchetti di dati, chiamare IMFASFMultiplexer::End per aggiornare l'oggetto ContentInfo di output con le informazioni raccolte durante la generazione di pacchetti di dati ASF.

Il codice di esempio seguente genera esempi di flusso dal splitter ASF e li invia al multiplexer. Il multiplexer genera pacchetti di dati ASF e lo scrive in un flusso.

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

Per ottenere pacchetti dal componente di divisione ASF, il codice precedente chiama la GetPacketsFromSplitter funzione, come illustrato di seguito:

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

La GenerateDataPackets funzione ottiene pacchetti di dati da multiplexer. Per altre informazioni, vedere Recupero di pacchetti di dati 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. Scrivere gli oggetti ASF nel nuovo file

Successivamente, si scriverà il contenuto dell'oggetto ContentInfo di output in un buffer multimediale chiamando IMFASFContentInfo::GenerateHeader. Questo metodo converte i dati archiviati nell'oggetto ContentInfo in dati binari in formato oggetto intestazione ASF. Per altre informazioni, vedere Generazione di un nuovo oggetto intestazione ASF.

Dopo aver generato il nuovo oggetto intestazione ASF, scrivere il file di output scrivendo prima l'oggetto header nel flusso di byte di output creato nel passaggio 2 chiamando la funzione helper WriteBufferToByteStream. Seguire l'oggetto Header con l'oggetto dati contenuto nel flusso di byte dati. Il codice di esempio mostra una funzione che trasferisce il contenuto del flusso dei byte di dati al flusso di byte di 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 Scrivere la funzione Entry-Point

È ora possibile mettere insieme i passaggi precedenti in un'applicazione completa. Prima di usare uno degli oggetti Media Foundation, inizializzare la piattaforma Media Foundation chiamando MFStartup. Al termine, chiama MFShutdown. Per altre informazioni, vedere Inizializzazione di Media Foundation.

Il codice seguente mostra l'applicazione console completa. L'argomento della riga di comando specifica il nome del file da convertire e il nome del nuovo file audio.

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

Componenti di WMContainer ASF

Supporto di ASF in Media Foundation