Aracılığıyla paylaş


EVR Sunucusu Nasıl Yazılır

[Gelişmiş Video oluşturucu bu sayfada açıklanan bileşen eski bir özelliktir. MediaPlayer ve IMFMediaEngine bileşenleri aracılığıyla kullanıma sunulan Basit Video Oluşturucu (SVR) tarafından değiştirildi. Video içeriğini oynatmak için bu bileşenlerden birine veri göndermeniz ve yeni video işleyicinin örneğini oluşturmalarına izin vermelisiniz. Bu bileşenler Windows 10 ve Windows 11 için iyileştirilmiştir. Microsoft, mümkün olduğunda yeni kodun EVR yerine Windows'ta video medyası yürütmek için MediaPlayer veya alt düzey IMFMediaEngine API'lerini kullanmasını kesinlikle önerir. Microsoft, mümkünse yeni API'leri kullanmak için eski API'leri kullanan mevcut kodun yeniden yazılmasını önerir.]

Bu makalede, gelişmiş video işleyicisi (EVR) için özel bir sunum aracı yazma işlemi açıklanmaktadır. Özel sunucu hem DirectShow hem de Media Foundation ile kullanılabilir; arabirimler ve nesne modeli her iki teknoloji için de aynıdır, ancak tam işlem dizisi farklılık gösterebilir.

Bu konudaki örnek kod, Windows SDK içerisinde sunulan EVRPresenter Örneği'den uyarlanmıştır.

Bu konu aşağıdaki bölümleri içerir:

Önkoşullar

Özel bir sunum arayüzü yazmadan önce aşağıdaki teknolojiler hakkında bilgi sahibi olmanız gerekir:

  • Gelişmiş video işleyicisi. Bkz. Gelişmiş Video İşleyici.
  • Direct3D grafikleri. Sunucu yazmak için 3B grafikleri anlamanız gerekmez, ancak Direct3D cihazı oluşturmayı ve Direct3D yüzeylerini yönetmeyi bilmeniz gerekir. Direct3D hakkında bilginiz yoksa, DirectX Grafik SDK'sı belgelerindeki "Direct3D Cihazları" ve "Direct3D Kaynakları" bölümlerini okuyun.
  • Uygulamanızın videoyu işlemek için kullanacağı teknolojiye bağlı olarak DirectShow filtre grafikleri veya Media Foundation işlem hattı.
  • Media Foundationdönüştürür. EVR mikseri bir Media Foundation dönüşümüdür ve sunucu doğrudan karıştırıcı üzerinde yöntemleri çağırır.
  • COM nesnelerini uygulamak. Sunucu, işlem içi, serbest iş parçacıklı bir COM nesnesidir.

Sunucu Nesne Modeli

Bu bölüm sunucu nesne modeline ve arabirimlerine genel bir bakış içerir.

EVR İçinde Veri Akışı

EVR, videoyu işlemek için iki eklenti bileşeni kullanır: mikser ve sunucu. Karıştırıcı, video akışlarını karıştırır ve gerekirse videonun ağ örgüsünü çözer. Sunum yapan kişi videoyu ekrana çizer (ya da sunar) ve her çerçevenin çizilmesi zamanlanır. Uygulamalar bu nesnelerden birini özel bir uygulamayla değiştirebilir.

EVR'de bir veya daha fazla giriş akışı vardır ve karıştırıcıda buna karşılık gelen sayıda giriş akışı vardır. Stream 0 her zaman referans akışı'dir. Diğer akışlar, karıştırıcının başvuru akışına alfa karışımı yaptığı alt akışlardır. Başvuru akışı, bileşik video için ana kare hızını belirler. Her referans çerçevesi için, karıştırıcı her alt akıştan en son kareyi alır, bu kareleri referans çerçevesine alfa-karıştırma yöntemiyle yerleştirir ve tek bir bileşik çerçeve oluşturur. Karıştırıcı ayrıca gerekirse YUV'den RGB'ye ayrıştırma ve renk dönüştürme gerçekleştirir. EVR, giriş akışlarının sayısından veya video biçiminden bağımsız olarak karıştırıcıyı her zaman video işlem hattına ekler. Aşağıdaki görüntüde bu işlem gösterilmektedir.

başvuru akışının ve alt akışın mikseri gösterdiği, mikserin sunucuyu, sunucunun da ekranı gösterdiği diyagram

Sunucu aşağıdaki görevleri gerçekleştirir:

  • Karıştırıcıda çıkış biçimini ayarlar. Akış başlamadan önce sunucu, karıştırıcının çıkış akışında bir medya türü ayarlar. Bu medya türü, bileşik görüntünün biçimini tanımlar.
  • Direct3D cihazını oluşturur.
  • Direct3D yüzeyleri ayırır. Mikser, bileşik çerçeveleri bu yüzeylere blitler.
  • Mikserden çıkışı alır.
  • Çerçevelerin sunulma zamanını planlar. EVR sunum saatini sağlar ve gösterici, çerçeveleri bu saate göre zamanlar.
  • Direct3D kullanarak her kareyi sunar.
  • Çerçeve adımlama ve tarama işlemi gerçekleştirir.

Sunucu Durumları

Sunucu herhangi bir zamanda aşağıdaki durumlardan birindedir:

  • başlatıldı. EVR'nin sunu saati çalışıyor. Sunucu, video karelerini geldikçe sunum için zamanlar.
  • Duraklatıldı. Sunu saati askıya alındı. Sunucu yeni örnek sunmaz ancak zamanlanmış örnek kuyruğunu korur. Yeni örnekler alınırsa, sunucu bunları kuyruğa ekler.
  • Durduruldu. Sunu saati durduruldu. Sunucu zamanlanmış örneklerin tümünü çöpe atar.
  • Kapat. Sunum yapan kişi, Direct3D yüzeyleri gibi akışla ilgili tüm kaynakları serbest bırakır. Bu sunucunun ilk durumudur ve sunucu yok edilmeden önceki son durumdur.

Bu konudaki örnek kodda, sunumcu durumu bir numaralandırma ile temsil edilir:

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

Sunucu kapatma durumundayken bazı işlemler geçerli değildir. Örnek kod, bir yardımcı yöntemi çağırarak bu durumu denetler:

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

Sunumcu Arabirimleri

Aşağıdaki arabirimleri uygulamak üzere bir sunum yapan gereklidir:

Arayüz Açıklama
IMFClockStateSink EVR'nin saati durum değiştirdiğinde sunan kişiye bildirir. Daha fazla bilgi için IMFClockStateSink'inuygulanmasına bakın.
IMFGetService Sunumcudan arabirimleri almak için işlem hattındaki uygulama ve diğer bileşenler için bir yol sağlar.
IMFTopologyServiceLookupClient Sunucunun EVR'den veya karıştırıcıdan arabirimler almasına olanak tanır. Bkz. IMFTopologyServiceLookupClientiçin Uygulama.
IMFVideoDeviceID Sunucunun ve mikserin uyumlu teknolojiler kullanmasını sağlar. IMFVideoDeviceIDUygulamasına Bakın.
IMFVideoPresenter EVR'den gelen iletileri işler. Bkz. IMFVideoPresenter Uygulaması.

 

Aşağıdaki arabirimler isteğe bağlıdır:

Arayüz Açıklama
IEVRTrustedVideoPlugin Sunucunun korumalı medyayla çalışmasını sağlar. Sunucunuz korumalı medya yolunda (PMP) çalışmak üzere tasarlanmış güvenilir bir bileşense bu arabirimi uygulayın.
IMFOranDestegi Sunumcunun desteklediği oynatma hız aralığını bildirir. Bkz. IMFRateSupport'u Uygulama.
IMFVideoPositionMapper Çıkış video çerçevesindeki koordinatları giriş video çerçevesindeki koordinatlarla eşler.
IQualProp Performans bilgilerini raporlar. EVR, kalite denetimi yönetimi için bu bilgileri kullanır. Bu arabirim DirectShow SDK'sında belgelenmiştir.

 

