Sdílet prostřednictvím


Jak napsat prezentér EVR

[Komponenta popsaná na této stránce, Enhanced Video Renderer, je starší funkce. Nahradil ho Simple Video Renderer (SVR), vystavený prostřednictvím komponent MediaPlayer a IMFMediaEngine. Pokud chcete přehrát videoobsáh, měli byste do jedné z těchto komponent odesílat data a umožnit jim vytvořit instanci nového rendereru videa. Tyto komponenty jsou optimalizované pro Windows 10 a Windows 11. Společnost Microsoft důrazně doporučuje, aby nový kód používal MediaPlayer nebo rozhraní API nižší úrovně IMFMediaEngine k přehrávání videosouborů ve Windows místo EVR, pokud je to možné. Microsoft navrhuje, aby se stávající kód, který používá starší rozhraní API, přepsal, aby se nová rozhraní API používala, pokud je to možné.]

Tento článek popisuje, jak napsat vlastní prezentační prvek pro vylepšený video renderer (EVR). Vlastní prezentující lze použít s DirectShow i Media Foundation; rozhraní a objektový model jsou pro obě technologie stejné, i když se přesná posloupnost operací může lišit.

Ukázkový kód v tomto tématu je upraven z ukázky EVRPresenter, která je poskytována v sadě Windows SDK.

Toto téma obsahuje následující části:

Požadavky

Před psaním vlastního prezentačního prvku byste měli být obeznámeni s následujícími technologiemi:

  • Vylepšený renderer videa. Podívejte se na vylepšený video renderer.
  • Direct3D grafika. Abyste mohli psát prezentér, nemusíte rozumět 3D grafice, ale musíte vědět, jak vytvořit zařízení Direct3D a spravovat povrchy Direct3D. Pokud direct3D neznáte, přečtěte si část "Zařízení Direct3D" a "Direct3D Resources" v dokumentaci sady DirectX Graphics SDK.
  • Grafy filtru DirectShow nebo potrubí Media Foundation, v závislosti na tom, kterou technologii vaše aplikace použije pro vykreslení videa.
  • Media Foundation Transforms. Směšovač EVR je transformace Media Foundation a prezentující volá metody přímo na mixéru.
  • Implementace objektů MODELU COM Prezentující je objekt COM s volným vláknem v procesu.

Objektový model Presenter

Tato část obsahuje přehled objektového modelu a rozhraní prezentujícího.

Tok dat uvnitř EVR

EVR používá k vykreslení videa dvě komponenty zásuvného modulu: mixér a prezentér. Mixér sloučí video stream a v případě potřeby provádí deinterlaci videa. Prezentující vykresluje (nebo prezentuje) video na displej a plánuje, kdy bude vykreslen každý snímek. Aplikace mohou nahradit některý z těchto objektů vlastní implementací.

EVR má jeden nebo více vstupních proudů a mixér má odpovídající počet vstupních proudů. Stream 0 je vždy referenční proud. Ostatní proudy jsou podřízené proudy , které mixér alfa-slučuje do referenčního proudu. Referenční datový proud určuje hlavní snímkovou frekvenci pro složené video. Pro každý referenční rámec přebírá mixér nejnovější rámec z každého podstreamu, alfa-mixuje je do referenčního rámce a výstupem je jeden složený rám. Mixér také v případě potřeby provádí deinterlacing a převod barev z YUV na RGB. EVR vždy vloží mixér do kanálu videa bez ohledu na počet vstupních datových proudů nebo formát videa. Tento proces znázorňuje následující obrázek.

diagram znázorňující referenční datový proud a podproud směrem k mixéru, který odkazuje na prezentujícího, který odkazuje na displej

Prezentující provádí následující úlohy:

  • Nastaví výstupní formát mixéru. Než začne streamování, prezentující nastaví typ média na výstupním streamu mixéru. Tento typ média definuje formát složeného obrázku.
  • Vytvoří zařízení Direct3D.
  • Přiřazuje povrchy Direct3D. Mixér přenáší skládané rámce na tyto povrchy.
  • Získá výstup z mixéru.
  • Plánuje, kdy se rámce prezentují. EVR poskytuje prezentační hodiny, a prezentační modul plánuje snímky podle těchto hodin.
  • Zobrazí každý rámec pomocí Direct3D.
  • Provádí krokování a čištění snímků.

Stavy prezentujícího

Prezentující je kdykoli v jednom z následujících stavů:

  • Spuštěno. Hodiny prezentace EVR běží. Moderátor plánuje snímky videa pro jejich prezentaci, jakmile dorazí.
  • Pozastaveno. Hodiny prezentace jsou pozastavené. Prezentující nezobrazuje žádné nové ukázky, ale udržuje frontu plánovaných ukázek. Pokud jsou obdrženy nové vzorky, prezentující je přidá do fronty.
  • bylo zastaveno. Prezentační hodiny jsou zastaveny. Prezentující zahodí všechny naplánované vzorky.
  • Vypnout. Prezentující uvolní všechny prostředky související se streamováním, jako jsou povrchy Direct3D. Jedná se o počáteční stav prezentujícího a konečný stav před zničením prezentujícího.

V ukázkovém kódu v tomto tématu je stav prezentujícího reprezentován výčtem:

enum RENDER_STATE
{
    RENDER_STATE_STARTED = 1,
    RENDER_STATE_STOPPED,
    RENDER_STATE_PAUSED,
    RENDER_STATE_SHUTDOWN,  // Initial state.
};

Některé operace nejsou platné, když je prezentující ve stavu vypnutí. Ukázkový kód kontroluje tento stav voláním pomocné metody:

    HRESULT CheckShutdown() const
    {
        if (m_RenderState == RENDER_STATE_SHUTDOWN)
        {
            return MF_E_SHUTDOWN;
        }
        else
        {
            return S_OK;
        }
    }

Uživatelská rozhraní pro prezentaci

K implementaci následujících rozhraní se vyžaduje prezentující:

Rozhraní Popis
MMFClockStateSink Upozorní prezentujícího, když se změní stav hodin EVR. Viz Implementace IMFClockStateSink.
MMFGetService Poskytuje způsob, jak aplikace a další komponenty v řetězci získat rozhraní od poskytovatele.
IMFTopologyServiceLookupClient Umožňuje prezentujícímu získat rozhraní z EVR nebo mixéru. Viz ImplementaceMMFTopologyServiceLookupClient .
MMFVideoDeviceID Zajišťuje, aby prezentující a mixér používaly kompatibilní technologie. Viz Implementace IMFVideoDeviceID.
MMFVideoPresenter Zpracovává zprávy z EVR. Viz implementaceMMFVideoPresenter .

 

Následující rozhraní jsou volitelná:

Rozhraní Popis
IEVRTrustedVideoPlugin Umožňuje prezentujícímu pracovat s chráněnými multimédii. Toto rozhraní implementujte, pokud je váš prezentační modul důvěryhodnou komponentou navrženou tak, aby fungovala v chráněné mediální cestě (PMP).
Podpora úrokových sazeb MMF Zaznamenává rozsah rychlostí přehrávání, které prezentující podporuje. Viz Implementace IMFRateSupport.
MMFVideoPositionMapper Mapuje souřadnice na výstupním rámečku videa na souřadnice na vstupním rámečku videa.
IQualProp Zaznamenává informace o výkonu. EVR tyto informace používá ke správě řízení kvality. Toto rozhraní je zdokumentováno v sadě DirectShow SDK.

 

Můžete také poskytnout rozhraní pro komunikaci aplikace s prezentujícím. Standardní prezentér implementuje rozhraní IMFVideoDisplayControl pro tento účel. Toto rozhraní můžete implementovat nebo definovat vlastní. Aplikace získá rozhraní od prezentujícího voláním IMFGetService::GetService na EVR. Když je identifikátor GUID služby MR_VIDEO_RENDER_SERVICE, předá EVR požadavek GetService prezentujícímu.

Implementace MMFVideoDeviceID

Rozhraní MMFVideoDeviceID obsahuje jednu metodu, GetDeviceID, která vrátí identifikátor GUID zařízení. Identifikátor GUID zařízení zajišťuje, aby prezentující a mixér používaly kompatibilní technologie. Pokud identifikátory GUID zařízení neodpovídají, EVR se nepodaří inicializovat.

Standardní mixér i prezentující používají Direct3D 9 s identifikátorem GUID zařízení, který se rovná IID_IDirect3DDevice9. Pokud chcete použít vlastní prezentér se standardním mixérem, identifikátor GUID zařízení prezentéru musí být IID_IDirect3DDevice9. Pokud nahradíte obě komponenty, můžete definovat nový identifikátor GUID zařízení. Ve zbývající části tohoto článku se předpokládá, že prezentující používá Direct3D 9. Zde je standardní implementace GetDeviceID:

HRESULT EVRCustomPresenter::GetDeviceID(IID* pDeviceID)
{
    if (pDeviceID == NULL)
    {
        return E_POINTER;
    }

    *pDeviceID = __uuidof(IDirect3DDevice9);
    return S_OK;
}

Metoda by měla být úspěšná i v případě, že je prezentér vypnutý.

Implementace IMFTopologyServiceLookupClient

Rozhraní IMFTopologyServiceLookupClient umožňuje prezentujícímu získat ukazatele rozhraní z EVR a mixéru následujícím způsobem:

  1. Když EVR inicializuje prezentujícího, volá metodu IMFTopologyServiceLookupClient::InitServicePointers. Argument je ukazatel na rozhraní IMFTopologyServiceLookup EVR.
  2. Prezentující volá IMFTopologyServiceLookup::LookupService, aby získal ukazatele rozhraní z EVR nebo z mixéru.

