Tutorial: Codificando um arquivo MP4

Este tutorial mostra como usar a API do Transcode para codificar um arquivo MP4, usando H.264 para o fluxo de vídeo e o AAC para o fluxo de áudio.

Cabeçalhos e arquivos de biblioteca

Inclua os arquivos de cabeçalho a seguir.

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

Vincule os seguintes arquivos de biblioteca.

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

Definir os perfis de codificação

Uma abordagem para codificação é definir uma lista de perfis de codificação de destino que são conhecidos com antecedência. Para este tutorial, adotamos uma abordagem relativamente simples e armazenamos uma lista de formatos de codificação para vídeo H.264 e áudio AAC.

Para H.264, os atributos de formato mais importantes são o perfil H.264, a taxa de quadros, o tamanho do quadro e a taxa de bits codificada. A matriz a seguir contém uma lista de formatos de codificação 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 },
};

Os perfis H.264 são especificados usando a enumeração eAVEncH264VProfile . Você também pode especificar o nível H.264, mas o Codificador de Vídeo H.264 do Microsoft Media Foundation pode derivar o nível adequado para um determinado fluxo de vídeo, portanto, é recomendável não substituir o nível selecionado do codificador. Para conteúdo entrelaçado, você também especificaria o modo de entrelaçamento (consulte Interlacing de vídeo).

Para áudio AAC, os atributos de formato mais importantes são a taxa de amostragem de áudio, o número de canais, o número de bits por amostra e a taxa de bits codificada. Opcionalmente, você pode definir a indicação de nível de perfil de áudio do AAC. Para obter mais informações, consulte Codificador AAC. A matriz a seguir contém uma lista de formatos de codificação 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}, 
};

Observação

As H264ProfileInfo estruturas e AACProfileInfo definidas aqui não fazem parte da API do Media Foundation.

 

Gravar a função wmain

O código a seguir mostra o ponto de entrada do aplicativo de console.

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;
}

A wmain função faz o seguinte:

  1. Chama a função CoInitializeEx para inicializar a biblioteca COM.
  2. Chama a função MFStartup para inicializar o Media Foundation.
  3. Chama a função definida EncodeFile pelo aplicativo. Essa função transcodifica o arquivo de entrada para o arquivo de saída e é mostrada na próxima seção.
  4. Chama a função MFShutdown para desligar o Media Foundation.
  5. Chame a função CoUninitialize para não inicializar a biblioteca COM.

Codificar o arquivo

O código a seguir mostra EncodeFile a função , que executa a transcodificação. Essa função consiste principalmente em chamadas para outras funções definidas pelo aplicativo, que são mostradas posteriormente neste tópico.

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;
}

A EncodeFile função executa as etapas a seguir.

  1. Cria uma fonte de mídia para o arquivo de entrada, usando a URL ou o caminho do arquivo de entrada. (Consulte Criar a fonte de mídia.)
  2. Obtém a duração do arquivo de entrada. (Consulte Obter a duração da origem.)
  3. Crie o perfil de transcodificação. (Consulte Criar o perfil do Transcode.)
  4. Chame MFCreateTranscodeTopology para criar a topologia de transcodificação parcial.
  5. Crie um objeto auxiliar que gerencie a Sessão de Mídia. (Consulte Auxiliar de Sessão de Mídia).
  6. Execute a sessão de codificação e aguarde até que ela seja concluída. (Consulte Executar a sessão de codificação.)
  7. Chame IMFMediaSource::Shutdown para desligar a fonte de mídia.
  8. Ponteiros da interface de versão. Esse código usa a função SafeRelease para liberar ponteiros de interface. Outra opção é usar uma classe de ponteiro inteligente COM, como CComPtr.

Criar a fonte de mídia

A fonte de mídia é o objeto que lê e analisa o arquivo de entrada. Para criar a fonte de mídia, passe a URL do arquivo de entrada para o Resolvedor de Origem. O código a seguir mostra como fazer isso.

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 obter mais informações, consulte Usando o resolvedor de origem.

Obter a duração da origem

Embora não seja necessário, é útil consultar a fonte de mídia durante a duração do arquivo de entrada. Esse valor pode ser usado para acompanhar o progresso da codificação. A duração é armazenada no atributo MF_PD_DURATION do descritor de apresentação. Obtenha o descritor de apresentação chamando 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;
}

Criar o perfil do Transcode

