Share via


Tutorial: Codificación de un archivo MP4

En este tutorial se muestra cómo usar la API Transcode para codificar un archivo MP4, mediante H.264 para la secuencia de vídeo y AAC para la secuencia de audio.

Encabezados y archivos de biblioteca

Incluya los siguientes archivos de encabezado.

#include <new>
#include <iostream>
#include <windows.h>
#include <mfapi.h>
#include <Mfidl.h>
#include <shlwapi.h>

Vincule los siguientes archivos de biblioteca.

#pragma comment(lib, "mfplat")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "shlwapi")

Definición de los perfiles de codificación

Un enfoque para la codificación es definir una lista de perfiles de codificación de destino que se conocen de antemano. En este tutorial, se toma un enfoque relativamente sencillo y se almacena una lista de formatos de codificación para vídeo H.264 y audio AAC.

Para H.264, los atributos de formato más importantes son el perfil H.264, la velocidad de fotogramas, el tamaño del fotograma y la velocidad de bits codificada. La matriz siguiente contiene una lista de formatos de codificación H.264.

struct H264ProfileInfo
{
    UINT32  profile;
    MFRatio fps;
    MFRatio frame_size;
    UINT32  bitrate;
};

H264ProfileInfo h264_profiles[] = 
{
    { eAVEncH264VProfile_Base, { 15, 1 },       { 176, 144 },   128000 },
    { eAVEncH264VProfile_Base, { 15, 1 },       { 352, 288 },   384000 },
    { eAVEncH264VProfile_Base, { 30, 1 },       { 352, 288 },   384000 },
    { eAVEncH264VProfile_Base, { 29970, 1000 }, { 320, 240 },   528560 },
    { eAVEncH264VProfile_Base, { 15, 1 },       { 720, 576 },  4000000 },
    { eAVEncH264VProfile_Main, { 25, 1 },       { 720, 576 }, 10000000 },
    { eAVEncH264VProfile_Main, { 30, 1 },       { 352, 288 }, 10000000 },
};

Los perfiles H.264 se especifican mediante la enumeración eAVEncH264VProfile . También puede especificar el nivel H.264, pero Microsoft Media Foundation H.264 Video Encoder puede derivar el nivel adecuado para una secuencia de vídeo determinada, por lo que se recomienda no invalidar el nivel seleccionado del codificador. Para el contenido entrelazado, también especificaría el modo de interlace (vea Video Interlacing).

En el caso del audio AAC, los atributos de formato más importantes son la frecuencia de muestreo de audio, el número de canales, el número de bits por muestra y la velocidad de bits codificada. Opcionalmente, puede establecer la indicación de nivel de perfil de audio AAC. Para obtener más información, consulte Codificador AAC. La matriz siguiente contiene una lista de formatos de codificación AAC.

struct AACProfileInfo
{
    UINT32  samplesPerSec;
    UINT32  numChannels;
    UINT32  bitsPerSample;
    UINT32  bytesPerSec;
    UINT32  aacProfile;
};

AACProfileInfo aac_profiles[] = 
{
    { 96000, 2, 16, 24000, 0x29}, 
    { 48000, 2, 16, 24000, 0x29}, 
    { 44100, 2, 16, 16000, 0x29}, 
    { 44100, 2, 16, 12000, 0x29}, 
};

Nota

Las H264ProfileInfo estructuras y AACProfileInfo definidas aquí no forman parte de la API de Media Foundation.

 

Escritura de la función wmain

En el código siguiente se muestra el punto de entrada de la aplicación de consola.

int video_profile = 0;
int audio_profile = 0;

int wmain(int argc, wchar_t* argv[])
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    if (argc < 3 || argc > 5)
    {
        std::cout << "Usage:" << std::endl;
        std::cout << "input output [ audio_profile video_profile ]" << std::endl;
        return 1;
    }

    if (argc > 3)
    {
        audio_profile = _wtoi(argv[3]);
    }
    if (argc > 4)
    {
        video_profile = _wtoi(argv[4]);
    }

    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (SUCCEEDED(hr))
    {
        hr = MFStartup(MF_VERSION);
        if (SUCCEEDED(hr))
        {
            hr = EncodeFile(argv[1], argv[2]);
            MFShutdown();
        }
        CoUninitialize();
    }

    if (SUCCEEDED(hr))
    {
        std::cout << "Done." << std::endl;
    }
    else
    {
        std::cout << "Error: " << std::hex << hr << std::endl;
    }

    return 0;
}

