Aracılığıyla paylaş


Kaynak Okuyucuyu Zaman Uyumsuz Modda Kullanma

Bu konuda, Kaynak Okuyucu zaman uyumsuz modda nasıl kullanılacağı açıklanmaktadır. Zaman uyumsuz modda uygulama, uygulamaya verilerin kullanılabilir olduğunu bildirmek için kullanılan bir geri çağırma arabirimi sağlar.

Bu konu başlığında, Medya Verilerini İşlemek için Kaynak Okuyucu kullanmakonusunu okuduğunuz varsayılır.

Asenkron Modu Kullanma

Kaynak Okuyucu zaman uyumlu modda veya zaman uyumsuz modda çalışır. Önceki bölümde gösterilen kod örneği, Kaynak Okuyucu'nun varsayılan olan zaman uyumlu modu kullandığını varsayar. Zaman uyumlu modda, medya kaynağı sonraki örneği oluştururken IMFSourceReader::ReadSample yöntemi bloklar. Medya kaynağı genellikle bir dış kaynaktan (yerel dosya veya ağ bağlantısı gibi) veri alır, bu nedenle yöntem çağrılan iş parçacığını belirgin bir süre engelleyebilir.

Zaman uyumsuz modda, ReadSample hemen geri döner ve görev başka bir iş parçacığında gerçekleştirilir. İşlem tamamlandıktan sonra Kaynak Okuyucu, uygulamayı IMFSourceReaderCallback geri çağırma arabirimi aracılığıyla çağırır. Zaman uyumsuz modu kullanmak için, Kaynak Okuyucuyu ilk oluşturduğunuzda aşağıdaki gibi bir geri çağırma işaretçisi sağlamanız gerekir:

  1. MFCreateAttributes işlevini çağırarak bir öznitelik deposu oluşturun.
  2. Öznitelik deposunda MF_SOURCE_READER_ASYNC_CALLBACK özniteliğini ayarlayın. Öznitelik değeri, geri çağırma nesnenizin işaretçisidir.
  3. Kaynak Okuyucu oluşturduğunuzda, öznitelik depoyu pAttributes parametresindeki oluşturma işlevine geçirin. Kaynak Okuyucu'nun oluşturulacağı tüm işlevler bu parametreye sahiptir.

Aşağıdaki örnekte bu adımlar gösterilmektedir.

HRESULT CreateSourceReaderAsync(
    PCWSTR pszURL, 
    IMFSourceReaderCallback *pCallback, 
    IMFSourceReader **ppReader)
{
    HRESULT hr = S_OK;
    IMFAttributes *pAttributes = NULL;

    hr = MFCreateAttributes(&pAttributes, 1);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, pCallback);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = MFCreateSourceReaderFromURL(pszURL, pAttributes, ppReader);

done:
    SafeRelease(&pAttributes);
    return hr;
}

Kaynak Okuyucuyu oluşturduktan sonra zaman uyumlu ve zaman uyumsuz modlar arasında geçiş yapamazsınız.

Zaman uyumsuz modda veri almak için ReadSample yöntemini çağırın, ancak aşağıdaki örnekte gösterildiği gibi son dört parametreyi null olarak ayarlayın.

    // Request the first sample.
    hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 
        0, NULL, NULL, NULL, NULL);

ReadSample yöntemi zaman uyumsuz olarak tamamlandığında, Kaynak Okuyucu IMFSourceReaderCallback::OnReadSample yönteminizi çağırır. Bu yöntemin beş parametresi vardır:

  • hrStatus: HRESULT değeri içerir. Bu, ReadSample zaman uyumlu modda döndüreceği değerle aynıdır. hrStatus bir hata kodu içeriyorsa, kalan parametreler yoksayılabilir.
  • dwStreamIndex, dwStreamFlags, llTimestamp ve pSample: Bu üç parametre ReadSampleiçindeki son üç parametreye eşdeğerdir. Bunlar sırasıyla akış numarasını, durum bayraklarını ve IMFSample işaretçisini içerir.
    STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
        DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample);

Buna ek olarak, geri çağırma arabirimi diğer iki yöntemi tanımlar:

  • OnEvent. Arabelleğe alma veya ağ bağlantısı olayları gibi belirli olaylar medya kaynağında gerçekleştiğinde uygulamaya bildirir.
  • onFlush. Flush yöntemi tamamlandığında çağrılır.

Geri Çağırım Arayüzünü Uygulama

OnReadSample ve diğer geri çağırma yöntemleri çalışan iş parçacıklarından çağrıldığından geri çağırma arabirimi iş parçacığı açısından güvenli olmalıdır.

Geri çağırmayı uygularken uygulayabileceğiniz birkaç farklı yaklaşım vardır. Örneğin, tüm işleri geri çağırma işlevi içinde yapabilir veya geri çağırma işlevini kullanarak uygulamayı haberdar edebilir (örneğin, bir olay tutacağı sinyali göndererek) ve ardından uygulama iş parçacığından iş yapabilirsiniz.

OnReadSample yöntemi, IMFSourceReader::ReadSample yöntemine yaptığınız her çağrı için bir kez çağrılır. Sonraki örneği almak için ReadSample yeniden çağırın. Hata oluşursa, OnReadSamplehrStatus parametresi için bir hata koduyla çağrılır.

