Tutorial: Copiar secuencias ASF mediante objetos WMContainer
Una manera de crear un archivo ASF es copiar secuencias ASF desde un archivo existente. Para ello, puede recuperar los datos multimedia del archivo de origen y escribir en el archivo de salida. Si el archivo de origen es un archivo ASF, puede copiar ejemplos de secuencias sin descomprimirlos y volver a comprimirlos.
En este tutorial se muestra este escenario extrayendo la primera secuencia de audio de un archivo de audio-vídeo ASF (.wmv) y copiandolo en un nuevo archivo de audio ASF (.wma). En este tutorial, creará una aplicación de consola que toma los nombres de archivo de entrada y salida como argumentos. La aplicación usa el divisor ASF para analizar los ejemplos de flujo de entrada y, a continuación, los envía al multiplexador ASF para escribir los paquetes de datos de ASF para la secuencia de audio.
Este tutorial contiene los siguientes pasos:
- Requisitos previos
- Terminología
- 1. Configurar el proyecto
- 2. Declarar funciones auxiliares
- 3. Abra el archivo ASF de entrada
- 4. Inicializar objetos para el archivo de entrada
- 5. Crear un perfil de audio
- 6. Inicializar objetos para el archivo de salida
- 7. Generar nuevos paquetes de datos asf
- 8. Escribir los objetos ASF en el nuevo archivo
- 9 Escribir la función Entry-Point
- Temas relacionados
Requisitos previos
En este tutorial se da por hecho lo siguiente:
- Está familiarizado con la estructura de un archivo ASF y los componentes proporcionados por Media Foundation para trabajar con objetos ASF. Estos componentes incluyen contentInfo, divisor, multiplexador y objetos de perfil. Para obtener más información, vea Componentes de ASF de WMContainer.
- Está familiarizado con el proceso de analizar el objeto de encabezado ASF y los paquetes de datos de ASF de un archivo existente y generar ejemplos de secuencias comprimidas mediante el divisor. Para obtener más información, vea Tutorial: Lectura de un archivo ASF.
- Está familiarizado con los búferes multimedia y las secuencias de bytes: en concreto, las operaciones de archivo que usan una secuencia de bytes y escriben el contenido de un búfer multimedia en una secuencia de bytes. (Véase 2. Declarar funciones auxiliares).
Terminología
En este tutorial se usan los términos siguientes:
- Secuencia de bytes de origen: objeto de secuencia de bytes, expone la interfaz IMFByteStream , que contiene el contenido del archivo de entrada.
- Objeto ContentInfo de origen: objeto ContentInfo, expone la interfaz IMFASFContentInfo , que representa el objeto de encabezado ASF del archivo de entrada.
- Perfil de audio: objeto profile, expone la interfaz IMFASFProfile , que contiene solo secuencias de audio del archivo de entrada.
- Ejemplo de secuencia: muestra multimedia, expone la interfaz IMFSample , generada por el divisor representa los datos multimedia de la secuencia seleccionados obtenidos del archivo de entrada en estado comprimido.
- Objeto ContentInfo de salida: objeto ContentInfo, expone la interfaz IMFASFContentInfo , que representa el objeto de encabezado ASF del archivo de salida.
- Flujo de bytes de datos: objeto de secuencia de bytes, expone la interfaz IMFByteStream , que representa toda la parte del objeto de datos ASF del archivo de salida.
- Paquete de datos: muestra multimedia, expone la interfaz IMFSample , generada por el multiplexador representa un paquete de datos ASF que se escribirá en el flujo de bytes de datos.
- Flujo de bytes de salida: objeto de secuencia de bytes, expone la interfaz IMFByteStream , que contiene el contenido del archivo de salida.
1. Configurar el proyecto
Incluya los siguientes encabezados en el archivo de origen:
#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
Vínculo a los siguientes archivos de biblioteca:
- mfplat.lib
- mf.lib
- mfuuid.lib
Declare la función SafeRelease :
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
2. Declarar funciones auxiliares
En este tutorial se usan las siguientes funciones auxiliares para leer y escribir desde una secuencia de bytes.
La AllocReadFromByteStream
función lee datos de un flujo de bytes y asigna un nuevo búfer multimedia para contener los datos. Para obtener más información, vea 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;
}
La WriteBufferToByteStream
función escribe datos de un búfer multimedia en una secuencia de bytes. Para obtener más información, vea 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;
}
La AppendToByteStream
función anexa el contenido de una secuencia de bytes a otra:
//-------------------------------------------------------------------
// 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 el archivo ASF de entrada
Abra el archivo de entrada llamando a la función MFCreateFile . El método devuelve un puntero al objeto de secuencia de bytes que contiene el contenido del archivo. El usuario especifica el nombre de archivo mediante argumentos de línea de comandos de la aplicación.
El código de ejemplo siguiente toma un nombre de archivo y devuelve un puntero a un objeto de secuencia de bytes que se puede usar para leer el archivo.
// Open the file.
hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST,
MF_FILEFLAGS_NONE, pszFileName, &pStream);
4. Inicializar objetos para el archivo de entrada
A continuación, creará e inicializará el objeto ContentInfo de origen y el divisor para generar ejemplos de flujo.
Esta secuencia de bytes de origen creada en el paso 2 se usará para analizar el objeto de encabezado ASF y rellenar el objeto ContentInfo de origen. Este objeto se usará para inicializar el divisor para facilitar el análisis de los paquetes de datos ASF para la secuencia de audio en el archivo de entrada. También recuperará la longitud del objeto de datos ASF en el archivo de entrada y el desplazamiento al primer paquete de datos de ASF en relación con el inicio del archivo. El divisor usará estos atributos para generar ejemplos de secuencias de audio.
Para crear e inicializar componentes de ASF para el archivo de entrada:
- Llame a MFCreateASFContentInfo para crear el objeto ContentInfo. Esta función devuelve un puntero a la interfaz IMFASFContentInfo .
- Llame a IMFASFContentInfo::P arseHeader para analizar el encabezado asf. Para obtener más información sobre este paso, vea Lectura del objeto de encabezado ASF de un archivo existente.
- Llame a MFCreateASFSplitter para crear el objeto divisor ASF. Esta función devuelve un puntero a la interfaz IMFASFSplitter .
- Llame a IMFASFSplitter::Initialize, pasando el puntero IMFASFContentInfo. Para obtener más información sobre este paso, vea Crear el objeto divisor ASF.
- Llame a IMFASFContentInfo::GeneratePresentationDescriptor para obtener un descriptor de presentación para el archivo ASF.
- Obtenga el valor del atributo MF_PD_ASF_DATA_START_OFFSET del descriptor de presentación. Este valor es la ubicación del objeto de datos ASF en el archivo, como desplazamiento de bytes desde el principio del archivo.
- Obtenga el valor del atributo MF_PD_ASF_DATA_LENGTH del descriptor de presentación. Este valor es el tamaño total del objeto de datos ASF, en bytes. Para obtener más información, vea Obtener información de objetos de encabezado ASF.
En el código de ejemplo siguiente se muestra una función que consolida todos los pasos. Esta función toma un puntero a la secuencia de bytes de origen y devuelve punteros al objeto ContentInfo de origen y al divisor. Además, recibe la longitud y los desplazamientos al objeto de datos de 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. Crear un perfil de audio
A continuación, creará un objeto de perfil para el archivo de entrada obteniendolo del objeto ContentInfo de origen. A continuación, configurará el perfil para que contenga solo las secuencias de audio del archivo de entrada. Para ello, enumere las secuencias y quite las secuencias que no son de audio del perfil. El objeto de perfil de audio se usará más adelante en este tutorial para inicializar el objeto ContentInfo de salida.
Para crear un perfil de audio
- Obtenga el objeto de perfil del archivo de entrada del objeto ContentInfo de origen llamando a IMFASFContentInfo::GetProfile. El método devuelve un puntero a un objeto de perfil que contiene todas las secuencias del archivo de entrada. Para obtener más información, vea Crear un perfil de ASF.
- Quite todos los objetos de exclusión mutua del perfil. Este paso es necesario porque las secuencias que no son de audio se quitarán del perfil, lo que podría invalidar los objetos de exclusión mutua.
- Quite todas las secuencias que no son de audio del perfil, como se indica a continuación:
- Llame a IMFASFProfile::GetStreamCount para obtener el número de secuencias.
- En un bucle, llame a IMFASFProfile::GetStream para obtener cada secuencia por índice.
- Llame a IMFASFStreamConfig::GetStreamType para obtener el tipo de secuencia.
- En el caso de las secuencias que no son de audio, llame a IMFASFProfile::RemoveStream para quitar la secuencia.
- Almacene el número de secuencia de la primera secuencia de audio. Se seleccionará en el divisor para generar ejemplos de secuencia. Si el número de secuencia es cero, el autor de la llamada puede suponer que no había ningún archivo de secuencias de audio.
En el código siguiente se describen estos pasos:
//-------------------------------------------------------------------
// 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
función quita los objetos de exclusión mutua del 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 el archivo de salida
A continuación, creará el objeto ContentInfo de salida y el multiplexador para generar paquetes de datos para el archivo de salida.
El perfil de audio creado en el paso 4 se usará para rellenar el objeto ContentInfo de salida. Este objeto contiene información como atributos de archivo globales y propiedades de secuencia. El objeto ContentInfo de salida se usará para inicializar el multiplexador que generará paquetes de datos para el archivo de salida. Una vez generados los paquetes de datos, el objeto ContentInfo debe actualizarse para reflejar los nuevos valores.
Para crear e inicializar componentes de ASF para el archivo de salida
- Cree un objeto ContentInfo vacío llamando a MFCreateASFContentInfo y rellénelo con información del perfil de audio creado en el paso 3 llamando a IMFASFContentInfo::SetProfile. Para obtener más información, vea Inicializar el objeto ContentInfo de un nuevo archivo ASF.
- Cree e inicialice el objeto multiplexador mediante el objeto ContentInfo de salida. Para obtener más información, vea Crear el objeto Multiplexer.
En el código de ejemplo siguiente se muestra una función que consolida los pasos. Esta función toma un puntero a un objeto de perfil y devuelve punteros al objeto ContentInfo de salida y al 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. Generar nuevos paquetes de datos asf
A continuación, generará ejemplos de secuencias de audio a partir de la secuencia de bytes de origen mediante el divisor y los enviará al multiplexador para crear paquetes de datos ASF. Estos paquetes de datos constituyen el objeto de datos ASF final para el nuevo archivo.
Para generar ejemplos de secuencias de audio
- Seleccione la primera secuencia de audio en el divisor llamando a IMFASFSplitter::SelectStreams.
- Lee bloques de tamaño fijo de datos multimedia del flujo de bytes de origen en un búfer multimedia.
- Recopile los ejemplos de secuencias como muestras multimedia del divisor llamando a IMFASFSplitter::GetNextSample en un bucle siempre que reciba la marca de ASF_STATUSFLAGS_INCOMPLETE en el parámetro pdwStatusFlags . Para obtener más información, vea Generar ejemplos para paquetes de datos de ASF" en Generar ejemplos de flujo a partir de un objeto de datos ASF existente.
- Para cada muestra multimedia, llame a IMFASFMultiplexer::P rocessSample para enviar la muestra multimedia al multiplexador. El multiplexador genera los paquetes de datos para el objeto de datos ASF.
- Escriba el paquete de datos generado por el multiplexador en el flujo de bytes de datos.
- Una vez generados todos los paquetes de datos, llame a IMFASFMultiplexer::End para actualizar el objeto ContentInfo de salida con información recopilada durante la generación de paquetes de datos de ASF.
El código de ejemplo siguiente genera ejemplos de secuencia desde el divisor ASF y los envía al multiplexador. El multiplexador genera paquetes de datos ASF y los escribe en una secuencia.
//-------------------------------------------------------------------
// 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 obtener paquetes del divisor ASF, el código anterior llama a la GetPacketsFromSplitter
función , que se muestra aquí:
//-------------------------------------------------------------------
// 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
función obtiene paquetes de datos de multiplexador. Para obtener más información, consulte Obtención de paquetes de datos de 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. Escribir los objetos ASF en el nuevo archivo
A continuación, escribirá el contenido del objeto ContentInfo de salida en un búfer multimedia llamando a IMFASFContentInfo::GenerateHeader. Este método convierte los datos almacenados en el objeto ContentInfo en datos binarios en formato de objeto de encabezado ASF. Para obtener más información, vea Generar un nuevo objeto de encabezado ASF.
Una vez generado el nuevo objeto de encabezado ASF, escriba primero el archivo de salida escribiendo el objeto header en el flujo de bytes de salida creado en el paso 2 llamando a la función auxiliar WriteBufferToByteStream. Siga el objeto Header con el objeto data contenido en el flujo de bytes de datos. El código de ejemplo muestra una función que transfiere el contenido del flujo de bytes de datos al flujo de bytes de salida.
//-------------------------------------------------------------------
// 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 Escribir la función Entry-Point
Ahora puede reunir los pasos anteriores en una aplicación completa. Antes de usar cualquiera de los objetos de Media Foundation, inicialice la plataforma Media Foundation llamando a MFStartup. Cuando haya terminado, llame a MFShutdown. Para obtener más información, vea Inicializar Media Foundation.
En el código siguiente se muestra la aplicación de consola completa. El argumento de la línea de comandos especifica el nombre del archivo que se va a convertir y el nombre del nuevo archivo de 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;
}
Temas relacionados