La wmain función hace lo siguiente:

  1. Llama a la función CoInitializeEx para inicializar la biblioteca COM.
  2. Llama a la función MFStartup para inicializar Media Foundation.
  3. Llama a la función definida por EncodeFile la aplicación. Esta función transcodifica el archivo de entrada en el archivo de salida y se muestra en la sección siguiente.
  4. Llama a la función MFShutdown para apagar Media Foundation.
  5. Llame a la función CoUninitialize para anular la inicialización de la biblioteca COM.

Codificar el archivo

En el código siguiente se muestra EncodeFile la función , que realiza la transcodificación. Esta función consta principalmente de llamadas a otras funciones definidas por la aplicación, que se muestran más adelante en este tema.

HRESULT EncodeFile(PCWSTR pszInput, PCWSTR pszOutput)
{
    IMFTranscodeProfile *pProfile = NULL;
    IMFMediaSource *pSource = NULL;
    IMFTopology *pTopology = NULL;
    CSession *pSession = NULL;

    MFTIME duration = 0;

    HRESULT hr = CreateMediaSource(pszInput, &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = GetSourceDuration(pSource, &duration);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = CreateTranscodeProfile(&pProfile);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = MFCreateTranscodeTopology(pSource, pszOutput, pProfile, &pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = CSession::Create(&pSession);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSession->StartEncodingSession(pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = RunEncodingSession(pSession, duration);

done:            
    if (pSource)
    {
        pSource->Shutdown();
    }

    SafeRelease(&pSession);
    SafeRelease(&pProfile);
    SafeRelease(&pSource);
    SafeRelease(&pTopology);
    return hr;
}

La EncodeFile función realiza los pasos siguientes.

  1. Crea un origen multimedia para el archivo de entrada mediante la dirección URL o la ruta de acceso del archivo de entrada. (Consulte Crear el origen multimedia).
  2. Obtiene la duración del archivo de entrada. (Consulte Obtener la duración del origen).
  3. Cree el perfil de transcodificación. (Consulte Crear el perfil de transcodificación).
  4. Llame a MFCreateTranscodeTopology para crear la topología de transcodificación parcial.
  5. Cree un objeto auxiliar que administre la sesión multimedia. (Consulte asistente de sesión multimedia).
  6. Ejecute la sesión de codificación y espere a que se complete. (Consulte Ejecución de la sesión de codificación).
  7. Llame a IMFMediaSource::Shutdown para apagar el origen multimedia.
  8. Punteros de interfaz de versión. Este código usa la función SafeRelease para liberar punteros de interfaz. Otra opción es usar una clase de puntero inteligente COM, como CComPtr.

Crear el origen multimedia

El origen multimedia es el objeto que lee y analiza el archivo de entrada. Para crear el origen multimedia, pase la dirección URL del archivo de entrada al solucionador de origen. El código siguiente muestra cómo hacerlo.

HRESULT CreateMediaSource(PCWSTR pszURL, IMFMediaSource **ppSource)
{
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver* pResolver = NULL;
    IUnknown* pSource = NULL;

    // Create the source resolver.
    HRESULT hr = MFCreateSourceResolver(&pResolver);
    if (FAILED(hr))
    {
        goto done;
    }

    // Use the source resolver to create the media source
    hr = pResolver->CreateObjectFromURL(pszURL, MF_RESOLUTION_MEDIASOURCE, 
        NULL, &ObjectType, &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the IMFMediaSource interface from the media source.
    hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));

done:
    SafeRelease(&pResolver);
    SafeRelease(&pSource);
    return hr;
}

Para obtener más información, consulte Uso del solucionador de origen.

Obtener la duración del origen

Aunque no es necesario, resulta útil consultar el origen multimedia durante la duración del archivo de entrada. Este valor se puede usar para realizar un seguimiento del progreso de la codificación. La duración se almacena en el atributo MF_PD_DURATION del descriptor de presentación. Obtenga el descriptor de presentación llamando a IMFMediaSource::CreatePresentationDescriptor.

HRESULT GetSourceDuration(IMFMediaSource *pSource, MFTIME *pDuration)
{
    *pDuration = 0;

    IMFPresentationDescriptor *pPD = NULL;

    HRESULT hr = pSource->CreatePresentationDescriptor(&pPD);
    if (SUCCEEDED(hr))
    {
        hr = pPD->GetUINT64(MF_PD_DURATION, (UINT64*)pDuration);
        pPD->Release();
    }
    return hr;
}

Creación del perfil de transcodificación

El perfil de transcodificación describe los parámetros de codificación. Para obtener más información sobre cómo crear un perfil de transcodificación, consulte Uso de la API de transcodificación. Para crear el perfil, realice los pasos siguientes.

  1. Llame a MFCreateTranscodeProfile para crear el perfil vacío.
  2. Cree un tipo de medio para la secuencia de audio AAC. Agréguelo al perfil llamando a IMFTranscodeProfile::SetAudioAttributes.
  3. Cree un tipo de medio para la secuencia de vídeo H.264. Agréguelo al perfil llamando a IMFTranscodeProfile::SetVideoAttributes.
  4. Llame a MFCreateAttributes para crear un almacén de atributos para los atributos de nivel de contenedor.
  5. Establezca el atributo MF_TRANSCODE_CONTAINERTYPE . Este es el único atributo de nivel de contenedor necesario. Para la salida del archivo MP4, establezca este atributo en MFTranscodeContainerType_MPEG4.
  6. Llame a IMFTranscodeProfile::SetContainerAttributes para establecer los atributos de nivel de contenedor.

En el código siguiente se muestran estos pasos.

HRESULT CreateTranscodeProfile(IMFTranscodeProfile **ppProfile)
{
    IMFTranscodeProfile *pProfile = NULL;
    IMFAttributes *pAudio = NULL;
    IMFAttributes *pVideo = NULL;
    IMFAttributes *pContainer = NULL;

    HRESULT hr = MFCreateTranscodeProfile(&pProfile);
    if (FAILED(hr)) 
    {
        goto done;
    }

    // Audio attributes.
    hr = CreateAACProfile(audio_profile, &pAudio);
    if (FAILED(hr)) 
    {
        goto done;
    }

    hr = pProfile->SetAudioAttributes(pAudio);
    if (FAILED(hr)) 
    {
        goto done;
    }

    // Video attributes.
    hr = CreateH264Profile(video_profile, &pVideo);
    if (FAILED(hr)) 
    {
        goto done;
    }

    hr = pProfile->SetVideoAttributes(pVideo);
    if (FAILED(hr)) 
    {
        goto done;
    }

    // Container attributes.
    hr = MFCreateAttributes(&pContainer, 1);
    if (FAILED(hr)) 
    {
        goto done;
    }

    hr = pContainer->SetGUID(MF_TRANSCODE_CONTAINERTYPE, MFTranscodeContainerType_MPEG4);
    if (FAILED(hr)) 
    {
        goto done;
    }

    hr = pProfile->SetContainerAttributes(pContainer);
    if (FAILED(hr)) 
    {
        goto done;
    }

    *ppProfile = pProfile;
    (*ppProfile)->AddRef();

done:
    SafeRelease(&pProfile);
    SafeRelease(&pAudio);
    SafeRelease(&pVideo);
    SafeRelease(&pContainer);
    return hr;
}

Para especificar los atributos de la secuencia de vídeo H.264, cree un almacén de atributos y establezca los siguientes atributos:

Atributo Descripción
MF_MT_SUBTYPE Establezca en MFVideoFormat_H264.
MF_MT_MPEG2_PROFILE Perfil de H.264.
MF_MT_FRAME_SIZE Tamaño del marco.
MF_MT_FRAME_RATE Velocidad de fotogramas.
MF_MT_AVG_BITRATE Velocidad de bits codificada.

 

Para especificar los atributos de la secuencia de audio AAC, cree un almacén de atributos y establezca los siguientes atributos:

Atributo Descripción
MF_MT_SUBTYPE Establézcalo en MFAudioFormat_AAC
MF_MT_AUDIO_SAMPLES_PER_SECOND Frecuencia de muestreo de audio.
MF_MT_AUDIO_BITS_PER_SAMPLE Bits por muestra de audio.
MF_MT_AUDIO_NUM_CHANNELS Número de canales de audio.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND Velocidad de bits codificada.
MF_MT_AUDIO_BLOCK_ALIGNMENT establézcalo en 1.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION Indicación de nivel de perfil de AAC (opcional).

 

El código siguiente crea los atributos de secuencia de vídeo.

HRESULT CreateH264Profile(DWORD index, IMFAttributes **ppAttributes)
{
    if (index >= ARRAYSIZE(h264_profiles))
    {
        return E_INVALIDARG;
    }

    IMFAttributes *pAttributes = NULL;

    const H264ProfileInfo& profile = h264_profiles[index];

    HRESULT hr = MFCreateAttributes(&pAttributes, 5);
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(MF_MT_MPEG2_PROFILE, profile.profile);
    }
    if (SUCCEEDED(hr))
    {
        hr = MFSetAttributeSize(
            pAttributes, MF_MT_FRAME_SIZE, 
            profile.frame_size.Numerator, profile.frame_size.Numerator);
    }
    if (SUCCEEDED(hr))
    {
        hr = MFSetAttributeRatio(
            pAttributes, MF_MT_FRAME_RATE, 
            profile.fps.Numerator, profile.fps.Denominator);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(MF_MT_AVG_BITRATE, profile.bitrate);
    }
    if (SUCCEEDED(hr))
    {
        *ppAttributes = pAttributes;
        (*ppAttributes)->AddRef();
    }
    SafeRelease(&pAttributes);
    return hr;
}

El código siguiente crea los atributos de secuencia de audio.

HRESULT CreateAACProfile(DWORD index, IMFAttributes **ppAttributes)
{
    if (index >= ARRAYSIZE(aac_profiles))
    {
        return E_INVALIDARG;
    }

    const AACProfileInfo& profile = aac_profiles[index];

    IMFAttributes *pAttributes = NULL;

    HRESULT hr = MFCreateAttributes(&pAttributes, 7);
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(
            MF_MT_AUDIO_BITS_PER_SAMPLE, profile.bitsPerSample);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(
            MF_MT_AUDIO_SAMPLES_PER_SECOND, profile.samplesPerSec);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(
            MF_MT_AUDIO_NUM_CHANNELS, profile.numChannels);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(
            MF_MT_AUDIO_AVG_BYTES_PER_SECOND, profile.bytesPerSec);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(
            MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, profile.aacProfile);
    }
    if (SUCCEEDED(hr))
    {
        *ppAttributes = pAttributes;
        (*ppAttributes)->AddRef();
    }
    SafeRelease(&pAttributes);
    return hr;
}

