Condividi tramite


Esercitazione: Codifica di un file MP4

Questa esercitazione illustra come usare l'API transcodifica 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, è necessario specificare anche la modalità interlacciato (vedere Video Interlacing).

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 strutture H264ProfileInfo 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 funzione wmain esegue le operazioni seguenti:

  1. Chiama la funzioneCoInitializeEx per inizializzare la libreria COM.
  2. Chiama la funzione MFStartup per inizializzare Media Foundation.
  3. Chiama la funzione di EncodeFile definita dall'applicazione. 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 la Media Foundation.
  5. Chiamare la funzione CoUninitialize per disinizializzare la libreria COM.

Codificare il file

Il codice seguente mostra la funzione EncodeFile 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 funzione EncodeFile esegue i passaggi seguenti.

  1. Crea un'origine multimediale per il file di input, usando l'URL o il percorso del file di input. Vedi Creare l'origine multimediale.
  2. Ottiene la durata del file di input. Vedi Ottieni la durata di origine.
  3. Creare il profilo di transcodifica. Vedi Creare il profilo di transcodifica.
  4. Chiamare MFCreateTranscodeTopology per creare la topologia transcodifica parziale.
  5. Creare un oggetto helper che gestisce la sessione multimediale. Consultare Media Session Helper.
  6. Eseguire la sessione di codifica e attendere il completamento. Vedi , eseguire la sessione di codifica.
  7. Chiamare IMFMediaSource::Shutdown per arrestare la sorgente multimediale.
  8. Rilasciare i puntatori dell'interfaccia. Questo codice utilizza la funzione SafeRelease per rilasciare i puntatori all'interfaccia. Un'altra opzione consiste nell'usare una classe 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 . Il codice seguente illustra come eseguire questa operazione.

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 ulteriori informazioni, vedere Utilizzare il Risolutore di Origine.

Ottenere la durata della sorgente

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 di transcodifica

Il profilo di transcodifica descrive i parametri di codifica. Per ulteriori informazioni sulla creazione di un profilo di transcodifica, vedere Utilizzare l'API di transcodifica. 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 formato 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 obbligatorio. 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 Impostato su MFVideoFormat_H264.
MF_MT_MPEG2_PROFILE Profilo H.264.
MF_MT_FRAME_SIZE Dimensione della cornice.
MF_MT_FRAME_RATE Frequenza dei fotogrammi.
MF_MT_AVG_BITRATE Velocità di trasmissione 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 campionamento audio.
MF_MT_AUDIO_BITS_PER_SAMPLE Bit per campione audio.
Numero di canali audio MF_MT Numero di canali audio.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND Velocità di trasmissione codificata.
MF_MT_AUDIO_BLOCK_ALIGNMENT Impostare su 1.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION Indicazione del livello del profilo AAC (facoltativo).

 

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 di transcodifica non richiede un vero tipo di media, anche se utilizza attributi di tipo media. In particolare, l'attributo MF_MT_MAJOR_TYPE non è necessario, perché i SetVideoAttributes e SetAudioAttributes metodi implicano il tipo principale. Tuttavia, è anche possibile passare un tipo di media effettivo a questi metodi. L'interfaccia IMFMediaType eredita IMFAttributes.

Eseguire la sessione di codifica

Il codice seguente esegue la sessione di codifica. Usa la classe di supporto Sessione multimediale, illustrata 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;
}

Supporto per sessioni multimediali

La sessione multimediale di è descritta in modo più completo nella sezione Media Foundation Architecture 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 per attendere l'evento successivo. L'esercitazione 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
Sessione Terminata Sollevato quando la codifica è completa.
MESessionClosed Generato quando si completa il 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 essere necessario un po' di tempo per completare la codifica. È possibile ottenere gli aggiornamenti dello stato di avanzamento come indicato di seguito:

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

Ecco la dichiarazione della classe CSession.

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 illustra l'implementazione completa della classe CSession.

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

API di transcodifica