Tutorial: Copiando fluxos ASF usando objetos WMContainer
Uma maneira de criar um arquivo ASF é copiar fluxos ASF de um arquivo existente. Para fazer isso, você pode recuperar os dados de mídia do arquivo de origem e gravar no arquivo de saída. Se o arquivo de origem for um arquivo ASF, você poderá copiar amostras de fluxo sem descompactá-las e recompactá-las.
Este tutorial demonstra esse cenário extraindo o primeiro fluxo de áudio de um arquivo de áudio-vídeo ASF (.wmv) e copiando-o para um novo arquivo de áudio ASF (.wma). Neste tutorial, você criará um aplicativo de console que usa os nomes de arquivos de entrada e saída como argumentos. O aplicativo usa o ASF Splitter para analisar as amostras de fluxo de entrada e, em seguida, envia-as para o multiplexador ASF para gravar os pacotes de dados ASF para o fluxo de áudio.
Este tutorial contém as seguintes etapas:
- Pré-requisitos
- Terminologia
- 1. Configurar o projeto
- 2. Declarar funções auxiliares
- 3. Abra o arquivo ASF de entrada
- 4. Inicializar objetos para o arquivo de entrada
- 5. Criar um perfil de áudio
- 6. Inicializar objetos para o arquivo de saída
- 7. Gerar novos pacotes de dados ASF
- 8. Escreva os objetos ASF no novo arquivo
- 9 Escreva a função Entry-Point
- Tópicos relacionados
Pré-requisitos
Este tutorial pressupõe o seguinte:
- Você está familiarizado com a estrutura de um arquivo ASF e os componentes fornecidos pelo Media Foundation para trabalhar com objetos ASF. Esses componentes incluem ContentInfo, divisor, multiplexador e objetos de perfil. Para obter mais informações, consulte WMContainer ASF Components.
- Você está familiarizado com o processo de análise do objeto de cabeçalho ASF e os pacotes de dados ASF de um arquivo existente e gerar amostras de fluxo compactado usando o divisor. Para obter mais informações, consulte Tutorial: Lendo um arquivo ASF.
- Você está familiarizado com buffers de mídia e fluxos de bytes; especificamente, operações de arquivo usando um fluxo de bytes e a escrita do conteúdo de um buffer de mídia num fluxo de bytes. (Ver 2. Declarar funções auxiliares.)
Terminologia
Este tutorial usa os seguintes termos:
- Fluxo de bytes de origem: objeto de fluxo de bytes, expõe a interface IMFByteStream, que contém o conteúdo do arquivo de entrada.
- Objeto de Origem ContentInfo: objeto ContentInfo, expõe a interface da IMFASFContentInfo, que representa o objeto de cabeçalho ASF do ficheiro de entrada.
- Perfil de áudio: O objeto de perfil expõe a interface IMFASFProfile, que contém apenas fluxos de áudio do arquivo de entrada.
- Amostra de fluxo: A amostra de mídia expõe a interface IMFSample. Esta amostra, gerada pelo divisor, representa os dados de mídia do fluxo selecionado, obtidos em estado compactado do arquivo de entrada.
- Objeto de saída ContentInfo: expõe a interface IMFASFContentInfo, que representa o cabeçalho ASF do ficheiro de saída.
- Fluxo de bytes de dados: objeto de fluxo de bytes, expõe a interface IMFByteStream, que representa toda a seção do objeto de dados ASF do ficheiro de saída.
- Pacote de dados: Amostra de mídia, expõe interface IMFSample , gerada pelo multiplexador, representa um pacote de dados ASF que será gravado no fluxo de bytes de dados.
- Fluxo de bytes de saída: objeto de fluxo de bytes que expõe a interface IMFByteStream , que contém o conteúdo do ficheiro de saída.
1. Configurar o projeto
Inclua os seguintes cabeçalhos no arquivo de origem:
#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
Link para os seguintes arquivos de biblioteca:
- mfplat.lib
- mf.lib
- mfuuid.lib
Declare a função SafeRelease:
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
2. Declarar funções auxiliares
Este tutorial usa as seguintes funções auxiliares para ler e gravar a partir de um fluxo de bytes.
A função AllocReadFromByteStream
lê dados de um fluxo de bytes e aloca um novo buffer de mídia para armazenar os dados. Para obter mais informações, consulte 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;
}
A função WriteBufferToByteStream
grava dados de um buffer de mídia em um fluxo de bytes. Para obter mais informações, consulte 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;
}
A função AppendToByteStream
acrescenta o conteúdo de um fluxo de bytes a outro:
//-------------------------------------------------------------------
// 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. Abra o arquivo ASF de entrada
Abra o arquivo de entrada chamando a função MFCreateFile. O método retorna um ponteiro para o objeto de fluxo de bytes que contém o conteúdo do arquivo. O nome do arquivo é especificado pelo usuário através de argumentos de linha de comando do aplicativo.
O código de exemplo a seguir usa um nome de arquivo e retorna um ponteiro para um objeto de fluxo de bytes que pode ser usado para ler o arquivo.
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
4. Inicializar objetos para o arquivo de entrada
Em seguida, você criará e inicializará o objeto ContentInfo de origem e o divisor para gerar amostras de fluxo.
Esse fluxo de bytes de origem criado na etapa 2 será usado para analisar o objeto de cabeçalho ASF e preencher o objeto ContentInfo de origem. Este objeto será usado para inicializar o divisor para facilitar a análise dos pacotes de dados ASF para o fluxo de áudio no arquivo de entrada. Você também recuperará o comprimento do objeto de dados ASF no arquivo de entrada e o deslocamento para o primeiro pacote de dados ASF relativo ao início do arquivo. Esses atributos serão usados pelo divisor para gerar amostras de fluxo de áudio.
Para criar e inicializar componentes ASF para o arquivo de entrada:
- Chame MFCreateASFContentInfo para criar o objeto ContentInfo. Esta função retorna um ponteiro para a IMFASFContentInfo interface.
- Chame IMFASFContentInfo::ParseHeader para analisar o cabeçalho ASF. Para obter mais informações sobre esta etapa, consulte Lendo o objeto de cabeçalho ASF de um arquivo existente.
- Chame MFCreateASFSplitter para criar o objeto divisor ASF. Esta função retorna um ponteiro para o interface IMFASFSplitter.
- Chame IMFASFSplitter::Initialize, passando o ponteiro IMFASFContentInfo . Para obter mais informações sobre esta etapa, consulte Criando o objeto divisor ASF.
- Chame IMFASFContentInfo::GeneratePresentationDescriptor para obter um descritor de apresentação para o arquivo ASF.
- Obtenha o valor do atributo MF_PD_ASF_DATA_START_OFFSET do descritor de apresentação. Este valor é a localização do objeto de dados ASF no arquivo, como um deslocamento em bytes desde o início do arquivo.
- Obtenha o valor do atributo MF_PD_ASF_DATA_LENGTH do descritor de apresentação. Esse valor é o tamanho total do objeto de dados ASF, em bytes. Para obter mais informações, consulte Obtendo informações de objetos de cabeçalho ASF.
O código de exemplo a seguir mostra uma função que consolida todas as etapas. Essa função usa um ponteiro para o fluxo de bytes de origem e retorna ponteiros para o objeto ContentInfo de origem e o divisor. Além disso, ele recebe o comprimento e os deslocamentos para o objeto de dados 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. Crie um perfil de áudio
Em seguida, você criará um objeto de perfil para o arquivo de entrada obtendo-o do objeto ContentInfo de origem. Em seguida, você configurará o perfil para que ele contenha apenas os fluxos de áudio do arquivo de entrada. Para fazer isso, enumere os fluxos e remova os fluxos que não são de áudio do perfil. O objeto de perfil de áudio será usado posteriormente neste tutorial para inicializar o objeto ContentInfo de saída.
Para criar um perfil de áudio
- Obtenha, do objeto ContentInfo de origem, o objeto de perfil para o arquivo de entrada chamando IMFASFContentInfo::GetProfile. O método retorna um ponteiro para um objeto de perfil que contém todos os fluxos no arquivo de entrada. Para obter mais informações, consulte Criando um perfil ASF.
- Remova todos os objetos de exclusão mútua do perfil. Esta etapa é necessária porque os fluxos que não são de áudio serão removidos do perfil, o que pode invalidar os objetos de exclusão mútua.
- Remova todos os fluxos que não sejam de áudio do perfil, da seguinte maneira:
- Ligue IMFASFProfile::GetStreamCount para obter o número de fluxos.
- Em um loop, chame IMFASFProfile::GetStream para obter cada fluxo por índice.
- Chame IMFASFStreamConfig::GetStreamType para obter o tipo de fluxo.
- Para fluxos que não sejam de áudio, chame IMFASFProfile::RemoveStream para remover o fluxo.
- Armazene o número do primeiro fluxo de áudio. Isso será selecionado no divisor para gerar amostras de fluxo. Se o número de fluxo for zero, o chamador pode presumir que não havia nenhum arquivo de fluxos de áudio.
O código a seguir executa estas etapas:
//-------------------------------------------------------------------
// 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;
}
A função RemoveMutexes
remove todos os objetos de exclusão mútua do perfil:
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. Inicializar objetos para o arquivo de saída
Em seguida, você criará o objeto ContentInfo de saída e o multiplexador para gerar pacotes de dados para o arquivo de saída.
O perfil de áudio criado na etapa 4 será usado para preencher o objeto ContentInfo de saída. Este objeto contém informações como atributos de arquivo global e propriedades de fluxo. O objeto ContentInfo de saída será usado para inicializar o multiplexador que gerará pacotes de dados para o arquivo de saída. Depois que os pacotes de dados são gerados, o objeto ContentInfo deve ser atualizado para refletir os novos valores.
Para criar e inicializar componentes ASF para o arquivo de saída
- Crie um objeto ContentInfo vazio chamando MFCreateASFContentInfo e preencha-o com informações do perfil de áudio criado na etapa 3 chamando IMFASFContentInfo::SetProfile. Para obter mais informações, consulte inicializando o objeto ContentInfo de um novo arquivo ASF.
- Crie e inicialize o objeto multiplexador usando o objeto ContentInfo de saída. Para obter mais informações, consulte Criando o objeto multiplexador.
O código de exemplo a seguir mostra uma função que consolida as etapas. Esta função leva um ponteiro para um objeto de perfil e retorna ponteiros para o objeto ContentInfo de saída e o multiplexador.
//-------------------------------------------------------------------
// 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. Gerar novos pacotes de dados ASF
Em seguida, você irá gerar amostras de fluxo de áudio do fluxo de bytes de origem usando o divisor e enviá-los para o multiplexador para criar pacotes de dados ASF. Esses pacotes de dados constituirão o objeto de dados ASF final para o novo arquivo.
Para gerar amostras de fluxo de áudio
- Selecione o primeiro fluxo de áudio no divisor chamando IMFASFSplitter::SelectStreams.
- Leia blocos de tamanho fixo de dados de mídia do fluxo de bytes de origem em um buffer de mídia.
- Colete as amostras de fluxo como amostras de mídia do divisor chamando IMFASFSplitter::GetNextSample em um loop, desde que receba o sinalizador ASF_STATUSFLAGS_INCOMPLETE no parâmetro pdwStatusFlags. Para obter mais informações, consulte Gerando amostras para pacotes de dados ASF" em Gerando amostras de fluxo de um objeto de dados ASF existente.
- Para cada amostra de mídia, chame IMFASFMultiplexer::P rocessSample para enviar a amostra de mídia para o multiplexador. O multiplexador gera os pacotes de dados para o objeto de dados ASF.
- Escreva o pacote de dados gerado pelo multiplexador no fluxo de bytes de dados.
- Depois que todos os pacotes de dados tiverem sido gerados, chame IMFASFMultiplexer::End para atualizar o objeto ContentInfo de saída com informações coletadas durante a geração de pacotes de dados ASF.
O código de exemplo a seguir gera amostras de fluxo do divisor ASF e as envia para o multiplexador. O multiplexador gera pacotes de dados ASF e grava-os em um fluxo.
//-------------------------------------------------------------------
// 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;
}
Para obter pacotes do divisor ASF, o código anterior chama a função GetPacketsFromSplitter
, mostrada aqui:
//-------------------------------------------------------------------
// 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;
}
A função GenerateDataPackets
obtém pacotes de dados do multiplexador. Para obter mais informações, consulte Obtendo pacotes de dados 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. Escreva os objetos ASF no novo arquivo
Em seguida, você gravará o conteúdo do objeto ContentInfo de saída em um buffer de mídia chamando IMFASFContentInfo::GenerateHeader. Esse método converte dados armazenados no objeto ContentInfo em dados binários no formato ASF Header Object. Para obter mais informações, consulte Gerando um novo objeto de cabeçalho ASF.
Depois que o novo objeto de cabeçalho ASF tiver sido gerado, grave o arquivo de saída primeiro gravando o objeto de cabeçalho no fluxo de bytes de saída criado na etapa 2 chamando a função auxiliar WriteBufferToByteStream. Siga o objeto de cabeçalho com o objeto de dados contido no fluxo de bytes de dados. O código de exemplo mostra uma função que transfere o conteúdo do fluxo de bytes de dados para o fluxo de bytes de saída.
//-------------------------------------------------------------------
// 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 Escreva a função Entry-Point
Agora você pode juntar as etapas anteriores em um aplicativo completo. Antes de usar qualquer um dos objetos do Media Foundation, inicialize a plataforma Media Foundation chamando MFStartup. Quando terminar, chame MFShutdown. Para obter mais informações, consulte Initializing Media Foundation.
O código a seguir mostra o aplicativo de console completo. O argumento de linha de comando especifica o nome do arquivo a ser convertido e o nome do novo arquivo de áudio.
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;
}
Tópicos relacionados