Tutorial: Codificación de Windows Media de paso único
Por codificación se entiende el proceso de conversión de medios digitales de un formato a otro. Por ejemplo, convertir audio MP3 en formato de audio de Windows Media tal como se define en la especificación Formato de sistemas avanzados (ASF).
Nota La especificación ASF define un tipo de contenedor para el archivo de salida (.wma o .wmv) que puede contener datos multimedia en cualquier formato, comprimido o sin comprimir. Por ejemplo, un contenedor ASF, como un archivo de .wma, puede contener datos multimedia en formato MP3. El proceso de codificación convierte el formato real de los datos contenidos en el archivo.
En este tutorial se muestra el origen de entrada de la codificación de contenido no cifrado (no protegido con DRM) en contenido de Windows Media y la escritura de los datos en un nuevo archivo ASF (.wm*) mediante componentes ASF de capa de canalización. Estos componentes se usarán para crear una topología de codificación parcial, que se controlará mediante una instancia de la sesión multimedia.
En este tutorial, creará una aplicación de consola que toma los nombres de archivo de entrada y salida y el modo de codificación como argumentos. El archivo de entrada puede estar en un formato multimedia comprimido o sin comprimir. Los modos de codificación válidos son "CBR" (velocidad de bits constante) o "VBR" (velocidad de bits variable). La aplicación crea un origen multimedia para representar el origen especificado por el nombre de archivo de entrada y el receptor del archivo ASF para archivar el contenido codificado del archivo de origen en un archivo ASF. Para simplificar la implementación del escenario, el archivo de salida solo tendrá una secuencia de audio y una secuencia de vídeo. La aplicación inserta el códec Windows Media Audio 9.1 Professional para la conversión de formato de secuencia de audio y el códec Windows Media Video 9 para la secuencia de vídeo.
Para la codificación de velocidad de bits constante, antes de que comience la sesión de codificación, el codificador debe conocer la velocidad de bits de destino que debe lograr. En este tutorial, para el modo "CBR", la aplicación usa la velocidad de bits disponible con el primer tipo de medio de salida que se recupera del codificador durante la negociación de tipos multimedia como velocidad de bits de destino. En el caso de la codificación de velocidad de bits variable, en este tutorial se muestra la codificación con velocidad de bits variable estableciendo un nivel de calidad. Las secuencias de audio se codifican en el nivel de calidad de 98 y las secuencias de vídeo en el nivel de calidad de 95.
En el procedimiento siguiente se resumen los pasos para codificar contenido de Windows Media en un contenedor ASF mediante un modo de codificación de un único paso.
- Cree un origen multimedia para el especificado mediante la resolución de origen.
- Enumerar las secuencias en el origen multimedia.
- Cree el receptor de medios ASF y agregue receptores de secuencias en función de las secuencias del origen multimedia que deban codificarse.
- Configure el receptor de medios con las propiedades de codificación necesarias.
- Cree los codificadores de Windows Media para las secuencias del archivo de salida.
- Configure los codificadores con las propiedades establecidas en el receptor de medios.
- Cree una topología de codificación parcial.
- Cree una instancia de la sesión multimedia y establezca la topología en la sesión multimedia.
- Inicie la sesión de codificación controlando la sesión multimedia y obteniendo todos los eventos pertinentes de la sesión multimedia.
- Para la codificación VBR, obtenga los valores de propiedad de codificación del codificador y establézcalos en el receptor de medios.
- Cierre y apague la sesión de codificación.
Este tutorial contiene las siguientes secciones:
- Requisitos previos
- Configuración del proyecto
- Creación del origen multimedia
- Creación del receptor de archivos ASF
- Creación de la topología de codificación parcial
- Control de la sesión de codificación
- Actualización de las propiedades de codificación en el receptor de archivos
- Implementación de la función principal
- Prueba del archivo de salida
- Códigos de error comunes y sugerencias de depuración
- Temas relacionados
Requisitos previos
En este tutorial se da por hecho lo siguiente:
Está familiarizado con la estructura de archivos ASF, los componentes ASF de la capa de canalización proporcionados por Media Foundation para trabajar con objetos ASF. Estos componentes incluyen los siguientes objetos:
-
Nota Si está realizando la conversión de velocidad (convirtiendo un archivo de velocidad de bits superior a un archivo de velocidad de bits inferior sin cambiar los formatos), deberá usar el origen multimedia ASF.
-
Está familiarizado con los codificadores de Windows Media y los distintos tipos de codificación, especialmente la Codificación de velocidad de bits constante y la Codificación de velocidad de bits variable basada en calidad.
Está familiarizado con las operaciones de MFT del codificador. En concreto, crear una instancia del codificador y establecer los tipos de entrada y salida en el codificador.
Está familiarizado con los objetos de topología y con cómo crear una topología de codificación. Para obtener más información sobre las topologías y los nodos de topología, consulte Creación de topologías.
Está familiarizado con el modelo de eventos de Sesión multimedia y las operaciones de control de flujo. Para obtener más información, consulte Eventos de sesión multimedia.
Configuración del proyecto
Incluya los siguientes encabezados en el archivo de origen:
#include <new> #include <mfidl.h> // Media Foundation interfaces #include <mfapi.h> // Media Foundation platform APIs #include <mferror.h> // Media Foundation error codes #include <wmcontainer.h> // ASF-specific components #include <wmcodecdsp.h> // Windows Media DSP interfaces #include <Dmo.h> // DMO objects #include <uuids.h> // Definition for FORMAT_VideoInfo #include <propvarutil.h>
Vincule a los siguientes archivos de biblioteca:
// The required link libraries #pragma comment(lib, "mfplat") #pragma comment(lib, "mf") #pragma comment(lib, "mfuuid") #pragma comment(lib, "msdmo") #pragma comment(lib, "strmiids") #pragma comment(lib, "propsys")
Declare la función SafeRelease.
template <class T> void SafeRelease(T **ppT) { if (*ppT) { (*ppT)->Release(); *ppT = NULL; } }
Declare la enumeración ENCODING_MODE para definir tipos de codificación CBR y VBR.
// Encoding mode typedef enum ENCODING_MODE { NONE = 0x00000000, CBR = 0x00000001, VBR = 0x00000002, } ;
Defina una constante para la ventana de búfer para una secuencia de vídeo.
// Video buffer window const INT32 VIDEO_WINDOW_MSEC = 3000;
Creación del origen multimedia
Cree un origen multimedia para el origen de entrada. Media Foundation proporciona orígenes multimedia integrados para varios formatos multimedia: MP3, MP4/3GP, AVI/WAVE. Para obtener información sobre los formatos admitidos por Media Foundation, consulte Formatos multimedia admitidos en Media Foundation.
Para crear el origen multimedia, use el Solucionador de origen. Este objeto analiza la dirección URL especificada para el archivo de origen y crea el origen multimedia adecuado.
Realice las siguientes llamadas:
IMFSourceResolver::CreateObjectFromURL
Para obtener más información sobre estas llamadas, consulte Uso del solucionador de origen.
Si el archivo de entrada está en formato ASF y desea convertirlo a un archivo con una velocidad de bits diferente sin cambiar los formatos, el solucionador de origen crea una instancia del Origen multimedia de ASF.
En el ejemplo de código siguiente se muestra una función CreateMediaSource que crea un origen multimedia para el archivo especificado.
// Create a media source from a URL.
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
{
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IMFSourceResolver* pSourceResolver = NULL;
IUnknown* pSource = NULL;
// Create the source resolver.
HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
if (FAILED(hr))
{
goto done;
}
// Use the source resolver to create the media source.
// Note: For simplicity this sample uses the synchronous method to create
// the media source. However, creating a media source can take a noticeable
// amount of time, especially for a network source. For a more responsive
// UI, use the asynchronous BeginCreateObjectFromURL method.
hr = pSourceResolver->CreateObjectFromURL(
sURL, // URL of the source.
MF_RESOLUTION_MEDIASOURCE, // Create a source object.
NULL, // Optional property store.
&ObjectType, // Receives the created object type.
&pSource // Receives a pointer to the media source.
);
if (FAILED(hr))
{
goto done;
}
// Get the IMFMediaSource interface from the media source.
hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));
done:
SafeRelease(&pSourceResolver);
SafeRelease(&pSource);
return hr;
}
Creación del receptor de archivos ASF
Cree una instancia del receptor de archivos ASF que archivará los datos multimedia codificados en un archivo ASF al final de la sesión de codificación.
En este tutorial, creará un objeto de activación para el receptor de archivos ASF. El receptor de archivos necesita la siguiente información para crear los receptores de secuencias necesarios.
- Secuencias que se van a codificar y escribir en el archivo final.
- Las propiedades de codificación que se usan para codificar el contenido multimedia, como el tipo de codificación, el número de pasos de codificación y las propiedades relacionadas.
- Las propiedades del archivo global que indican al receptor de medios si debe ajustar automáticamente los parámetros del cubo filtrado (velocidad de bits y tamaño del búfer).
La información de la secuencia flujo se configura en el objeto de perfil de ASF y las propiedades globales y de codificación se establecen en un almacén de propiedades administrado por el objeto ContentInfo de ASF.
Para obtener información general sobre el receptor de archivos ASF, consulte Receptores de medios ASF.
Creación del objeto de perfil de ASF
Para que el receptor de archivos ASF escriba datos multimedia codificados en un archivo ASF, el receptor debe conocer el número de secuencias y el tipo de secuencias para los que se van a crear receptores de secuencias. En este tutorial extraerá esa información del origen multimedia y creará las secuencias de salida correspondientes. Restrinja las secuencias de salida a una secuencia de audio y una secuencia de vídeo. Para cada secuencia seleccionada en el origen, cree una secuencia de destino y agréguela al perfil.
Para implementar este paso, necesita los siguientes objetos.
- Perfil de ASF
- Descriptor de presentación para el origen multimedia
- Descriptores de secuencia para las secuencias seleccionadas en el origen multimedia.
- Tipos de medios para las secuencias seleccionadas.
En los pasos siguientes se describe el proceso de creación del perfil de ASF y las secuencias de destino.
Para crear el perfil de ASF
Llame a MFCreateASFProfile para crear un objeto de perfil vacío.
Llame a IMFMediaSource::CreatePresentationDescriptor para crear el descriptor de presentación para el origen multimedia creado en el paso descrito en la sección "Creación del origen multimedia" de este tutorial.
Llame a IMFPresentationDescriptor::GetStreamDescriptorCount para obtener el número de secuencias en el origen multimedia.
Llame a IMFPresentationDescriptor::GetStreamDescriptorByIndex para cada secuencia del origen multimedia y obtenga el descriptor de la secuencia.
Llame a IMFStreamDescriptor::GetMediaTypeHandler seguido de IMFMediaTypeHandler::GetMediaTypeByIndex y obtenga el primer tipo de medio para la secuencia.
Nota Para evitar llamadas complejas, suponga que solo existe un tipo de medio por secuencia y seleccione el primer tipo de medio de la secuencia. Para secuencias complejas, debe enumerar cada tipo de medio desde el controlador de tipos de medios y elegir el tipo de medio que desea codificar.
Llame a IMFMediaType::GetMajorType para obtener el tipo principal de la secuencia para determinar si la secuencia contiene audio o vídeo.
En función del tipo principal de la secuencia, cree tipos de medios de destino. Estos tipos de medios contendrán información de formato que usará el codificador durante la sesión de codificación. En las secciones siguientes de este tutorial se describe el proceso de creación de los tipos de medios de destino.
- Creación de un tipo de medio de audio comprimido
- Creación de un tipo de medio de vídeo comprimido
Cree una secuencia basada en el tipo de medio de destino, configure la secuencia según los requisitos de la aplicación y agregue la secuencia al perfil. Para obtener más información, consulte Adición de información de secuencia al receptor de archivos ASF.
- Llame a IMFASFProfile::CreateStream y transfiera el tipo de medio de destino para crear la secuencia de salida. El método recupera la interfaz IMFASFStreamConfig del objeto de la secuencia.
- Configure la secuencia.
- Llame a IMFASFStreamConfig::SetStreamNumber para asignar un número a la secuencia. Esta configuración es obligatoria.
- También puede configurar los parámetros del cubo filtrado, la extensión de carga y la exclusión mutua en cada secuencia llamando a los métodos IMFASFStreamConfig y a los atributos de configuración de secuencias pertinentes.
- Agregue las propiedades de codificación de nivel de secuencia mediante el objeto ContentInfo de ASF. Para obtener más información sobre este paso, consulte la sección "Creación del objeto ContentInfo de ASF" en este tutorial.
- Llame a IMFASFProfile::SetStream para agregar la secuencia al perfil.
En el ejemplo de código siguiente se crea una secuencia de audio de salida.
//------------------------------------------------------------------- // CreateAudioStream // Create an audio stream and add it to the profile. // // pProfile: A pointer to the ASF profile. // wStreamNumber: Stream number to assign for the new stream. //------------------------------------------------------------------- HRESULT CreateAudioStream(IMFASFProfile* pProfile, WORD wStreamNumber) { if (!pProfile) { return E_INVALIDARG; } if (wStreamNumber < 1 || wStreamNumber > 127 ) { return MF_E_INVALIDSTREAMNUMBER; } IMFMediaType* pAudioType = NULL; IMFASFStreamConfig* pAudioStream = NULL; //Create an output type from the encoder HRESULT hr = GetOutputTypeFromWMAEncoder(&pAudioType); if (FAILED(hr)) { goto done; } //Create a new stream with the audio type hr = pProfile->CreateStream(pAudioType, &pAudioStream); if (FAILED(hr)) { goto done; } //Set stream number hr = pAudioStream->SetStreamNumber(wStreamNumber); if (FAILED(hr)) { goto done; } //Add the stream to the profile hr = pProfile->SetStream(pAudioStream); if (FAILED(hr)) { goto done; } wprintf_s(L"Audio Stream created. Stream Number: %d.\n", wStreamNumber); done: SafeRelease(&pAudioStream); SafeRelease(&pAudioType); return hr; }
En el ejemplo de código siguiente se crea una secuencia de vídeo de salida.
//------------------------------------------------------------------- // CreateVideoStream // Create an video stream and add it to the profile. // // pProfile: A pointer to the ASF profile. // wStreamNumber: Stream number to assign for the new stream. // pType: A pointer to the source's video media type. //------------------------------------------------------------------- HRESULT CreateVideoStream(IMFASFProfile* pProfile, WORD wStreamNumber, IMFMediaType* pType) { if (!pProfile) { return E_INVALIDARG; } if (wStreamNumber < 1 || wStreamNumber > 127 ) { return MF_E_INVALIDSTREAMNUMBER; } HRESULT hr = S_OK; IMFMediaType* pVideoType = NULL; IMFASFStreamConfig* pVideoStream = NULL; UINT32 dwBitRate = 0; //Create a new video type from the source type hr = CreateCompressedVideoType(pType, &pVideoType); if (FAILED(hr)) { goto done; } //Create a new stream with the video type hr = pProfile->CreateStream(pVideoType, &pVideoStream); if (FAILED(hr)) { goto done; } //Set a valid stream number hr = pVideoStream->SetStreamNumber(wStreamNumber); if (FAILED(hr)) { goto done; } //Add the stream to the profile hr = pProfile->SetStream(pVideoStream); if (FAILED(hr)) { goto done; } wprintf_s(L"Video Stream created. Stream Number: %d .\n", wStreamNumber); done: SafeRelease(&pVideoStream); SafeRelease(&pVideoType); return hr; }
En el ejemplo de código siguiente se crean secuencias de salida en función de las secuencias del origen.
//For each stream in the source, add target stream information in the profile
for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
{
hr = pPD->GetStreamDescriptorByIndex(
iStream, &fSelected, &pStreamDesc);
if (FAILED(hr))
{
goto done;
}
if (!fSelected)
{
continue;
}
//Get the media type handler
hr = pStreamDesc->GetMediaTypeHandler(&pHandler);
if (FAILED(hr))
{
goto done;
}
//Get the first media type
hr = pHandler->GetMediaTypeByIndex(0, &pMediaType);
if (FAILED(hr))
{
goto done;
}
//Get the major type
hr = pMediaType->GetMajorType(&guidMajor);
if (FAILED(hr))
{
goto done;
}
// If this is a video stream, create the target video stream
if (guidMajor == MFMediaType_Video)
{
//The stream level encoding properties will be set in this call
hr = CreateVideoStream(pProfile, wStreamID, pMediaType);
if (FAILED(hr))
{
goto done;
}
}
// If this is an audio stream, create the target audio stream
if (guidMajor == MFMediaType_Audio)
{
//The stream level encoding properties will be set in this call
hr = CreateAudioStream(pProfile, wStreamID);
if (FAILED(hr))
{
goto done;
}
}
//Get stream's encoding property
hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
if (FAILED(hr))
{
goto done;
}
//Set the stream-level encoding properties
hr = SetEncodingProperties(guidMajor, pContentInfoProps);
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pMediaType);
SafeRelease(&pStreamDesc);
SafeRelease(&pContentInfoProps);
wStreamID++;
}
Creación de un tipo de medio de audio comprimido
Si desea incluir una secuencia de audio en el archivo de salida, cree un tipo de audio especificando las características del tipo codificado y definiendo los atributos necesarios. Para asegurarse de que el tipo de audio es compatible con el codificador de audio de Windows Media, cree una instancia del MFT del codificador, establezca las propiedades de codificación y obtenga un tipo de medio llamando a IMFTransform::GetOutputAvailableType. Para obtener el tipo de salida necesario, recorra en bucle todos los tipos disponibles, obtenga los atributos de cada tipo de medio y seleccione el tipo más cercano a sus requisitos. En este tutorial, obtenga el primer tipo disponible de la lista de tipos de medios de salida admitidos por el codificador.
Nota Para Windows 7, Media Foundation proporciona una nueva función, MFTranscodeGetAudioOutputAvailableTypes que recupera una lista de los tipos de audio compatibles. Esta función solo obtiene tipos de medios para la codificación CBR.
Un tipo de audio completo debe tener los siguientes atributos establecidos:
- MF_MT_MAJOR_TYPE
- MF_MT_SUBTYPE
- MF_MT_AUDIO_NUM_CHANNELS
- MF_MT_AUDIO_SAMPLES_PER_SECOND
- MF_MT_AUDIO_BLOCK_ALIGNMENT
- MF_MT_AUDIO_AVG_BYTES_PER_SECOND
- MF_MT_AUDIO_BITS_PER_SAMPLE
En el ejemplo de código siguiente se crea un tipo de audio comprimido obteniendo un tipo compatible del codificador de audio de Windows Media. La implementación de SetEncodingProperties se muestra en la sección "Creación del objeto ContentInfo de ASF" de este tutorial.
//-------------------------------------------------------------------
// GetOutputTypeFromWMAEncoder
// Gets a compatible output type from the Windows Media audio encoder.
//
// ppAudioType: Receives a pointer to the media type.
//-------------------------------------------------------------------
HRESULT GetOutputTypeFromWMAEncoder(IMFMediaType** ppAudioType)
{
if (!ppAudioType)
{
return E_POINTER;
}
IMFTransform* pMFT = NULL;
IMFMediaType* pType = NULL;
IPropertyStore* pProps = NULL;
//We need to find a suitable output media type
//We need to create the encoder to get the available output types
//and discard the instance.
CLSID *pMFTCLSIDs = NULL; // Pointer to an array of CLISDs.
UINT32 cCLSID = 0; // Size of the array.
MFT_REGISTER_TYPE_INFO tinfo;
tinfo.guidMajorType = MFMediaType_Audio;
tinfo.guidSubtype = MFAudioFormat_WMAudioV9;
// Look for an encoder.
HRESULT hr = MFTEnum(
MFT_CATEGORY_AUDIO_ENCODER,
0, // Reserved
NULL, // Input type to match. None.
&tinfo, // WMV encoded type.
NULL, // Attributes to match. (None.)
&pMFTCLSIDs, // Receives a pointer to an array of CLSIDs.
&cCLSID // Receives the size of the array.
);
if (FAILED(hr))
{
goto done;
}
// MFTEnum can return zero matches.
if (cCLSID == 0)
{
hr = MF_E_TOPO_CODEC_NOT_FOUND;
goto done;
}
else
{
// Create the MFT decoder
hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));
if (FAILED(hr))
{
goto done;
}
}
// Get the encoder's property store
hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
if (FAILED(hr))
{
goto done;
}
//Set encoding properties
hr = SetEncodingProperties(MFMediaType_Audio, pProps);
if (FAILED(hr))
{
goto done;
}
//Get the first output type
//You can loop through the available output types to choose
//the one that meets your target requirements
hr = pMFT->GetOutputAvailableType(0, 0, &pType);
if (FAILED(hr))
{
goto done;
}
//Return to the caller
*ppAudioType = pType;
(*ppAudioType)->AddRef();
done:
SafeRelease(&pProps);
SafeRelease(&pType);
SafeRelease(&pMFT);
CoTaskMemFree(pMFTCLSIDs);
return hr;
}
Creación de un tipo de medio de vídeo comprimido
Si desea incluir una secuencia de vídeo en el archivo de salida, cree un tipo de vídeo codificado completo. El tipo de medio completo debe incluir la velocidad de bits deseada y los datos privados del códec.
Hay dos maneras de crear un tipo de medio de vídeo completo.
Cree un tipo de medio vacío y copie los atributos de tipo de medio del tipo de vídeo de origen y sobrescriba el atributo MF_MT_SUBTYPE con la constante GUID MFVideoFormat_WMV3.
Un tipo de vídeo completo debe tener los siguientes atributos establecidos:
- MF_MT_MAJOR_TYPE
- MF_MT_SUBTYPE
- MF_MT_FRAME_RATE
- MF_MT_FRAME_SIZE
- MF_MT_INTERLACE_MODE
- MF_MT_PIXEL_ASPECT_RATIO
- MF_MT_AVG_BITRATE
- MF_MT_USER_DATA
En el ejemplo de código siguiente se crea un tipo de vídeo comprimido a partir del tipo de vídeo del origen.
//------------------------------------------------------------------- // CreateCompressedVideoType // Creates an output type from source's video media type. // // pType: A pointer to the source's video media type. // ppVideoType: Receives a pointer to the media type. //------------------------------------------------------------------- HRESULT CreateCompressedVideoType( IMFMediaType* pType, IMFMediaType** ppVideoType) { if (!pType) { return E_INVALIDARG; } if (!ppVideoType) { return E_POINTER; } HRESULT hr = S_OK; MFRatio fps = { 0 }; MFRatio par = { 0 }; UINT32 width = 0, height = 0; UINT32 interlace = MFVideoInterlace_Unknown; UINT32 fSamplesIndependent = 0; UINT32 cBitrate = 0; IMFMediaType* pMediaType = NULL; GUID guidMajor = GUID_NULL; hr = pType->GetMajorType(&guidMajor); if (FAILED(hr)) { goto done; } if (guidMajor != MFMediaType_Video ) { hr = MF_E_INVALID_FORMAT; goto done; } hr = MFCreateMediaType(&pMediaType); if (FAILED(hr)) { goto done; } hr = pType->CopyAllItems(pMediaType); if (FAILED(hr)) { goto done; } //Fill the missing attributes //1. Frame rate hr = MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator); if (hr == MF_E_ATTRIBUTENOTFOUND) { fps.Numerator = 30000; fps.Denominator = 1001; hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator); if (FAILED(hr)) { goto done; } } ////2. Frame size hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height); if (hr == MF_E_ATTRIBUTENOTFOUND) { width = 1280; height = 720; hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height); if (FAILED(hr)) { goto done; } } ////3. Interlace mode if (FAILED(pMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &interlace))) { hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); if (FAILED(hr)) { goto done; } } ////4. Pixel aspect Ratio hr = MFGetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator); if (hr == MF_E_ATTRIBUTENOTFOUND) { par.Numerator = par.Denominator = 1; hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32)par.Numerator, (UINT32)par.Denominator); if (FAILED(hr)) { goto done; } } //6. bit rate if (FAILED(pMediaType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate))) { hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, 1000000); if (FAILED(hr)) { goto done; } } hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3); if (FAILED(hr)) { goto done; } // Return the pointer to the caller. *ppVideoType = pMediaType; (*ppVideoType)->AddRef(); done: SafeRelease(&pMediaType); return hr; }
Obtenga un tipo de medio compatible del codificador de vídeo de Windows Media estableciendo las propiedades de codificación y, a continuación, llamando a IMFTransform::GetOutputAvailableType. Este método devuelve el tipo parcial. Asegúrese de convertir el tipo parcial en un tipo completo agregando la siguiente información:
- Establezca la velocidad de bits de vídeo en el atributo MF_MT_AVG_BITRATE.
- Agregue datos privados de códec estableciendo el atributo MF_MT_USER_DATA. Para obtener instrucciones detalladas, consulte "Datos de códec privados" en Configuración de un codificador WMV.
Dado que IWMCodecPrivateData::GetPrivateData comprueba la velocidad de bits antes de devolver los datos privados del códec, asegúrese de establecer la velocidad de bits antes de obtener los datos privados del códec.
En el ejemplo de código siguiente se crea un tipo de vídeo comprimido obteniendo un tipo compatible del codificador de vídeo de Windows Media. También crea un tipo de vídeo sin comprimir y lo establece como entrada del codificador. Esto se implementa en la función auxiliar CreateUncompressedVideoType. GetOutputTypeFromWMVEncoder convierte el tipo parcial devuelto en un tipo completo agregando datos privados de códec. La implementación de SetEncodingProperties se muestra en la sección "Creación del objeto ContentInfo de ASF" de este tutorial.
//------------------------------------------------------------------- // GetOutputTypeFromWMVEncoder // Gets a compatible output type from the Windows Media video encoder. // // pType: A pointer to the source video stream's media type. // ppVideoType: Receives a pointer to the media type. //------------------------------------------------------------------- HRESULT GetOutputTypeFromWMVEncoder(IMFMediaType* pType, IMFMediaType** ppVideoType) { if (!ppVideoType) { return E_POINTER; } IMFTransform* pMFT = NULL; IPropertyStore* pProps = NULL; IMFMediaType* pInputType = NULL; IMFMediaType* pOutputType = NULL; UINT32 cBitrate = 0; //We need to find a suitable output media type //We need to create the encoder to get the available output types //and discard the instance. CLSID *pMFTCLSIDs = NULL; // Pointer to an array of CLISDs. UINT32 cCLSID = 0; // Size of the array. MFT_REGISTER_TYPE_INFO tinfo; tinfo.guidMajorType = MFMediaType_Video; tinfo.guidSubtype = MFVideoFormat_WMV3; // Look for an encoder. HRESULT hr = MFTEnum( MFT_CATEGORY_VIDEO_ENCODER, 0, // Reserved NULL, // Input type to match. None. &tinfo, // WMV encoded type. NULL, // Attributes to match. (None.) &pMFTCLSIDs, // Receives a pointer to an array of CLSIDs. &cCLSID // Receives the size of the array. ); if (FAILED(hr)) { goto done; } // MFTEnum can return zero matches. if (cCLSID == 0) { hr = MF_E_TOPO_CODEC_NOT_FOUND; goto done; } else { //Create the MFT decoder hr = CoCreateInstance(pMFTCLSIDs[0], NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT)); if (FAILED(hr)) { goto done; } } //Get the video encoder property store hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps)); if (FAILED(hr)) { goto done; } //Set encoding properties hr = SetEncodingProperties(MFMediaType_Video, pProps); if (FAILED(hr)) { goto done; } hr = CreateUncompressedVideoType(pType, &pInputType); if (FAILED(hr)) { goto done; } hr = pMFT->SetInputType(0, pInputType, 0); //Get the first output type //You can loop through the available output types to choose //the one that meets your target requirements hr = pMFT->GetOutputAvailableType(0, 0, &pOutputType); if (FAILED(hr)) { goto done; } hr = pType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate); if (FAILED(hr)) { goto done; } //Now set the bit rate hr = pOutputType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate); if (FAILED(hr)) { goto done; } hr = AddPrivateData(pMFT, pOutputType); if (FAILED(hr)) { goto done; } //Return to the caller *ppVideoType = pOutputType; (*ppVideoType)->AddRef(); done: SafeRelease(&pProps); SafeRelease(&pOutputType); SafeRelease(&pInputType); SafeRelease(&pMFT); CoTaskMemFree(pMFTCLSIDs); return hr; }
En el ejemplo de código siguiente se crea un tipo de vídeo sin comprimir.
//------------------------------------------------------------------- // CreateUncompressedVideoType // Creates an uncompressed video type. // // pType: A pointer to the source's video media type. // ppVideoType: Receives a pointer to the media type. //------------------------------------------------------------------- HRESULT CreateUncompressedVideoType(IMFMediaType* pType, IMFMediaType** ppMediaType) { if (!pType) { return E_INVALIDARG; } if (!ppMediaType) { return E_POINTER; } MFRatio fps = { 0 }; MFRatio par = { 0 }; UINT32 width = 0, height = 0; UINT32 interlace = MFVideoInterlace_Unknown; UINT32 cBitrate = 0; IMFMediaType* pMediaType = NULL; GUID guidMajor = GUID_NULL; HRESULT hr = pType->GetMajorType(&guidMajor); if (FAILED(hr)) { goto done; } if (guidMajor != MFMediaType_Video ) { hr = MF_E_INVALID_FORMAT; goto done; } hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator); if (hr == MF_E_ATTRIBUTENOTFOUND) { fps.Numerator = 30000; fps.Denominator = 1001; } hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height); if (hr == MF_E_ATTRIBUTENOTFOUND) { width = 1280; height = 720; } interlace = MFGetAttributeUINT32(pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); hr = MFGetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator); if (FAILED(hr)) { par.Numerator = par.Denominator = 1; } cBitrate = MFGetAttributeUINT32(pType, MF_MT_AVG_BITRATE, 1000000); hr = MFCreateMediaType(&pMediaType); if (FAILED(hr)) { goto done; } hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, guidMajor); if (FAILED(hr)) { goto done; } hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); if (FAILED(hr)) { goto done; } hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator); if (FAILED(hr)) { goto done; } hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height); if (FAILED(hr)) { goto done; } hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, 2); if (FAILED(hr)) { goto done; } hr = pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); if (FAILED(hr)) { goto done; } hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate); if (FAILED(hr)) { goto done; } // Return the pointer to the caller. *ppMediaType = pMediaType; (*ppMediaType)->AddRef(); done: SafeRelease(&pMediaType); return hr; }
En el ejemplo de código siguiente se agregan datos privados de códec al tipo de medio de vídeo especificado.
// // AddPrivateData // Appends the private codec data to a media type. // // pMFT: The video encoder // pTypeOut: A video type from the encoder's type list. // // The function modifies pTypeOut by adding the codec data. // HRESULT AddPrivateData(IMFTransform *pMFT, IMFMediaType *pTypeOut) { HRESULT hr = S_OK; ULONG cbData = 0; BYTE *pData = NULL; IWMCodecPrivateData *pPrivData = NULL; DMO_MEDIA_TYPE mtOut = { 0 }; // Convert the type to a DMO type. hr = MFInitAMMediaTypeFromMFMediaType( pTypeOut, FORMAT_VideoInfo, (AM_MEDIA_TYPE*)&mtOut ); if (SUCCEEDED(hr)) { hr = pMFT->QueryInterface(IID_PPV_ARGS(&pPrivData)); } if (SUCCEEDED(hr)) { hr = pPrivData->SetPartialOutputType(&mtOut); } // // Get the private codec data // // First get the buffer size. if (SUCCEEDED(hr)) { hr = pPrivData->GetPrivateData(NULL, &cbData); } if (SUCCEEDED(hr)) { pData = new BYTE[cbData]; if (pData == NULL) { hr = E_OUTOFMEMORY; } } // Now get the data. if (SUCCEEDED(hr)) { hr = pPrivData->GetPrivateData(pData, &cbData); } // Add the data to the media type. if (SUCCEEDED(hr)) { hr = pTypeOut->SetBlob(MF_MT_USER_DATA, pData, cbData); } delete [] pData; MoFreeMediaType(&mtOut); SafeRelease(&pPrivData); return hr; }
Creación del objeto ContentInfo de ASF
El objeto ContentInfo de ASF es un componente de nivel WMContainer diseñado principalmente para almacenar información del objeto de encabezado ASF. El receptor de archivos ASF implementa el objeto ContentInfo para almacenar información (en un almacén de propiedades) que se usará para escribir el objeto de encabezado ASF del archivo codificado. Para ello, debe crear y configurar el siguiente conjunto de información sobre el objeto ContentInfo antes de iniciar la sesión de codificación.
Llame a MFCreateASFContentInfo para crear un objeto ContentInfo vacío.
En el ejemplo de código siguiente se crea un objeto ContentInfo vacío.
// Create an empty ContentInfo object hr = MFCreateASFContentInfo(&pContentInfo); if (FAILED(hr)) { goto done; }
Llame a IMFASFContentInfo::GetEncodingConfigurationPropertyStore para obtener el almacén de propiedades de nivel de secuencia del receptor de archivos. En esta llamada, debe transferir el número de secuencia que asignó para la secuencia al crear el perfil de ASF.
Establezca las Propiedades de codificación de nivel de secuencia en el almacén de propiedades de secuencia del receptor de archivos. Para obtener más información, consulte "Propiedades de codificación de secuencias" en Establecimiento de propiedades en el receptor de archivos.
En el ejemplo de código siguiente se establecen las propiedades de nivel de secuencia en el almacén de propiedades del receptor de archivos.
//Get stream's encoding property hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps); if (FAILED(hr)) { goto done; } //Set the stream-level encoding properties hr = SetEncodingProperties(guidMajor, pContentInfoProps); if (FAILED(hr)) { goto done; }
En el ejemplo de código siguiente se muestra la implementación de SetEncodingProperties. Esta función establece las propiedades de codificación de nivel de secuencia para CBR y VBR.
//------------------------------------------------------------------- // SetEncodingProperties // Create a media source from a URL. // // guidMT: Major type of the stream, audio or video // pProps: A pointer to the property store in which // to set the required encoding properties. //------------------------------------------------------------------- HRESULT SetEncodingProperties (const GUID guidMT, IPropertyStore* pProps) { if (!pProps) { return E_INVALIDARG; } if (EncodingMode == NONE) { return MF_E_NOT_INITIALIZED; } HRESULT hr = S_OK; PROPVARIANT var; switch (EncodingMode) { case CBR: // Set VBR to false. hr = InitPropVariantFromBoolean(FALSE, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VBRENABLED, var); if (FAILED(hr)) { goto done; } // Set the video buffer window. if (guidMT == MFMediaType_Video) { hr = InitPropVariantFromInt32(VIDEO_WINDOW_MSEC, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VIDEOWINDOW, var); if (FAILED(hr)) { goto done; } } break; case VBR: //Set VBR to true. hr = InitPropVariantFromBoolean(TRUE, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VBRENABLED, var); if (FAILED(hr)) { goto done; } // Number of encoding passes is 1. hr = InitPropVariantFromInt32(1, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_PASSESUSED, var); if (FAILED(hr)) { goto done; } // Set the quality level. if (guidMT == MFMediaType_Audio) { hr = InitPropVariantFromUInt32(98, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_DESIRED_VBRQUALITY, var); if (FAILED(hr)) { goto done; } } else if (guidMT == MFMediaType_Video) { hr = InitPropVariantFromUInt32(95, &var); if (FAILED(hr)) { goto done; } hr = pProps->SetValue(MFPKEY_VBRQUALITY, var); if (FAILED(hr)) { goto done; } } break; default: hr = E_UNEXPECTED; break; } done: PropVariantClear(&var); return hr; }
Llame a IMFASFContentInfo::GetEncodingConfigurationPropertyStore para obtener el almacén de propiedades globales del receptor de archivos. En esta llamada, debe transferir 0 en el primer parámetro. Para obtener más información, consulte "Propiedades del receptor de archivos globales" en Establecimiento de propiedades en el receptor de archivos.
Establezca el valor de MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE en VARIANT_TRUE para asegurarse de que los valores del cubo filtrado en el multiplexor de ASF se ajustan correctamente. Para obtener información sobre esta propiedad, consulte "Inicialización del multiplexor y configuración del cubo filtrado" en Creación del objeto de mutiplexor.
En el ejemplo de código siguiente se establecen las propiedades de nivel de secuencia en el almacén de propiedades del receptor de archivos.
//Now we need to set global properties that will be set on the media sink hr = pContentInfo->GetEncodingConfigurationPropertyStore(0, &pContentInfoProps); if (FAILED(hr)) { goto done; } //Auto adjust Bitrate var.vt = VT_BOOL; var.boolVal = VARIANT_TRUE; hr = pContentInfoProps->SetValue(MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, var); //Initialize with the profile hr = pContentInfo->SetProfile(pProfile); if (FAILED(hr)) { goto done; }
Nota:
Hay otras propiedades que puede establecer en el nivel global para el receptor de archivos. Para obtener más información, consulte "Configuración del objeto ContentInfo con configuración del codificador" en Establecimiento de propiedades en el objeto ContentInfo.
Usará el ContentInfo asf configurado para crear un objeto de activación para el receptor de archivos ASF (que se describe en la sección siguiente).
Si va a crear un receptor de archivos fuera de proceso (MFCreateASFMediaSinkActivate), es decir, mediante un objeto de activación, puede usar el objeto ContentInfo configurado para crear instancias del receptor de medios ASF (se describe en la sección siguiente). Si va a crear un receptor de archivos dentro del proceso (MFCreateASFMediaSink), en lugar de crear el objeto ContentInfo vacío como se describe en el paso 1, obtenga una referencia a la interfaz IMFASFContentInfo llamando a IMFMediaSink::QueryInterface en el receptor de archivos. A continuación, debe configurar el objeto ContentInfo como se describe en esta sección.
Creación del receptor de archivos ASF
En este paso del tutorial, usará la instancia de ContentInfo de ASF configurada, que creó en el paso anterior, para crear un objeto de activación para el receptor de archivos ASF llamando a la función MFCreateASFMediaSinkActivate. Para obtener más información, consulte Creación del receptor de archivos ASF.
En el ejemplo de código siguiente se crea el objeto de activación para el receptor de archivos.
//Create the activation object for the file sink
hr = MFCreateASFMediaSinkActivate(sURL, pContentInfo, &pActivate);
if (FAILED(hr))
{
goto done;
}
Creación de la topología de codificación parcial
A continuación, creará una topología de codificación parcial mediante la creación de nodos de topología para el origen multimedia, los codificadores de Windows Media necesarios y el receptor de archivos ASF. Después de agregar los nodos de topología, conectará el origen, la transformación y los nodos receptores. Antes de agregar nodos de topología, debe crear un objeto de topología vacío llamando a MFCreateTopology.
Creación del nodo de topología de origen para el origen multimedia
En este paso, creará el nodo de topología de origen para el origen multimedia.
Para crear este nodo, necesita las siguientes referencias:
- Puntero al origen multimedia que creó en el paso descrito en la sección "Creación del origen multimedia" de este tutorial.
- Un puntero al descriptor de presentación para el origen multimedia. Puede obtener una referencia a la interfaz IMFPresentationDescriptor del origen multimedia llamando a IMFMediaSource::CreatePresentationDescriptor.
- Puntero al descriptor de secuencia para cada secuencia del origen multimedia para el que ha creado una secuencia de destino en el paso descrito en la sección "Creación del objeto de perfil de ASF" de este tutorial.
Para obtener más información sobre cómo crear nodos de origen y el ejemplo de código, consulte Creación de nodos de origen.
En el ejemplo de código siguiente se crea una topología parcial agregando el nodo de origen y los nodos de transformación necesarios. Este código llama a las funciones auxiliares AddSourceNode y AddTransformOutputNodes. Estas funciones se incluyen más adelante en este tutorial.
//-------------------------------------------------------------------
// BuildPartialTopology
// Create a partial encoding topology by adding the source and the sink.
//
// pSource: A pointer to the media source to enumerate the source streams.
// pSinkActivate: A pointer to the activation object for ASF file sink.
// ppTopology: Receives a pointer to the topology.
//-------------------------------------------------------------------
HRESULT BuildPartialTopology(
IMFMediaSource *pSource,
IMFActivate* pSinkActivate,
IMFTopology** ppTopology)
{
if (!pSource || !pSinkActivate)
{
return E_INVALIDARG;
}
if (!ppTopology)
{
return E_POINTER;
}
HRESULT hr = S_OK;
IMFPresentationDescriptor* pPD = NULL;
IMFStreamDescriptor *pStreamDesc = NULL;
IMFMediaTypeHandler* pMediaTypeHandler = NULL;
IMFMediaType* pSrcType = NULL;
IMFTopology* pTopology = NULL;
IMFTopologyNode* pSrcNode = NULL;
IMFTopologyNode* pEncoderNode = NULL;
IMFTopologyNode* pOutputNode = NULL;
DWORD cElems = 0;
DWORD dwSrcStream = 0;
DWORD StreamID = 0;
GUID guidMajor = GUID_NULL;
BOOL fSelected = FALSE;
//Create the topology that represents the encoding pipeline
hr = MFCreateTopology (&pTopology);
if (FAILED(hr))
{
goto done;
}
hr = pSource->CreatePresentationDescriptor(&pPD);
if (FAILED(hr))
{
goto done;
}
hr = pPD->GetStreamDescriptorCount(&dwSrcStream);
if (FAILED(hr))
{
goto done;
}
for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
{
hr = pPD->GetStreamDescriptorByIndex(
iStream, &fSelected, &pStreamDesc);
if (FAILED(hr))
{
goto done;
}
if (!fSelected)
{
continue;
}
hr = AddSourceNode(pTopology, pSource, pPD, pStreamDesc, &pSrcNode);
if (FAILED(hr))
{
goto done;
}
hr = pStreamDesc->GetMediaTypeHandler (&pMediaTypeHandler);
if (FAILED(hr))
{
goto done;
}
hr = pStreamDesc->GetStreamIdentifier(&StreamID);
if (FAILED(hr))
{
goto done;
}
hr = pMediaTypeHandler->GetMediaTypeByIndex(0, &pSrcType);
if (FAILED(hr))
{
goto done;
}
hr = pSrcType->GetMajorType(&guidMajor);
if (FAILED(hr))
{
goto done;
}
hr = AddTransformOutputNodes(pTopology, pSinkActivate, pSrcType, &pEncoderNode);
if (FAILED(hr))
{
goto done;
}
//now we have the transform node, connect it to the source node
hr = pSrcNode->ConnectOutput(0, pEncoderNode, 0);
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pStreamDesc);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pSrcType);
SafeRelease(&pEncoderNode);
SafeRelease(&pOutputNode);
guidMajor = GUID_NULL;
}
*ppTopology = pTopology;
(*ppTopology)->AddRef();
wprintf_s(L"Partial Topology Built.\n");
done:
SafeRelease(&pStreamDesc);
SafeRelease(&pMediaTypeHandler);
SafeRelease(&pSrcType);
SafeRelease(&pEncoderNode);
SafeRelease(&pOutputNode);
SafeRelease(&pTopology);
return hr;
}
En el ejemplo de código siguiente se crea y se agrega el nodo de topología de origen a la topología de codificación. Toma punteros a un objeto de topología anterior, el origen multimedia, para enumerar las secuencias de origen, el descriptor de presentación del origen multimedia y el descriptor de secuencia del origen multimedia. El autor de la llamada recibe un puntero al nodo de topología de origen.
// Add a source node to a topology.
HRESULT AddSourceNode(
IMFTopology *pTopology, // Topology.
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFTopologyNode **ppNode) // Receives the node pointer.
{
IMFTopologyNode *pNode = NULL;
// Create the node.
HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
if (FAILED(hr))
{
goto done;
}
// Set the attributes.
hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
if (FAILED(hr))
{
goto done;
}
hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
if (FAILED(hr))
{
goto done;
}
hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
if (FAILED(hr))
{
goto done;
}
// Add the node to the topology.
hr = pTopology->AddNode(pNode);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppNode = pNode;
(*ppNode)->AddRef();
done:
SafeRelease(&pNode);
return hr;
}
Creación de instancias de los codificadores necesarios y creación de los nodos de transformación
La canalización de Media Foundation no inserta automáticamente los codificadores de Windows Media necesarios para las secuencias que debe codificar. La aplicación debe agregar manualmente los codificadores. Para ello, enumere las secuencias en el perfil de ASF que creó en el paso descrito en la sección "Creación del objeto de perfil de ASF" de este tutorial. Para cada secuencia del origen y la secuencia correspondiente del perfil, cree una instancia de los codificadores necesarios. Para este paso, necesita un puntero al objeto de activación para el receptor de archivos que creó en el paso descrito en la sección "Creación del receptor de archivos ASF" de este tutorial.
Para obtener información general sobre cómo crear codificadores a través de objetos de activación, consulte Uso de objetos de activación de un codificador.
En el procedimiento siguiente se describen los pasos necesarios para crear instancias de los codificadores necesarios.
Obtenga una referencia al objeto ContentInfo del receptor llamando a IMFActivate::ActivateObject en el receptor de archivos y, a continuación, consulte IMFASFContentInfo desde el receptor de archivos llamando a QueryInterface.
Obtenga el perfil de ASF asociado al objeto ContentInfo llamando a IMFASFContentInfo::GetProfile.
Enumere las secuencias en el perfil. Para ello, necesitará el recuento de secuencias y una referencia a la interfaz IMFASFStreamConfig de cada secuencia.
Llame a los métodos siguientes:
Para cada secuencia, obtenga el tipo principal y las propiedades de codificación de la secuencia del objeto ContentInfo.
Llame a los métodos siguientes:
IMFASFContentInfo::GetEncodingConfigurationPropertyStore
Para esta llamada necesita el número de secuencia recuperado por la llamada IMFASFProfile::GetStream.
Según el tipo de secuencia, audio o vídeo, cree una instancia del objeto de activación para el codificador llamando a MFCreateWMAEncoderActivate o MFCreateWMVEncoderActivate.
Para llamar a estas funciones, necesita las siguientes referencias:
- Puntero al tipo de medio de la secuencia recuperado por IMFASFStreamConfig::GetMediaType en el paso anterior.
- Puntero al almacén de propiedades de codificación de la secuencia recuperado por IMFASFContentInfo::GetEncodingConfigurationPropertyStore. Al transferir un puntero al almacén de propiedades, las propiedades de secuencia establecidas en el receptor de archivos se copian en el MFT del codificador.
Actualice el parámetro de cubo filtrado para la secuencia de audio.
MFCreateWMAEncoderActivate establece el tipo de salida en el MFT del codificador subyacente para el códec de audio de Windows Media. Después de establecer el tipo de medio de salida, el codificador obtiene la velocidad de bits media del tipo de medio de salida, calcula la velocidad de bits media de la ventana de búfer y establece los valores de cubo filtrado que se usarán durante la sesión de codificación. Puede actualizar estos valores en el receptor de archivos consultando el codificador o estableciendo valores personalizados. Para actualizar los valores, necesita el siguiente conjunto de información:
- Velocidad de bits media: obtenga la velocidad de bits media del atributo MF_MT_AUDIO_AVG_BYTES_PER_SECOND establecido en el tipo de medio de salida seleccionado durante la negociación de tipos multimedia.
- Ventana de búfer: puede recuperarla llamando a IWMCodecLeakyBucket::GetBufferSizeBits.
- Tamaño inicial del búfer: se establece en 0.
Cree una matriz de DWORDs y establezca el valor en la propiedad MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET del receptor de la secuencia de audio. Si no proporciona los valores actualizados, la sesión multimedia los establece correctamente.
Para obtener más información, consulte El modelo de búfer de cubo filtrado.
Los objetos de activación creados en el paso 5 se deben agregar a la topología como nodos de topología de transformación. Para obtener más información y el ejemplo de código, consulte "Creación de un nodo de transformación a partir de un objeto de activación" en Creación de nodos de transformación.
En el ejemplo de código siguiente se crean y agregan las activaciones necesarias del codificador. Toma punteros a un objeto de topología creado anteriormente, el objeto de activación del receptor de archivos y el tipo de medio de la secuencia de origen. También llama a AddOutputNode (consulte el siguiente ejemplo de código) que crea y agrega el nodo receptor a la topología de codificación. El autor de la llamada recibe un puntero al nodo de topología de origen.
//-------------------------------------------------------------------
// AddTransformOutputNodes
// Creates and adds the sink node to the encoding topology.
// Creates and adds the required encoder activates.
// pTopology: A pointer to the topology.
// pSinkActivate: A pointer to the file sink's activation object.
// pSourceType: A pointer to the source stream's media type.
// ppNode: Receives a pointer to the topology node.
//-------------------------------------------------------------------
HRESULT AddTransformOutputNodes(
IMFTopology* pTopology,
IMFActivate* pSinkActivate,
IMFMediaType* pSourceType,
IMFTopologyNode **ppNode // Receives the node pointer.
)
{
if (!pTopology || !pSinkActivate || !pSourceType)
{
return E_INVALIDARG;
}
IMFTopologyNode* pEncNode = NULL;
IMFTopologyNode* pOutputNode = NULL;
IMFASFContentInfo* pContentInfo = NULL;
IMFASFProfile* pProfile = NULL;
IMFASFStreamConfig* pStream = NULL;
IMFMediaType* pMediaType = NULL;
IPropertyStore* pProps = NULL;
IMFActivate *pEncoderActivate = NULL;
IMFMediaSink *pSink = NULL;
GUID guidMT = GUID_NULL;
GUID guidMajor = GUID_NULL;
DWORD cStreams = 0;
WORD wStreamNumber = 0;
HRESULT hr = S_OK;
hr = pSourceType->GetMajorType(&guidMajor);
if (FAILED(hr))
{
goto done;
}
// Create the node.
hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &pEncNode);
if (FAILED(hr))
{
goto done;
}
//Activate the sink
hr = pSinkActivate->ActivateObject(__uuidof(IMFMediaSink), (void**)&pSink);
if (FAILED(hr))
{
goto done;
}
//find the media type in the sink
//Get content info from the sink
hr = pSink->QueryInterface(__uuidof(IMFASFContentInfo), (void**)&pContentInfo);
if (FAILED(hr))
{
goto done;
}
hr = pContentInfo->GetProfile(&pProfile);
if (FAILED(hr))
{
goto done;
}
hr = pProfile->GetStreamCount(&cStreams);
if (FAILED(hr))
{
goto done;
}
for(DWORD index = 0; index < cStreams ; index++)
{
hr = pProfile->GetStream(index, &wStreamNumber, &pStream);
if (FAILED(hr))
{
goto done;
}
hr = pStream->GetMediaType(&pMediaType);
if (FAILED(hr))
{
goto done;
}
hr = pMediaType->GetMajorType(&guidMT);
if (FAILED(hr))
{
goto done;
}
if (guidMT!=guidMajor)
{
SafeRelease(&pStream);
SafeRelease(&pMediaType);
guidMT = GUID_NULL;
continue;
}
//We need to activate the encoder
hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamNumber, &pProps);
if (FAILED(hr))
{
goto done;
}
if (guidMT == MFMediaType_Audio)
{
hr = MFCreateWMAEncoderActivate(pMediaType, pProps, &pEncoderActivate);
if (FAILED(hr))
{
goto done;
}
wprintf_s(L"Audio Encoder created. Stream Number: %d .\n", wStreamNumber);
break;
}
if (guidMT == MFMediaType_Video)
{
hr = MFCreateWMVEncoderActivate(pMediaType, pProps, &pEncoderActivate);
if (FAILED(hr))
{
goto done;
}
wprintf_s(L"Video Encoder created. Stream Number: %d .\n", wStreamNumber);
break;
}
}
// Set the object pointer.
hr = pEncNode->SetObject(pEncoderActivate);
if (FAILED(hr))
{
goto done;
}
// Add the node to the topology.
hr = pTopology->AddNode(pEncNode);
if (FAILED(hr))
{
goto done;
}
//Add the output node to this node.
hr = AddOutputNode(pTopology, pSinkActivate, wStreamNumber, &pOutputNode);
if (FAILED(hr))
{
goto done;
}
//now we have the output node, connect it to the transform node
hr = pEncNode->ConnectOutput(0, pOutputNode, 0);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppNode = pEncNode;
(*ppNode)->AddRef();
done:
SafeRelease(&pEncNode);
SafeRelease(&pOutputNode);
SafeRelease(&pEncoderActivate);
SafeRelease(&pMediaType);
SafeRelease(&pProps);
SafeRelease(&pStream);
SafeRelease(&pProfile);
SafeRelease(&pContentInfo);
SafeRelease(&pSink);
return hr;
}
Creación de los nodos de topología de salida para el receptor de archivos
En este paso, creará el nodo de topología de salida para el receptor de archivos ASF.
Para crear este nodo, necesita las siguientes referencias:
- Puntero al objeto de activación que creó en el paso descrito en la sección "Creación del receptor de archivos ASF" de este tutorial.
- Los números de secuencia para identificar los receptores de secuencia agregados al receptor de archivos. Los números de secuencia coinciden con la identificación de la secuencia que se estableció durante la creación de la secuencia.
Para obtener más información sobre cómo crear nodos de salida y sobre el ejemplo de código, consulte "Creación de un nodo de salida a partir de un objeto de activación" en Creación de nodos de salida.
Si no usa el objeto de activación para el receptor de archivos, debe enumerar los receptores de secuencia en el receptor de archivos ASF y establecer cada receptor de secuencia como el nodo de salida de la topología. Para obtener información sobre la enumeración de receptores de secuencias, consulte "Enumeración de receptores de secuencias" en Adición de información de secuencia al receptor de archivos ASF.
En el ejemplo de código siguiente se crea y se agrega el nodo receptor a la topología de codificación. Toma punteros a un objeto de topología creado anteriormente, el objeto de activación del receptor de archivos y el número de identificación de la secuencia. El autor de la llamada recibe un puntero al nodo de topología de origen.
// Add an output node to a topology.
HRESULT AddOutputNode(
IMFTopology *pTopology, // Topology.
IMFActivate *pActivate, // Media sink activation object.
DWORD dwId, // Identifier of the stream sink.
IMFTopologyNode **ppNode) // Receives the node pointer.
{
IMFTopologyNode *pNode = NULL;
// Create the node.
HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
if (FAILED(hr))
{
goto done;
}
// Set the object pointer.
hr = pNode->SetObject(pActivate);
if (FAILED(hr))
{
goto done;
}
// Set the stream sink ID attribute.
hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId);
if (FAILED(hr))
{
goto done;
}
hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
if (FAILED(hr))
{
goto done;
}
// Add the node to the topology.
hr = pTopology->AddNode(pNode);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppNode = pNode;
(*ppNode)->AddRef();
done:
SafeRelease(&pNode);
return hr;
}
En el ejemplo de código siguiente se enumeran los receptores de secuencias de un receptor multimedia determinado.
//-------------------------------------------------------------------
// EnumerateStreamSinks
// Enumerates the stream sinks within the specified media sink.
//
// pSink: A pointer to the media sink.
//-------------------------------------------------------------------
HRESULT EnumerateStreamSinks (IMFMediaSink* pSink)
{
if (!pSink)
{
return E_INVALIDARG;
}
IMFStreamSink* pStreamSink = NULL;
DWORD cStreamSinks = 0;
HRESULT hr = pSink->GetStreamSinkCount(&cStreamSinks);
if (FAILED(hr))
{
goto done;
}
for(DWORD index = 0; index < cStreamSinks; index++)
{
hr = pSink->GetStreamSinkByIndex (index, &pStreamSink);
if (FAILED(hr))
{
goto done;
}
//Use the stream sink
//Not shown.
}
done:
SafeRelease(&pStreamSink);
return hr;
}
Conexión de los nodos de origen, transformación y receptor
En este paso, conectará el nodo de origen al nodo de transformación que hace referencia a las activaciones de codificación que creó en el paso descrito en la sección "Creación de instancias de los codificadores necesarios y creación de los nodos de transformación" de este tutorial. El nodo de transformación se conectará al nodo de salida que contiene el objeto de activación del receptor de archivos.
Control de la sesión de codificación
En este paso, llevará a cabo los siguientes pasos:
Llame a MFCreateMediaSession para crear una sesión de codificación.
Llame a IMFMediaSession::SetTopology para establecer la topología de codificación en la sesión. Si se completa la llamada, la sesión multimedia evalúa los nodos de topología e inserta objetos de transformación adicionales, como un descodificador que convierte el origen comprimido especificado en muestras sin comprimir para insertarlas como entradas en el codificador.
Llame a IMFMediaSession::GetEvent para solicitar los eventos generados por la sesión multimedia.
En el bucle de eventos, iniciará y cerrará la sesión de codificación en función de los eventos generados por la sesión multimedia. La llamada a IMFMediaSession::SetTopology da lugar a una sesión multimedia que genera el evento MESessionTopologyStatus con la marca MF_TOPOSTATUS_READY establecida. Todos los eventos se generan de forma asincrónica y una aplicación puede recuperar estos eventos de forma sincrónica o asincrónica. Dado que la aplicación de este tutorial es una aplicación de consola y bloquear los subprocesos de la interfaz de usuario no es un problema, obtendremos los eventos de la sesión multimedia de forma sincrónica.
Para obtener los eventos de forma asincrónica, la aplicación debe implementar la interfaz IMFAsyncCallback. Para obtener más información y una implementación de ejemplo de esta interfaz, consulte "Control de eventos de sesión" en Cómo reproducir archivos multimedia con Media Foundation.
En el bucle de eventos para obtener eventos de sesión multimedia, espere al evento MESessionTopologyStatus que se genera cuando se completa IMFMediaSession::SetTopology y se resuelve la topología. Al obtener el evento MESessionTopologyStatus, inicie la sesión de codificación llamando a IMFMediaSession::Start. La sesión multimedia genera el evento MEEndOfPresentation cuando se completan todas las operaciones de codificación. Este evento debe controlarse para la codificación VBR y se describe en la sección siguiente "Actualización de las propiedades de codificación en el receptor de archivos para la codificación VBR" de este tutorial.
La sesión multimedia genera el objeto de encabezado ASF y finaliza el archivo cuando se completa la sesión de codificación y, a continuación, genera el evento MESessionClosed. Este evento debe controlarse realizando operaciones de apagado adecuadas en la sesión multimedia. Para iniciar las operaciones de apagado, llame a IMFMediaSession::Shutdown. Una vez cerrada la sesión de codificación, no se puede establecer otra topología para la codificación en la misma instancia de sesión multimedia. Para codificar otro archivo, la sesión multimedia actual debe cerrarse y liberarse y la nueva topología debe establecerse en una sesión multimedia recién creada. En el ejemplo de código siguiente se crea la sesión multimedia, se establece la topología de codificación y se controlan los eventos de sesión multimedia.
En el ejemplo de código siguiente se crea la sesión multimedia, se establece la topología de codificación y se controla la sesión de codificación mediante el control de eventos desde la sesión multimedia.
//-------------------------------------------------------------------
// Encode
// Controls the encoding session and handles events from the media session.
//
// pTopology: A pointer to the encoding topology.
//-------------------------------------------------------------------
HRESULT Encode(IMFTopology *pTopology)
{
if (!pTopology)
{
return E_INVALIDARG;
}
IMFMediaSession *pSession = NULL;
IMFMediaEvent* pEvent = NULL;
IMFTopology* pFullTopology = NULL;
IUnknown* pTopoUnk = NULL;
MediaEventType meType = MEUnknown; // Event type
HRESULT hr = S_OK;
HRESULT hrStatus = S_OK; // Event status
MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.
hr = MFCreateMediaSession(NULL, &pSession);
if (FAILED(hr))
{
goto done;
}
hr = pSession->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, pTopology);
if (FAILED(hr))
{
goto done;
}
//Get media session events synchronously
while (1)
{
hr = pSession->GetEvent(0, &pEvent);
if (FAILED(hr))
{
goto done;
}
hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
hr = pEvent->GetStatus(&hrStatus);
if (FAILED(hr))
{
goto done;
}
if (FAILED(hrStatus))
{
hr = hrStatus;
goto done;
}
switch(meType)
{
case MESessionTopologyStatus:
{
// Get the status code.
MF_TOPOSTATUS status = (MF_TOPOSTATUS)MFGetAttributeUINT32(
pEvent, MF_EVENT_TOPOLOGY_STATUS, MF_TOPOSTATUS_INVALID);
if (status == MF_TOPOSTATUS_READY)
{
PROPVARIANT var;
PropVariantInit(&var);
wprintf_s(L"Topology resolved and set on the media session.\n");
hr = pSession->Start(NULL, &var);
if (FAILED(hr))
{
goto done;
}
}
if (status == MF_TOPOSTATUS_STARTED_SOURCE)
{
wprintf_s(L"Encoding started.\n");
break;
}
if (status == MF_TOPOSTATUS_ENDED)
{
wprintf_s(L"Encoding complete.\n");
hr = pSession->Close();
if (FAILED(hr))
{
goto done;
}
break;
}
}
break;
case MESessionEnded:
wprintf_s(L"Encoding complete.\n");
hr = pSession->Close();
if (FAILED(hr))
{
goto done;
}
break;
case MEEndOfPresentation:
{
if (EncodingMode == VBR)
{
hr = pSession->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &pFullTopology);
if (FAILED(hr))
{
goto done;
}
hr = PostEncodingUpdate(pFullTopology);
if (FAILED(hr))
{
goto done;
}
wprintf_s(L"Updated sinks for VBR. \n");
}
}
break;
case MESessionClosed:
wprintf_s(L"Encoding session closed.\n");
hr = pSession->Shutdown();
goto done;
}
if (FAILED(hr))
{
goto done;
}
SafeRelease(&pEvent);
}
done:
SafeRelease(&pEvent);
SafeRelease(&pSession);
SafeRelease(&pFullTopology);
SafeRelease(&pTopoUnk);
return hr;
}
Actualización de las propiedades de codificación en el receptor de archivos
Algunas propiedades de codificación, como la velocidad de bits de codificación y los valores de cubo filtrado precisos no se conocen hasta que se completa la codificación, especialmente para la codificación VBR. Para obtener los valores correctos, la aplicación debe esperar al evento MEEndOfPresentation que indica que la sesión de codificación está completa. Los valores del cubo filtrado deben actualizarse en el receptor para que el objeto de encabezado ASF pueda reflejar los valores precisos.
En el procedimiento siguiente se describen los pasos necesarios para recorrer los nodos de la topología de codificación a fin de obtener el nodo receptor de archivos y establecer las propiedades de cubo filtrado necesarias.
Para actualizar los valores de propiedad posteriores a la codificación en el receptor de archivos ASF
- Llame a IMFTopology::GetOutputNodeCollection para obtener la colección de nodos de salida de la topología de codificación.
- Para cada nodo, obtenga un puntero al receptor de secuencias en el nodo llamando a IMFTopologyNode::GetObject. Consulte la interfaz IMFStreamSink en el puntero IUnknown devuelto por IMFTopologyNode::GetObject.
- Para cada receptor de secuencias, obtenga el nodo descendente (codificador) llamando a IMFTopologyNode::GetInput.
- Consulte el nodo para obtener el puntero IMFTransform desde el nodo del codificador.
- Consulte el codificador del puntero IPropertyStore para obtener el almacén de propiedades de codificación del codificador.
- Consulte el receptor de secuencias del puntero IPropertyStore para obtener el almacén de propiedades del receptor de secuencias.
- Llame a IPropertyStore::GetValue para obtener los valores de propiedad necesarios del almacén de propiedades del codificador y cópielos en el almacén de propiedades del receptor de secuencias llamando a IPropertyStore::SetValue.
En la tabla siguiente se muestran los valores de propiedad posteriores a la codificación que se deben establecer en el receptor de secuencias para la secuencia de vídeo.
Tipo de codificación | Nombre de la propiedad (GetValue) | Nombre de la propiedad (SetValue) |
---|---|---|
Codificación de velocidad de bits constante | MFPKEY_BAVG MFPKEY_RAVG |
MFPKEY_STAT_BAVG MFPKEY_STAT_RAVG |
Codificación de velocidad de bits variable basada en calidad | MFPKEY_BAVG MFPKEY_RAVG MFPKEY_BMAX MFPKEY_RMAX |
MFPKEY_STAT_BAVG MFPKEY_STAT_RAVG MFPKEY_STAT_BMAX MFPKEY_STAT_RMAX |
En el ejemplo de código siguiente se establecen los valores de propiedad posteriores a la codificación.
//-------------------------------------------------------------------
// PostEncodingUpdate
// Updates the file sink with encoding properties set on the encoder
// during the encoding session.
//1. Get the output nodes
//2. For each node, get the downstream node
//3. For the downstream node, get the MFT
//4. Get the property store
//5. Get the required values
//6. Set them on the stream sink
//
// pTopology: A pointer to the full topology retrieved from the media session.
//-------------------------------------------------------------------
HRESULT PostEncodingUpdate(IMFTopology *pTopology)
{
if (!pTopology)
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
IMFCollection* pOutputColl = NULL;
IUnknown* pNodeUnk = NULL;
IMFMediaType* pType = NULL;
IMFTopologyNode* pNode = NULL;
IUnknown* pSinkUnk = NULL;
IMFStreamSink* pStreamSink = NULL;
IMFTopologyNode* pEncoderNode = NULL;
IUnknown* pEncoderUnk = NULL;
IMFTransform* pEncoder = NULL;
IPropertyStore* pStreamSinkProps = NULL;
IPropertyStore* pEncoderProps = NULL;
GUID guidMajorType = GUID_NULL;
PROPVARIANT var;
PropVariantInit( &var );
DWORD cElements = 0;
hr = pTopology->GetOutputNodeCollection( &pOutputColl);
if (FAILED(hr))
{
goto done;
}
hr = pOutputColl->GetElementCount(&cElements);
if (FAILED(hr))
{
goto done;
}
for(DWORD index = 0; index < cElements; index++)
{
hr = pOutputColl->GetElement(index, &pNodeUnk);
if (FAILED(hr))
{
goto done;
}
hr = pNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**)&pNode);
if (FAILED(hr))
{
goto done;
}
hr = pNode->GetInputPrefType(0, &pType);
if (FAILED(hr))
{
goto done;
}
hr = pType->GetMajorType( &guidMajorType );
if (FAILED(hr))
{
goto done;
}
hr = pNode->GetObject(&pSinkUnk);
if (FAILED(hr))
{
goto done;
}
hr = pSinkUnk->QueryInterface(IID_IMFStreamSink, (void**)&pStreamSink);
if (FAILED(hr))
{
goto done;
}
hr = pNode->GetInput( 0, &pEncoderNode, NULL );
if (FAILED(hr))
{
goto done;
}
hr = pEncoderNode->GetObject(&pEncoderUnk);
if (FAILED(hr))
{
goto done;
}
hr = pEncoderUnk->QueryInterface(IID_IMFTransform, (void**)&pEncoder);
if (FAILED(hr))
{
goto done;
}
hr = pStreamSink->QueryInterface(IID_IPropertyStore, (void**)&pStreamSinkProps);
if (FAILED(hr))
{
goto done;
}
hr = pEncoder->QueryInterface(IID_IPropertyStore, (void**)&pEncoderProps);
if (FAILED(hr))
{
goto done;
}
if( guidMajorType == MFMediaType_Video )
{
hr = pEncoderProps->GetValue( MFPKEY_BAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_RAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var);
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_BMAX, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_RMAX, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var);
if (FAILED(hr))
{
goto done;
}
}
else if( guidMajorType == MFMediaType_Audio )
{
hr = pEncoderProps->GetValue( MFPKEY_STAT_BAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_STAT_RAVG, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_STAT_BMAX, &var);
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_STAT_RMAX, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var );
if (FAILED(hr))
{
goto done;
}
PropVariantClear( &var );
hr = pEncoderProps->GetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, &var );
if (FAILED(hr))
{
goto done;
}
hr = pStreamSinkProps->SetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, var );
if (FAILED(hr))
{
goto done;
}
}
PropVariantClear( &var );
}
done:
SafeRelease (&pOutputColl);
SafeRelease (&pNodeUnk);
SafeRelease (&pType);
SafeRelease (&pNode);
SafeRelease (&pSinkUnk);
SafeRelease (&pStreamSink);
SafeRelease (&pEncoderNode);
SafeRelease (&pEncoderUnk);
SafeRelease (&pEncoder);
SafeRelease (&pStreamSinkProps);
SafeRelease (&pEncoderProps);
return hr;
}
Implementación de la función principal
En el ejemplo de código siguiente se muestra la función principal de la aplicación de consola.
int wmain(int argc, wchar_t* argv[])
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (argc != 4)
{
wprintf_s(L"Usage: %s inputaudio.mp3, %s output.wm*, %Encoding Type: CBR, VBR\n");
return 0;
}
HRESULT hr = S_OK;
IMFMediaSource* pSource = NULL;
IMFTopology* pTopology = NULL;
IMFActivate* pFileSinkActivate = NULL;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr))
{
goto done;
}
//Set the requested encoding mode
if (wcscmp(argv[3], L"CBR")==0)
{
EncodingMode = CBR;
}
else if (wcscmp(argv[3], L"VBR")==0)
{
EncodingMode = VBR;
}
else
{
EncodingMode = CBR;
}
// Start up Media Foundation platform.
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
goto done;
}
//Create the media source
hr = CreateMediaSource(argv[1], &pSource);
if (FAILED(hr))
{
goto done;
}
//Create the file sink activate
hr = CreateMediaSink(argv[2], pSource, &pFileSinkActivate);
if (FAILED(hr))
{
goto done;
}
//Build the encoding topology.
hr = BuildPartialTopology(pSource, pFileSinkActivate, &pTopology);
if (FAILED(hr))
{
goto done;
}
//Instantiate the media session and start encoding
hr = Encode(pTopology);
if (FAILED(hr))
{
goto done;
}
done:
// Clean up.
SafeRelease(&pSource);
SafeRelease(&pTopology);
SafeRelease(&pFileSinkActivate);
MFShutdown();
CoUninitialize();
if (FAILED(hr))
{
wprintf_s(L"Could not create the output file due to 0x%X\n", hr);
}
return 0;
}
Prueba del archivo de salida
En la lista siguiente se describe una lista de comprobación para probar el archivo codificado. Estos valores se pueden activar en el cuadro de diálogo de propiedades del archivo que puede ver haciendo clic con el botón derecho en el archivo codificado y seleccionando Propiedades en el menú contextual.
- La ruta de acceso del archivo codificado es precisa.
- El tamaño del archivo es superior a cero KB y la duración de reproducción coincide con la duración del archivo de origen.
- Para la secuencia de vídeo, compruebe la anchura y la altura de los fotogramas y la velocidad de fotogramas. Estos valores deben coincidir con los valores especificados en el perfil de ASF que creó en el paso descrito en la sección "Creación del objeto de perfil de ASF".
- Para la secuencia de audio, la velocidad de bits debe ser cercana al valor especificado en el tipo de medio de destino.
- Abra el archivo en el Reproductor multimedia de Windows y compruebe la calidad de la codificación.
- Abra el archivo ASF en ASFViewer para ver la estructura de un archivo ASF. Esta herramienta se puede descargar desde este sitio web de Microsoft.
Códigos de error comunes y sugerencias de depuración
En la lista siguiente se describen los códigos de error comunes que puede recibir y las sugerencias de depuración.
La llamada a IMFSourceResolver::CreateObjectFromURL detiene la aplicación.
Asegúrese de que ha inicializado la plataforma de Media Foundation llamando a MFStartup. Esta función configura la plataforma asincrónica que usan todos los métodos que inician operaciones asincrónicas, como IMFSourceResolver::CreateObjectFromURL, internamente.
IMFSourceResolver::CreateObjectFromURL devuelve HRESULT 0x80070002 "El sistema no encuentra el archivo especificado.
Asegúrese de que existe el nombre de archivo de entrada especificado por el usuario en el primer argumento.
HRESULT 0x80070020 "El proceso no puede acceder al archivo porque otro proceso lo está utilizando. "
Asegúrese de que los archivos de entrada y salida no estén actualmente en uso por otro recurso del sistema.
Las llamadas a los métodos IMFTransform devuelven MF_E_INVALIDMEDIATYPE.
Asegúrese de que las siguientes condiciones sean verdaderas:
- El tipo de entrada o el tipo de salida que especificó son compatibles con los tipos de medios que admite el codificador.
- Los tipos de medios que especificó están completos. Para que los tipos de medios sean completos, consulte los atributos necesarios en las secciones "Creación de un tipo de medio de audio comprimido" y "Creación de un tipo de medio de vídeo comprimido" de este tutorial.
- Asegúrese de que ha establecido la velocidad de bits de destino en el tipo de medio parcial al que va a agregar los datos privados del códec.
La sesión multimedia devuelve MF_E_UNSUPPORTED_D3D_TYPE en el estado del evento.
Este error se devuelve cuando el tipo de medio del origen indica un modo de entrelazado mixto que no es compatible con el codificador de vídeo de Windows Media. Si el tipo de medio de vídeo comprimido está establecido para usar el modo progresivo, la canalización debe usar una transformación de deshacer entrelazado. Dado que la canalización no puede encontrar una coincidencia (indicada por este código de error), debe insertar un elemento para deshacer el entrelazado (procesador de vídeo transcodificador) entre el descodificador y los nodos del codificador manualmente.
La sesión multimedia devuelve E_INVALIDARG en el estado del evento.
Este error se devuelve cuando los atributos de tipo multimedia del origen no son compatibles con los atributos del tipo de medio de salida establecido en el codificador de Windows Media.
IWMCodecPrivateData::GetPrivateData devuelve HRESULT 0x80040203 "Error de sintaxis al intentar evaluar una cadena de consulta"
Asegúrese de que el tipo de entrada está establecido en el MFT del codificador.
Temas relacionados