Tenga en cuenta que la API de transcodificación no requiere un tipo de medio verdadero, aunque usa atributos de tipo multimedia. En concreto, el atributo MF_MT_MAJOR_TYPE no es necesario, porque los métodos SetVideoAttributes y SetAudioAttributes implican el tipo principal. Sin embargo, también es válido pasar un tipo de medio real a estos métodos. (La interfaz IMFMediaType hereda IMFAttributes).

Ejecución de la sesión de codificación

El código siguiente ejecuta la sesión de codificación. Usa la clase auxiliar Media Session, que se muestra en la sección siguiente.

HRESULT RunEncodingSession(CSession *pSession, MFTIME duration)
{
    const DWORD WAIT_PERIOD = 500;
    const int   UPDATE_INCR = 5;

    HRESULT hr = S_OK;
    MFTIME pos;
    LONGLONG prev = 0;
    while (1)
    {
        hr = pSession->Wait(WAIT_PERIOD);
        if (hr == E_PENDING)
        {
            hr = pSession->GetEncodingPosition(&pos);

            LONGLONG percent = (100 * pos) / duration ;
            if (percent >= prev + UPDATE_INCR)
            {
                std::cout << percent << "% .. ";  
                prev = percent;
            }
        }
        else
        {
            std::cout << std::endl;
            break;
        }
    }
    return hr;
}

