Condividi tramite


Esercitazione: Codifica di un file MP4

Questa esercitazione illustra come usare l'API Transcode per codificare un file MP4, usando H.264 per il flusso video e AAC per il flusso audio.

Intestazioni e file di libreria

Includere i file di intestazione seguenti.

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

Collegare i file di libreria seguenti.

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

Definire i profili di codifica

Un approccio alla codifica consiste nel definire un elenco di profili di codifica di destinazione noti in anticipo. Per questa esercitazione viene adottato un approccio relativamente semplice e viene archiviato un elenco di formati di codifica per video H.264 e audio AAC.

Per H.264, gli attributi di formato più importanti sono il profilo H.264, la frequenza dei fotogrammi, le dimensioni del frame e la velocità di bit codificata. La matrice seguente contiene un elenco di formati di codifica 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 },
};

I profili H.264 vengono specificati usando l'eNUMerazione eAVEncH264VProfile . È anche possibile specificare il livello H.264, ma Microsoft Media Foundation H.264 Video Encoder può derivare il livello appropriato per un determinato flusso video, pertanto è consigliabile non eseguire l'override del livello selezionato del codificatore. Per il contenuto interlacciato, è anche possibile specificare la modalità interlacciatura (vedere Interlacciamento video).

Per l'audio AAC, gli attributi di formato più importanti sono la frequenza di campionamento audio, il numero di canali, il numero di bit per campione e la velocità di bit codificata. Facoltativamente, è possibile impostare l'indicazione del livello del profilo audio AAC. Per altre informazioni, vedere Codificatore AAC. La matrice seguente contiene un elenco di formati di codifica 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

Le H264ProfileInfo strutture e AACProfileInfo definite qui non fanno parte dell'API Media Foundation.

 

Scrivere la funzione wmain

Il codice seguente mostra il punto di ingresso per l'applicazione 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;
}

La wmain funzione esegue le operazioni seguenti:

  1. Chiama la funzione CoInitializeEx per inizializzare la libreria COM.
  2. Chiama la funzione MFStartup per inizializzare Media Foundation.
  3. Chiama la funzione definita dall'applicazione EncodeFile . Questa funzione esegue la transcodifica del file di input nel file di output e viene visualizzata nella sezione successiva.
  4. Chiama la funzione MFShutdown per arrestare Media Foundation.
  5. Chiamare la funzione CoUninitialize per inizializzare la libreria COM.

Codificare il file

Il codice seguente mostra EncodeFile la funzione , che esegue la transcodifica. Questa funzione è costituita principalmente da chiamate ad altre funzioni definite dall'applicazione, illustrate più avanti in questo argomento.

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 funzione esegue i passaggi seguenti.

  1. Crea un'origine multimediale per il file di input, usando l'URL o il percorso del file di input. Vedere Creare l'origine multimediale.
  2. Ottiene la durata del file di input. Vedere Ottenere la durata dell'origine.
  3. Creare il profilo di transcodifica. Vedere Creare il profilo transcodifica.
  4. Chiamare MFCreateTranscodeTopology per creare la topologia di transcodifica parziale.
  5. Creare un oggetto helper che gestisce la sessione multimediale. Vedere l'helper di sessione multimediale.
  6. Eseguire la sessione di codifica e attendere il completamento. Vedere Eseguire la sessione di codifica.
  7. Chiamare IMFMediaSource::Shutdown per arrestare l'origine multimediale.
  8. Puntatori all'interfaccia di rilascio. Questo codice usa la funzione SafeRelease per rilasciare puntatori all'interfaccia. Un'altra opzione consiste nell'usare una classe di puntatore intelligente COM, ad esempio CComPtr.

Creare l'origine multimediale

L'origine multimediale è l'oggetto che legge e analizza il file di input. Per creare l'origine multimediale, passare l'URL del file di input al resolver di origine. A tal fine, osservare il codice indicato di seguito.

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

Per altre informazioni, vedere Uso del resolver di origine.

Ottenere la durata dell'origine

Sebbene non sia necessario, è utile eseguire una query sull'origine multimediale per la durata del file di input. Questo valore può essere usato per tenere traccia dello stato di avanzamento della codifica. La durata viene archiviata nell'attributo MF_PD_DURATION del descrittore di presentazione. Ottenere il descrittore di presentazione chiamando 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;
}

Creare il profilo transcodifica

