Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este tutorial demonstra como escrever um novo arquivo de áudio (.wma) extraindo conteúdo de mídia de um arquivo de áudio não compactado (.wav) e, em seguida, compactando-o no formato ASF. O modo de codificação usado para a conversão é Constant Bit Rate Encoding (CBR). Nesse modo, antes da sessão de codificação, o aplicativo especifica uma taxa de bits de destino que o codificador deve alcançar.
Neste tutorial, você criará um aplicativo de console que usa os nomes de arquivos de entrada e saída como argumentos. O aplicativo obtém as amostras de mídia não compactadas de um aplicativo de análise de arquivos wave, que é fornecido com este tutorial. Estas amostras são enviadas para o codificador para conversão para o formato Windows Media Audio 9. O codificador é configurado para codificação CBR e usa a primeira taxa de bits disponível durante a negociação do tipo de mídia como a taxa de bits de destino. As amostras codificadas são enviadas para o multiplexador para empacotamento no formato de dados ASF. Esses pacotes serão gravados em um fluxo de bytes que representa o objeto de dados ASF. Depois que a seção de dados estiver pronta, você criará um arquivo de áudio ASF e gravará o novo objeto de cabeçalho ASF que consolida todas as informações de cabeçalho e, em seguida, acrescentará o fluxo de bytes do objeto de dados ASF.
Este tutorial contém as seguintes seções:
- Pré-requisitos
- Terminologia
- 1. Configurar o projeto
- 2. Declarar funções auxiliares
- 3. Abrir um arquivo de áudio
- 4. Configurar o codificador
- 5. Crie o objeto ASF ContentInfo.
- 6. Criar o Multiplexador ASF
- 7. Gerar novos pacotes de dados ASF
- 8. Escreva o arquivo ASF
- 9. Definir 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 os codificadores do Windows Media e os vários tipos de codificação, particularmente CBR. Para mais informações, consulte Windows Media Encoder .
- Você está familiarizado com Media Buffers e fluxos de bytes: especificamente, operações de arquivo usando um fluxo de bytes e gravando o conteúdo de um buffer de mídia em um fluxo de bytes.
Terminologia
Este tutorial usa os seguintes termos:
- Tipo de mídia de origem: objeto de tipo de mídia, expõe interface IMFMediaType, que descreve o conteúdo do arquivo de entrada.
- Perfil de áudio: Objeto de perfil, expõe a interface IMFASFProfile, que contém apenas fluxos de áudio do arquivo de saída.
- Exemplo de fluxo: Exemplo de mídia, expõe interface IMFSample, representa os dados de mídia do arquivo de entrada obtidos do codificador em um estado compactado.
- Objeto ContentInfo: Objeto ASF ContentInfo, expõe a interfaceIMFASFContentInfo, que representa o Objeto de 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 parte ASF Data Object do arquivo de saída.
- Pacote de dados: amostra de multimédia, expõe a interface IMFSample, gerada pelo ASF Multiplexer; representa um pacote de dados ASF que será gravado no fluxo de dados em bytes.
- Fluxo de bytes de saída: objeto de fluxo de bytes, 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 <new> #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 codesLink para os seguintes arquivos de biblioteca:
- mfplat.lib
- mf.lib
- mfuuid.lib
Inclua a classe CWmaEncoder em seu projeto. Para obter o código-fonte completo dessa classe, consulte Encoder Example Code.
2. Declarar funções auxiliares
Este tutorial usa as seguintes funções auxiliares para ler e gravar a partir de um fluxo de bytes.
-
AppendToByteStream: Acrescenta o conteúdo de um fluxo de bytes a outro fluxo de bytes. - WriteBufferToByteStream: Grava dados de um buffer de mídia em um fluxo de bytes.
Para obter mais informações, consulte IMFByteStream::Write. O código a seguir mostra essas funções auxiliares:
//-------------------------------------------------------------------
// 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;
}
//-------------------------------------------------------------------
// 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;
}
3. Abra um arquivo de áudio
Este tutorial pressupõe que seu aplicativo gerará áudio não compactado para codificação. Para esse efeito, duas funções são declaradas neste tutorial:
HRESULT OpenAudioFile(PCWSTR pszURL, IMFMediaType **ppAudioFormat);
HRESULT GetNextAudioSample(BOOL *pbEOS, IMFSample **ppSample);
A implementação destas funções é deixada ao leitor.
- A função
OpenAudioFiledeve abrir o arquivo de mídia especificado por pszURL e retornar um ponteiro para um tipo de mídia que descreve um fluxo de áudio. - A função
GetNextAudioSampledeve ler áudio PCM não comprimido do ficheiro que foi aberto porOpenAudioFile. Quando o final do arquivo é atingido, pbEOS recebe o valor TRUE. Caso contrário, ppSample receberá uma amostra de mídia que contém o buffer de áudio.
4. Configurar o codificador
Em seguida, crie o codificador, configure-o para produzir amostras de fluxo codificadas em CBR e negocie os tipos de mídia de entrada e saída.
No Media Foundation, os codificadores (expõem o interface IMFTransform) são implementados como Media Foundation Transforms (MFT).
Neste tutorial, o codificador é implementado na classe CWmaEncoder que fornece um wrapper para o MFT. Para obter o código-fonte completo dessa classe, consulte Encoder Example Code.
Observação
Opcionalmente, você pode especificar o tipo de codificação como CBR. Por padrão, o codificador é configurado para usar a codificação CBR. Para obter mais informações, consulte Constant Bit Rate Encoding. Você pode definir propriedades adicionais dependendo do tipo de codificação, para obter informações sobre as propriedades específicas de um modo de codificação, consulte Quality-Based Variable Bit Rate Encoding, Unconsttrained Variable Bit Rate Encodinge Peak-Constrained Variable Bit Rate Encoding.
CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.
hr = OpenAudioFile(sInputFileName, &pInputType);
if (FAILED(hr))
{
goto done;
}
// Initialize the WMA encoder wrapper.
pEncoder = new (std::nothrow) CWmaEncoder();
if (pEncoder == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pEncoder->Initialize();
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetEncodingType(EncodeMode_CBR);
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetInputType(pInputType);
if (FAILED(hr))
{
goto done;
}
5. Crie o objeto ASF ContentInfo.
O ASF ContentInfo Object contém informações sobre os vários objetos de cabeçalho do arquivo de saída.
Primeiro, crie um perfil ASF para o fluxo de áudio:
- Chame MFCreateASFProfile para criar um objeto de perfil ASF vazio. O perfil ASF expõe a interface IMFASFProfile. Para obter mais informações, consulte Criando e configurando fluxos ASF.
- Obtenha o formato de áudio codificado do objeto
CWmaEncoder. - Chame IMFASFProfile::CreateStream para criar um novo fluxo para o perfil ASF. Passe um ponteiro para a interface IMFMediaType, que representa o formato de fluxo.
- Chame IMFASFStreamConfig::SetStreamNumber para atribuir um identificador de fluxo.
- Defina os parâmetros "leaky bucket" definindo o atributo MF_ASFSTREAMCONFIG_LEAKYBUCKET1 no objeto stream.
- Chame IMFASFProfile::SetStream para adicionar o novo fluxo ao perfil.
Agora crie o objeto ASF ContentInfo da seguinte maneira:
- Chame MFCreateASFContentInfo para criar um objeto ContentInfo vazio.
- Ligue IMFASFContentInfo::SetProfile para definir o perfil ASF.
O código a seguir mostra essas etapas:
HRESULT CreateASFContentInfo(
CWmaEncoder* pEncoder,
IMFASFContentInfo** ppContentInfo
)
{
HRESULT hr = S_OK;
IMFASFProfile* pProfile = NULL;
IMFMediaType* pMediaType = NULL;
IMFASFStreamConfig* pStream = NULL;
IMFASFContentInfo* pContentInfo = NULL;
// Create the ASF profile object.
hr = MFCreateASFProfile(&pProfile);
if (FAILED(hr))
{
goto done;
}
// Create a stream description for the encoded audio.
hr = pEncoder->GetOutputType(&pMediaType);
if (FAILED(hr))
{
goto done;
}
hr = pProfile->CreateStream(pMediaType, &pStream);
if (FAILED(hr))
{
goto done;
}
hr = pStream->SetStreamNumber(DEFAULT_STREAM_NUMBER);
if (FAILED(hr))
{
goto done;
}
// Set "leaky bucket" values.
LeakyBucket bucket;
hr = pEncoder->GetLeakyBucket1(&bucket);
if (FAILED(hr))
{
goto done;
}
hr = pStream->SetBlob(
MF_ASFSTREAMCONFIG_LEAKYBUCKET1,
(UINT8*)&bucket,
sizeof(bucket)
);
if (FAILED(hr))
{
goto done;
}
//Add the stream to the profile
hr = pProfile->SetStream(pStream);
if (FAILED(hr))
{
goto done;
}
// Create the ASF ContentInfo object.
hr = MFCreateASFContentInfo(&pContentInfo);
if (FAILED(hr))
{
goto done;
}
hr = pContentInfo->SetProfile(pProfile);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppContentInfo = pContentInfo;
(*ppContentInfo)->AddRef();
done:
SafeRelease(&pProfile);
SafeRelease(&pStream);
SafeRelease(&pMediaType);
SafeRelease(&pContentInfo);
return hr;
}
6. Crie o multiplexador ASF
O Multiplexador ASF gera pacotes de dados ASF.
- Chame MFCreateASFMultiplexer para criar o multiplexador ASF.
- Chame IMFASFMultiplexer::Inicialize para inicializar o multiplexador. Passe um ponteiro para o objeto ASF Content Info, que foi criado na seção anterior.
- Chame IMFASFMultiplexer::SetFlags para definir o sinalizador MFASF_MULTIPLEXER_AUTOADJUST_BITRATE. Quando esta configuração é usada, o multiplexador ajusta automaticamente a taxa de bits do conteúdo ASF para corresponder às características dos fluxos que estão sendo multiplexados.
HRESULT CreateASFMux(
IMFASFContentInfo* pContentInfo,
IMFASFMultiplexer** ppMultiplexer
)
{
HRESULT hr = S_OK;
IMFMediaType* pMediaType = NULL;
IMFASFMultiplexer *pMultiplexer = NULL;
// Create and initialize the ASF Multiplexer object.
hr = MFCreateASFMultiplexer(&pMultiplexer);
if (FAILED(hr))
{
goto done;
}
hr = pMultiplexer->Initialize(pContentInfo);
if (FAILED(hr))
{
goto done;
}
// Enable automatic bit-rate adjustment.
hr = pMultiplexer->SetFlags(MFASF_MULTIPLEXER_AUTOADJUST_BITRATE);
if (FAILED(hr))
{
goto done;
}
*ppMultiplexer = pMultiplexer;
(*ppMultiplexer)->AddRef();
done:
SafeRelease(&pMultiplexer);
return hr;
}
7. Gerar novos pacotes de dados ASF
Em seguida, gere pacotes de dados ASF para o novo arquivo. Esses pacotes de dados constituirão o objeto de dados ASF final para o novo arquivo. Para gerar novos pacotes de dados ASF:
- Chame MFCreateTempFile para criar um fluxo de bytes temporário para armazenar os pacotes de dados ASF.
- Chame a função
GetNextAudioSampledefinida pelo aplicativo para obter dados de áudio não compactados para o codificador. - Passe o áudio não comprimido para o codificador para compressão. Para mais informações, consulte Processamento de Dados no Codificador
- Ligue IMFASFMultiplexer::ProcessSample para enviar as amostras de áudio comprimido para multiplexador ASF para packetização.
- Obtenha os pacotes ASF do multiplexador e escreva-os no fluxo de bytes temporário. Para obter mais informações, consulte Gerando novos pacotes de dados ASF.
- Quando chegar ao final do fluxo de origem, drene o codificador e extraia as amostras comprimidas restantes do codificador. Para obter mais informações sobre o esvaziamento de um MFT, consulte Modelo Básico de Processamento de MFT.
- Depois de todas as amostras serem enviadas para o multiplexador, chame IMFASFMultiplexer::Flush e retire os pacotes ASF restantes do multiplexador.
- Chamada IMFASFMultiplexer::End.
O código a seguir gera pacotes de dados ASF. A função retorna um ponteiro para um fluxo de bytes que contém o objeto de dados ASF.
HRESULT EncodeData(
CWmaEncoder* pEncoder,
IMFASFContentInfo* pContentInfo,
IMFASFMultiplexer* pMux,
IMFByteStream** ppDataStream)
{
HRESULT hr = S_OK;
IMFByteStream* pStream = NULL;
IMFSample* pInputSample = NULL;
IMFSample* pWmaSample = NULL;
BOOL bEOF = FALSE;
// Create a temporary file to hold the data stream.
hr = MFCreateTempFile(
MF_ACCESSMODE_READWRITE,
MF_OPENMODE_DELETE_IF_EXIST,
MF_FILEFLAGS_NONE,
&pStream
);
if (FAILED(hr))
{
goto done;
}
BOOL bNeedInput = TRUE;
while (TRUE)
{
if (bNeedInput)
{
hr = GetNextAudioSample(&bEOF, &pInputSample);
if (FAILED(hr))
{
goto done;
}
if (bEOF)
{
// Reached the end of the input file.
break;
}
// Encode the uncompressed audio sample.
hr = pEncoder->ProcessInput(pInputSample);
if (FAILED(hr))
{
goto done;
}
bNeedInput = FALSE;
}
if (bNeedInput == FALSE)
{
// Get data from the encoder.
hr = pEncoder->ProcessOutput(&pWmaSample);
if (FAILED(hr))
{
goto done;
}
// pWmaSample can be NULL if the encoder needs more input.
if (pWmaSample)
{
hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
if (FAILED(hr))
{
goto done;
}
//Collect the data packets and write them to a stream
hr = GenerateASFDataPackets(pMux, pStream);
if (FAILED(hr))
{
goto done;
}
}
else
{
bNeedInput = TRUE;
}
}
SafeRelease(&pInputSample);
SafeRelease(&pWmaSample);
}
// Drain the MFT and pull any remaining samples from the encoder.
hr = pEncoder->Drain();
if (FAILED(hr))
{
goto done;
}
while (TRUE)
{
hr = pEncoder->ProcessOutput(&pWmaSample);
if (FAILED(hr))
{
goto done;
}
if (pWmaSample == NULL)
{
break;
}
hr = pMux->ProcessSample(DEFAULT_STREAM_NUMBER, pWmaSample, 0);
if (FAILED(hr))
{
goto done;
}
//Collect the data packets and write them to a stream
hr = GenerateASFDataPackets(pMux, pStream);
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pWmaSample);
}
// Flush the mux and get any pending ASF data packets.
hr = pMux->Flush();
if (FAILED(hr))
{
goto done;
}
hr = GenerateASFDataPackets(pMux, pStream);
if (FAILED(hr))
{
goto done;
}
// Update the ContentInfo object
hr = pMux->End(pContentInfo);
if (FAILED(hr))
{
goto done;
}
//Return stream to the caller that contains the ASF encoded data.
*ppDataStream = pStream;
(*ppDataStream)->AddRef();
done:
SafeRelease(&pStream);
SafeRelease(&pInputSample);
SafeRelease(&pWmaSample);
return hr;
}
O código para a função GenerateASFDataPackets é mostrado no tópico Gerando novos pacotes de dados ASF.
8. Escreva o arquivo ASF
Em seguida, escreva o cabeçalho ASF para um buffer de mídia ao chamar 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, crie um fluxo de bytes para o arquivo de saída. Primeiro, escreva o objeto de cabeçalho no fluxo de bytes de saída. Siga o objeto de cabeçalho, seguido pelo objeto de dados contido no fluxo de bytes de dados.
HRESULT WriteASFFile(
IMFASFContentInfo *pContentInfo,
IMFByteStream *pDataStream,
PCWSTR pszFile
)
{
HRESULT hr = S_OK;
IMFMediaBuffer* pHeaderBuffer = NULL;
IMFByteStream* pWmaStream = NULL;
DWORD cbHeaderSize = 0;
DWORD cbWritten = 0;
//Create output file
hr = MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST,
MF_FILEFLAGS_NONE, pszFile, &pWmaStream);
if (FAILED(hr))
{
goto done;
}
// Get the size of the ASF Header Object.
hr = pContentInfo->GenerateHeader (NULL, &cbHeaderSize);
if (FAILED(hr))
{
goto done;
}
// Create a media buffer.
hr = MFCreateMemoryBuffer(cbHeaderSize, &pHeaderBuffer);
if (FAILED(hr))
{
goto done;
}
// Populate the media buffer with the ASF Header Object.
hr = pContentInfo->GenerateHeader(pHeaderBuffer, &cbHeaderSize);
if (FAILED(hr))
{
goto done;
}
// Write the ASF header to the output file.
hr = WriteBufferToByteStream(pWmaStream, pHeaderBuffer, &cbWritten);
if (FAILED(hr))
{
goto done;
}
// Append the data stream to the file.
hr = pDataStream->SetCurrentPosition(0);
if (FAILED(hr))
{
goto done;
}
hr = AppendToByteStream(pDataStream, pWmaStream);
done:
SafeRelease(&pHeaderBuffer);
SafeRelease(&pWmaStream);
return hr;
}
9. Defina 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[])
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (argc != 3)
{
wprintf_s(L"Usage: %s input.wmv, %s output.wma");
return 0;
}
const WCHAR* sInputFileName = argv[1]; // Source file name
const WCHAR* sOutputFileName = argv[2]; // Output file name
IMFMediaType* pInputType = NULL;
IMFASFContentInfo* pContentInfo = NULL;
IMFASFMultiplexer* pMux = NULL;
IMFByteStream* pDataStream = NULL;
CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.
HRESULT hr = CoInitializeEx(
NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr))
{
goto done;
}
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
goto done;
}
CWmaEncoder* pEncoder = NULL; //Pointer to the Encoder object.
hr = OpenAudioFile(sInputFileName, &pInputType);
if (FAILED(hr))
{
goto done;
}
// Initialize the WMA encoder wrapper.
pEncoder = new (std::nothrow) CWmaEncoder();
if (pEncoder == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pEncoder->Initialize();
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetEncodingType(EncodeMode_CBR);
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->SetInputType(pInputType);
if (FAILED(hr))
{
goto done;
}
// Create the WMContainer objects.
hr = CreateASFContentInfo(pEncoder, &pContentInfo);
if (FAILED(hr))
{
goto done;
}
hr = CreateASFMux(pContentInfo, &pMux);
if (FAILED(hr))
{
goto done;
}
// Convert uncompressed data to ASF format.
hr = EncodeData(pEncoder, pContentInfo, pMux, &pDataStream);
if (FAILED(hr))
{
goto done;
}
// Write the ASF objects to the output file.
hr = WriteASFFile(pContentInfo, pDataStream, sOutputFileName);
done:
SafeRelease(&pInputType);
SafeRelease(&pContentInfo);
SafeRelease(&pMux);
SafeRelease(&pDataStream);
delete pEncoder;
MFShutdown();
CoUninitialize();
if (FAILED(hr))
{
wprintf_s(L"Error: 0x%X\n", hr);
}
return 0;
}
Tópicos relacionados