Freigeben über


Tutorial: Codieren einer MP4-Datei

In diesem Tutorial wird gezeigt, wie Sie die Transcode-API verwenden, um eine MP4-Datei zu codieren, wobei H.264 für den Videostream und AAC für den Audiostream verwendet werden.

Header und Bibliotheksdateien

Schließen Sie die folgenden Headerdateien ein.

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

Verknüpfen Sie die folgenden Bibliotheksdateien.

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

Definieren der Codierungsprofile

Ein Ansatz für die Codierung besteht darin, eine Liste von Zielcodierungsprofilen zu definieren, die im Voraus bekannt sind. In diesem Tutorial verfolgen wir einen relativ einfachen Ansatz und speichern eine Liste der Codierungsformate für H.264-Video und AAC-Audio.

Für H.264 sind die wichtigsten Formatattribute das H.264-Profil, die Bildrate, die Framegröße und die codierte Bitrate. Das folgende Array enthält eine Liste der H.264-Codierungsformate.

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-Profile werden mithilfe der eAVEncH264VProfile-Enumeration angegeben. Sie können auch die H.264-Ebene angeben, aber der Microsoft Media Foundation H.264 Video Encoder kann die richtige Ebene für einen bestimmten Videostream ableiten. Daher wird empfohlen, die ausgewählte Ebene des Encoders nicht außer Kraft zu setzen. Für interlaced Content würden Sie auch den Interlace-Modus angeben (siehe Video-Interlacing).

Für AAC-Audio sind die wichtigsten Formatattribute die Audio-Samplerate, die Anzahl der Kanäle, die Anzahl der Bits pro Sample und die codierte Bitrate. Optional können Sie die AAC-Audioprofilpegelanzeige festlegen. Weitere Informationen finden Sie unter AAC Encoder. Das folgende Array enthält eine Liste der AAC-Codierungsformate.

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

Hinweis

Die H264ProfileInfo hier definierten Strukturen und AACProfileInfo sind nicht Teil der Media Foundation-API.

 

Schreiben der wmain-Funktion

Der folgende Code zeigt den Einstiegspunkt für die Konsolenanwendung.

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

Die wmain Funktion führt Folgendes aus:

  1. Ruft die CoInitializeEx-Funktion auf, um die COM-Bibliothek zu initialisieren.
  2. Ruft die MFStartup-Funktion auf, um Media Foundation zu initialisieren.
  3. Ruft die anwendungsdefinierte Funktion auf EncodeFile . Diese Funktion transcodiert die Eingabedatei in die Ausgabedatei und wird im nächsten Abschnitt angezeigt.
  4. Ruft die MFShutdown-Funktion auf, um Media Foundation herunterzufahren.
  5. Rufen Sie die CoUninitialize-Funktion auf, um die COM-Bibliothek zu entinitialisieren.

Codieren der Datei

Der folgende Code zeigt EncodeFile die Funktion, die die Transcodierung ausführt. Diese Funktion besteht hauptsächlich aus Aufrufen anderer anwendungsdefinierter Funktionen, die weiter unten in diesem Thema gezeigt werden.

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

Die EncodeFile Funktion führt die folgenden Schritte aus.

  1. Erstellt eine Medienquelle für die Eingabedatei unter Verwendung der URL oder des Dateipfads der Eingabedatei. (Siehe Erstellen der Medienquelle.)
  2. Ruft die Dauer der Eingabedatei ab. (Siehe Abrufen der Quelldauer.)
  3. Erstellen Sie das Transcodierungsprofil. (Siehe Erstellen des Transcodierungsprofils.)
  4. Rufen Sie MFCreateTranscodeTopology auf, um die partielle Transcodierungstopologie zu erstellen.
  5. Erstellen Sie ein Hilfsobjekt, das die Mediensitzung verwaltet. (Siehe Mediensitzungshilfsprogramm).
  6. Führen Sie die Codierungssitzung aus, und warten Sie, bis sie abgeschlossen ist. (Siehe Ausführen der Codierungssitzung.)
  7. Rufen Sie IMFMediaSource::Shutdown auf, um die Medienquelle herunterzufahren.
  8. Freigeben von Schnittstellenzeigern. In diesem Code wird die SafeRelease-Funktion verwendet, um Schnittstellenzeiger freizugeben. Eine weitere Option ist die Verwendung einer COM Smart Pointer-Klasse, z. B. CComPtr.

Erstellen der Medienquelle

Die Medienquelle ist das Objekt, das die Eingabedatei liest und analysiert. Um die Medienquelle zu erstellen, übergeben Sie die URL der Eingabedatei an den Quelllöser. Dies wird im folgenden Code veranschaulicht.

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

Weitere Informationen finden Sie unter Verwenden des Quellrelösers.

Abrufen der Quelldauer

Obwohl nicht erforderlich, ist es nützlich, die Medienquelle für die Dauer der Eingabedatei abzufragen. Dieser Wert kann verwendet werden, um den Codierungsfortschritt nachzuverfolgen. Die Dauer wird im MF_PD_DURATION-Attribut des Präsentationsdeskriptors gespeichert. Rufen Sie den Präsentationsdeskriptor ab, indem Sie IMFMediaSource::CreatePresentationDescriptor aufrufen.

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

Erstellen des Transcodierungsprofils

