Udostępnij przez


Samouczek: kodowanie pliku MP4

W tym samouczku pokazano, jak za pomocą interfejsu API transkodowania zakodować plik MP4, używając kodeka H.264 dla strumienia wideo i kodeka AAC dla strumienia audio.

Nagłówki i pliki biblioteki

Dołącz następujące pliki nagłówkowe.

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

Połącz następujące pliki biblioteki.

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

Definiowanie profilów kodowania

Jednym z podejść do kodowania jest zdefiniowanie listy profilów kodowania docelowego, które są znane z wyprzedzeniem. Na potrzeby tego samouczka użyjemy stosunkowo prostego podejścia i zapiszemy listę formatów kodowania dla wideo H.264 i audio AAC.

W przypadku H.264 najważniejsze atrybuty formatu to profil H.264, szybkość klatek, rozmiar ramki i zakodowana szybkość bitów. Poniższa tablica zawiera listę formatów kodowania 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 },
};

Profile H.264 są określane przy użyciu wyliczenia eAVEncH264VProfile. Można również określić poziom H.264, ale program Microsoft Media Foundation H.264 Video Encoder może uzyskać odpowiedni poziom dla danego strumienia wideo, dlatego zaleca się, aby nie przesłaniać wybranego poziomu kodera. W przypadku zawartości z przeplotem należy również określić tryb przeplotu (zobacz przeplot wideo).

W przypadku dźwięku AAC najważniejsze atrybuty formatu to częstotliwość próbkowania audio, liczba kanałów, liczba bitów na próbkę i zakodowana szybkość bitów. Opcjonalnie można ustawić wskazanie poziomu profilu audio usługi AAC. Aby uzyskać więcej informacji, zobacz AAC Encoder. Poniższa tablica zawiera listę formatów kodowania 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}, 
};

Notatka

Zdefiniowane tutaj struktury H264ProfileInfo i AACProfileInfo nie są częścią interfejsu API programu Media Foundation.

 

Napisz funkcję wmain

Poniższy kod przedstawia punkt wejścia dla aplikacji konsolowej.

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

Funkcja wmain wykonuje następujące czynności:

  1. Wywołuje funkcję CoInitializeEx w celu zainicjowania biblioteki COM.
  2. Wywołuje funkcję MFStartup w celu zainicjowania programu Media Foundation.
  3. Wywołuje funkcję EncodeFile zdefiniowaną przez aplikację. Ta funkcja transkoduje plik wejściowy do pliku wyjściowego i jest wyświetlana w następnej sekcji.
  4. Wywołuje funkcję MFShutdown , aby zamknąć Media Foundation.
  5. Wywołaj funkcję CoUninitialize, aby zdeinicjować bibliotekę COM.

Kodowanie pliku

Poniższy kod przedstawia funkcję EncodeFile, która wykonuje transkodowanie. Ta funkcja składa się głównie z wywołań do innych funkcji zdefiniowanych przez aplikację, które są wyświetlane w dalszej części tego tematu.

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

Funkcja EncodeFile wykonuje następujące kroki.

  1. Tworzy źródło multimediów dla pliku wejściowego przy użyciu adresu URL lub ścieżki pliku wejściowego. (Zobacz Utwórz źródło multimedialne.)
  2. Pobiera czas trwania pliku wejściowego. (Zobacz Pobierz czas trwania źródła.)
  3. Utwórz profil transkodowania. (Zobacz Tworzenie profilu transkodowania.)
  4. Wywołaj MFCreateTranscodeTopology, aby utworzyć częściową topologię transkodu.
  5. Utwórz obiekt pomocnika, który zarządza sesją multimediów. (Zobacz Asystenta sesji multimediów).
  6. Uruchom sesję kodowania i poczekaj na jej zakończenie. (Zobacz Uruchom sesję kodowania.)
  7. Wywołaj IMFMediaSource::Shutdown, aby zamknąć źródło multimediów.
  8. Zwolnij wskaźniki interfejsu. Ten kod używa funkcji SafeRelease w celu wydania wskaźników interfejsu. Inną opcją jest użycie klasy inteligentnego wskaźnika COM, takiej jak CComPtr.

Tworzenie źródła multimediów

Źródło multimediów to obiekt, który odczytuje i analizuje plik wejściowy. Aby utworzyć źródło multimediów, przekaż adres URL pliku wejściowego do Source Resolver. Poniższy kod pokazuje, jak to zrobić.

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

Aby uzyskać więcej informacji, zobacz Korzystanie z Rozwiązywacza Źródła.

Pobieranie czasu trwania źródła

Chociaż nie jest to wymagane, warto wykonać zapytanie źródła multimediów dotyczące długości pliku wejściowego. Ta wartość może służyć do śledzenia postępu kodowania. Czas trwania jest przechowywany w atrybucie MF_PD_DURATION deskryptora prezentacji. Pobierz deskryptor prezentacji, wywołując 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;
}

Tworzenie profilu transkodowania