Il profilo di transcodifica descrive i parametri di codifica. Per altre informazioni sulla creazione di un profilo transcodifica, vedere Uso dell'API Transcode. Per creare il profilo, seguire questa procedura.

  1. Chiama MFCreateTranscodeProfile per creare il profilo vuoto.
  2. Creare un tipo di supporto per il flusso audio AAC. Aggiungerlo al profilo chiamando IMFTranscodeProfile::SetAudioAttributes.
  3. Creare un tipo di supporto per il flusso video H.264. Aggiungerlo al profilo chiamando IMFTranscodeProfile::SetVideoAttributes.
  4. Chiamare MFCreateAttributes per creare un archivio attributi per gli attributi a livello di contenitore.
  5. Impostare l'attributo MF_TRANSCODE_CONTAINERTYPE . Si tratta dell'unico attributo a livello di contenitore richiesto. Per l'output del file MP4, impostare questo attributo su MFTranscodeContainerType_MPEG4.
  6. Chiamare IMFTranscodeProfile::SetContainerAttributes per impostare gli attributi a livello di contenitore.

Il codice seguente illustra questi passaggi.

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

Per specificare gli attributi per il flusso video H.264, creare un archivio attributi e impostare gli attributi seguenti:

Attributo Descrizione
MF_MT_SUBTYPE Impostare su MFVideoFormat_H264.
MF_MT_MPEG2_PROFILE Profilo H.264.
MF_MT_FRAME_SIZE Dimensioni cornice.
MF_MT_FRAME_RATE Frequenza dei fotogrammi.
MF_MT_AVG_BITRATE Velocità in bit codificata.

 

Per specificare gli attributi per il flusso audio AAC, creare un archivio attributi e impostare gli attributi seguenti:

Attributo Descrizione
MF_MT_SUBTYPE Impostare su MFAudioFormat_AAC
MF_MT_AUDIO_SAMPLES_PER_SECOND Frequenza di esempio audio.
MF_MT_AUDIO_BITS_PER_SAMPLE Bit per esempio audio.
MF_MT_AUDIO_NUM_CHANNELS Numero dei canali audio.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND Velocità di bit codificata.
MF_MT_AUDIO_BLOCK_ALIGNMENT impostare su 1.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION Indicazione del livello di profilo AAC (facoltativa).

 

Il codice seguente crea gli attributi del flusso video.

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

Il codice seguente crea gli attributi del flusso 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;
}

Si noti che l'API transcode non richiede un vero tipo di supporto, anche se usa attributi di tipo multimediale. In particolare, l'attributo MF_MT_MAJOR_TYPE non richiesto, perché i metodi SetVideoAttributes e SetAudioAttributes implicano il tipo principale. Tuttavia, è anche valido passare un tipo di supporto effettivo a questi metodi. L'interfaccia FMMediaType eredita FMAttributes.

Eseguire la sessione di codifica

Il codice seguente esegue la sessione di codifica. Usa la classe helper Media Session, visualizzata nella sezione successiva.

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

Helper sessione multimediale

La sessione multimediale è descritta più completamente nella sezione Architettura di Media Foundation di questa documentazione. La sessione multimediale usa un modello di evento asincrono. In un'applicazione GUI è necessario rispondere agli eventi di sessione senza bloccare il thread dell'interfaccia utente in attesa dell'evento successivo. L'esercitazione Su come riprodurre file multimediali non protetti illustra come eseguire questa operazione in un'applicazione di riproduzione. Per la codifica, il principio è lo stesso, ma meno eventi sono rilevanti:

Evento Descrizione
MESessionEnded Generato al termine della codifica.
MESessionClosed Generato al completamento del metodo IMFMediaSession::Close . Dopo aver generato questo evento, è possibile arrestare la sessione multimediale.

 

Per un'applicazione console, è ragionevole bloccare e attendere gli eventi. A seconda del file di origine e delle impostazioni di codifica, potrebbe richiedere un po' di tempo per completare la codifica. È possibile ottenere gli aggiornamenti dello stato di avanzamento come indicato di seguito:

  1. Chiamare FMIMediaSession::GetClock per ottenere l'orologio della presentazione.
  2. Eseguire una query sull'orologio per l'interfaccia FMPresentationClock .
  3. Chiamare FMPresentationClock::GetTime per ottenere la posizione corrente.
  4. La posizione viene specificata in unità di tempo. Per ottenere la percentuale completata, usare il valore (100 * position) / duration.

Ecco la dichiarazione della 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;
};

Il codice seguente mostra l'implementazione completa della 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;
}

Codificatore AAC

Codificatore video H.264

Sessione multimediale

Tipi di supporti

Transcode API