Metoda LookupService je podobná metodě MMFGetService::GetService. Obě metody jako vstup přebírají identifikátor SLUŽBY a identifikátor rozhraní (IID), ale LookupService vrací pole ukazatelů rozhraní, zatímco GetService vrátí jeden ukazatel. V praxi ale můžete velikost pole nastavit na 1. Dotazovaný objekt závisí na identifikátoru GUID služby:

  • Pokud je identifikátor GUID služby MR_VIDEO_RENDER_SERVICE, dotazuje se EVR.
  • Pokud je identifikátor GUID služby MR_VIDEO_MIXER_SERVICE, je dotazován mixér.

V implementaci InitServicePointerszískejte z EVR následující rozhraní:

Rozhraní EVR Popis
IMediaEventSink Poskytuje způsob, jak prezentující odesílat zprávy do EVR. Toto rozhraní je definováno v sadě DIRECTShow SDK, takže zprávy se řídí vzorem událostí DirectShow, nikoli událostí Media Foundation.
MMFClock Představuje hodiny EVR. Prezentující toto rozhraní používá k naplánování ukázek pro prezentaci. EVR může běžet bez hodin, takže toto rozhraní nemusí být dostupné. Pokud ne, ignorujte kód chyby ze služby LookupService.
Hodiny také implementují rozhraní MMFTimer. V kanálu Media Foundation hodiny implementují rozhraní IMFPresentationClock. Toto rozhraní neimplementuje v DirectShow.

 

Z mixéru získejte následující rozhraní:

Rozhraní mixéru Popis
MMFTransform Umožňuje prezentujícímu komunikovat s mixérem.
MMFVideoDeviceID Umožňuje prezentujícímu ověřit identifikátor GUID zařízení mixéru.

 

Následující kód implementuje metodu InitServicePointers:

