다음을 통해 공유


자습서: MP4 파일 인코딩

이 자습서에서는 비디오 스트림에 H.264를 사용하고 오디오 스트림에 AAC를 사용하여 Transcode API 사용하여 MP4 파일을 인코딩하는 방법을 보여줍니다.

헤더 및 라이브러리 파일

다음 헤더 파일을 포함합니다.

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

다음 라이브러리 파일을 연결합니다.

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

인코딩 프로필 정의

인코딩에 대한 한 가지 방법은 미리 알려진 대상 인코딩 프로필 목록을 정의하는 것입니다. 이 자습서에서는 비교적 간단한 방법을 사용하고 H.264 비디오 및 AAC 오디오에 대한 인코딩 형식 목록을 저장합니다.

H.264의 경우 가장 중요한 형식 특성은 H.264 프로필, 프레임 속도, 프레임 크기 및 인코딩된 비트 속도입니다. 다음 배열에는 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 },
};

H.264 프로필은 eAVEncH264VProfile 열거형을 사용하여 지정됩니다. H.264 수준을 지정할 수도 있지만 Microsoft Media Foundation H.264 비디오 인코더 지정된 비디오 스트림에 대한 적절한 수준을 파생시킬 수 있으므로 인코더의 선택한 수준을 재정의하지 않는 것이 좋습니다. 인터레이스 콘텐츠의 경우 인터레이스 모드를 지정합니다(비디오 인터레이스 참조).

AAC 오디오의 경우 가장 중요한 형식 특성은 오디오 샘플 속도, 채널 수, 샘플당 비트 수 및 인코딩된 비트 속도입니다. 필요에 따라 AAC 오디오 프로필 수준 표시를 설정할 수 있습니다. 자세한 내용은 AAC 인코더참조하세요. 다음 배열에는 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}, 
};

메모

여기에 정의된 H264ProfileInfoAACProfileInfo 구조는 Media Foundation API의 일부가 아닙니다.

 

wmain 함수 작성

다음 코드는 콘솔 애플리케이션의 진입점을 보여줍니다.

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

wmain 함수는 다음을 수행합니다.

  1. CoInitializeEx 함수를 호출하여 COM 라이브러리를 초기화합니다.
  2. MFStartup 함수를 호출하여 Media Foundation을 초기화합니다.
  3. 애플리케이션 정의 EncodeFile 함수를 호출합니다. 이 함수는 입력 파일을 출력 파일로 트랜스코딩하고 다음 섹션에 표시됩니다.
  4. MFShutdown 함수를 호출하여 Media Foundation을 종료합니다.
  5. CoUninitialize 함수를 호출하여 COM 라이브러리를 해제합니다.

파일 인코딩

다음 코드는 코드 변환을 수행하는 EncodeFile 함수를 보여 줍니다. 이 함수는 주로 이 항목의 뒷부분에 나와 있는 다른 애플리케이션 정의 함수에 대한 호출로 구성됩니다.

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

EncodeFile 함수는 다음 단계를 수행합니다.

  1. 입력 파일의 URL 또는 파일 경로를 사용하여 입력 파일에 대한 미디어 원본을 만듭니다. (미디어 소스 만들기을 참조하세요.)
  2. 입력 파일의 기간을 가져옵니다. (원본 기간 가져오기참조하세요.)
  3. 트랜스코딩 프로필을 만듭니다. (코드 변환 프로필 만들기 참조하세요.)
  4. MFCreateTranscodeTopology 호출하여 부분 트랜스코드 토폴로지 만들기
  5. 미디어 세션을 관리하는 도우미 개체를 만듭니다. (미디어 세션 도우미 참조).
  6. 인코딩 세션을 실행하고 완료될 때까지 기다립니다. (인코딩 세션 실행참조하세요.)
  7. IMFMediaSource::Shutdown 호출하여 미디어 소스를 종료합니다.
  8. 인터페이스 포인터를 해제합니다. 이 코드는 SafeRelease 함수를 사용하여 인터페이스 포인터를 해제합니다. 또 다른 옵션은 CComPtr같은 COM 스마트 포인터 클래스를 사용하는 것입니다.

미디어 원본 만들기

미디어 원본은 입력 파일을 읽고 구문 분석하는 개체입니다. 미디어 원본을 만들려면 입력 파일의 URL을 원본 확인자전달합니다. 다음 코드는 이 작업을 수행하는 방법을 보여줍니다.

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

자세한 내용은 소스 해석기 을 사용하는을 참조하세요.

원본 지속 시간 가져오기

필수는 아니지만 입력 파일의 기간 동안 미디어 원본을 쿼리하는 것이 유용합니다. 이 값을 사용하여 인코딩 진행률을 추적할 수 있습니다. 기간은 프레젠테이션 설명자의 MF_PD_DURATION 특성에 저장됩니다. 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;
}

코드 변환 프로필 만들기

