Bagikan melalui


Tutorial: Mengodekan File MP4

Tutorial ini menunjukkan cara menggunakan API Transcode untuk mengodekan file MP4, menggunakan H.264 untuk aliran video dan AAC untuk aliran audio.

Header dan File Pustaka

Sertakan file header berikut.

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

Tautkan file pustaka berikut.

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

Menentukan Profil Pengodean

Salah satu pendekatan untuk pengodean adalah menentukan daftar profil pengodean target yang diketahui sebelumnya. Untuk tutorial ini, kami mengambil pendekatan yang relatif sederhana, dan menyimpan daftar format pengodean untuk video H.264 dan audio AAC.

Untuk H.264, atribut format yang paling penting adalah profil H.264, kecepatan bingkai, ukuran bingkai, dan laju bit yang dikodekan. Array berikut berisi daftar format pengodean 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 },
};

Profil H.264 ditentukan menggunakan enumerasi eAVEncH264VProfile . Anda juga dapat menentukan tingkat H.264, tetapi Microsoft Media Foundation H.264 Video Encoder dapat memperoleh tingkat yang tepat untuk aliran video tertentu, sehingga disarankan untuk tidak mengambil alih tingkat yang dipilih encoder. Untuk konten yang terjalin, Anda juga akan menentukan mode interlace (lihat Video Interlacing).

Untuk audio AAC, atribut format yang paling penting adalah laju sampel audio, jumlah saluran, jumlah bit per sampel, dan laju bit yang dikodekan. Secara opsional, Anda dapat mengatur indikasi tingkat profil audio AAC. Untuk informasi selengkapnya, lihat Encoder AAC. Array berikut berisi daftar format pengodean 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}, 
};

Catatan

Struktur H264ProfileInfo dan AACProfileInfo yang ditentukan di sini bukan bagian dari MEDIA Foundation API.

 

Menulis Fungsi wmain

Kode berikut menunjukkan titik masuk untuk aplikasi konsol.

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

Fungsi melakukan wmain hal berikut:

  1. Memanggil fungsi CoInitializeEx untuk menginisialisasi pustaka COM.
  2. Memanggil fungsi MFStartup untuk menginisialisasi Media Foundation.
  3. Memanggil fungsi yang ditentukan EncodeFile aplikasi. Fungsi ini mentranskode file input ke file output, dan ditampilkan di bagian berikutnya.
  4. Memanggil fungsi MFShutdown untuk mematikan Media Foundation.
  5. Panggil fungsi CoUninitialize untuk membatalkan inisialisasi pustaka COM.

Mengodekan File

Kode berikut menunjukkan EncodeFile fungsi, yang melakukan transcoding. Fungsi ini sebagian besar terdiri dari panggilan ke fungsi lain yang ditentukan aplikasi, yang ditampilkan nanti dalam topik ini.

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

Fungsi melakukan EncodeFile langkah-langkah berikut.

  1. Membuat sumber media untuk file input, menggunakan URL atau jalur file file input. (Lihat Membuat Sumber Media.)
  2. Mendapatkan durasi file input. (Lihat Mendapatkan Durasi Sumber.)
  3. Buat profil transkode. (Lihat Membuat Profil Transkode.)
  4. Panggil MFCreateTranscodeTopology untuk membuat topologi transkode parsial.
  5. Buat objek pembantu yang mengelola Sesi Media. (Lihat Media Session Helper).
  6. Jalankan sesi pengodean dan tunggu hingga selesai. (Lihat Menjalankan Sesi Pengodean.)
  7. Panggil IMFMediaSource::Shutdown untuk mematikan sumber media.
  8. Lepaskan penunjuk antarmuka. Kode ini menggunakan fungsi SafeRelease untuk merilis penunjuk antarmuka. Opsi lain adalah menggunakan kelas pointer pintar COM, seperti CComPtr.

Membuat Sumber Media

Sumber media adalah objek yang membaca dan mengurai file input. Untuk membuat sumber media, teruskan URL file input ke Pemecah Masalah Sumber. Kode berikut menunjukkan cara melakukannya.

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

Untuk informasi selengkapnya, lihat Menggunakan Pemecah Masalah Sumber.

Mendapatkan Durasi Sumber

Meskipun tidak diperlukan, akan berguna untuk mengkueri sumber media selama durasi file input. Nilai ini dapat digunakan untuk melacak kemajuan pengodean. Durasi disimpan dalam atribut MF_PD_DURATION deskriptor presentasi. Dapatkan deskriptor presentasi dengan memanggil 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;
}

Membuat Profil Transcode