Asistente de sesión multimedia

La sesión multimedia se describe más por completo en la sección Arquitectura de Media Foundation de esta documentación. La sesión multimedia usa un modelo de eventos asincrónico. En una aplicación de GUI, debe responder a eventos de sesión sin bloquear el subproceso de interfaz de usuario para esperar al siguiente evento. En el tutorial Cómo reproducir archivos multimedia desprotegidos se muestra cómo hacerlo en una aplicación de reproducción. Para la codificación, el principio es el mismo, pero hay menos eventos relevantes:

Evento Descripción
MESessionEnded Se genera cuando se completa la codificación.
MESessionClosed Se genera cuando se completa el método IMFMediaSession::Close . Una vez que se genera este evento, es seguro apagar la sesión multimedia.

 

Para una aplicación de consola, es razonable bloquear y esperar eventos. Según el archivo de origen y la configuración de codificación, es posible que tarde un tiempo en completar la codificación. Puede obtener actualizaciones de progreso de la siguiente manera:

  1. Llame a IMFMediaSession::GetClock para obtener el reloj de presentación.
  2. Consulte el reloj de la interfaz IMFPresentationClock .
  3. Llame a IMFPresentationClock::GetTime para obtener la posición actual.
  4. La posición se da en unidades de tiempo. Para completar el porcentaje, use el valor (100 * position) / duration.