Uygulamanın sunum yapanla iletişim kurması için arabirimler de sağlayabilirsiniz. Standart sunucu bu amaçla IMFVideoDisplayControl arabirimini uygular. Bu arabirimi uygulayabilir veya kendi arabiriminizi tanımlayabilirsiniz. Uygulama, EVR'de IMFGetService::GetService çağırarak sunucudan arabirimler alır. Hizmet GUID'i MR_VIDEO_RENDER_SERVICEolduğunda, EVR GetService isteğini sunum yapana geçirir.

IMFVideoDeviceID'in Uygulanması

IMFVideoDeviceID arabirimi, cihaz GUID'sini döndüren GetDeviceIDbir yöntem içerir. Cihaz GUID'i, sunucunun ve karıştırıcının uyumlu teknolojiler kullanmasını sağlar. Cihaz GUID'leri eşleşmiyorsa EVR başlatılamaz.

Standart mikser ve sunucu direct3D 9 kullanır ve cihaz GUID değeri IID_IDirect3DDevice9değerine eşittir. Özel sunucunuzu standart karıştırıcıyla kullanmayı planlıyorsanız, sunucunun cihaz GUID'sinin IID_IDirect3DDevice9olması gerekir. Her iki bileşeni de değiştirirseniz, yeni bir cihaz GUID'i tanımlayabilirsiniz. Bu makalenin geri kalanında sunucunun Direct3D 9 kullandığı varsayılır. GetDeviceIDstandart uygulaması aşağıdadır:

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

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

Sunum yapan kapatıldığında bile yöntem başarılı olmalıdır.

IMFTopologyServiceLookupClient'i Uygulama

IMFTopologyServiceLookupClient arabirimi, sunum yapan kişinin EVR'den ve karıştırıcıdan arabirim işaretçilerini aşağıdaki gibi almasına olanak tanır.

  1. EVR sunucuyu başlatırken sunucunun IMFTopologyServiceLookupClient::InitServicePointers yöntemini çağırır. Bağımsız değişken, EVR'nin IMFTopologyServiceLookup arabirimine işaret eden bir işaretçidir.
  2. Sunucu, EVR veya karıştırıcıdan arabirim işaretçileri almak için IMFTopologyServiceLookup::LookupService çağırır.

LookupService yöntemi, IMFGetService::GetService yöntemine benzer. Her iki yöntem de giriş olarak bir hizmet GUID'sini ve arabirim tanımlayıcısı (IID) alır, ancak LookupService bir arabirim işaretçileri dizisi döndürürken, GetService tek bir işaretçi döndürür. Ancak uygulamada dizi boyutunu her zaman 1 olarak ayarlayabilirsiniz. Sorgulanan nesne hizmet GUID'sine bağlıdır:

  • Hizmet GUID'i MR_VIDEO_RENDER_SERVICEise, EVR sorgulanır.
  • Hizmet GUID'i MR_VIDEO_MIXER_SERVICEise, karıştırıcı sorgulanır.

InitServicePointersuygulamanızda EVR'den aşağıdaki arabirimleri alın:

EVR Arabirimi Açıklama
IMediaEventSink Sunucunun EVR'ye ileti göndermesi için bir yol sağlar. Bu arabirim, DirectShow SDK'sında tanımlandığı için iletiler, Media Foundation olaylarının değil, DirectShow olaylarının desenine uyar.
IMFClock EVR'nin saatini temsil eder. Sunucu, sunu için örnekler zamanlamak için bu arabirimi kullanır. EVR saat olmadan çalıştırılabilir, bu nedenle bu arabirim kullanılamayabilir. Yoksa LookupServicehata kodunu yoksayın.
Saat ayrıca IMFTimer arabirimini de uygular. Media Foundation işlem hattında saat, IMFPresentationClock arabirimini uygular. Bu arabirimi DirectShow'da uygulamaz.

 

Karıştırıcıdan aşağıdaki arabirimleri alın:

Mixer Arabirimi Açıklama
IMFTransform Sunucunun karıştırıcıyla iletişim kurmasını sağlar.
IMFVideoDeviceID Sunum yapan kişinin karıştırıcının cihaz GUID'sini doğrulamasını sağlar.

 

Aşağıdaki kod InitServicePointers yöntemini uygular:

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

LookupService'dan alınan arabirim işaretçileri artık geçerli olmadığında, EVR IMFTopologyServiceLookupClient::ReleaseServicePointersçağırır. Bu yöntemin içinde tüm arabirim işaretçilerini serbest bırakın ve sunucu durumunu kapatacak şekilde ayarlayın:

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, aşağıdakiler gibi çeşitli nedenlerle ReleaseServicePointers çağırır:

  • Pinlerin bağlantısını kesme veya yeniden bağlama (DirectShow) veya akış çıkışlarını ekleme veya kaldırma (Media Foundation).
  • Biçim değiştiriliyor.
  • Yeni bir saat ayarlama.
  • EVR'nin son kapatılması.

Sunucunun ömrü boyunca EVR, InitServicePointers ve ReleaseServicePointers birkaç kez çağırabilir.

IMFVideoPresenter'i Uygulama

IMFVideoPresenter arabirimi, IMFClockStateSink devralır ve iki yöntem ekler:

Yöntem Açıklama
GetCurrentMediaType Bileşik video karelerinin medya türünü döndürür.
İşlemMesajı Sunucuya çeşitli eylemler gerçekleştirmesi için sinyal gönderir.

 

GetCurrentMediaType yöntemi sunucunun medya türünü döndürür. (Medya türünü ayarlama hakkında ayrıntılar için bkz. Anlaşma Biçimleri.) Medya türü, BIR IMFVideoMediaType arabirim işaretçisi olarak döndürülür. Aşağıdaki örnekte sunucunun medya türünü bir IMFMediaType işaretçisi olarak depoladığını varsayar. Medya türünden IMFVideoMediaType arabirimini almak için QueryInterfaceçağrısında bulunma:

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

ProcessMessage yöntemi, EVR'nin sunucuyla iletişim kurması için birincil mekanizmadır. Aşağıdaki iletiler tanımlanır. Her iletiyi uygulama ayrıntıları bu konunun geri kalanında verilmiştir.

İleti Açıklama
MFVP_MESSAGE_INVALIDATEMEDIATYPE Karıştırıcının çıkış medya türü geçersiz. Sunucu, karıştırıcıyla yeni bir medya türü anlaşması yapmalıdır. Bkz. Anlaşma Biçimleri.
MFVP_MESSAGE_BEGINSTREAMING Akış başlatıldı. Bu ileti için belirli bir eylem gerekmez, ancak kaynakları ayırmak için bunu kullanabilirsiniz.
MFVP_MESSAGE_ENDSTREAMING Akış sona erdi. MFVP_MESSAGE_BEGINSTREAMING iletisine yanıt olarak ayırmış olduğunuz tüm kaynakları serbest bırakın.
MFVP_MESSAGE_PROCESSINPUTNOTIFY Karıştırıcı yeni bir giriş örneği aldı ve yeni bir çıkış çerçevesi oluşturabilir. Sunum yapıcı, karıştırıcıda IMFTransform::ProcessOutput çağırmalıdır. Bkz. işleme çıktısı.
MFVP_MESSAGE_ENDOFSTREAM Sunu sona erdi. Bakınız Akış Sonu.
MFVP_MESSAGE_FLUSH EVR, işleme işlem hattındaki verileri temizliyor. Sunucu, sunu için zamanlanmış video çerçevelerini atmalıdır.
MFVP_MESSAGE_STEP Sunucudan N kare ileriye adım atmayı istemektedir. Görüntüleyici sonraki N-1 kareleri atlamalı ve N. kareyi görüntülemelidir. Bkz. çerçeve adımlama.
MFVP_MESAJ_ADIMIİPTAL Kare geçişini iptal eder.

 