Das Transcodierungsprofil beschreibt die Codierungsparameter. Weitere Informationen zum Erstellen eines Transcodierungsprofils finden Sie unter Verwenden der Transcodierungs-API. Führen Sie zum Erstellen des Profils die folgenden Schritte aus.

  1. Rufen Sie MFCreateTranscodeProfile auf, um das leere Profil zu erstellen.
  2. Erstellen Sie einen Medientyp für den AAC-Audiostream. Fügen Sie es dem Profil hinzu, indem Sie IMFTranscodeProfile::SetAudioAttributes aufrufen.
  3. Erstellen Sie einen Medientyp für den H.264-Videostream. Fügen Sie es dem Profil hinzu, indem Sie IMFTranscodeProfile::SetVideoAttributes aufrufen.
  4. Rufen Sie MFCreateAttributes auf, um einen Attributspeicher für die Attribute auf Containerebene zu erstellen.
  5. Legen Sie das attribut MF_TRANSCODE_CONTAINERTYPE fest. Dies ist das einzige erforderliche Attribut auf Containerebene. Legen Sie für die MP4-Dateiausgabe dieses Attribut auf MFTranscodeContainerType_MPEG4 fest.
  6. Rufen Sie IMFTranscodeProfile::SetContainerAttributes auf, um die Attribute auf Containerebene festzulegen.

Im folgenden Code werden diese Schritte veranschaulicht.

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

Um die Attribute für den H.264-Videostream anzugeben, erstellen Sie einen Attributspeicher, und legen Sie die folgenden Attribute fest:

attribute Beschreibung
MF_MT_SUBTYPE Legen Sie auf MFVideoFormat_H264 fest.
MF_MT_MPEG2_PROFILE H.264-Profil.
MF_MT_FRAME_SIZE Framegröße.
MF_MT_FRAME_RATE Bildrate.
MF_MT_AVG_BITRATE Codierte Bitrate.

 

Um die Attribute für den AAC-Audiostream anzugeben, erstellen Sie einen Attributspeicher, und legen Sie die folgenden Attribute fest:

attribute Beschreibung
MF_MT_SUBTYPE Auf MFAudioFormat_AAC festgelegt
MF_MT_AUDIO_SAMPLES_PER_SECOND Audio-Abtastrate.
MF_MT_AUDIO_BITS_PER_SAMPLE Bits pro Audiobeispiel.
MF_MT_AUDIO_NUM_CHANNELS Anzahl der Audiokanäle.
MF_MT_AUDIO_AVG_BYTES_PER_SECOND Codierte Bitrate.
MF_MT_AUDIO_BLOCK_ALIGNMENT Auf 1 festlegen.
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION AAC-Profilebenenanzeige (optional).

 

Der folgende Code erstellt die Videostreamattribute.

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

Der folgende Code erstellt die Audiostreamattribute.

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

Beachten Sie, dass die Transcodierungs-API keinen echten Medientyp erfordert, obwohl sie Medientypattribute verwendet. Insbesondere die MF_MT_MAJOR_TYPE Attribut nicht erforderlich, da die Methoden SetVideoAttributes und SetAudioAttributes den Haupttyp implizieren. Es ist jedoch auch gültig, einen tatsächlichen Medientyp an diese Methoden zu übergeben. (Die IMFMediaType-Schnittstelle erbt IMFAttributes.)

Ausführen der Codierungssitzung

Der folgende Code führt die Codierungssitzung aus. Es verwendet die Media Session-Hilfsklasse, die im nächsten Abschnitt gezeigt wird.

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

Mediensitzungshilfsprogramm

Die Mediensitzung wird im Abschnitt Media Foundation Architecture dieser Dokumentation ausführlicher beschrieben. Die Mediensitzung verwendet ein asynchrones Ereignismodell. In einer GUI-Anwendung sollten Sie auf Sitzungsereignisse reagieren, ohne den UI-Thread zu blockieren, um auf das nächste Ereignis zu warten. Das Tutorial Wiedergeben ungeschützter Mediendateien zeigt, wie Sie dies in einer Wiedergabeanwendung tun. Für die Codierung ist das Prinzip identisch, aber es sind weniger Ereignisse relevant:

Ereignis Beschreibung
MESessionEnded Wird ausgelöst, wenn die Codierung abgeschlossen ist.
MESessionClosed Wird ausgelöst, wenn die IMFMediaSession::Close-Methode abgeschlossen ist. Nachdem dieses Ereignis ausgelöst wurde, kann die Mediensitzung sicher heruntergefahren werden.

 

Für eine Konsolenanwendung ist es sinnvoll, Ereignisse zu blockieren und darauf zu warten. Abhängig von der Quelldatei und den Codierungseinstellungen kann es eine Weile dauern, bis die Codierung abgeschlossen ist. Sie können Statusupdates wie folgt abrufen:

  1. Rufen Sie IMFMediaSession::GetClock auf, um die Präsentationsuhr abzurufen.
  2. Fragen Sie die Uhr für die IMFPresentationClock-Schnittstelle ab.
  3. Rufen Sie IMFPresentationClock::GetTime auf, um die aktuelle Position abzurufen.
  4. Die Position wird in Zeiteinheiten angegeben. Um den abgeschlossenen Prozentsatz zu erhalten, verwenden Sie den Wert (100 * position) / duration.

Hier ist die Deklaration der CSession -Klasse.

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

Der folgende Code zeigt die vollständige Implementierung der CSession -Klasse.

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

H.264-Videoencoder

Mediensitzung

Medientypen

Transcodierungs-API