Dela via


Använda källläsaren i asynkront läge

I det här avsnittet beskrivs hur du använder Källläsare i asynkront läge. I asynkront läge tillhandahåller programmet ett motringningsgränssnitt som används för att meddela programmet att data är tillgängliga.

Det här avsnittet förutsätter att du redan har läst ämnet Använda källläsaren för att bearbeta mediedata.

Använda asynkront läge

Källläsaren fungerar antingen i synkront läge eller asynkront läge. Kodexemplet som visas i föregående avsnitt förutsätter att källläsaren använder synkront läge, vilket är standard. I synkront läge blockerar IMFSourceReader::ReadSample-metoden medan mediekällan genererar nästa prov. En mediekälla hämtar vanligtvis data från en extern källa (till exempel en lokal fil eller en nätverksanslutning), så metoden kan blockera den anropande tråden under en märkbar tid.

I asynkront läge returnerar ReadSample- omedelbart och arbetet utförs på en annan tråd. När åtgärden är klar anropar källläsaren programmet via IMFSourceReaderCallback motringningsgränssnitt. Om du vill använda asynkront läge måste du ange en motringningspekare när du först skapar källläsaren enligt följande:

  1. Skapa ett attributarkiv genom att anropa funktionen MFCreateAttributes.
  2. Ange attributet MF_SOURCE_READER_ASYNC_CALLBACK i attributarkivet. Attributvärdet är en pekare till ditt callback-objekt.
  3. När du skapar källläsaren skickar du attributarkivet till funktionen skapa i parametern pAttributes. Alla funktioner för att skapa källläsaren har den här parametern.

I följande exempel visas de här stegen.

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

När du har skapat källläsaren kan du inte växla lägen mellan synkrona och asynkrona.

Om du vill hämta data i asynkront läge anropar du metoden ReadSample men ställer in de sista fyra parametrarna på NULL-, enligt följande exempel.

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

När metoden ReadSample slutförs asynkront anropar källläsaren din IMFSourceReaderCallback::OnReadSample-metoden. Den här metoden har fem parametrar:

  • hrStatus: Innehåller ett HRESULT- värde. Det här är samma värde som ReadSample- skulle returnera i synkront läge. Om hrStatus innehåller en felkod kan du ignorera de återstående parametrarna.
  • dwStreamIndex, dwStreamFlags, llTimestamp och pSample: Dessa tre parametrar motsvarar de tre sista parametrarna i ReadSample. De innehåller strömnummer, statusflaggor och IMFSample pekare.
    STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
        DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample);

Dessutom definierar motringningsgränssnittet två andra metoder:

  • OnEvent. Meddelar programmet när vissa händelser inträffar i mediekällan, till exempel buffring eller nätverksanslutningshändelser.
  • OnFlush. Anropas när metoden Flush slutförs.

Implementera Callback-gränssnittet

Motringningsgränssnittet måste vara trådsäkert eftersom OnReadSample- och de andra motringningsmetoderna anropas från arbetstrådar.

Det finns flera olika metoder som du kan använda när du implementerar återanropet. Du kan till exempel utföra allt arbete i återanropet, eller så kan du använda återanropet för att meddela programmet (till exempel genom att signalera ett händelsehandtag) och sedan arbeta från programtråden.

Metoden OnReadSample anropas en gång för varje anrop som du gör till metoden IMFSourceReader::ReadSample. Hämta nästa exempel genom att anropa ReadSample igen. Om ett fel uppstår anropas OnReadSample- med en felkod för parametern hrStatus.

I följande exempel visas en minimal implementering av callbackgränssnittet. Först är här deklarationen för en klass som implementerar gränssnittet.

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

};

I det här exemplet är vi inte intresserade av metoderna OnEvent och OnFlush, så de returnerar helt enkelt S_OK. Klassen använder ett händelsehandtag för att signalera programmet. det här handtaget tillhandahålls via konstruktorn.

I det här minimala exemplet skriver metoden OnReadSample bara ut tidsstämpeln till konsolfönstret. Sedan lagrar den statuskoden och flaggan för slutströmmen och signalerar den angivna händelsen.

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

Följande kod visar att programmet skulle använda den här återanropklassen för att läsa alla videobilder från en mediefil.

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

källläsare