O perfil de transcodificação descreve os parâmetros de codificação. Para obter mais informações sobre como criar um perfil de transcodificação, consulte Usando a API do Transcode. Para criar o perfil, execute as etapas a seguir.

  1. Chame MFCreateTranscodeProfile para criar o perfil vazio.
  2. Crie um tipo de mídia para o fluxo de áudio do AAC. Adicione-o ao perfil chamando IMFTranscodeProfile::SetAudioAttributes.
  3. Crie um tipo de mídia para o fluxo de vídeo H.264. Adicione-o ao perfil chamando IMFTranscodeProfile::SetVideoAttributes.
  4. Chame MFCreateAttributes para criar um repositório de atributos para os atributos no nível do contêiner.
  5. Defina o atributo MF_TRANSCODE_CONTAINERTYPE . Esse é o único atributo de nível de contêiner necessário. Para saída de arquivo MP4, defina esse atributo como MFTranscodeContainerType_MPEG4.
  6. Chame IMFTranscodeProfile::SetContainerAttributes para definir os atributos no nível do contêiner.

O código a seguir mostra essas etapas.

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 os atributos para o fluxo de vídeo H.264, crie um repositório de atributos e defina os seguintes atributos:

Atributo Descrição
MF_MT_SUBTYPE Defina como MFVideoFormat_H264.
MF_MT_MPEG2_PROFILE Perfil H.264.
MF_MT_FRAME_SIZE Tamanho do quadro.
MF_MT_FRAME_RATE Taxa de quadros.
MF_MT_AVG_BITRATE Taxa de bits codificada.

 

Para especificar os atributos para o fluxo de áudio do AAC, crie um repositório de atributos e defina os seguintes atributos:

Atributo Descrição
MF_MT_SUBTYPE Definir como MFAudioFormat_AAC
MF_MT_AUDIO_SAMPLES_PER_SECOND Taxa de amostragem de áudio.
MF_MT_AUDIO_BITS_PER_SAMPLE Bits por exemplo de áudio.
MF_MT_AUDIO_NUM_CHANNELS Número de canais de áudio.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND Taxa de bits codificada.
MF_MT_AUDIO_BLOCK_ALIGNMENT defina como 1.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION Indicação de nível de perfil do AAC (opcional).

 

O código a seguir cria os atributos de fluxo 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;
}

O código a seguir cria os atributos de fluxo de áudio.

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;
}

Observe que a API de transcodificação não requer um tipo de mídia verdadeiro, embora use atributos de tipo de mídia. Em particular, o atributo MF_MT_MAJOR_TYPE não é necessário, pois os métodos SetVideoAttributes e SetAudioAttributes implicam o tipo principal. No entanto, também é válido passar um tipo de mídia real para esses métodos. (A interface IMFMediaType herda IMFAttributes.)

Executar a sessão de codificação

O código a seguir executa a sessão de codificação. Ele usa a classe auxiliar Sessão de Mídia, que é mostrada na próxima seção.

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;
}

Auxiliar de Sessão de Mídia

A Sessão de Mídia é descrita mais detalhadamente na seção Arquitetura do Media Foundation desta documentação. A Sessão de Mídia usa um modelo de evento assíncrono. Em um aplicativo de GUI, você deve responder a eventos de sessão sem bloquear o thread da interface do usuário para aguardar o próximo evento. O tutorial Como reproduzir arquivos de mídia desprotegidos mostra como fazer isso em um aplicativo de reprodução. Para codificação, o princípio é o mesmo, mas menos eventos são relevantes:

Evento Descrição
MESessionEnded Gerado quando a codificação é concluída.
MESessionClosed Gerado quando o método IMFMediaSession::Close é concluído. Depois que esse evento for acionado, é seguro desligar a Sessão de Mídia.

 

Para um aplicativo de console, é razoável bloquear e aguardar eventos. Dependendo do arquivo de origem e das configurações de codificação, pode levar algum tempo para concluir a codificação. Você pode obter atualizações de progresso da seguinte maneira:

  1. Chame IMFMediaSession::GetClock para obter o relógio da apresentação.
  2. Consulte o relógio para a interface IMFPresentationClock .
  3. Chame IMFPresentationClock::GetTime para obter a posição atual.
  4. A posição é fornecida em unidades de tempo. Para concluir a porcentagem, use o valor (100 * position) / duration.

Aqui está a declaração da CSession classe .

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;
};

O código a seguir mostra a implementação completa da CSession classe .

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

Sessão de Mídia

Tipos de mídia

Transcode API