IMFClockStateSink'i Uygulama

Sunucu, IMFClockStateSinkdevralan IMFVideoPresenteruygulamasının bir parçası olarak IMFClockStateSink arabirimini uygulamalıdır. EVR, EVR'nin saati her değiştiğinde sunucuyu bilgilendirmek için bu arabirimi kullanır. Saat durumları hakkında daha fazla bilgi için bkz. Sunu Saati.

Bu arabirimdeki yöntemleri uygulamaya yönelik bazı yönergeler aşağıdadır. Sunucu kapatılırsa tüm yöntemler başarısız olmalıdır.

Yöntem Açıklama
OnClockStart
  1. Sunucu durumunu başlatıldı olarak ayarlayın.
  2. llClockStartOffsetPRESENTATION_CURRENT_POSITIONdeğilse, sunucunun örnek kuyruğunu temizle. (Bu, MFVP_MESSAGE_FLUSH iletisi almaya eşdeğerdir.)
  3. Önceki bir çerçeve adımı isteği hala bekliyorsa, isteği işleyin (bkz. Çerçeve Adımlama). Aksi takdirde, karıştırıcıdan çıktıyı işlemeye çalışın (bkz. Çıktı İşlemi).
OnClockStop
  1. Sunum yapanın durumunu durdurulmuş olarak ayarlayın.
  2. Sunucunun numune kuyruğunu temizleyin.
  3. Bekleyen çerçeve adımı işlemini iptal edin.
OnClockPause Sunucu durumunu duraklatıldı olarak ayarlayın.
OnClockRestart OnClockStart ile aynı şekilde davranın, ancak örnek kuyruğunu boşaltmayın.
OnClockSetRate
  1. Hız sıfırdan sıfır olmayana değişiyorsa çerçeve adımlama işlemini iptal edin.
  2. Yeni saat hızını depolayın. Saat hızı, örneklerin ne zaman sunulacağını etkiler. Daha fazla bilgi için bkz. Zamanlama Örnekleri.

 

IMFRateSupport'u Uygulamak

Sunumcunun 1× hız dışındaki kayıttan yürütme hızlarını desteklemek için IMFRateSupport arabirimini uygulaması gerekir. Bu arabirimdeki yöntemleri uygulamaya yönelik bazı yönergeler aşağıdadır. Sunucu kapatıldıktan sonra tüm yöntemler başarısız olmalıdır. Bu arabirim hakkında daha fazla bilgi için bkz. Hız Denetimi.

Değer Açıklama
GetSlowestRate Minimum bir oynatma hızının olmadığını belirtmek için sıfır döndür.
EnHızlıOranıAl İnceltilmemiş kayıttan yürütme için, kayıttan yürütme hızı monitörün yenileme hızını aşmamalıdır: maksimum hız = yenileme hızı (Hz) / video kare hızı (fps). Video kare hızı sunucunun medya türünde belirtilir.
İnceletilmiş oynatma için oynatma hızı sınırsızdır; FLT_MAXdeğerini döndürün. Uygulamada, kaynak ve kod çözücü seyreltilmiş oynatma sırasında sınırlayıcı faktörler olacaktır.
Ters oynatma için maksimum oranın negatifine dönün.
OranDestekleniyorMu flRate mutlak değeri sunucunun maksimum oynatma hızını aşarsa, MF_E_UNSUPPORTED_RATE döner. GetFastestRateiçin açıklandığı gibi maksimum kayıttan yürütme hızını hesaplayın.

 

Aşağıdaki örnekte, GetFastestRate yönteminin nasıl uygulandığı gösterilmektedir:

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

Önceki örnekteki en yüksek ileri oynatma hızını hesaplamak için GetMaxRate yardımcı yöntemi çağrılır.

Aşağıdaki örnek, IsRateSupported yönteminin nasıl uygulandığını göstermektedir.

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

Olayları EVR'ye Gönderme

