Share via


자습서: 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}, 
};

참고

H264ProfileInfo 여기에 정의된 및 AACProfileInfo 구조체는 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을 Source Resolver에 전달합니다. 다음 코드에서는 이 작업을 수행하는 방법을 보여 줍니다.

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 비디오 스트림에 대한 특성을 지정하려면 특성 저장소를 만들고 다음 특성을 설정합니다.

attribute 설명
MF_MT_SUBTYPE 를 MFVideoFormat_H264.
MF_MT_MPEG2_PROFILE H.264 프로필.
MF_MT_FRAME_SIZE 프레임 크기입니다.
MF_MT_FRAME_RATE 프레임 속도.
MF_MT_AVG_BITRATE 인코딩된 비트 속도입니다.

 

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

attribute 설명
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 스레드를 차단하지 않고 세션 이벤트에 응답해야 합니다. 보호 되지 않은 미디어 파일을 재생하는 방법 자습서에서는 재생 애플리케이션에서 이 작업을 수행하는 방법을 보여줍니다. 인코딩의 경우 원칙은 동일하지만 관련성이 있는 이벤트는 적습니다.

이벤트 설명
MESessionEnded 인코딩이 완료되면 발생합니다.
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 코드 변환