Profil transkode menjelaskan parameter pengodean. Untuk informasi selengkapnya tentang membuat profil transkode, lihat Menggunakan API Transcode. Untuk membuat profil, lakukan langkah-langkah berikut.

  1. Panggil MFCreateTranscodeProfile untuk membuat profil kosong.
  2. Buat jenis media untuk aliran audio AAC. Tambahkan ke profil dengan memanggil IMFTranscodeProfile::SetAudioAttributes.
  3. Buat jenis media untuk aliran video H.264. Tambahkan ke profil dengan memanggil IMFTranscodeProfile::SetVideoAttributes.
  4. Panggil MFCreateAttributes untuk membuat penyimpanan atribut untuk atribut tingkat kontainer.
  5. Atur atribut MF_TRANSCODE_CONTAINERTYPE . Ini adalah satu-satunya atribut tingkat kontainer yang diperlukan. Untuk output file MP4, atur atribut ini ke MFTranscodeContainerType_MPEG4.
  6. Panggil IMFTranscodeProfile::SetContainerAttributes untuk mengatur atribut tingkat kontainer.

Kode berikut menunjukkan langkah-langkah ini.

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

Untuk menentukan atribut untuk aliran video H.264, buat penyimpanan atribut dan atur atribut berikut:

Atribut Desripsi
MF_MT_SUBTYPE Atur ke MFVideoFormat_H264.
MF_MT_MPEG2_PROFILE Profil H.264.
MF_MT_FRAME_SIZE Ukuran bingkai.
MF_MT_FRAME_RATE Kecepatan bingkai.
MF_MT_AVG_BITRATE Laju bit yang dikodekan.

 

Untuk menentukan atribut untuk aliran audio AAC, buat penyimpanan atribut dan atur atribut berikut:

Atribut Desripsi
MF_MT_SUBTYPE Atur ke MFAudioFormat_AAC
MF_MT_AUDIO_SAMPLES_PER_SECOND Laju sampel audio.
MF_MT_AUDIO_BITS_PER_SAMPLE Bit per sampel audio.
MF_MT_AUDIO_NUM_CHANNELS Jumlah saluran audio.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND Laju bit yang dikodekan.
MF_MT_AUDIO_BLOCK_ALIGNMENT Atur ke 1.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION Indikasi tingkat profil AAC (opsional).

 

Kode berikut membuat atribut streaming 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;
}

Kode berikut membuat atribut aliran 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;
}

Perhatikan bahwa API transcode tidak memerlukan jenis media yang benar, meskipun menggunakan atribut jenis media. Secara khusus, atribut MF_MT_MAJOR_TYPE tidak diperlukan, karena metode SetVideoAttributes dan SetAudioAttributes menyiratkan jenis utama. Namun, juga valid untuk meneruskan jenis media aktual ke metode ini. (Antarmuka IMFMediaType mewarisi IMFAttributes.)

Jalankan Sesi Pengodean

Kode berikut menjalankan sesi pengodean. Ini menggunakan kelas pembantu Sesi Media, yang ditampilkan di bagian berikutnya.

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

Pembantu Sesi Media

Sesi Media dijelaskan lebih lengkap di bagian Arsitektur Media Foundation dari dokumentasi ini. Sesi Media menggunakan model peristiwa asinkron. Dalam aplikasi GUI, Anda harus merespons peristiwa sesi tanpa memblokir utas UI untuk menunggu peristiwa berikutnya. Tutorial Cara Memutar File Media yang Tidak Terlindungi menunjukkan cara melakukan ini dalam aplikasi pemutaran. Untuk pengodean, prinsipnya sama, tetapi lebih sedikit peristiwa yang relevan:

Kejadian Desripsi
MESessionEnded Dimunculkan ketika pengodean selesai.
MESessionClosed Dimunculkan ketika metode IMFMediaSession::Close selesai. Setelah acara ini dinaikkan, aman untuk mematikan Sesi Media.

 

Untuk aplikasi konsol, masuk akal untuk memblokir dan menunggu peristiwa. Bergantung pada file sumber dan pengaturan pengodean, mungkin perlu beberapa saat untuk menyelesaikan pengodean. Anda bisa mendapatkan pembaruan kemajuan sebagai berikut:

  1. Panggil IMFMediaSession::GetClock untuk mendapatkan jam presentasi.
  2. Kueri jam untuk antarmuka IMFPresentationClock .
  3. Panggil IMFPresentationClock::GetTime untuk mendapatkan posisi saat ini.
  4. Posisi diberikan dalam satuan waktu. Untuk menyelesaikan persentase, gunakan nilai (100 * position) / duration.

Berikut adalah deklarasi CSession kelas .

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

Kode berikut menunjukkan implementasi CSession lengkap kelas .

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

AAC Encoder

Pengode Video H.264

Sesi Media

Jenis Media

API Transkode