Sunucunun çeşitli olayları EVR'ye bildirmesi gerekir. Bunu yapmak için, EVR sunucunun IMFTopologyServiceLookupClient::InitServicePointers yöntemini çağırdığında elde edilen EVR'nin IMediaEventSink arabirimini kullanır. (IMediaEventSink arabirimi başlangıçta bir DirectShow arabirimidir, ancak hem DirectShow EVR hem de Media Foundation'da kullanılır.) Aşağıdaki kod, EVR'ye olay gönderme işlemini gösterir:

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

Aşağıdaki tabloda sunucunun gönderdiği olaylar ve olay parametreleri listelenir.

Etkinlik Açıklama
EC_COMPLETE Sunucu, MFVP_MESSAGE_ENDOFSTREAM iletisinden sonra tüm çerçevelerin işlenmesini tamamladı.
  • Param1: İşlemin durumunu gösteren HRESULT.
  • Param2: Kullanılmaz.
Daha fazla bilgi için bkz. Akış Sonu.
GÖRÜNTÜ_DEĞİŞTİRİLDİ Direct3D cihazı değişti.
  • Param1: Kullanılmaz.
  • Param2: Kullanılmaz.
Daha fazla bilgi için, Direct3D Cihazı Yönetimi konusunda 'e bakın.
EC_ERRORABORT Akışın durmasını gerektiren bir hata oluştu.
  • Param1: Oluşan hatayı belirten HRESULT.
  • Param2: Kullanılmaz.
EC_PROCESSING_LATENCY Sunucunun her kareyi işlemek için harcadığı süreyi belirtir. (İsteğe bağlı.)
  • Param1: 100 nanosaniyelik birimlerde çerçevenin işlenmesi için gerekli süreyi içeren sabit bir LONGLONG değerine işaret eden bir gösterici.
  • Param2: Kullanılmaz.
Daha fazla bilgi için bkz. İşleme Çıktısı.
EC_SAMPLE_LATENCY İşleme örneklerinde geçerli gecikme süresini belirtir. Değer pozitifse örnekler zamanlamanın gerisindedir. Değer negatifse, örnekler zamanlamanın önündedir. (İsteğe bağlı.)
  • Param1: 100 nanosaniye cinsinden gecikme süresini içeren sabit LONGLONG değeri işaretçisi.
  • Param2: Kullanılmaz.
EC_SCRUB_TIME Oynatma hızı sıfır olduğunda, EC_STEP_COMPLETE hemen ardından gönderilir. Bu olay, görüntülenen çerçevenin zaman damgasını içerir.
  • Param1: Zaman damgasının alt 32 biti.
  • Param2: Zaman damgasının üst 32 biti.
Daha fazla bilgi için bkz. Çerçeve Adımlama.
EC_STEP_COMPLETE Sunucu bir çerçeve adımını tamamladı veya iptal etti.
- Param1: Kullanılmaz.
- Param2: Kullanılmaz.
Daha fazla bilgi için bkz. Çerçeve İlerletme.
Not: Önceki bir belgede, Param1 yanlış tanımlanmıştı. Bu parametre bu olay için kullanılmaz.

 

Anlaşma Biçimleri

Sunucu EVR'den bir MFVP_MESSAGE_INVALIDATEMEDIATYPE iletisi aldığında, karıştırıcıda çıkış biçimini aşağıdaki gibi ayarlaması gerekir:

  1. Olası bir çıkış türünü almak için karıştırıcıda IMFTransform::GetOutputAvailableType çağırın. Bu tür, giriş akışları ve grafik cihazının video işleme özellikleri göz önüne alındığında karıştırıcının üretebileceği bir biçimi açıklar.

  2. Sunucunun bu medya türünü işleme biçimi olarak kullanıp kullanamayacağını denetleyin. Uygulamanızın kendi gereksinimleri olsa da, denetlenecek bazı şeyler şunlardır:

    • Video sıkıştırılmamış olmalıdır.
    • Videoda yalnızca aşamalı kareler olmalıdır. MF_MT_INTERLACE_MODE özniteliğinin MFVideoInterlace_Progressiveeşit olup olmadığını denetleyin.
    • Biçimin Direct3D cihazıyla uyumlu olması gerekir.

    Tür kabul edilebilir değilse, 1. adıma dönün ve karıştırıcının bir sonraki önerilen türünü alın.

  3. Özgün türün kopyası olan yeni bir medya türü oluşturun ve aşağıdaki öznitelikleri değiştirin:

    • MF_MT_FRAME_SIZE özniteliğini ayıracağınız Direct3D yüzeyleri için istediğiniz genişlik ve yükseklik değerine eşit olarak ayarlayın.
    • MF_MT_PAN_SCAN_ENABLED özniteliğini FALSEolarak ayarlayın.
    • MF_MT_PIXEL_ASPECT_RATIO özniteliğini ekranın PAR değerine eşit olarak ayarlayın (genellikle 1:1).
    • Geometrik açıklığı (MF_MT_GEOMETRIC_APERTURE özniteliği) Direct3D yüzeyi içindeki bir dikdörtgene eşit olarak ayarlayın. Karıştırıcı bir çıkış çerçevesi oluşturduğunda, kaynak görüntüyü bu dikdörtgen üzerine aktarır. Geometrik açıklık, yüzey kadar büyük olabilir veya yüzey içinde bir alt dikdörtgen olabilir. Daha fazla bilgi için bkz. Kaynak ve Hedef Dikdörtgenler.
  4. Karıştırıcının değiştirilmiş çıkış türünü kabul edip etmeyeceğini test etmek için, MFT_SET_TYPE_TEST_ONLY bayrağıyla IMFTransform::SetOutputType çağırın. Karıştırıcı türü reddederse, 1. adıma dönün ve sonraki türü alın.

  5. Direct3D Yüzeyleri Ayırma bölümünde açıklandığı gibi bir Direct3D yüzey havuzu ayırın. Karıştırıcı, bileşik video çerçevelerini çizdiğinde bu yüzeyleri kullanır.

  6. İşaretsiz SetOutputType çağırarak karıştırıcıda çıkış türünü ayarlayın. SetOutputType ilk çağrı 4. adımda başarılı, yöntemin yeniden başarılı olması gerekir.

Karıştırıcının türleri tükenirse GetOutputAvailableTypeyöntemiMF_E_NO_MORE_TYPESdöndürür. Sunucu, karıştırıcı için uygun bir çıkış türü bulamazsa akış işlenemez. Bu durumda, DirectShow veya Media Foundation başka bir akış biçimi deneyebilir. Bu nedenle sunucu, geçerli bir tür bulunana kadar bir satırda birkaç MFVP_MESSAGE_INVALIDATEMEDIATYPE iletisi alabilir.

Karıştırıcı, kaynağın ve hedefin piksel en boy oranını (PAR) dikkate alarak videoyu otomatik olarak letterbox formatına getirir. En iyi sonuçları elde etmek için, yüzey genişliği ve yüksekliği ile geometrik diyafram genişliği, videonun ekranda görünmesini istediğiniz gerçek boyuta eşit olmalıdır. Aşağıdaki görüntüde bu işlem gösterilmektedir.

bileşik çerçevenin bir direct3d yüzeye, oradan da bir pencereye yol açtığını gösteren diyagram

Aşağıdaki kod, işlemin ana hattını gösterir. Adımlardan bazıları yardımcı işlevlere konulur, bu işlevlerin tam ayrıntıları ise sunum yapan kişinin gereksinimlerine bağlıdır.

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

Video medya türleri hakkında daha fazla bilgi için bkz. Video Medya Türleri.

Direct3D Cihazını Yönetme

Sunum yapan kişi Direct3D cihazını oluşturur ve akış sırasında cihaz kaybını ele alır. Gösterici, diğer bileşenlerin aynı cihazı kullanabilmesi için Direct3D cihaz yöneticisini de barındırıyor. Örneğin, karıştırıcı alt akışları karıştırmak, ayrıştırmak ve renk ayarlamaları yapmak için Direct3D cihazını kullanır. Kod çözücüler video hızlandırmalı kod çözme için Direct3D cihazını kullanabilir. (Video hızlandırma hakkında daha fazla bilgi için bkz. DirectX Video Acceleration 2.0.)

Direct3D cihazını ayarlamak için aşağıdaki adımları uygulayın:

  1. Direct3DCreate9 veya Direct3DCreate9Exçağırarak Direct3D nesnesini oluşturun.
  2. IDirect3D9::CreateDevice veya IDirect3D9Ex::CreateDevice çağırarak cihazı oluşturun.
  3. DXVA2CreateDirect3DDeviceManager9çağırarak cihaz yöneticisini oluşturun.
  4. IDirect3DDeviceManager9::ResetDeviceçağrısı yaparak cihazı cihaz yöneticisinde ayarlayın.

Başka bir işlem hattı bileşeninin cihaz yöneticisine ihtiyacı varsa, EVR'de IMFGetService::GetService çağırır ve hizmet GUID'i için MR_VIDEO_ACCELERATION_SERVICE belirtir. EVR, isteği sunucuya geçirir. Nesne, IDirect3DDeviceManager9 işaretçisini aldıktan sonra, IDirect3DDeviceManager9::OpenDeviceHandleçağırarak cihaz için bir tanıtıcı alabilir. Nesnenin cihazı kullanması gerektiğinde, cihaz tanıtıcısını IDirect3DDeviceManager9::LockDevice yöntemine geçirir ve bu yöntem IDirect3DDevice9 işaretçisini döndürür.

Cihaz oluşturulduktan sonra sunucu cihazı yok eder ve yeni bir cihaz oluşturursa sunucunun resetdeviceyeniden çağırması gerekir. ResetDevice yöntemi mevcut tüm cihaz tanıtıcılarını geçersiz kıldığı için LockDeviceDXVA2_E_NEW_VIDEO_DEVICEdöndürür. Bu hata kodu, cihazı kullanarak diğer nesnelere yeni bir cihaz tanıtıcısı açmaları gerektiğini bildirir. Cihaz yöneticisini kullanma hakkında daha fazla bilgi için bkz. Direct3D Device Manager.

Sunumcu, cihazı pencereli modda veya tam ekran özel modda oluşturabilir. Pencereli mod için, uygulamanın video penceresini belirtmesi için bir yol sağlamanız gerekir. Standart sunucu bu amaçla IMFVideoDisplayControl::SetVideoWindow yöntemini uygular. Sunum yapan ilk oluşturulduğunda cihazı oluşturmanız gerekir. Genellikle şu anda pencere veya arka arabellek biçimi gibi tüm cihaz parametrelerini bilmezsiniz. Geçici bir cihaz oluşturabilir ve daha sonra #&değiştirebilirsiniz; cihaz yöneticisinde ResetDeviceçağırmayı unutmayın.

Yeni bir cihaz oluşturursanız veya mevcut bir cihazda IDirect3DDevice9::Reset veya IDirect3DDevice9Ex::ResetEx çağırırsanız, EVR'ye bir EC_DISPLAY_CHANGED olayı gönderin. Bu olay, EVR'yi medya türünü yeniden görüşmeye çağırır. EVR, bu olayın olay parametrelerini yoksayar.

Direct3D Yüzeyleri Tahsis Etme

Sunucu medya türünü ayarladıktan sonra, karıştırıcının video karelerini yazmak için kullanacağı Direct3D yüzeylerini ayırabilir. Yüzey sunucunun medya türüyle eşleşmelidir:

  • Yüzey biçimi medya alt türüyle eşleşmelidir. Örneğin, alt tür MFVideoFormat_RGB24ise yüzey biçimi D3DFMT_X8R8G8B8olmalıdır. Alt tür ve Direct3D biçimleri hakkında daha fazla bilgi için bkz. Video Alt Türü GUID'leri.
  • Yüzey genişliği ve yüksekliği, medya türünün MF_MT_FRAME_SIZE özniteliğinde verilen boyutlarla eşleşmelidir.

Yüzeyleri ayırmanın önerilen yolu, sunucunun pencereli mi yoksa tam ekran mı çalıştığına bağlıdır.

Direct3D cihazı pencere modunda ise, her biri tek bir arka tampona sahip birden fazla değiştirme zinciri oluşturabilirsiniz. Bu yaklaşımı kullanarak her yüzeyi bağımsız olarak sunabilirsiniz, çünkü bir takas zincirinin sunulması diğer takas zincirleriyle karışmaz. Karıştırıcı, başka bir yüzey sunum için zamanlanmışken bir yüzeye veri yazabilir.

İlk olarak, kaç takas zinciri oluşturulacağını belirleyin. En az üç önerilir. Her takas zinciri için aşağıdakileri yapın:

  1. Takas zincirini oluşturmak için IDirect3DDevice9::CreateAdditionalSwapChain çağırın.
  2. Takas zincirinin arka arabellek yüzeyine bir işaretçi elde etmek için IDirect3DSwapChain9::GetBackBuffer'i çağırın.
  3. MFCreateVideoSampleFromSurface çağırın ve yüzeye bir pointer iletin. Bu işlev, video örneği nesnesine bir işaretçi döndürür. Video örneği nesnesi IMFSample arabirimini uygular ve prezantör bu arabirimi kullanarak, prezantör karıştırıcının IMFTransform::ProcessOutput yöntemini çağırdığında görseli karıştırıcıya teslim eder. Video örnek nesnesi hakkında daha fazla bilgi için bkz. Video Örnekleri.
  4. IMFSample işaretçisini bir kuyrukta depolayın. Sunucu, İşleme Çıktısıbölümünde açıklandığı gibi işlem sırasında bu kuyruktan örnekler çeker.
  5. Değiştirme zincirinin serbest bırakılmaması için IDirect3DSwapChain9 göstericisine bir referans tutun.

Tam ekran özel modda, cihazda birden fazla takas zinciri bulunamaz. Bu değiştirme zinciri, tam ekran cihazı oluşturduğunuzda örtük olarak oluşturulur. Takas zincirinde birden fazla arka arabellek olabilir. Ne yazık ki, aynı değiştirme zincirinde başka bir arka arabelleğe yazarken bir arka arabellek sunarsanız, iki işlemi koordine etmenin kolay bir yolu yoktur. Bunun nedeni Direct3D'nin yüzey çevirmeyi uygulama biçimidir. Present'i çağırdığınızda, grafik sürücüsü grafik belleğindeki yüzey işaretçilerini günceller. IDirect3DSurface9 işaretçileri tutuyorsanız, Presentçağırdığınızda, Present çağrısı döndürüldükten sonra farklı arabelleklere işaret edeceklerdir.

En basit seçenek, değiştirme zinciri için bir video örneği oluşturmaktır. Bu seçeneği belirlerseniz, pencereli mod için verilen adımların aynısını izleyin. Tek fark, örnek kuyruğun tek bir video örneği içermesidir. Bir diğer seçenek de ekran dışı yüzeyler oluşturmak ve sonra bunları arka arabelleğe aktarmaktır. Oluşturduğunuz yüzeyler, karıştırıcının çıktı çerçevelerini birleştirmede kullandığı IDirectXVideoProcessor::VideoProcessBltyönteminidesteklemelidir.

İzleme Örnekleri

Sunucu video örneklerini ilk kez ayırdığında, bunları bir kuyruğa yerleştirir. Sunum yapan kişi, mikserden yeni bir kare alması gerektiğinde bu kuyruktan çeker. Mikser çerçeveyi çıktıya aldıktan sonra, sunucu örneği ikinci bir sıraya taşır. İkinci kuyruk, zamanlanmış sunu zamanlarını bekleyen örnekler içindir.

Video örneği nesnesi, her örneğin durumunu izlemeyi kolaylaştırmak için IMFTrackedSample arabirimini uygular. Bu arabirimi aşağıdaki gibi kullanabilirsiniz:

  1. Sunucunuzda IMFAsyncCallback arabirimini uygulayın.

  2. Zamanlanan kuyruğa bir örnek yerleştirmeden önce, IMFTrackedSample arabirimi için video örnek nesnesini sorgulayın.

  3. Geri çağırma arabiriminize bir işaretçi belirterek IMFTrackedSample::SetAllocator çağrısını yapın.

  4. Örnek sunuma hazır olduğunda, zamanlanmış kuyruktan kaldırın, sunun ve örneğe dair tüm referansları bırakın.

  5. Örnek, callback'i çalıştırır. (Örnek nesne bu durumda silinmez çünkü geri çağırma çağrılana kadar kendisinde bir başvuru sayısı tutar.)

  6. Geri arama işlevinde, örneği kullanılabilir kuyruğa döndürün.

Sunucuların örnekleri izlemek için IMFTrackedSample kullanması gerekmez; tasarımınız için en uygun olan herhangi bir tekniği uygulayabilirsiniz. IMFTrackedSample avantajlarından biri, sunucunun zamanlama ve işleme işlevlerini yardımcı nesnelere taşıyabilmenizdir ve örnek nesne bu mekanizmayı sağladığından, bu nesneler video örneklerini serbest bıraktığında sunucuya geri çağrı yapmak için özel bir mekanizmaya ihtiyaç duymamasıdır.

Aşağıdaki kod geri çağırmanın nasıl ayarlandığını gösterir:

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

Geri çağırmada, örnekteki işaretçiyi almak için zaman uyumsuz sonuç nesnesinde IMFAsyncResult::GetObject çağırın:

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

İşlem Çıktısı

Karıştırıcı yeni bir giriş örneği aldığında EVR, göstericiye bir MFVP_MESSAGE_PROCESSINPUTNOTIFY iletisi gönderir. Bu mesaj, karıştırıcının teslim etmek üzere yeni bir video çerçevesine sahip olabileceğini gösterir. Yanıt olarak sunucu, karıştırıcıda IMFTransform::ProcessOutput çağırır. Yöntem başarılı olursa, sunum yapan kişi örneği sunum için planlar.

Karıştırıcıdan çıktı almak için aşağıdaki adımları gerçekleştirin:

  1. Saat durumunu denetleyin. Saat durdurulmuşsa, ilk video karesi olmadığı sürece MFVP_MESSAGE_PROCESSINPUTNOTIFY iletisini yoksayın. Saat çalışıyorsa veya bu ilk video çerçevesiyse devam edin.

  2. Kullanılabilir örneklerin kuyruğundan bir örnek alın. Kuyruk boşsa, ayrılan tüm örneklerin şu anda sunu için zamanlandığı anlamına gelir. Bu durumda, bu aşamada MFVP_MESSAGE_PROCESSINPUTNOTIFY iletisini yoksayın. Sonraki örnek kullanılabilir olduğunda, burada listelenen adımları yineleyin.

  3. (İsteğe bağlı.) Saat kullanılabiliyorsa, IMFClock::GetCorrelatedTimeçağırarak mevcut saat zamanını (T1) alın.

  4. Karıştırıcıda IMFTransform::ProcessOutput çağırın. ProcessOutput başarılı olursa, örnek bir video çerçevesi içerir. Yöntem başarısız olursa dönüş kodunu denetleyin. ProcessOutput aşağıdaki hata kodları kritik hatalar değildir:

    Hata kodu Açıklama
    MF_E_TRANSFORM_NEED_MORE_INPUT Karıştırıcının yeni bir çıkış çerçevesi oluşturabilmesi için daha fazla girişe ihtiyacı vardır.
    Bu hata kodunu alırsanız, Akış Sonubölümünde açıklandığı gibi EVR'nin akışın sonuna ulaşıp ulaşmadığını denetleyin ve buna göre yanıt verin. Aksi takdirde, bu MF_E_TRANSFORM_NEED_MORE_INPUT iletisini yoksayın. Karıştırıcı daha fazla giriş aldığında EVR başka bir tane gönderir.
    MF_E_TRANSFORM_STREAM_CHANGE Karıştırıcının çıkış türü, muhtemelen yukarı akıştaki bir biçim değişikliği nedeniyle geçersiz hale geldi.
    Bu hata kodunu alırsanız, sunum yapan kişinin medya türünü NULLolarak ayarlayın. EVR yeni bir biçim isteyecektir.
    MF_E_TRANSFORM_TYPE_NOT_SET Karıştırıcı yeni bir medya türü gerektirir.
    Bu hata kodunu alırsanız, Anlaşma Biçimleribölümünde açıklandığı gibi karıştırıcının çıkış türünü yeniden tartışın.

     

    ProcessOutput başarılı olursa devam edin.

  5. (İsteğe bağlı.) Saat mevcutsa, geçerli saat dilimini (T2) alın. Karıştırıcı tarafından sunulan gecikme süresi miktarıdır (T2 - T1). EVR'ye bu değere sahip bir EC_PROCESSING_LATENCY olayı gönderin. EVR, kalite denetimi için bu değeri kullanır. Kullanılabilir bir saat yoksa, EC_PROCESSING_LATENCY olayını göndermek için bir neden yoktur.

  6. (İsteğe bağlı.) IMFTrackedSample örneğini sorgulayın ve İzleme Örnekleri bölümünde açıklandığı gibi IMFTrackedSample::SetAllocator çağrısı yapın.

  7. Sunum için örneği ayarlayın.

Sunucu karıştırıcıdan herhangi bir çıkış almadan önce bu adım dizisi sonlandırılabilir. Hiçbir isteğin bırakılmadığından emin olmak için, aşağıdakiler gerçekleştiğinde aynı adımları yinelemelisiniz:

  • Sunucunun IMFClockStateSink::OnClockStart veya IMFClockStateSink::OnClockStart yöntemi çağrılır. Karıştırıcı, saat duraklatıldığı için girişi göz ardı ettiği durumu ele alır (1. adım).
  • IMFTrackedSample geri çağrım tetiklenir. Bu, karıştırıcının giriş aldığı ancak sunucunun tüm video örneklerinin kullanımda olduğu durumu işler (2. adım).

Sonraki birkaç kod örneğinde bu adımlar daha ayrıntılı olarak gösterilir. Sunucu, MFVP_MESSAGE_PROCESSINPUTNOTIFY iletisini aldığında ProcessInputNotify yöntemini (aşağıdaki örnekte gösterilmiştir) çağırır.

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

Bu ProcessInputNotify yöntemi, karıştırıcının yeni girişe sahip olduğu gerçeğini kaydetmek için bir Boole bayrağı ayarlar. Ardından sonraki örnekte gösterilen ProcessOutputLoop yöntemini çağırır. Bu yöntem, karıştırıcıdan mümkün olduğunca çok örnek çekmeye çalışır:

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

Sonraki örnekte gösterilen ProcessOutput yöntemi, karıştırıcıdan tek bir video çerçevesi almayı dener. Kullanılabilir video çerçevesi yoksa, ProcessSample S_FALSE veya bir hata kodu döndürür; bu kodlardan biri ProcessOutputLoop'de döngünün kesilmesini sağlar. İşin çoğu ProcessOutput yönteminde gerçekleştirilir:

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

Bu örnekle ilgili bazı açıklamalar:

  • m_SamplePool değişkeninin, kullanılabilir video örnekleri kuyruğunun bulunduğu bir koleksiyon nesnesi olduğu varsayılır. Nesnenin GetSample yöntemi, kuyruk boşsa MF_E_SAMPLEALLOCATOR_EMPTY döndürür.
  • Karıştırıcının ProcessOutput yöntemi MF_E_TRANSFORM_NEED_MORE_INPUT döndürüyorsa, bu, karıştırıcının daha fazla çıkış üretemediği anlamına gelir, bu nedenle sunucu m_fSampleNotify bayrağını temizler.
  • TrackSample yöntemi, IMFTrackedSample geri çağırmayı ayarlar ve İzleme Örnekleri bölümünde gösterilir.

Çerçeveleri Yeniden Boyama

Bazen sunucunun en son video çerçevesini yeniden boyaması gerekebilir. Örneğin, standart sunucu aşağıdaki durumlarda çerçeveyi yeniden boyar:

Karıştırıcıdan en son kareyi yeniden oluşturmasını istemek için aşağıdaki adımları kullanın:

  1. Kuyruktan bir video örneği alın.
  2. IMFDesiredSample arabirimi için örneği sorgula.
  3. Çağrısı IMFDesiredSample::SetDesiredSampleTimeAndDuration. En son video çerçevesinin zaman damgasını belirtin. (Bu değeri önbelleğe almanız ve her kare için güncelleştirmeniz gerekir.)
  4. Karıştırıcıda ProcessOutput çağırın.

Çerçeveyi yeniden boyarken, sunu saatini yoksayabilir ve çerçeveyi hemen sunabilirsiniz.

Zamanlama Örnekleri

Video kareleri herhangi bir zamanda EVR'ye ulaşabilir. Sunucu, çerçevenin zaman damgasına göre her çerçeveyi doğru zamanda sunmakla sorumludur. Sunucu, karıştırıcıdan yeni bir örnek aldığında, örneği zamanlanmış kuyruğa yerleştirir. Ayrı bir iş parçacığında anlatıcı, kuyruğun başından sürekli olarak ilk örneği alır ve şunları yapıp yapmayacağını belirler:

  • Örneği sunun.
  • Numuneyi, erkenden alındığı için kuyrukta tutun.
  • Geç olduğu için örneği atın. Mümkünse kareleri bırakmaktan kaçınmanız gerekir, ancak anlatıcı sürekli geri kalıyorsa bunu yapmanız gerekebilir.

Video çerçevesinin zaman damgasını almak için, video örneğinde IMFSample::GetSampleTimearayın. Zaman damgası EVR'nin sunum saatine göredir. Geçerli saat saatini almak için IMFClock::GetCorrelatedTime'ni arayın. EVR'de sunu saati yoksa veya bir örneğin zaman damgası yoksa, örneği aldıktan hemen sonra sunabilirsiniz.

Her örneğin süresini almak için IMFSample::GetSampleDurationçağrısı yapın. Örneğin süresi yoksa, kare hızından süreyi hesaplamak için MFFrameRateToAverageTimePerFrame işlevini kullanabilirsiniz.

Örnekleri planlarken aşağıdakileri göz önünde bulundurun:

  • Kayıttan yürütme hızı normal hızdan daha hızlı veya daha yavaşsa, saat daha hızlı veya daha yavaş bir hızda çalışır. Bu, bir örnekteki zaman damgasının her zaman sunu saatiyle ilgili olarak doğru hedef zamanı verdiği anlamına gelir. Ancak, sunu zamanlarını başka bir saat saatine (örneğin, yüksek çözünürlüklü performans sayacı) çevirirseniz, zamanları saat hızına göre ölçeklendirmeniz gerekir. Saat hızı değişirse EVR sunucunun IMFClockStateSink::OnClockSetRate yöntemini çağırır.
  • Çalma hızı, ters çalma için negatif olabilir. Kayıttan yürütme hızı negatif olduğunda, sunu saati geriye doğru çalışır. Başka bir deyişle, N + 1 zamanı Nzamanından önce gerçekleşir.

Aşağıdaki örnek, bir örneğin sunu saatine göre ne kadar erken veya geç olduğunu hesaplar:

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

Sunu saati genellikle sistem saati veya ses işleyicisi tarafından yönlendirilir. (Ses işleyicisi, ses kartının ses tükettiği hızdan süreyi türetir.) Genel olarak, sunu saati monitörün yenileme hızıyla eşitlenmez.

Direct3D sunum parametreleriniz sunum aralığı için D3DPRESENT_INTERVAL_DEFAULT veya D3DPRESENT_INTERVAL_ONE belirtilmişse, Göster işlemi monitörün dikey yenilemesini bekler. Bu, yırtılma önlemenin kolay bir yoludur, ancak zamanlama algoritmanızın doğruluğunu azaltır. Buna karşılık, sunu aralığı D3DPRESENT_INTERVAL_IMMEDIATEise Present yöntemi hemen yürütülür ve zamanlama algoritmanız yalnızca dikey geri çekme süresi boyunca Present çağıracak kadar doğru olmadığı sürece yırtılmalara neden olur.

Aşağıdaki işlevler doğru zamanlama bilgilerini almanıza yardımcı olabilir:

  • IDirect3DDevice9::GetRasterStatus, geçerli tarama çizgisi ve rasterin dikey boş dönemde olup olmadığı dahil olmak üzere raster hakkındaki bilgileri döndürür.
  • DwmGetCompositionTimingInfo, masaüstü pencere yöneticisi için zamanlama bilgilerini döndürür. Masaüstü oluşturma etkinse bu bilgiler yararlıdır.

Örnekleri Sunma

Her yüzey için ayrı bir swap chain oluşturduğunuz, Direct3D Yüzeylerini Ayırmakısmında açıklandığı varsayılır. Bir örnek sunmak için, video örneğinden aşağıdaki gibi takas zincirini alın:

  1. Arabelleği almak için video örneğinde IMFSample::GetBufferByIndex işlevini çağırın.
  2. IMFGetServicearabirimi içinarabelleği sorgula.
  3. Direct3D yüzeyinin IDirect3DSurface9 arabirimini almak için IMFGetService::GetServiceçağırın. (MFGetServiceçağırarak bu adımı ve önceki adımı tek adımda birleştirebilirsiniz.)
  4. Yüzeydeki değişim zincirine bir işaretçi almak için IDirect3DSurface9::GetContainer çağrısını yapın.
  5. Değiştirme zincirinde IDirect3DSwapChain9::Present çağırın.

Aşağıdaki kod şu adımları gösterir:

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

Kaynak ve Hedef Dikdörtgenler

kaynak dikdörtgen, görüntülenecek video çerçevesinin bölümüdür. Normalleştirilmiş koordinat sistemine göre tanımlanır ve video çerçevesinin tamamı {0, 0, 1, 1} koordinatlarına sahip bir dikdörtgen kaplar. hedef dikdörtgen, hedef yüzey içinde video çerçevesinin çizildiği alandır. Standart sunucu, uygulamanın IMFVideoDisplayControl::SetVideoPositionçağrısı yaparak bu dikdörtgenleri ayarlamasına olanak tanır.

Kaynak ve hedef dikdörtgenleri uygulamak için çeşitli seçenekler vardır. İlk seçenek, karıştırıcının bunları uygulamasına izin vermektir:

  • VIDEO_ZOOM_RECT özniteliğini kullanarak kaynak dikdörtgeni ayarlayın. Karıştırıcı, videoyu hedef yüzeye aktarırken kaynak dikdörtgeni uygular. Karıştırıcının varsayılan kaynak dikdörtgeni çerçevenin tamamıdır.
  • Hedef dikdörtgeni, karıştırıcının çıkış türünde geometrik diyafram olarak ayarlayın. Daha fazla bilgi için bkz. Anlaşma Biçimleri.

İkinci seçenek, IDirect3DSwapChain9::Present işleminde, Present yönteminde pSourceRect ve pDestRect parametrelerini belirterek dikdörtgenleri uygulamaktır. Bu seçenekleri birleştirebilirsiniz. Örneğin, karıştırıcıda kaynak dikdörtgeni ayarlayabilir, ancak hedef dikdörtgeni Present yöntemine uygulayabilirsiniz.

Uygulama hedef dikdörtgeni değiştirirse veya pencereyi yeniden boyutlandırırsa, yeni yüzeyleri ayırmanız gerekebilir. Öyleyse, bu işlemi zamanlama iş parçacığınızla eşitlemeye dikkat etmeniz gerekir. Yeni yüzeyleri ayırmadan önce zamanlama kuyruğunu boşaltın ve eski örnekleri atın.

Akış Sonu

EVR'de her giriş akışı sona erdiğinde, EVR görüntüleyiciye bir MFVP_MESSAGE_ENDOFSTREAM iletisi gönderir. Ancak iletiyi aldığınız noktada işlenecek birkaç video karesi kalmış olabilir. Akış sonu iletisine yanıt vermeden önce, mikserden tüm çıkışı boşaltmanız ve tüm kalan çerçeveleri sunmanız gerekir. Son çerçeve sunulduktan sonra EVR'ye bir EC_COMPLETE olayı gönderin.

Sonraki örnekte, çeşitli koşullar karşılanırsa EC_COMPLETE olayı gönderen bir yöntem gösterilmektedir. Aksi durumda, olayı göndermeden S_OK değerini döndürür.

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

Bu yöntem aşağıdaki durumları denetler:

  • m_fSampleNotify değişkeni TRUEise, karıştırıcının henüz işlenmemiş bir veya daha fazla çerçevesi olduğu anlamına gelir. (Ayrıntılar için bkz. İşleme Çıktısı.)
  • m_fEndStreaming değişkeni, ilk değeri FALSEolan bir Boole bayrağıdır. EVR MFVP_MESSAGE_ENDOFSTREAM iletisini gönderdiğinde sunan kişi bayrağı TRUE olarak ayarlar.
  • AreSamplesPending yönteminin, zamanlanan kuyrukta bir veya daha fazla çerçeve beklediği sürece TRUE döndüreceği varsayılır.

IMFVideoPresenter::ProcessMessage yönteminde, EVR MFVP_MESSAGE_ENDOFSTREAM iletisini gönderdiğinde, TRUE olarak m_fEndStreaming ayarlayın ve CheckEndOfStream çağırın.

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

Ayrıca, karıştırıcının IMFTransform::ProcessOutput yöntemi MF_E_TRANSFORM_NEED_MORE_INPUTdöndürmesi durumunda, CheckEndOfStream çağırın. Bu hata kodu, karıştırıcıda daha fazla giriş örneği olmadığını gösterir (bkz. İşleme Çıktısı).

Kare İlerlemesi

EVR, DirectShow'da çerçeve adımını ve Media Foundation'da kaydırmayı destekleyecek şekilde tasarlanmıştır. Çerçeve adımlama ve kaydırma kavramsal olarak benzerdir. Her iki durumda da uygulama bir kerede bir video çerçevesi istemektedir. Dahili olarak, sunum aracı her iki özelliği de uygulamak için aynı mekanizmayı kullanır.

DirectShow'da çerçeve adımlama aşağıdaki gibi çalışır:

  • Uygulama IVideoFrameStep::Stepçağırır. Adım sayısı dwSteps parametresinde verilir. EVR sunucuya bir MFVP_MESSAGE_STEP iletisi gönderir; burada ileti parametresi (ulParam) adım sayısıdır.
  • Uygulama IVideoFrameStep::CancelStep çağırırsa veya grafik durumunu değiştirirse (çalışıyor, duraklatıldı veya durduruldu), EVR bir MFVP_MESSAGE_CANCELSTEP iletisi gönderir.

Media Foundation'da temizleme aşağıdaki gibi çalışır:

  • Uygulama, IMFRateControl::SetRatefonksiyonunu çağırarak oynatma hızını sıfıra ayarlar.
  • Yeni bir çerçeve oluşturmak için, uygulama istenen konumla IMFMediaSession::Start çağırır. EVR, 1'e eşit ulParam içeren bir MFVP_MESSAGE_STEP iletisi gönderir.
  • Temizlemeyi durdurmak için uygulama kayıttan yürütme hızını sıfır olmayan bir değere ayarlar. EVR, MFVP_MESSAGE_CANCELSTEP iletisini gönderir.

MFVP_MESSAGE_STEP iletisini aldıktan sonra sunucu hedef çerçevenin gelmesini bekler. Adım sayısı Nise, sunucu sonraki (N - 1) örnekleri atar ve N. örneği sunar. Sunucu çerçeve adımını tamamladığında, lParam1 false olarak ayarlanmış bir EC_STEP_COMPLETE olayı EVR'ye gönderir. Ayrıca, oynatma hızı sıfır ise sunucu bir EC_SCRUB_TIME olayı gönderir. EVR, bir çerçeve adımı işlemi beklemedeyken çerçeve adımlama işlemini iptal ederse, sunucu lParam1TRUEolarak ayarlanmış bir EC_STEP_COMPLETE olayı gönderir.

Uygulama adımı birden çok kez çerçeveleyebilir veya temizleyebilir, böylece sunucu bir MFVP_MESSAGE_CANCELSTEP iletisi almadan önce birden çok MFVP_MESSAGE_STEP iletisi alabilir. Ayrıca sunum yapan kişi, saat başlamadan önce veya saat çalışırken MFVP_MESSAGE_STEP iletisini alabilir.

Çerçeve Adımlama Uygulama

Bu bölümde, çerçeve adımlama uygulamak için bir algoritma açıklanmaktadır. Çerçeve adımlama algoritması aşağıdaki değişkenleri kullanır:

  • step_count. Geçerli çerçeve adımlama işlemindeki adım sayısını belirten imzasız bir tamsayı.

  • step_queue. ,,, işaretçileri içeren bir IMFSample kuyruğu.

  • step_state. Sunucu, çerçeve adımlamaya ilişkin olarak herhangi bir zaman diliminde aşağıdaki durumlardan birinde olabilir.

    Devlet Açıklama
    AYAK ATMIYOR Kare kare ilerleme yok.
    BEKLEMEK Konuşmacı MFVP_MESSAGE_STEP iletisini aldı, ancak saat başlatılmadı.
    BEKLİYOR Sunucu MFVP_MESSAGE_STEP iletisini aldı ve saat başladı, ancak sunucu hedef çerçeveyi almayı bekliyor.
    PLANLANAN Sunucu hedef çerçeveyi aldı ve sunu için zamanlandı, ancak çerçeve sunulmadı.
    TAMAMLAMAK Konuşmacı hedef çerçeveyi sundu ve EC_STEP_COMPLETE olayını gönderdi, şimdi de sonraki MFVP_MESSAGE_STEP veya MFVP_MESSAGE_CANCELSTEP iletisini bekliyor.

     

    Bu durumlar Sunucu Durumlarıbölümünde listelenen sunucu durumlarından bağımsızdır.

Çerçeve adımlama algoritması için aşağıdaki yordamlar tanımlanır:

PrepareFrameStep Yordamı

  1. adım_sayısıarttır.
  2. step_state BEKLIYOR olarak ayarlayın.
  3. Saat çalışıyorsa StartFrameStep'i çağırın.

StartFrameStep Yordamı

  1. step_state BEKLİYOR değerine eşitse, step_state BEKLEMEDE olarak ayarlayın. step_queueiçindeki her örnek için DeliverFrameStepSample'ı çağır.
  2. step_state NOT_STEPPING'e eşitse, step_queue'daki tüm örnekleri çıkarıp sunum için zamanlayın.

CompleteFrameStep Yordamı

  1. step_state’i TAMAMLANDI olarak ayarlayın.
  2. EC_STEP_COMPLETE olayını lParam1 = FALSEile gönderin.
  3. Saat hızı sıfırsa, örnek saatle EC_SCRUB_TIME olayını gönderin.

DeliverFrameStepSample Prosedürü

  1. Eğer saat hızı sıfırsa ve örnek zamanı + ve örnek süresi<saat süresiise, örneği atın. Çıkış.
  2. step_state SCHEDULED veya COMPLETE değerine eşitse, örneği step_queueekleyin. Çıkış.
  3. Azaltma step_count.
  4. step_count> 0 ise örneği atın. Çıkış.
  5. step_state BEKLİYOR'a eşitse, örneği step_queue'e ekleyin. Çıkış.
  6. Sunum için örneği zamanlayın.
  7. step_state ZAMANLANMIŞ olarak ayarlayın.

CancelFrameStep Yordamı

  1. step_state NOT_STEPPING olarak ayarlama
  2. step_count sıfıra sıfırlayın.
  3. step_state önceki değeri WAITING, PENDING veya SCHEDULED ise, lParam1 = TRUEile EC_STEP_COMPLETE gönderin.

Bu işlemleri aşağıdaki şekilde çağırın:

Sunucu iletisi veya yöntemi Prosedür
İletiyi MFVP_MESSAGE_STEP PrepareFrameStep
MFVP_MESSAGE_STEP ileti mesajı CancelStep
IMFClockStateSink::OnClockStart StartFrameStep
IMFClockStateSink::OnClockRestart StartFrameStep
IMFTrackedSample geri çağırma CompleteFrameStep
IMFClockStateSink::OnClockStop CancelFrameStep
IMFClockStateSink::OnClockSetRate CancelFrameStep

 

Aşağıdaki akış grafiği, çerçeve adımlama prosedürlerini göstermektedir.

mfvp-message-step ve mfvp-message-processinputnotify ile başlayan ve "state = complete" ile biten yolları gösteren akış grafiği

EVR'de Sunucuyu ayarlama

Sunucuyu uyguladıktan sonra, sonraki adım EVR'yi bunu kullanacak şekilde yapılandırmaktır.

DirectShow'da Sunucu ayarlama

DirectShow uygulamasında, EVR'de göstericiyi aşağıdaki gibi ayarlayın:

  1. CoCreateInstanceçağırarak EVR filtresini oluşturun. CLSID CLSID_EnhancedVideoRenderer.
  2. EVR'yi filtre grafiğine ekleyin.
  3. Sunucunuzun bir örneğini oluşturun. Sunucunuz IClassFactoryaracılığıyla standart COM nesnesi oluşturmayı destekleyebilir, ancak bu zorunlu değildir.
  4. IMFVideoRenderer arabirimi için EVR filtresini sorgula.
  5. Çağrı IMFVideoRenderer::InitializeRenderer.

Media Foundation'da Sunucu ayarlama

Media Foundation'da, EVR medya havuzu mu yoksa EVR etkinleştirme nesnesi mi oluşturduğunuza bağlı olarak çeşitli seçenekleriniz vardır. Etkinleştirme nesneleri hakkında daha fazla bilgi için bkz. Etkinleştirme Nesneleri.

EVR medya havuzu için aşağıdakileri yapın:

  1. Medya havuzu oluşturmak için MFCreateVideoRendererçağırın.
  2. Sunucunuzun bir örneğini oluşturun.
  3. IMFVideoRenderer arabirimi için EVR medya havuzu sorgula.
  4. Çağrı IMFVideoRenderer::InitializeRenderer.

EVR etkinleştirme nesnesi için aşağıdakileri yapın:

  1. Etkinleştirme nesnesini oluşturmak için MFCreateVideoRendererActivate çağırın.

  2. Etkinleştirme nesnesinde aşağıdaki özniteliklerden birini ayarlayın:

    Öznitelik Açıklama
    MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE Sunum yapan kişi için etkinleştirme nesnesine işaretçi.
    Bu bayrakla, sunucunuz için bir etkinleştirme nesnesi sağlamanız gerekir. Etkinleştirme nesnesinin IMFActivate arabirimini uygulaması gerekir.
    MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_CLSID Sunucunun CLSID'i.
    Bu bayrakla, sunumcunuzun IClassFactoryaracılığıyla standart COM nesnesi oluşturmayı desteklemesi gerekir.

     

  3. İsteğe bağlı olarak, etkinleştirme nesnesinde MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_FLAGS özniteliğini ayarlayın.

Gelişmiş Video İşleyici

EVRPresenter Örnek