HRESULT EVRCustomPresenter::InitServicePointers(
    IMFTopologyServiceLookup *pLookup
    )
{
    if (pLookup == NULL)
    {
        return E_POINTER;
    }

    HRESULT             hr = S_OK;
    DWORD               dwObjectCount = 0;

    EnterCriticalSection(&m_ObjectLock);

    // Do not allow initializing when playing or paused.
    if (IsActive())
    {
        hr = MF_E_INVALIDREQUEST;
        goto done;
    }

    SafeRelease(&m_pClock);
    SafeRelease(&m_pMixer);
    SafeRelease(&m_pMediaEventSink);

    // Ask for the clock. Optional, because the EVR might not have a clock.
    dwObjectCount = 1;

    (void)pLookup->LookupService(
        MF_SERVICE_LOOKUP_GLOBAL,   // Not used.
        0,                          // Reserved.
        MR_VIDEO_RENDER_SERVICE,    // Service to look up.
        IID_PPV_ARGS(&m_pClock),    // Interface to retrieve.
        &dwObjectCount              // Number of elements retrieved.
        );

    // Ask for the mixer. (Required.)
    dwObjectCount = 1;

    hr = pLookup->LookupService(
        MF_SERVICE_LOOKUP_GLOBAL, 0,
        MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_pMixer), &dwObjectCount
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Make sure that we can work with this mixer.
    hr = ConfigureMixer(m_pMixer);
    if (FAILED(hr))
    {
        goto done;
    }

    // Ask for the EVR's event-sink interface. (Required.)
    dwObjectCount = 1;

    hr = pLookup->LookupService(
        MF_SERVICE_LOOKUP_GLOBAL, 0,
        MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_pMediaEventSink),
        &dwObjectCount
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Successfully initialized. Set the state to "stopped."
    m_RenderState = RENDER_STATE_STOPPED;

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

Pokud ukazatele rozhraní získané z LookupService již nejsou platné, EVR zavolá IMFTopologyServiceLookupClient::ReleaseServicePointers. Uvnitř této metody uvolněte všechny ukazatele rozhraní a nastavte stav prezentujícího tak, aby se vypnul:

HRESULT EVRCustomPresenter::ReleaseServicePointers()
{
    // Enter the shut-down state.
    EnterCriticalSection(&m_ObjectLock);

    m_RenderState = RENDER_STATE_SHUTDOWN;

    LeaveCriticalSection(&m_ObjectLock);

    // Flush any samples that were scheduled.
    Flush();

    // Clear the media type and release related resources.
    SetMediaType(NULL);

    // Release all services that were acquired from InitServicePointers.
    SafeRelease(&m_pClock);
    SafeRelease(&m_pMixer);
    SafeRelease(&m_pMediaEventSink);

    return S_OK;
}

EVR volá ReleaseServicePointers z různých důvodů, mezi které patří:

  • Odpojení nebo opětovné připojení kolíků (DirectShow) nebo přidání nebo odebrání jímek datového proudu (Media Foundation).
  • Změna formátu
  • Nastavení nových hodin
  • Konečné vypnutí EVR.

Během životnosti prezentujícího může EVR volat InitServicePointers a ReleaseServicePointers několikrát.

Implementování IMFVideoPresenter

Rozhraní MMFVideoPresenter dědí MMFClockStateSink a přidává dvě metody:

Metoda Popis
GetCurrentMediaType Vrátí typ média složených snímků videa.
ProcessMessage Signalizuje prezentujícího, aby provedl různé akce.

 

Metoda GetCurrentMediaType vrátí typ média prezentujícího. (Podrobnosti o nastavení typu média najdete v tématu Vyjednávání formátů.) Typ média je vrácen jako MMFVideoMediaType ukazatel rozhraní. Následující příklad předpokládá, že prezentující ukládá typ média jako MMFMediaType ukazatel. Chcete-li získat rozhraní IMFVideoMediaType z typu média, zavolejte QueryInterface:

HRESULT EVRCustomPresenter::GetCurrentMediaType(
    IMFVideoMediaType** ppMediaType
    )
{
    HRESULT hr = S_OK;

    if (ppMediaType == NULL)
    {
        return E_POINTER;
    }

    *ppMediaType = NULL;

    EnterCriticalSection(&m_ObjectLock);

    hr = CheckShutdown();
    if (FAILED(hr))
    {
        goto done;
    }

    if (m_pMediaType == NULL)
    {
        hr = MF_E_NOT_INITIALIZED;
        goto done;
    }

    hr = m_pMediaType->QueryInterface(IID_PPV_ARGS(ppMediaType));

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

Metoda ProcessMessage je primárním mechanismem, který EVR komunikuje s prezentujícím. Jsou definovány následující zprávy. Podrobnosti o implementaci jednotlivých zpráv najdete ve zbývající části tohoto tématu.

Zpráva Popis
MFVP_MESSAGE_INVALIDATEMEDIATYPE Výstupní typ média mixéru je neplatný. Prezentující by měl s mixérem vyjednat nový typ média. Viz Formáty vyjednávání.
ZPRÁVA_ZAHÁJENÍ_STREAMOVÁNÍ Streamování se spustilo. Tato zpráva nevyžaduje žádnou konkrétní akci, ale můžete ji použít k přidělení prostředků.
MFVP_MESSAGE_ENDSTREAMING Streamování skončilo. Uvolněte všechny prostředky, které jste alokovali v reakci na zprávu MFVP_MESSAGE_BEGINSTREAMING.
MFVP_MESSAGE_PROCESSINPUTNOTIFY Mixér obdržel novou vstupní ukázku a může být schopen vygenerovat nový výstupní rámec. Prezentující by měl volat IMFTransform::ProcessOutput na mixéru. Podívejte se na Zpracování výstupu.
MFVP_MESSAGE_ENDOFSTREAM Prezentace skončila. Viz Konec streamu.
MFVP_MESSAGE_FLUSH EVR proplachuje data ve svém vykreslovacím kanálu. Prezentující by měl zahodit všechny snímky videa, které jsou naplánované pro prezentaci.
MFVP_MESSAGE_STEP Požádá prezentujícího, aby se posunul o N snímků dopředu. Prezentující by měl zahodit následujících N-1 snímků a zobrazit N-tý snímek. Viz postupné prohlížení snímků.
MFVP_ZPRÁVA_ZRUŠITKROK Zruší postupné zobrazování rámce.

 

Implementace MMFClockStateSink

Prezentující musí implementovat rozhraní IMFClockStateSink v rámci své implementace IMFVideoPresenter, které dědí IMFClockStateSink. EVR používá toto rozhraní k upozornění prezentujícího vždy, když se změní stav hodin EVR. Další informace o stavech hodin naleznete v tématu Hodiny prezentace.

Tady jsou některé pokyny pro implementaci metod v tomto rozhraní. Pokud je prezentující vypnutý, měly by všechny metody selhat.

Metoda Popis
OnClockStart
  1. Nastavte stav prezentujícího na začátek.
  2. Pokud llClockStartOffset není PRESENTATION_CURRENT_POSITION, vyprázdněte frontu vzorků prezentujícího. (Odpovídá přijetí MFVP_MESSAGE_FLUSH zprávy.)
  3. Pokud předchozí požadavek na krok rámce stále čeká na vyřízení, zpracujte požadavek (viz krokování rámce). Jinak zkuste zpracovat výstup z mixéru (viz Zpracování výstupu.
onClockStop
  1. Nastavte stav prezentace na zastavený.
  2. Vyprázdněte frontu vzorků prezentátora.
  3. Zrušte všechny čekající operace v rámci kroku.
OnClockPause Nastavte stav prezentace na pozastaveno.
OnClockRestart Zacházejte stejně jako s OnClockStart, ale nevyprázdněte frontu vzorků.
OnClockSetRate
  1. Pokud se rychlost mění z nuly na nenulovou, zrušte posun snímku.
  2. Uložte novou sazbu hodin. Frekvence hodin ovlivňuje, kdy jsou vzorky prezentovány. Další informace najdete v sekci Ukázky plánování.

 

Implementace podpory sazeb MMF

Aby mohl prezentující podporovat jiné rychlosti přehrávání než 1×, musí implementovat rozhraní IMFRateSupport. Tady jsou některé pokyny pro implementaci metod v tomto rozhraní. Všechny metody by měly selhat po vypnutí prezentujícího. Další informace o tomto rozhraní naleznete v tématu Řízení rychlosti.

Hodnota Popis
GetSlowestRate Vrátí nulu k označení, že neexistuje minimální rychlost přehrávání.
GetFastestRate U neredukovaného přehrávání by rychlost přehrávání neměla překročit obnovovací frekvenci monitoru: maximální rychlost = obnovovací frekvence (Hz) / rychlost snímků videa (fps). Frekvence snímků videa je určena v typu média prezentujícího.
U ztenčeného přehrávání je rychlost přehrávání nevázaná; vrátí hodnotu FLT_MAX. V praxi budou zdroj a dekodér limitujícími faktory při ztenčeném přehrávání.
Pro zpětné přehrávání použijte zápornou hodnotu maximální rychlosti.
IsRateSupported Vrátí MF_E_UNSUPPORTED_RATE, pokud absolutní hodnota flRate překročí maximální přehrávací rychlost přehrávače. Vypočítat maximální rychlost přehrávání, jak je popsáno pro GetFastestRate.

 

Následující příklad ukazuje, jak implementovat GetFastestRate metoda:

float EVRCustomPresenter::GetMaxRate(BOOL bThin)
{
    // Non-thinned:
    // If we have a valid frame rate and a monitor refresh rate, the maximum
    // playback rate is equal to the refresh rate. Otherwise, the maximum rate
    // is unbounded (FLT_MAX).

    // Thinned: The maximum rate is unbounded.

    float   fMaxRate = FLT_MAX;
    MFRatio fps = { 0, 0 };
    UINT    MonitorRateHz = 0;

    if (!bThin && (m_pMediaType != NULL))
    {
        GetFrameRate(m_pMediaType, &fps);
        MonitorRateHz = m_pD3DPresentEngine->RefreshRate();

        if (fps.Denominator && fps.Numerator && MonitorRateHz)
        {
            // Max Rate = Refresh Rate / Frame Rate
            fMaxRate = (float)MulDiv(
                MonitorRateHz, fps.Denominator, fps.Numerator);
        }
    }

    return fMaxRate;
}

Předchozí příklad volá pomocnou metodu GetMaxRate pro výpočet maximální rychlosti přeposílání:

Následující příklad ukazuje, jak implementovat metodu IsRateSupported:

HRESULT EVRCustomPresenter::IsRateSupported(
    BOOL bThin,
    float fRate,
    float *pfNearestSupportedRate
    )
{
    EnterCriticalSection(&m_ObjectLock);

    float   fMaxRate = 0.0f;
    float   fNearestRate = fRate;  // If we support fRate, that is the nearest.

    HRESULT hr = CheckShutdown();
    if (FAILED(hr))
    {
        goto done;
    }

    // Find the maximum forward rate.
    // Note: We have no minimum rate (that is, we support anything down to 0).
    fMaxRate = GetMaxRate(bThin);

    if (fabsf(fRate) > fMaxRate)
    {
        // The (absolute) requested rate exceeds the maximum rate.
        hr = MF_E_UNSUPPORTED_RATE;

        // The nearest supported rate is fMaxRate.
        fNearestRate = fMaxRate;
        if (fRate < 0)
        {
            // Negative for reverse playback.
            fNearestRate = -fNearestRate;
        }
    }

    // Return the nearest supported rate.
    if (pfNearestSupportedRate != NULL)
    {
        *pfNearestSupportedRate = fNearestRate;
    }

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

Odesílání událostí do EVR

Prezentující musí informovat EVR o různých událostech. K tomu používá rozhraní IMediaEventSink, které je získáno z EVR, když EVR volá metodu IMFTopologyServiceLookupClient::InitServicePointers. (Rozhraní IMediaEventSink je původně rozhraní DirectShow, ale používá se v DirectShow EVR i Media Foundation.) Následující kód ukazuje, jak odeslat událost do EVR:

    // NotifyEvent: Send an event to the EVR through its IMediaEventSink interface.
    void NotifyEvent(long EventCode, LONG_PTR Param1, LONG_PTR Param2)
    {
        if (m_pMediaEventSink)
        {
            m_pMediaEventSink->Notify(EventCode, Param1, Param2);
        }
    }

Následující tabulka uvádí události, které prezentující odesílá spolu s parametry události.

Událost Popis
EC_COMPLETE Prezentující dokončil vykreslování všech snímků po zprávě MFVP_MESSAGE_ENDOFSTREAM.
  • Param1: HRESULT označující stav operace.
  • param2: Nepoužívá se.
Další informace najdete v tématu Konec streamu.
EC_DISPLAY_CHANGED Zařízení Direct3D se změnilo.
  • param1: Nepoužívá se.
  • param2: Nepoužívá se.
Další informace naleznete v tématu Správa zařízení Direct3D.
EC_ERRORABORT Došlo k chybě, která vyžaduje zastavení streamování.
  • param1: HRESULT označující chybu, ke které došlo.
  • param2: Nepoužívá se.
EC_PROCESSING_LATENCY Určuje dobu, po kterou prezentující vykreslí každý snímek. (Volitelné.)
  • param1: Ukazatel na konstantní hodnotu LONGLONG obsahující dobu zpracování rámce v 100 nanosekundových jednotkách.
  • param2: Nepoužívá se.
Další informace naleznete v tématu Zpracování výstupu .
LATENCE_VZORKU_EC Určuje aktuální prodlevu při vykreslování vzorků. Pokud je hodnota kladná, vzorky jsou za plánem. Pokud je hodnota záporná, vzorky jsou v předstihu oproti plánu. (Volitelné.)
  • Param1: Ukazatel na konstantní hodnotu LONGLONG obsahující prodlevu v 100 nanosekundových jednotkách.
  • param2: Nepoužívá se.
EC_SCRUB_TIME Odesláno okamžitě po EC_STEP_COMPLETE, pokud je rychlost přehrávání nulová. Tato událost obsahuje časové razítko zobrazeného snímku.
  • Param1: Dolní 32 bitů časové značky.
  • Param2: horních 32 bitů časového razítka.
Další informace naleznete v tématu Krokování.
EC_KROK_DOKONČEN Prezentující dokončil nebo zrušil krok rámce.
- param1: Nepoužívá se.
- param2: Nepoužívá se.
Další informace naleznete v tématu Krokování.
Poznámka: Předchozí verze dokumentace popisovala Param1 nesprávně. Tento parametr se pro tuto událost nepoužívá.

 

Vyjednávání formátů

Kdykoli moderátor obdrží MFVP_MESSAGE_INVALIDATEMEDIATYPE zprávu z EVR, musí nastavit výstupní formát pro mixér následujícím způsobem:

  1. Pro získání možného typu výstupu volejte na mixéru IMFTransform::GetOutputAvailableType. Tento typ popisuje formát, který může mixér vytvořit vzhledem ke vstupním datovým proudům a schopnostem zpracování videa grafického zařízení.

  2. Zkontrolujte, jestli může prezentující použít tento typ média jako formát vykreslování. Tady je několik věcí, které je potřeba zkontrolovat, i když vaše implementace může mít vlastní požadavky:

    • Video musí být nekomprimované.
    • Video musí mít pouze progresivní snímky. Zkontrolujte, zda se atribut MF_MT_INTERLACE_MODE rovná MFVideoInterlace_Progressive.
    • Formát musí být kompatibilní se zařízením Direct3D.

    Pokud typ není přijatelný, vraťte se ke kroku 1 a získejte další navržený typ mixéru.

  3. Vytvořte nový typ média, který je klonem původního typu, a pak změňte následující atributy:

    • Nastavte atribut MF_MT_FRAME_SIZE, který se rovná šířce a výšce požadovaných pro plochy Direct3D, které přidělíte.
    • Nastavte atribut MF_MT_PAN_SCAN_ENABLED na hodnotu FALSE.
    • Nastavte atribut MF_MT_PIXEL_ASPECT_RATIO roven hodnotě PAR zobrazení (obvykle 1:1).
    • Nastavte geometrickou aperturu (MF_MT_GEOMETRIC_APERTURE atribut) rovnou obdélníku uvnitř povrchu Direct3D. Když mixér vygeneruje výstupní rámec, přenese zdrojový obraz na tento obdélník. Geometrická clona může být tak velká jako povrch, nebo může být podobdélníkem uvnitř povrchu. Další informace naleznete v tématu zdrojové a cílové obdélníky.
  4. Chcete-li otestovat, zda mixér přijme upravený výstupní typ, volejte IMFTransform::SetOutputType s vlajkou MFT_SET_TYPE_TEST_ONLY. Pokud mixér odmítne typ, vraťte se ke kroku 1 a získejte další typ.

  5. Přidělte fond ploch Direct3D, jak je popsáno v Přidělování povrchů Direct3D. Mixér použije tyto povrchy, když zobrazí složené video snímky.

  6. Nastavte typ výstupu v mixéru voláním SetOutputType bez příznaků. Pokud bylo první volání SetOutputType úspěšné v kroku 4, pak by měla být metoda úspěšná znovu.

Pokud mixér vyčerpá typy, metoda GetOutputAvailableType vrátí MF_E_NO_MORE_TYPES. Pokud prezentující nemůže najít vhodný typ výstupu pro mixér, stream nelze vykreslit. V takovém případě může DirectShow nebo Media Foundation vyzkoušet jiný formát datového proudu. Prezentující proto může obdržet několik zpráv MFVP_MESSAGE_INVALIDATEMEDIATYPE za sebou, dokud se nenajde platný typ.

Míchačka automaticky přidá černé pruhy k videu, přičemž bere v úvahu poměr stran pixelu (PAR) zdroje a cíle. Pro dosažení nejlepších výsledků by se měla šířka a výška povrchu a geometrická clona rovnat skutečné velikosti, ve které chcete, aby se video zobrazovalo na displeji. Tento proces znázorňuje následující obrázek.

diagram znázorňující složený rám, který vede k přímé 3D ploše, což vede k oknu

Následující kód ukazuje přehled procesu. Některé z kroků jsou umístěny v pomocných funkcích, přičemž konkrétní detaily budou záviset na požadavcích prezentátora.

HRESULT EVRCustomPresenter::RenegotiateMediaType()
{
    HRESULT hr = S_OK;
    BOOL bFoundMediaType = FALSE;

    IMFMediaType *pMixerType = NULL;
    IMFMediaType *pOptimalType = NULL;
    IMFVideoMediaType *pVideoType = NULL;

    if (!m_pMixer)
    {
        return MF_E_INVALIDREQUEST;
    }

    // Loop through all of the mixer's proposed output types.
    DWORD iTypeIndex = 0;
    while (!bFoundMediaType && (hr != MF_E_NO_MORE_TYPES))
    {
        SafeRelease(&pMixerType);
        SafeRelease(&pOptimalType);

        // Step 1. Get the next media type supported by mixer.
        hr = m_pMixer->GetOutputAvailableType(0, iTypeIndex++, &pMixerType);
        if (FAILED(hr))
        {
            break;
        }

        // From now on, if anything in this loop fails, try the next type,
        // until we succeed or the mixer runs out of types.

        // Step 2. Check if we support this media type.
        if (SUCCEEDED(hr))
        {
            // Note: None of the modifications that we make later in CreateOptimalVideoType
            // will affect the suitability of the type, at least for us. (Possibly for the mixer.)
            hr = IsMediaTypeSupported(pMixerType);
        }

        // Step 3. Adjust the mixer's type to match our requirements.
        if (SUCCEEDED(hr))
        {
            hr = CreateOptimalVideoType(pMixerType, &pOptimalType);
        }

        // Step 4. Check if the mixer will accept this media type.
        if (SUCCEEDED(hr))
        {
            hr = m_pMixer->SetOutputType(0, pOptimalType, MFT_SET_TYPE_TEST_ONLY);
        }

        // Step 5. Try to set the media type on ourselves.
        if (SUCCEEDED(hr))
        {
            hr = SetMediaType(pOptimalType);
        }

        // Step 6. Set output media type on mixer.
        if (SUCCEEDED(hr))
        {
            hr = m_pMixer->SetOutputType(0, pOptimalType, 0);

            assert(SUCCEEDED(hr)); // This should succeed unless the MFT lied in the previous call.

            // If something went wrong, clear the media type.
            if (FAILED(hr))
            {
                SetMediaType(NULL);
            }
        }

        if (SUCCEEDED(hr))
        {
            bFoundMediaType = TRUE;
        }
    }

    SafeRelease(&pMixerType);
    SafeRelease(&pOptimalType);
    SafeRelease(&pVideoType);

    return hr;
}

Další informace o typech médií videa naleznete v tématu Typy médií videa.

Správa zařízení Direct3D

Prezentující vytvoří zařízení Direct3D a zpracuje všechny ztráty zařízení během streamování. Prezentační modul také hostuje správce zařízení Direct3D, který umožňuje ostatním komponentám používat stejné zařízení. Například mixér používá zařízení Direct3D ke kombinování podstreamů, deinterlace a provádění úprav barev. Dekodéry můžou používat zařízení Direct3D pro akcelerované dekódování videa. (Další informace o akceleraci videa naleznete v tématu DirectX Video Acceleration 2.0.)

Pokud chcete nastavit zařízení Direct3D, proveďte následující kroky:

  1. Vytvořte objekt Direct3D voláním Direct3DCreate9 nebo Direct3DCreate9Ex.
  2. Vytvořte zařízení voláním IDirect3D9::CreateDevice nebo IDirect3D9Ex::CreateDevice.
  3. Vytvořte správce zařízení voláním DXVA2CreateDirect3DDeviceManager9.
  4. Nastavte zařízení ve správci zařízení voláním IDirect3DDeviceManager9::ResetDevice.

Pokud další komponenta kanálu potřebuje správce zařízení, volá IMFGetService::GetService na EVR a specifikuje MR_VIDEO_ACCELERATION_SERVICE pro identifikátor GUID služby. EVR předá žádost prezentujícímu. Jakmile objekt získá ukazatel IDirect3DDeviceManager9, může získat popisovač zařízení pomocí volání IDirect3DDeviceManager9::OpenDeviceHandle. Pokud objekt potřebuje použít zařízení, předá popisovač zařízení IDirect3DDeviceManager9::LockDevice metoda, která vrátí IDirect3DDevice9 ukazatel.

Po vytvoření zařízení, pokud prezentující zničí zařízení a vytvoří nový, musí prezentující znovu zavolat ResetDevice. Metoda ResetDevice zneplatní všechny existující popisovače zařízení, což způsobí, že LockDevice vrátí DXVA2_E_NEW_VIDEO_DEVICE. Tento kód chyby signalizuje ostatním objektům používajícím zařízení, že by měly otevřít nový popisovač zařízení. Další informace o použití správce zařízení naleznete v tématu Direct3D Device Manager.

Prezentující může zařízení vytvořit v režimu okna nebo ve výhradním režimu na celé obrazovce. V případě režimu okna byste měli aplikaci poskytnout způsob, jak určit okno videa. Standardní prezentátor implementuje IMFVideoDisplayControl::SetVideoWindow metodu pro tento účel. Zařízení musíte vytvořit, když je prezentér poprvé vytvořen. Obvykle nebudete znát všechny parametry zařízení v daný okamžik, například okno nebo formát zadní vyrovnávací paměti. Dočasné zařízení můžete vytvořit a později ho nahradit&#; nezapomeňte volat ResetDevice ve správci zařízení.

Pokud vytvoříte nové zařízení nebo zavoláte IDirect3DDevice9::Reset nebo IDirect3DDevice9Ex::ResetEx na existujícím zařízení, odešlete do EVR událost EC_DISPLAY_CHANGED. Tato událost upozorní EVR, aby znovu projednal typ média. EVR ignoruje parametry události pro tuto událost.

Přidělování povrchů Direct3D

Jakmile prezentující nastaví typ média, může přidělit plochy Direct3D, které mixér použije k zápisu snímků videa. Povrch musí odpovídat typu média prezentujícího:

  • Formát povrchu musí odpovídat podtypu média. Pokud je například podtyp MFVideoFormat_RGB24, musí být formát povrchu D3DFMT_X8R8G8B8. Další informace o podtypech a formátech Direct3D najdete v tématu identifikátory GUID podtypů videa.
  • Šířka a výška povrchu musí odpovídat rozměrům zadaným v MF_MT_FRAME_SIZE atributu typu média.

Doporučený způsob přidělování ploch závisí na tom, jestli prezentující spouští okno nebo celou obrazovku.

Je-li zařízení Direct3D v okněném režimu, můžete vytvořit několik řetězců výměny, z nichž každý má jeden zadní vyrovnávací buffer. Pomocí tohoto přístupu můžete prezentovat každou plochu nezávisle, protože prezentace jednoho swapového řetězce nebude rušit ostatní swapové řetězce. Mixér může zapisovat data na povrch, zatímco pro prezentaci je naplánován jiný povrch.

Nejprve se rozhodněte, kolik swapových řetězců se má vytvořit. Doporučuje se minimálně tři. Pro každý swap chain postupujte takto:

  1. Zavolejte IDirect3DDevice9::CreateAdditionalSwapChain k vytvoření přídavného řetězce prohození.
  2. Zavolejte IDirect3DSwapChain9::GetBackBuffer, abyste získali ukazatel na povrch zadní vyrovnávací paměti řetězce výměny.
  3. Zavolejte MFCreateVideoSampleFromSurface a předejte ukazatel na povrch. Tato funkce vrátí ukazatel na ukázkový objekt videa. Ukázkový objekt videa implementuje rozhraní IMFSample a prezentující používá toto rozhraní k dodání povrchu mixéru, když volá metodu mixéru IMFTransform::ProcessOutput. Další informace o ukázkovém objektu videa najdete v tématu Ukázky videa.
  4. Uložte ukazatel MMFSample do fronty. Prezentátor během zpracování odebere vzorky z této fronty, jak je popsáno v Zpracování výstupu.
  5. Ponechte odkaz na ukazatel IDirect3DSwapChain9, aby se řetězec prohození neuvolnil.

Ve výhradním režimu zobrazení na celé obrazovce nemůže mít zařízení více než jeden swap chain. Tento řetězec výměny se vytvoří automaticky při vytváření zařízení v režimu celé obrazovky. Swap chain může mít více než jeden zadní buffer. Pokud však prezentujete jednu zadní vyrovnávací paměť při zápisu do jiné zadní vyrovnávací paměti ve stejném řetězci výměny, neexistuje snadný způsob, jak tyto dvě operace koordinovat. Důvodem je způsob, jakým Direct3D implementuje překlopení povrchu. Když zavoláte funkci Present, ovladač grafiky aktualizuje ukazatele na povrchy v grafické paměti. Pokud při volání Presentdržíte nějaké ukazatele IDirect3DSurface9, po návratu z tohoto volání Present budou tyto ukazatele odkazovat na různé vyrovnávací paměti.

Nejjednodušší možností je vytvořit jednu ukázku videa pro swap chain. Pokud zvolíte tuto možnost, postupujte podle stejných kroků pro režim okna. Jediným rozdílem je, že ukázková fronta obsahuje jednu ukázku videa. Další možností je vytvořit povrchy mimo obrazovku a pak je přenést do zadní vyrovnávací paměti. Povrchy, které vytvoříte, musí podporovat metodu IDirectXVideoProcessor::VideoProcessBlt, kterou mixér používá ke složení výstupních snímků.

Sledování vzorků

Když přednášející poprvé přidělí ukázky videa, umístí je do fronty. Prezentér načte z této fronty pokaždé, když potřebuje získat nový snímek z mixéru. Po výstupu mixéru snímek prezentující přesune ukázku do druhé fronty. Druhá fronta je určená pro ukázky, které čekají na naplánované doby prezentace.

Aby bylo snazší sledovat stav jednotlivých ukázek, ukázkový objekt videa implementuje rozhraní MMFTrackedSample. Toto rozhraní můžete použít následujícím způsobem:

  1. Implementujte rozhraní IMFAsyncCallback ve svém prezentujícím.

  2. Před umístěním ukázky do naplánované fronty zadejte dotaz na ukázkový objekt videa pro rozhraní IMFTrackedSample.

  3. Volání IMFTrackedSample::SetAllocator s ukazatelem na vaše rozhraní zpětného volání.

  4. Až bude ukázka připravená k prezentaci, odeberte ji z plánované fronty, prezentujte ji a uvolněte všechny odkazy na ukázku.

  5. Ukázka vyvolá zpětné volání. (V tomto případě se ukázkový objekt neodstraní, protože obsahuje počet odkazů na sebe, dokud se nevyvolá zpětné volání.)

  6. Uvnitř callbacku vraťte vzorek do dostupné fronty.

Prezentující není povinen používat IMFTrackedSample ke sledování vzorků; můžete implementovat jakoukoli techniku, která nejlépe vyhovuje vašemu návrhu. Jednou z výhod IMFTrackedSample je, že můžete přesunout funkce plánování a vykreslování prezentujícího do pomocných objektů, a tyto objekty nepotřebují žádný speciální mechanismus pro navázání zpětné komunikace s prezentujícím při uvolnění vzorků videa, protože tento mechanismus poskytuje samotný vzorový objekt.

Následující kód ukazuje, jak nastavit zpětné volání:

HRESULT EVRCustomPresenter::TrackSample(IMFSample *pSample)
{
    IMFTrackedSample *pTracked = NULL;

    HRESULT hr = pSample->QueryInterface(IID_PPV_ARGS(&pTracked));

    if (SUCCEEDED(hr))
    {
        hr = pTracked->SetAllocator(&m_SampleFreeCB, NULL);
    }

    SafeRelease(&pTracked);
    return hr;
}

Ve zpětném volání volejte IMFAsyncResult::GetObject na asynchronním objektu výsledku a získejte ukazatel na vzorek:

HRESULT EVRCustomPresenter::OnSampleFree(IMFAsyncResult *pResult)
{
    IUnknown *pObject = NULL;
    IMFSample *pSample = NULL;
    IUnknown *pUnk = NULL;

    // Get the sample from the async result object.
    HRESULT hr = pResult->GetObject(&pObject);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pObject->QueryInterface(IID_PPV_ARGS(&pSample));
    if (FAILED(hr))
    {
        goto done;
    }

    // If this sample was submitted for a frame-step, the frame step operation
    // is complete.

    if (m_FrameStep.state == FRAMESTEP_SCHEDULED)
    {
        // Query the sample for IUnknown and compare it to our cached value.
        hr = pSample->QueryInterface(IID_PPV_ARGS(&pUnk));
        if (FAILED(hr))
        {
            goto done;
        }

        if (m_FrameStep.pSampleNoRef == (DWORD_PTR)pUnk)
        {
            // Notify the EVR.
            hr = CompleteFrameStep(pSample);
            if (FAILED(hr))
            {
                goto done;
            }
        }

        // Note: Although pObject is also an IUnknown pointer, it is not
        // guaranteed to be the exact pointer value returned through
        // QueryInterface. Therefore, the second QueryInterface call is
        // required.
    }

    /*** Begin lock ***/

    EnterCriticalSection(&m_ObjectLock);

    UINT32 token = MFGetAttributeUINT32(
        pSample, MFSamplePresenter_SampleCounter, (UINT32)-1);

    if (token == m_TokenCounter)
    {
        // Return the sample to the sample pool.
        hr = m_SamplePool.ReturnSample(pSample);
        if (SUCCEEDED(hr))
        {
            // A free sample is available. Process more data if possible.
            (void)ProcessOutputLoop();
        }
    }

    LeaveCriticalSection(&m_ObjectLock);

    /*** End lock ***/

done:
    if (FAILED(hr))
    {
        NotifyEvent(EC_ERRORABORT, hr, 0);
    }
    SafeRelease(&pObject);
    SafeRelease(&pSample);
    SafeRelease(&pUnk);
    return hr;
}

Zpracování výstupu

Pokaždé, když mixér obdrží novou vstupní ukázku, odešle EVR prezentujícímu zprávu MFVP_MESSAGE_PROCESSINPUTNOTIFY. Tato zpráva značí, že mixér může mít k dispozici nový snímek videa. V reakci prezentující volá IMFTransform::ProcessOutput na mixéru. Pokud je metoda úspěšná, prezentující naplánuje ukázku prezentace.

Pokud chcete získat výstup z mixéru, proveďte následující kroky:

  1. Zkontrolujte stav hodin. Pokud jsou hodiny pozastavené, ignorujte MFVP_MESSAGE_PROCESSINPUTNOTIFY zprávu, pokud se nejedná o první snímek videa. Pokud hodiny běží nebo pokud se jedná o první snímek videa, pokračujte.

  2. Získejte ukázku z fronty dostupných ukázek. Pokud je fronta prázdná, znamená to, že pro prezentaci jsou aktuálně naplánované všechny přidělené vzorky. V takovém případě momentálně ignorujte zprávu MFVP_MESSAGE_PROCESSINPUTNOTIFY. Až bude k dispozici další ukázka, opakujte zde uvedené kroky.

  3. (Volitelné.) Pokud jsou hodiny k dispozici, získejte aktuální hodiny (T1) voláním MMFClock::GetCorrelatedTime.

  4. Zavolejte IMFTransform::ProcessOutput na mixeru. Pokud ProcessOutput proběhne úspěšně, ukázka obsahuje snímek videa. Pokud metoda selže, zkontrolujte návratový kód. Následující kódy chyb z ProcessOutput nejsou kritické chyby:

    Kód chyby Popis
    MF_E_TRANSFORM_NEED_MORE_INPUT Mixér potřebuje ještě více vstupu, aby mohl vytvořit nový výstupní rámec.
    Pokud se zobrazí tento kód chyby, zkontrolujte, zda EVR dosáhl konce datového proudu, a reagujte odpovídajícím způsobem, jak je popsáno v Konec streamu. Jinak tuto zprávu MF_E_TRANSFORM_NEED_MORE_INPUT ignorujte. EVR pošle další, když mixér získá více vstupu.
    MF_E_TRANSFORM_STREAM_CHANGE Výstupní typ mixéru je neplatný, pravděpodobně kvůli změně formátu upstreamu.
    Pokud se zobrazí tento kód chyby, nastavte typ média prezentujícího na NULL. EVR požádá o nový formát.
    MF_E_TRANSFORM_TYPE_NOT_SET Mixér vyžaduje nový typ média.
    Pokud obdržíte tento chybový kód, znovu sjednejte výstupní typ mixéru, jak je uvedeno v Vyjednávání formátů.

     

    Pokud ProcessOutput proběhne úspěšně, pokračujte.

  5. (Volitelné.) Pokud jsou hodiny k dispozici, získejte aktuální čas (T2). Množství latence zavedené mixérem je (T2 - T1). Odešlete EC_PROCESSING_LATENCY událost s touto hodnotou do EVR. EVR používá tuto hodnotu pro kontrolu kvality. Pokud nejsou k dispozici žádné hodiny, není důvod odeslat událost EC_PROCESSING_LATENCY.

  6. (Volitelné.) Dotaz na ukázku pro IMFTrackedSample a volání IMFTrackedSample::SetAllocator, jak je popsáno v Sledování Ukázek.

  7. Naplánujte ukázku prezentace.

Tato posloupnost kroků se může ukončit před tím, než prezentující získá jakýkoli výstup z mixéru. Pokud chcete zajistit, aby nedošlo k vyřazení žádných požadavků, měli byste stejný postup zopakovat v následujících případech:

  • Je volána metoda IMFClockStateSink::OnClockStart nebo IMFClockStateSink::OnClockStart. To zpracovává případ, kdy mixér ignoruje vstup, protože hodiny jsou pozastavené (krok 1).
  • Vyvolá se IMFTrackedSample zpětné volání. To zvládá případ, kdy mixér přijímá vstup, ale všechny videoukázky prezentujícího se používají (krok 2).

Další několik příkladů kódu ukazuje tyto kroky podrobněji. Prezentér volá metodu ProcessInputNotify, jakmile obdrží zprávu MFVP_MESSAGE_PROCESSINPUTNOTIFY, jak je zobrazeno v následujícím příkladu.

//-----------------------------------------------------------------------------
// ProcessInputNotify
//
// Attempts to get a new output sample from the mixer.
//
// This method is called when the EVR sends an MFVP_MESSAGE_PROCESSINPUTNOTIFY
// message, which indicates that the mixer has a new input sample.
//
// Note: If there are multiple input streams, the mixer might not deliver an
// output sample for every input sample.
//-----------------------------------------------------------------------------

HRESULT EVRCustomPresenter::ProcessInputNotify()
{
    HRESULT hr = S_OK;

    // Set the flag that says the mixer has a new sample.
    m_bSampleNotify = TRUE;

    if (m_pMediaType == NULL)
    {
        // We don't have a valid media type yet.
        hr = MF_E_TRANSFORM_TYPE_NOT_SET;
    }
    else
    {
        // Try to process an output sample.
        ProcessOutputLoop();
    }
    return hr;
}

Tato ProcessInputNotify metoda nastaví logický příznak pro zaznamenání skutečnosti, že mixér má nový vstup. Potom volá metodu ProcessOutputLoop, která je znázorněna v dalším příkladu. Tato metoda se pokusí vytáhnout co nejvíce vzorků z mixéru:

void EVRCustomPresenter::ProcessOutputLoop()
{
    HRESULT hr = S_OK;

    // Process as many samples as possible.
    while (hr == S_OK)
    {
        // If the mixer doesn't have a new input sample, break from the loop.
        if (!m_bSampleNotify)
        {
            hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
            break;
        }

        // Try to process a sample.
        hr = ProcessOutput();

        // NOTE: ProcessOutput can return S_FALSE to indicate it did not
        // process a sample. If so, break out of the loop.
    }

    if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
    {
        // The mixer has run out of input data. Check for end-of-stream.
        CheckEndOfStream();
    }
}

Metoda ProcessOutput, jak je znázorněno v dalším příkladu, se pokusí získat jeden snímek videa z mixéru. Pokud není k dispozici žádný snímek videa, ProcessSample vrátí S_FALSE nebo kód chyby, který v obou případech přeruší běh smyčky v ProcessOutputLoop. Většina práce se provádí uvnitř metody ProcessOutput:

//-----------------------------------------------------------------------------
// ProcessOutput
//
// Attempts to get a new output sample from the mixer.
//
// Called in two situations:
// (1) ProcessOutputLoop, if the mixer has a new input sample.
// (2) Repainting the last frame.
//-----------------------------------------------------------------------------

HRESULT EVRCustomPresenter::ProcessOutput()
{
    assert(m_bSampleNotify || m_bRepaint);  // See note above.

    HRESULT     hr = S_OK;
    DWORD       dwStatus = 0;
    LONGLONG    mixerStartTime = 0, mixerEndTime = 0;
    MFTIME      systemTime = 0;
    BOOL        bRepaint = m_bRepaint; // Temporarily store this state flag.

    MFT_OUTPUT_DATA_BUFFER dataBuffer;
    ZeroMemory(&dataBuffer, sizeof(dataBuffer));

    IMFSample *pSample = NULL;

    // If the clock is not running, we present the first sample,
    // and then don't present any more until the clock starts.

    if ((m_RenderState != RENDER_STATE_STARTED) &&  // Not running.
         !m_bRepaint &&             // Not a repaint request.
         m_bPrerolled               // At least one sample has been presented.
         )
    {
        return S_FALSE;
    }

    // Make sure we have a pointer to the mixer.
    if (m_pMixer == NULL)
    {
        return MF_E_INVALIDREQUEST;
    }

    // Try to get a free sample from the video sample pool.
    hr = m_SamplePool.GetSample(&pSample);
    if (hr == MF_E_SAMPLEALLOCATOR_EMPTY)
    {
        // No free samples. Try again when a sample is released.
        return S_FALSE;
    }
    else if (FAILED(hr))
    {
        return hr;
    }

    // From now on, we have a valid video sample pointer, where the mixer will
    // write the video data.
    assert(pSample != NULL);

    // (If the following assertion fires, it means we are not managing the sample pool correctly.)
    assert(MFGetAttributeUINT32(pSample, MFSamplePresenter_SampleCounter, (UINT32)-1) == m_TokenCounter);

    if (m_bRepaint)
    {
        // Repaint request. Ask the mixer for the most recent sample.
        SetDesiredSampleTime(
            pSample,
            m_scheduler.LastSampleTime(),
            m_scheduler.FrameDuration()
            );

        m_bRepaint = FALSE; // OK to clear this flag now.
    }
    else
    {
        // Not a repaint request. Clear the desired sample time; the mixer will
        // give us the next frame in the stream.
        ClearDesiredSampleTime(pSample);

        if (m_pClock)
        {
            // Latency: Record the starting time for ProcessOutput.
            (void)m_pClock->GetCorrelatedTime(0, &mixerStartTime, &systemTime);
        }
    }

    // Now we are ready to get an output sample from the mixer.
    dataBuffer.dwStreamID = 0;
    dataBuffer.pSample = pSample;
    dataBuffer.dwStatus = 0;

    hr = m_pMixer->ProcessOutput(0, 1, &dataBuffer, &dwStatus);

    if (FAILED(hr))
    {
        // Return the sample to the pool.
        HRESULT hr2 = m_SamplePool.ReturnSample(pSample);
        if (FAILED(hr2))
        {
            hr = hr2;
            goto done;
        }
        // Handle some known error codes from ProcessOutput.
        if (hr == MF_E_TRANSFORM_TYPE_NOT_SET)
        {
            // The mixer's format is not set. Negotiate a new format.
            hr = RenegotiateMediaType();
        }
        else if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
        {
            // There was a dynamic media type change. Clear our media type.
            SetMediaType(NULL);
        }
        else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
        {
            // The mixer needs more input.
            // We have to wait for the mixer to get more input.
            m_bSampleNotify = FALSE;
        }
    }
    else
    {
        // We got an output sample from the mixer.

        if (m_pClock && !bRepaint)
        {
            // Latency: Record the ending time for the ProcessOutput operation,
            // and notify the EVR of the latency.

            (void)m_pClock->GetCorrelatedTime(0, &mixerEndTime, &systemTime);

            LONGLONG latencyTime = mixerEndTime - mixerStartTime;
            NotifyEvent(EC_PROCESSING_LATENCY, (LONG_PTR)&latencyTime, 0);
        }

        // Set up notification for when the sample is released.
        hr = TrackSample(pSample);
        if (FAILED(hr))
        {
            goto done;
        }

        // Schedule the sample.
        if ((m_FrameStep.state == FRAMESTEP_NONE) || bRepaint)
        {
            hr = DeliverSample(pSample, bRepaint);
            if (FAILED(hr))
            {
                goto done;
            }
        }
        else
        {
            // We are frame-stepping (and this is not a repaint request).
            hr = DeliverFrameStepSample(pSample);
            if (FAILED(hr))
            {
                goto done;
            }
        }

        m_bPrerolled = TRUE; // We have presented at least one sample now.
    }

done:
    SafeRelease(&pSample);

    // Important: Release any events returned from the ProcessOutput method.
    SafeRelease(&dataBuffer.pEvents);
    return hr;
}

Několik poznámek k tomuto příkladu:

  • Proměnná m_SamplePool se předpokládá jako objekt kolekce, který obsahuje frontu dostupných ukázek videa. Metoda GetSample objektu vrátí MF_E_SAMPLEALLOCATOR_EMPTY, pokud je fronta prázdná.
  • Pokud metoda mixéru ProcessOutput vrátí MF_E_TRANSFORM_NEED_MORE_INPUT, znamená to, že mixér nemůže vytvořit žádný další výstup, a tak prezentující vymaže příznak m_fSampleNotify.
  • Metoda TrackSample, která nastavuje MMFTrackedSample zpětné volání, je uvedena v části Tracking Samples.

Přemalování rámů

Někdy může prezentující potřebovat překreslit nejnovější snímek videa. Například standardní prezentující překreslí rámeček v následujících situacích:

Pomocí následujících kroků požádejte mixér o opětovné vytvoření nejnovějšího snímku.

  1. Získejte ukázku videa z fronty.
  2. Zadejte dotaz na ukázku pro rozhraní MMFDesiredSample.
  3. Volání MMFDesiredSample::SetDesiredSampleTimeAndDuration. Zadejte časové razítko posledního rámečku videa. (Tuto hodnotu budete muset uložit do mezipaměti a aktualizovat ji pro každý rámec.)
  4. Volání ProcessOutput na mixéru.

Při překreslení rámečku můžete ignorovat hodiny prezentace a okamžitě ho prezentovat.

Ukázky plánování

Snímky videa mohou kdykoli dorazit k EVR. Moderátor je zodpovědný za prezentaci každého snímku ve správný čas na základě časového razítka snímku. Když moderátor získá z mixéru novou ukázku, vloží ji do předem naplánované fronty. V samostatném vlákně získává prezentující osoba průběžně první vzorek z hlavy fronty a určuje, zda:

  • Představte ukázku.
  • Ponechte ukázku ve frontě, protože je brzy.
  • Zahoďte ukázku, protože je pozdě. I když byste se měli vyhnout vypouštění snímků, pokud je to možné, možná budete muset, pokud prezentující neustále zaostává.

Pokud chcete získat časové razítko pro snímek videa, zavolejte IMFSample::GetSampleTime v ukázce videa. Časové razítko je relativní vzhledem k prezentačním hodinám EVR. Chcete-li získat aktuální hodinový čas, zavolejte MMFClock::GetCorrelatedTime. Pokud EVR nemá hodiny prezentace nebo pokud ukázka nemá časový údaj, můžete ukázku prezentovat hned po jejím obdržení.

Chcete-li získat dobu trvání každého vzorku, zavolejte MMFSample::GetSampleDuration. Pokud ukázka nemá dobu trvání, můžete použít funkci MFFrameRateToAverageTimePerFrame k výpočtu doby trvání z frekvence snímků.

Při plánování ukázek mějte na paměti následující skutečnosti:

  • Pokud je rychlost přehrávání rychlejší nebo pomalejší než normální rychlost, hodiny běží rychleji nebo pomaleji. To znamená, že časová značka vzorku vždy dává správný cílový čas vzhledem k prezentačním hodinám. Pokud ale přeložíte časy prezentace do jiného času (například čítač výkonu s vysokým rozlišením), musíte časy škálovat na základě rychlosti hodin. Pokud se změní rychlost hodin, EVR volá metodu IMFClockStateSink::OnClockSetRate.
  • Rychlost přehrávání může být záporná pro zpětné přehrávání. Pokud je rychlost přehrávání záporná, hodiny prezentace se posunou dozadu. Jinými slovy, čas N + 1 nastane před časem N.

Následující příklad vypočítá, jak brzy nebo pozdě je vzorek vzhledem k hodinám prezentace:

    LONGLONG hnsPresentationTime = 0;
    LONGLONG hnsTimeNow = 0;
    MFTIME   hnsSystemTime = 0;

    BOOL bPresentNow = TRUE;
    LONG lNextSleep = 0;

    if (m_pClock)
    {
        // Get the sample's time stamp. It is valid for a sample to
        // have no time stamp.
        hr = pSample->GetSampleTime(&hnsPresentationTime);

        // Get the clock time. (But if the sample does not have a time stamp,
        // we don't need the clock time.)
        if (SUCCEEDED(hr))
        {
            hr = m_pClock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime);
        }

        // Calculate the time until the sample's presentation time.
        // A negative value means the sample is late.
        LONGLONG hnsDelta = hnsPresentationTime - hnsTimeNow;
        if (m_fRate < 0)
        {
            // For reverse playback, the clock runs backward. Therefore, the
            // delta is reversed.
            hnsDelta = - hnsDelta;
        }

Hodiny prezentace obvykle řídí systémové hodiny nebo renderer zvuku. (Zvukový renderer odvodí čas od rychlosti, s jakou zvuková karta spotřebovává zvuk.) Obecně platí, že prezentační hodiny se nesynchronizují s obnovovací rychlostí monitoru.

Pokud parametry prezentace Direct3D určují D3DPRESENT_INTERVAL_DEFAULT nebo D3DPRESENT_INTERVAL_ONE pro interval prezentace, operace Prezentovat čeká na vertikální trasování monitoru. Jedná se o snadný způsob, jak zabránit slzám, ale snižuje přesnost vašeho plánovacího algoritmu. Naopak, pokud je interval prezentace D3DPRESENT_INTERVAL_IMMEDIATE, metoda Present se provádí okamžitě, což způsobí trhání obrazovky, pokud váš plánovací algoritmus není dostatečně přesný natolik, aby jste volali Present pouze během vertikálního zpětného chodu.

Následující funkce vám můžou pomoct získat přesné informace o načasování:

  • IDirect3DDevice9::GetRasterStatus vrátí informace o rasteru, včetně aktuální čáry skenování a zda rastr je ve svislém prázdném období.
  • DwmGetCompositionTimingInfo vrátí informace o časování správce okna plochy. Tyto informace jsou užitečné, pokud je povolena kompozice pracovní plochy.

Prezentace ukázek

V této části se předpokládá, že jste pro každý povrch vytvořili samostatný swap chain, jak je popsáno v Přidělování povrchů Direct3D. Chcete-li prezentovat vzorek, získejte z video vzorku řetězec výměny následujícím způsobem:

  1. Zavolejte MMFSample::GetBufferByIndex na ukázce videa, abyste získali vyrovnávací paměť.
  2. Zadejte dotaz na vyrovnávací paměť pro rozhraní MMFGetService.
  3. Zavolejte IMFGetService::GetService pro získání rozhraní IDirect3DSurface9 Direct3D povrchu. (Tento krok a předchozí krok můžete zkombinovat do jednoho voláním MFGetService.)
  4. Zavolejte IDirect3DSurface9::GetContainer na povrchu, abyste získali ukazatel na swap chain.
  5. Volání IDirect3DSwapChain9::Present na swap chain.

Následující kód ukazuje následující kroky:

HRESULT D3DPresentEngine::PresentSample(IMFSample* pSample, LONGLONG llTarget)
{
    HRESULT hr = S_OK;

    IMFMediaBuffer* pBuffer = NULL;
    IDirect3DSurface9* pSurface = NULL;
    IDirect3DSwapChain9* pSwapChain = NULL;

    if (pSample)
    {
        // Get the buffer from the sample.
        hr = pSample->GetBufferByIndex(0, &pBuffer);
        if (FAILED(hr))
        {
            goto done;
        }

        // Get the surface from the buffer.
        hr = MFGetService(pBuffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&pSurface));
        if (FAILED(hr))
        {
            goto done;
        }
    }
    else if (m_pSurfaceRepaint)
    {
        // Redraw from the last surface.
        pSurface = m_pSurfaceRepaint;
        pSurface->AddRef();
    }

    if (pSurface)
    {
        // Get the swap chain from the surface.
        hr = pSurface->GetContainer(IID_PPV_ARGS(&pSwapChain));
        if (FAILED(hr))
        {
            goto done;
        }

        // Present the swap chain.
        hr = PresentSwapChain(pSwapChain, pSurface);
        if (FAILED(hr))
        {
            goto done;
        }

        // Store this pointer in case we need to repaint the surface.
        CopyComPointer(m_pSurfaceRepaint, pSurface);
    }
    else
    {
        // No surface. All we can do is paint a black rectangle.
        PaintFrameWithGDI();
    }

done:
    SafeRelease(&pSwapChain);
    SafeRelease(&pSurface);
    SafeRelease(&pBuffer);

    if (FAILED(hr))
    {
        if (hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET || hr == D3DERR_DEVICEHUNG)
        {
            // We failed because the device was lost. Fill the destination rectangle.
            PaintFrameWithGDI();

            // Ignore. We need to reset or re-create the device, but this method
            // is probably being called from the scheduler thread, which is not the
            // same thread that created the device. The Reset(Ex) method must be
            // called from the thread that created the device.

            // The presenter will detect the state when it calls CheckDeviceState()
            // on the next sample.
            hr = S_OK;
        }
    }
    return hr;
}

Zdrojové a cílové obdélníky

zdrojový obdélník je část rámečku videa, která se má zobrazit. Definuje se vzhledem k normalizovanému souřadnicovém systému, ve kterém celý obrazový rámeček zabírá obdélník se souřadnicemi {0, 0, 1, 1}. cílový obdélník je oblast na cílové ploše, kde je vykreslen snímek videa. Standardní prezentující umožňuje aplikaci nastavit tyto obdélníky voláním MMFVideoDisplayControl::SetVideoPosition.

Existuje několik možností použití zdrojových a cílových obdélníků. První možností je nechat mixér je použít.

  • Nastavte zdrojový obdélník pomocí atributu VIDEO_ZOOM_RECT. Mixér použije zdrojový obdélník při blití videa na cílový povrch. Výchozí zdrojový obdélník mixéru je celý rámec.
  • Nastavte cílový obdélník jako geometrický otvor ve výstupním typu mixéru. Další informace naleznete v tématu Vyjednávání formátů.

Druhou možností je použít obdélníky při IDirect3DSwapChain9::Present zadáním parametrů pSourceRect a pDestRect v metodě Present. Tyto možnosti můžete kombinovat. Můžete například nastavit zdrojový obdélník na mixéru, ale použít cílový obdélník v metodě Present.

Pokud aplikace změní cílový obdélník nebo změní velikost okna, budete možná muset přidělit nové plochy. Pokud ano, musíte být opatrní při synchronizaci této operace s plánovacím vláknem. Vyprázdněte plánovací frontu a zahoďte staré vzorky před přidělením nových ploch.

Konec streamu

Když skončí každý vstupní datový proud v EVR, odešle EVR prezentujícímu zprávu MFVP_MESSAGE_ENDOFSTREAM. V okamžiku, kdy zprávu obdržíte, však může zůstat několik snímků videa, které se mají zpracovat. Než odpovíte na zprávu koncového proudu, musíte vyprázdnit veškerý výstup z mixéru a zobrazit všechny zbývající snímky. Po zobrazení posledního rámce odešlete EC_COMPLETE událost do EVR.

Následující příklad ukazuje metodu, která odešle EC_COMPLETE událost, pokud jsou splněny různé podmínky. V opačném případě vrátí S_OK bez odeslání události:

HRESULT EVRCustomPresenter::CheckEndOfStream()
{
    if (!m_bEndStreaming)
    {
        // The EVR did not send the MFVP_MESSAGE_ENDOFSTREAM message.
        return S_OK;
    }

    if (m_bSampleNotify)
    {
        // The mixer still has input.
        return S_OK;
    }

    if (m_SamplePool.AreSamplesPending())
    {
        // Samples are still scheduled for rendering.
        return S_OK;
    }

    // Everything is complete. Now we can tell the EVR that we are done.
    NotifyEvent(EC_COMPLETE, (LONG_PTR)S_OK, 0);
    m_bEndStreaming = FALSE;
    return S_OK;
}

Tato metoda zkontroluje následující stavy:

  • Pokud je proměnná m_fSampleNotifyTRUE, znamená to, že mixér má jeden nebo více snímků, které ještě nebyly zpracovány. (Podrobnosti najdete v tématu výstup zpracování.)
  • Proměnná m_fEndStreaming je booleovský příznak, jehož počáteční hodnota je FALSE. Prezentující nastaví příznak na TRUE, když EVR odešle zprávu MFVP_MESSAGE_ENDOFSTREAM.
  • Předpokládá se, že metoda AreSamplesPending vrací TRUE, dokud jeden nebo více snímků čeká v naplánované frontě.

V IMFVideoPresenter::ProcessMessage metodě, nastavte m_fEndStreaming na TRUE a zavolejte CheckEndOfStream, když EVR odešle MFVP_MESSAGE_ENDOFSTREAM zprávu:

HRESULT EVRCustomPresenter::ProcessMessage(
    MFVP_MESSAGE_TYPE eMessage,
    ULONG_PTR ulParam
    )
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_ObjectLock);

    hr = CheckShutdown();
    if (FAILED(hr))
    {
        goto done;
    }

    switch (eMessage)
    {
    // Flush all pending samples.
    case MFVP_MESSAGE_FLUSH:
        hr = Flush();
        break;

    // Renegotiate the media type with the mixer.
    case MFVP_MESSAGE_INVALIDATEMEDIATYPE:
        hr = RenegotiateMediaType();
        break;

    // The mixer received a new input sample.
    case MFVP_MESSAGE_PROCESSINPUTNOTIFY:
        hr = ProcessInputNotify();
        break;

    // Streaming is about to start.
    case MFVP_MESSAGE_BEGINSTREAMING:
        hr = BeginStreaming();
        break;

    // Streaming has ended. (The EVR has stopped.)
    case MFVP_MESSAGE_ENDSTREAMING:
        hr = EndStreaming();
        break;

    // All input streams have ended.
    case MFVP_MESSAGE_ENDOFSTREAM:
        // Set the EOS flag.
        m_bEndStreaming = TRUE;
        // Check if it's time to send the EC_COMPLETE event to the EVR.
        hr = CheckEndOfStream();
        break;

    // Frame-stepping is starting.
    case MFVP_MESSAGE_STEP:
        hr = PrepareFrameStep(LODWORD(ulParam));
        break;

    // Cancels frame-stepping.
    case MFVP_MESSAGE_CANCELSTEP:
        hr = CancelFrameStep();
        break;

    default:
        hr = E_INVALIDARG; // Unknown message. This case should never occur.
        break;
    }

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

Kromě toho zavolejte CheckEndOfStream, pokud metoda mixéru IMFTransform::ProcessOutput vrátí MF_E_TRANSFORM_NEED_MORE_INPUT. Tento kód chyby označuje, že mixér nemá další vstupní vzorky (viz Zpracování výstupu).

Postupování snímků

EVR je navržen tak, aby podporoval postupné přehrávání snímků v DirectShow a procházení v Media Foundation. Krokování rámečku a scrubbing jsou koncepčně podobné. V obou případech aplikace požádá o jeden snímek videa najednou. Prezentující interně používá stejný mechanismus k implementaci obou funkcí.

Krokování snímků v DirectShow funguje následujícím způsobem:

  • Aplikace volá IVideoFrameStep::Step. Počet kroků je uveden v parametru dwSteps. EVR odešle prezentujícímu zprávu MFVP_MESSAGE_STEP, kde parametr zprávy (ulParam) je počet kroků.
  • Pokud aplikace volá IVideoFrameStep::CancelStep nebo změní stav grafu (spuštěno, pozastaveno nebo zastaveno), EVR odešle MFVP_MESSAGE_CANCELSTEP zprávu.

Scrubbing v rámci Media Foundation funguje takto:

  • Aplikace nastaví rychlost přehrávání na nulu voláním MMFRateControl::SetRate.
  • Pokud chcete vykreslit nový rámec, aplikace volá MMFMediaSession::Start s požadovanou pozicí. EVR odešle MFVP_MESSAGE_STEP zprávu s ulParam rovna 1.
  • Pro zastavení čištění aplikace nastaví rychlost přehrávání na nenulovou hodnotu. EVR odešle zprávu MFVP_MESSAGE_CANCELSTEP.

Po přijetí zprávy MFVP_MESSAGE_STEP prezentující osoba čeká, až dorazí cílový rámec. Pokud je počet kroků N, prezentující zahodí další vzorky (N - 1) a zobrazí N th vzorek. Když prezentující dokončí krok snímku, odešle událost EC_STEP_COMPLETE k EVR, přičemž lParam1 je nastavený na FALSE. Pokud je rychlost přehrávání nula, prezentující odešle událost EC_SCRUB_TIME. Pokud EVR zruší krokování rámce, zatímco operace krokování rámce stále čeká na vyřízení, prezentující odešle událost EC_STEP_COMPLETE s lParam1 nastaveno na TRUE.

Aplikace může několikrát zarámovat krok nebo scrubovat, takže prezentující může dostat více MFVP_MESSAGE_STEP zpráv, než dostane MFVP_MESSAGE_CANCELSTEP zprávu. Prezentující může také obdržet MFVP_MESSAGE_STEP zprávu před spuštěním hodin nebo během běhu hodin.

Implementace krokového průběhu snímků

Tato část popisuje algoritmus pro realizaci postupování po snímcích. Algoritmus krokování rámce používá následující proměnné:

  • počet_kroků. Celé číslo bez znaménka, které určuje počet kroků v aktuální operaci krokování rámce.

  • step_queue. Fronta ukazatelů IMFSample.

  • step_state. Prezentující může kdykoli být v jednom z následujících stavů ohledně postupného posouvání rámce.

    Stát Popis
    NEKROKUJE Není posunování rámečku.
    ČEKÁNÍ Prezentující obdržel zprávu MFVP_MESSAGE_STEP, ale hodiny se nespustily.
    ČEKÁ SE Prezentující obdržel zprávu MFVP_MESSAGE_STEP a časovač se spustil, ale prezentující čeká na přijetí cílového snímku.
    NAPLÁNOVÁNO Prezentující obdržel cílový rámec a naplánoval ho pro prezentaci, ale snímek nebyl prezentován.
    KOMPLETNÍ Prezentující zobrazil cílový rámec, odeslal událost EC_STEP_COMPLETE a čeká na další zprávu MFVP_MESSAGE_STEP nebo MFVP_MESSAGE_CANCELSTEP.

     

    Tyto stavy jsou nezávislé na stavech přednášejícího uvedených v části Státy přednášejícího.

Následující postupy jsou definovány pro algoritmus krokování rámců:

PrepareFrameStep – postup

  1. Zvýšení počet_kroků.
  2. Nastavte step_state na ČEKÁNÍ.
  3. Pokud jsou hodiny spuštěné, zavolejte StartFrameStep.

Procedura StartFrameStep

  1. Pokud se step_state rovná čekání, nastavte step_state na ČEKAJÍCÍ. Pro každou ukázku v step_queuevolejte DeliverFrameStepSample.
  2. Pokud se step_state rovná NOT_STEPPING, odeberte všechny vzorky z step_queue a naplánujte jejich prezentaci.

CompleteFrameStep – procedura

  1. Nastavte step_state na DOKONČENO.
  2. Odešlete událost EC_STEP_COMPLETE s lParam1 = FALSE.
  3. Pokud je hodinová rychlost nula, odešlete EC_SCRUB_TIME událost s ukázkovým časem.

Procedura DeliverFrameStepSample

  1. Pokud je frekvence hodin nula a čas vzorku + dobu trvání vzorku<hodinový čas, zahoďte vzorek. Východ.
  2. Pokud se step_state rovná hodnotě SCHEDULED nebo COMPLETE, přidejte ukázku do step_queue. Východ.
  3. Snížit step_count.
  4. Pokud step_count> 0, zahoďte vzorek. Východ.
  5. Pokud se step_state rovná ČEKÁNÍ, přidejte vzorek do step_queue. Východ.
  6. Naplánujte ukázku prezentace.
  7. Nastavte step_state na NAPLÁNOVANÉ.

Procedura CancelFrameStep

  1. Nastavení step_state na NOT_STEPPING
  2. Obnovte step_count na nulu.
  3. Pokud byla předchozí hodnota step_state ČEKÁNÍ, ČEKÁJÍCÍ nebo NAPLÁNOVANÁ, odešlete EC_STEP_COMPLETE s lParam1 = TRUE.

Následujícím způsobem volejte tyto postupy:

Zpráva nebo metoda prezentujícího Postup
zpráva MFVP_MESSAGE_STEP PrepareFrameStep
MFVP_MESSAGE_STEP zpráva CancelStep
IMFClockStateSink::OnClockStart StartFrameStep
IMFClockStateSink::OnClockRestart StartFrameStep
ZPĚTNÉ VOLÁNÍ MMFTrackedSample CompleteFrameStep
IMFClockStateSink::OnClockStop CancelFrameStep
MMFClockStateSink::OnClockSetRate CancelFrameStep

 

Následující vývojový diagram znázorňuje postupy postupného přehrávání snímků.

vývojový diagram znázorňující cesty, které začínají krokem mfvp-message-step a mfvp-message-processinputnotify a končí na

Nastavení prezentéru na EVR

Po implementaci prezentéru je dalším krokem konfigurace EVR, aby ho používalo.

Nastavení prezentujícího v režimu DirectShow

V aplikaci DirectShow nastavte prezentujícího na EVR následujícím způsobem:

  1. Vytvořte filtr EVR voláním CoCreateInstance. CLSID je CLSID_EnhancedVideoRenderer.
  2. Přidejte EVR do grafu filtru.
  3. Vytvořte instanci prezenteru. Prezentující může podporovat standardní vytváření COM objektů pomocí IClassFactory, ale to není povinné.
  4. Zadejte dotaz na filtr EVR pro rozhraní MMFVideoRenderer.
  5. Volejte IMFVideoRenderer::InitializeRenderer.

Nastavení prezentujícího ve službě Media Foundation

V Media Foundation máte několik možností v závislosti na tom, jestli vytvoříte jímku média EVR nebo aktivační objekt EVR. Další informace o aktivačních objektech naleznete v tématu aktivační objekty.

U jímky médií EVR postupujte takto:

  1. Zavolejte MFCreateVideoRenderer k vytvoření média sink.
  2. Vytvořte instanci prezenteru.
  3. Zadejte dotaz na jímku médií EVR pro rozhraní IMFVideoRenderer.
  4. Volejte IMFVideoRenderer::InitializeRenderer.

U aktivačního objektu EVR postupujte takto:

  1. Zavolejte MFCreateVideoRendererActivate pro vytvoření aktivačního objektu.

  2. Nastavte u aktivačního objektu jeden z následujících atributů:

    Atribut Popis
    MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE Ukazatel na aktivační objekt pro prezentér.
    Použijte tento příznak k zadání aktivačního objektu pro svého prezentéra. Aktivační objekt musí implementovat MMFActivate rozhraní.
    MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_CLSID CLSID prezentujícího.
    S tímto příznakem musí prezentující podporovat standardní vytváření objektů MODELU COM prostřednictvím IClassFactory.

     

  3. Volitelně můžete nastavit atribut MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_FLAGS objektu aktivace.

vylepšený renderer videa

Příklad EVRPresenter