Esta es la declaración de la CSession clase .

class CSession  : public IMFAsyncCallback 
{
public:
    static HRESULT Create(CSession **ppSession);

    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IMFAsyncCallback methods
    STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
    {
        // Implementation of this method is optional.
        return E_NOTIMPL;
    }
    STDMETHODIMP Invoke(IMFAsyncResult *pResult);

    // Other methods
    HRESULT StartEncodingSession(IMFTopology *pTopology);
    HRESULT GetEncodingPosition(MFTIME *pTime);
    HRESULT Wait(DWORD dwMsec);

private:
    CSession() : m_cRef(1), m_pSession(NULL), m_pClock(NULL), m_hrStatus(S_OK), m_hWaitEvent(NULL)
    {
    }
    virtual ~CSession()
    {
        if (m_pSession)
        {
            m_pSession->Shutdown();
        }

        SafeRelease(&m_pClock);
        SafeRelease(&m_pSession);
        CloseHandle(m_hWaitEvent);
    }

    HRESULT Initialize();

private:
    IMFMediaSession      *m_pSession;
    IMFPresentationClock *m_pClock;
    HRESULT m_hrStatus;
    HANDLE  m_hWaitEvent;
    long    m_cRef;
};

En el código siguiente se muestra la implementación completa de la CSession clase .

HRESULT CSession::Create(CSession **ppSession)
{
    *ppSession = NULL;

    CSession *pSession = new (std::nothrow) CSession();
    if (pSession == NULL)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT hr = pSession->Initialize();
    if (FAILED(hr))
    {
        pSession->Release();
        return hr;
    }
    *ppSession = pSession;
    return S_OK;
}

STDMETHODIMP CSession::QueryInterface(REFIID riid, void** ppv)
{
    static const QITAB qit[] = 
    {
        QITABENT(CSession, IMFAsyncCallback),
        { 0 }
    };
    return QISearch(this, qit, riid, ppv);
}

STDMETHODIMP_(ULONG) CSession::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CSession::Release()
{
    long cRef = InterlockedDecrement(&m_cRef);
    if (cRef == 0)
    {
        delete this;
    }
    return cRef;
}

HRESULT CSession::Initialize()
{
    IMFClock *pClock = NULL;

    HRESULT hr = MFCreateMediaSession(NULL, &m_pSession);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pSession->GetClock(&pClock);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pClock->QueryInterface(IID_PPV_ARGS(&m_pClock));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pSession->BeginGetEvent(this, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

    m_hWaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);  
    if (m_hWaitEvent == NULL)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }
done:
    SafeRelease(&pClock);
    return hr;
}

// Implements IMFAsyncCallback::Invoke
STDMETHODIMP CSession::Invoke(IMFAsyncResult *pResult)
{
    IMFMediaEvent* pEvent = NULL;
    MediaEventType meType = MEUnknown;
    HRESULT hrStatus = S_OK;

    HRESULT hr = m_pSession->EndGetEvent(pResult, &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 MESessionEnded:
        hr = m_pSession->Close();
        if (FAILED(hr))
        {
            goto done;
        }
        break;

    case MESessionClosed:
        SetEvent(m_hWaitEvent);
        break;
    }

    if (meType != MESessionClosed)
    {
        hr = m_pSession->BeginGetEvent(this, NULL);
    }

done:
    if (FAILED(hr))
    {
        m_hrStatus = hr;
        m_pSession->Close();
    }

    SafeRelease(&pEvent);
    return hr;
}

HRESULT CSession::StartEncodingSession(IMFTopology *pTopology)
{
    HRESULT hr = m_pSession->SetTopology(0, pTopology);
    if (SUCCEEDED(hr))
    {
        PROPVARIANT varStart;
        PropVariantClear(&varStart);
        hr = m_pSession->Start(&GUID_NULL, &varStart);
    }
    return hr;
}

HRESULT CSession::GetEncodingPosition(MFTIME *pTime)
{
    return m_pClock->GetTime(pTime);
}

HRESULT CSession::Wait(DWORD dwMsec)
{
    HRESULT hr = S_OK;

    DWORD dwTimeoutStatus = WaitForSingleObject(m_hWaitEvent, dwMsec);
    if (dwTimeoutStatus != WAIT_OBJECT_0)
    {
        hr = E_PENDING;
    }
    else
    {
        hr = m_hrStatus;
    }
    return hr;
}

Codificador AAC

Codificador de vídeo H.264

Sesión multimedia

Tipos de medios

Api de transcodificación