코드 변환 프로필은 인코딩 매개 변수를 설명합니다. 코드 변환 프로필을 만드는 방법에 대한 자세한 내용은 Transcode API사용을 참조하세요. 프로필을 만들려면 다음 단계를 수행합니다.

  1. MFCreateTranscodeProfile 호출하여 빈 프로필을 만듭니다.
  2. AAC 오디오 스트림에 대한 미디어 형식을 만듭니다. IMFTranscodeProfile::SetAudioAttributes호출하여 프로필에 추가합니다.
  3. H.264 비디오 스트림에 대한 미디어 형식을 만듭니다. IMFTranscodeProfile::SetVideoAttributes호출하여 프로필에 추가합니다.
  4. MFCreateAttributes 호출하여 컨테이너 수준 특성에 대한 특성 저장소를 만듭니다.
  5. MF_TRANSCODE_CONTAINERTYPE 특성을 설정합니다. 이것이 유일한 필수 컨테이너 수준 특성입니다. MP4 파일 출력의 경우 이 특성을 MFTranscodeContainerType_MPEG4으로 설정하십시오.
  6. IMFTranscodeProfile::SetContainerAttributes 호출하여 컨테이너 수준 특성을 설정합니다.

다음 코드는 이러한 단계를 보여줍니다.

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

H.264 비디오 스트림에 대한 특성을 지정하려면 특성 저장소를 만들고 다음 특성을 설정합니다.

속성 설명
MF_MT_SUBTYPE MFVideoFormat_H264로 설정하십시오.
MF_MT_MPEG2_PROFILE H.264 프로필.
MF_MT_FRAME_SIZE 프레임 크기
MF_MT_FRAME_RATE 프레임 속도입니다.
MF_MT_AVG_BITRATE 인코딩된 비트 전송률입니다.

 

AAC 오디오 스트림에 대한 특성을 지정하려면 특성 저장소를 만들고 다음 특성을 설정합니다.

속성 설명
MF_MT_SUBTYPE MFAudioFormat_AAC로 설정
MF_MT_AUDIO_SAMPLES_PER_SECOND 오디오 샘플 속도입니다.
MF_MT_AUDIO_BITS_PER_SAMPLE 오디오 샘플당 비트 수입니다.
MF_MT_AUDIO_NUM_CHANNELS (오디오 채널 수) 오디오 채널 수입니다.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND 인코딩된 비트 전송률입니다.
MF_MT_AUDIO_BLOCK_ALIGNMENT 1로 설정합니다.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION AAC 프로필 수준 표시(선택 사항).

 

다음 코드는 비디오 스트림 특성을 만듭니다.

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

다음 코드는 오디오 스트림 특성을 만듭니다.

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

코드 변환 API는 미디어 형식 특성을 사용하지만 실제 미디어 형식이 필요하지 않습니다. 특히 SetVideoAttributesSetAudioAttributes 메서드가 주 형식을 의미하기 때문에 MF_MT_MAJOR_TYPE 특성은 필요하지 않습니다. 그러나 실제 미디어 형식을 이러한 메서드에 전달하는 것도 유효합니다. (IMFMediaType 인터페이스는 IMFAttributes상속합니다.)

인코딩 세션 실행

다음 코드는 인코딩 세션을 실행합니다. 다음 섹션에 표시된 미디어 세션 도우미 클래스를 사용합니다.

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

미디어 세션 도우미

미디어 세션 이 설명서의 Media Foundation 아키텍처 섹션에서 자세히 설명합니다. 미디어 세션은 비동기 이벤트 모델을 사용합니다. GUI 애플리케이션에서는 다음 이벤트를 기다리도록 UI 스레드를 차단하지 않고 세션 이벤트에 응답해야 합니다. 보호되지 않은 미디어 파일 재생하는 방법에 자습서에서는 재생 애플리케이션에서 이 작업을 수행하는 방법을 보여줍니다. 인코딩의 경우 원칙은 동일하지만 관련된 이벤트는 더 적습니다.

이벤트 설명
메시 세션 종료됨 인코딩이 완료되면 발생합니다.
MESessionClosed IMFMediaSession::Close 메서드가 완료될 때 발생합니다. 이 이벤트가 발생한 후에는 미디어 세션을 종료해도 안전합니다.

 

콘솔 애플리케이션의 경우 이벤트를 차단하고 기다리는 것이 합리적입니다. 원본 파일 및 인코딩 설정에 따라 인코딩을 완료하는 데 시간이 걸릴 수 있습니다. 다음과 같이 진행률 업데이트를 받을 수 있습니다.

  1. IMFMediaSession::GetClock 호출하여 프레젠테이션 시계를 가져옵니다.
  2. IMFPresentationClock 인터페이스에 대한 클록을 쿼리합니다.
  3. IMFPresentationClock::GetTime 호출하여 현재 위치를 가져옵니다.
  4. 위치는 시간 단위로 제공됩니다. 완료된 백분율을 얻으려면 (100 * position) / duration값을 사용합니다.

다음은 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;
};

다음 코드는 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;
}

AAC 인코더

H.264 비디오 인코더

미디어 세션

미디어 형식

트랜스코드 API