Aşağıdaki örnekte geri çağırma arabiriminin en düşük düzeyde uygulanması gösterilmektedir. İlk olarak, arabirimini uygulayan bir sınıfın bildirimi aşağıdadır.

#include <shlwapi.h>

class SourceReaderCB : public IMFSourceReaderCallback
{
public:
    SourceReaderCB(HANDLE hEvent) : 
      m_nRefCount(1), m_hEvent(hEvent), m_bEOS(FALSE), m_hrStatus(S_OK)
    {
        InitializeCriticalSection(&m_critsec);
    }

    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
    {
        static const QITAB qit[] =
        {
            QITABENT(SourceReaderCB, IMFSourceReaderCallback),
            { 0 },
        };
        return QISearch(this, qit, iid, ppv);
    }
    STDMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&m_nRefCount);
    }
    STDMETHODIMP_(ULONG) Release()
    {
        ULONG uCount = InterlockedDecrement(&m_nRefCount);
        if (uCount == 0)
        {
            delete this;
        }
        return uCount;
    }

    // IMFSourceReaderCallback methods
    STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
        DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample);

    STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *)
    {
        return S_OK;
    }

    STDMETHODIMP OnFlush(DWORD)
    {
        return S_OK;
    }

public:
    HRESULT Wait(DWORD dwMilliseconds, BOOL *pbEOS)
    {
        *pbEOS = FALSE;

        DWORD dwResult = WaitForSingleObject(m_hEvent, dwMilliseconds);
        if (dwResult == WAIT_TIMEOUT)
        {
            return E_PENDING;
        }
        else if (dwResult != WAIT_OBJECT_0)
        {
            return HRESULT_FROM_WIN32(GetLastError());
        }

        *pbEOS = m_bEOS;
        return m_hrStatus;
    }
    
private:
    
    // Destructor is private. Caller should call Release.
    virtual ~SourceReaderCB() 
    {
    }

    void NotifyError(HRESULT hr)
    {
        wprintf(L"Source Reader error: 0x%X\n", hr);
    }

private:
    long                m_nRefCount;        // Reference count.
    CRITICAL_SECTION    m_critsec;
    HANDLE              m_hEvent;
    BOOL                m_bEOS;
    HRESULT             m_hrStatus;

};

Bu örnekte, OnEvent ve OnFlush yöntemleriyle ilgilenmeyiz, bu nedenle yalnızca S_OKdöndürürler. sınıfı, uygulamaya sinyal vermek için bir olay tanıtıcısı kullanır; bu tanıtıcı oluşturucu aracılığıyla sağlanır.

Bu en düşük örnekte, OnReadSample yöntemi yalnızca zaman damgasını konsol penceresine yazdırır. Ardından durum kodunu ve akış sonu bayrağını depolar ve olay tanıtıcısına sinyal gönderir:

HRESULT SourceReaderCB::OnReadSample(
    HRESULT hrStatus,
    DWORD /* dwStreamIndex */,
    DWORD dwStreamFlags,
    LONGLONG llTimestamp,
    IMFSample *pSample      // Can be NULL
    )
{
    EnterCriticalSection(&m_critsec);

    if (SUCCEEDED(hrStatus))
    {
        if (pSample)
        {
            // Do something with the sample.
            wprintf(L"Frame @ %I64d\n", llTimestamp);
        }
    }
    else
    {
        // Streaming error.
        NotifyError(hrStatus);
    }

    if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
    {
        // Reached the end of the stream.
        m_bEOS = TRUE;
    }
    m_hrStatus = hrStatus;

    LeaveCriticalSection(&m_critsec);
    SetEvent(m_hEvent);
    return S_OK;
}

Aşağıdaki kod, uygulamanın bir medya dosyasındaki tüm video çerçevelerini okumak için bu geri çağırma sınıfını kullanacağını gösterir:

HRESULT ReadMediaFile(PCWSTR pszURL)
{
    HRESULT hr = S_OK;

    IMFSourceReader *pReader = NULL;
    SourceReaderCB *pCallback = NULL;

    HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (hEvent == NULL)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto done;
    }

    // Create an instance of the callback object.
    pCallback = new (std::nothrow) SourceReaderCB(hEvent);
    if (pCallback == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto done;
    }

    // Create the Source Reader.
    hr = CreateSourceReaderAsync(pszURL, pCallback, &pReader);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = ConfigureDecoder(pReader, MF_SOURCE_READER_FIRST_VIDEO_STREAM);
    if (FAILED(hr))
    {
        goto done;
    }

    // Request the first sample.
    hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 
        0, NULL, NULL, NULL, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

    while (SUCCEEDED(hr))
    {
        BOOL bEOS;
        hr = pCallback->Wait(INFINITE, &bEOS);
        if (FAILED(hr) || bEOS)
        {
            break;
        }
        hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            0, NULL, NULL, NULL, NULL);
    }

done:
    SafeRelease(&pReader);
    SafeRelease(&pCallback);
    return hr;
}

kaynak okuyucu