Profil transkodowania opisuje parametry kodowania. Aby uzyskać więcej informacji na temat tworzenia profilu transkodowania, zobacz Using the Transcode API. Aby utworzyć profil, wykonaj następujące kroki.

  1. Wywołaj MFCreateTranscodeProfile, aby utworzyć pusty profil.
  2. Utwórz typ nośnika dla strumienia audio AAC. Dodaj go do profilu, wywołując IMFTranscodeProfile::SetAudioAttributes.
  3. Utwórz typ nośnika dla strumienia wideo H.264. Dodaj go do profilu, wywołując IMFTranscodeProfile::SetVideoAttributes.
  4. Wywołaj MFCreateAttributes, aby utworzyć magazyn atrybutów dla atrybutów na poziomie kontenera.
  5. Ustaw atrybut MF_TRANSCODE_CONTAINERTYPE. Jest to jedyny wymagany atrybut na poziomie kontenera. W przypadku pliku wyjściowego MP4 ustaw ten atrybut na MFTranscodeContainerType_MPEG4.
  6. Wywołaj IMFTranscodeProfile::SetContainerAttributes, aby ustawić atrybuty na poziomie kontenera.

Poniższy kod przedstawia te kroki.

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

Aby określić atrybuty strumienia wideo H.264, utwórz magazyn atrybutów i ustaw następujące atrybuty:

Atrybut Opis
MF_MT_SUBTYPE Ustaw wartość MFVideoFormat_H264.
MF_MT_MPEG2_PROFILE Profil H.264.
MF_MT_FRAME_SIZE Rozmiar ramki.
MF_MT_FRAME_RATE Częstotliwość klatek.
MF_MT_AVG_BITRATE Zakodowana szybkość bitów.

 

Aby określić atrybuty strumienia audio AAC, utwórz magazyn atrybutów i ustaw następujące atrybuty:

Atrybut Opis
MF_MT_SUBTYPE Ustaw wartość MFAudioFormat_AAC
MF_MT_AUDIO_SAMPLES_PER_SECOND Częstotliwość próbkowania audio.
MF_MT_AUDIO_BITS_PER_SAMPLE Bity na próbkę dźwięku.
MF_MT_AUDIO_NUM_CHANNELS Liczba kanałów audio.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND Zakodowana szybkość bitów.
MF_MT_AUDIO_BLOCK_ALIGNMENT Ustaw wartość 1.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION Wskazanie poziomu profilu AAC (opcjonalnie).

 

Poniższy kod tworzy atrybuty strumienia wideo.

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

Poniższy kod tworzy atrybuty strumienia 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;
}

Należy pamiętać, że interfejs API transkodowania nie wymaga prawdziwego typu nośnika, chociaż wykorzystuje atrybuty typu nośnika. W szczególności atrybut MF_MT_MAJOR_TYPE nie jest wymagany, ponieważ metody SetVideoAttributes i SetAudioAttributes określają główny typ. Jednak ważne jest również przekazanie rzeczywistego formatu mediów do tych metod. (Interfejs IMFMediaType dziedziczy IMFAttributes.)

Uruchamianie sesji kodowania

Poniższy kod uruchamia sesję kodowania. Używa klasy pomocniczej Media Session, która jest pokazana w następnej sekcji.

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

Pomocnik sesji medialnej

Sesja multimediów jest bardziej szczegółowo opisana w sekcji Architektura Media Foundation tej dokumentacji. Sesja multimediów używa asynchronicznego modelu zdarzeń. W aplikacji z graficznym interfejsem użytkownika należy reagować na zdarzenia sesji, nie blokując wątku interfejsu użytkownika podczas oczekiwania na następne zdarzenie. W samouczku How to Play Unprotected Media Files pokazano, jak to zrobić w aplikacji odtwarzania. W przypadku kodowania zasada jest taka sama, ale mniejsza liczba zdarzeń jest istotna:

Zdarzenie Opis
MESessionEnded Wywoływane po zakończeniu kodowania.
MESessionClosed Gdy metoda IMFMediaSession::Close zostanie ukończona, zostanie podniesione. Po wystąpieniu tego zdarzenia można bezpiecznie zamknąć sesję multimedialną.

 

W przypadku aplikacji konsolowej uzasadnione jest zablokowanie i oczekiwanie na zdarzenia. W zależności od pliku źródłowego i ustawień kodowania może upłynąć trochę czasu, aby ukończyć kodowanie. Aktualizacje postępu można uzyskać w następujący sposób:

  1. Wywołaj IMFMediaSession::GetClock, aby uzyskać zegar prezentacji.
  2. Wykonaj zapytanie o zegar dla interfejsuIMFPresentationClock.
  3. Wywołaj IMFPresentationClock::GetTime, aby uzyskać bieżącą pozycję.
  4. Pozycja jest podana w jednostkach czasu. Aby uzyskać wartość procentową ukończenia, użyj wartości (100 * position) / duration.

Oto deklaracja klasy 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;
};

Poniższy kod przedstawia pełną implementację klasy 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;
}

koder AAC

Koder wideo H.264

sesja multimedialna

typy multimediów

API transkodowania