Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a témakör azt ismerteti, hogyan használható a Forrásolvasó aszinkron módban. Aszinkron módban az alkalmazás egy visszahívási felületet biztosít, amellyel értesíti az alkalmazást arról, hogy az adatok elérhetők.
Ez a témakör feltételezi, hogy már elolvasta a témakört A forrásolvasó használata a médiaadatok feldolgozásához.
Aszinkron mód használata
A Forrásolvasó szinkron vagy aszinkron módban működik. Az előző szakaszban látható példakód azt feltételezi, hogy a Forrásolvasó szinkron módot használ, ami az alapértelmezett mód. Szinkron módban az IMFSourceReader::ReadSample metódus blokkolja, miközben a médiaforrás létrehozza a következő mintát. A médiaforrások általában valamilyen külső forrásból (például helyi fájlból vagy hálózati kapcsolatból) szereznek be adatokat, így a metódus jelentős ideig blokkolhatja a hívásláncot.
Aszinkron módban a ReadSample azonnal visszatér, és a munka egy másik szálon történik. A művelet befejezése után a Forrásolvasó meghívja az alkalmazást az IMFSourceReaderCallback visszahívási felületen keresztül. Az aszinkron mód használatához meg kell adnia egy visszahívási mutatót a Forrásolvasó első létrehozásakor, az alábbiak szerint:
- Hozzon létre egy attribútumtárolót az MFCreateAttributes függvény meghívásával.
- Állítsa be a MF_SOURCE_READER_ASYNC_CALLBACK attribútumot az attribútumtárolóban. Az attribútum értéke a visszahívási objektum mutatója.
- A Forrásolvasó létrehozásakor adja át az attribútumtárat a pAttributes paraméter létrehozási függvényének. A Forrásolvasó létrehozásához szükséges összes függvény rendelkezik ezzel a paraméterével.
Az alábbi példa ezeket a lépéseket mutatja be.
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;
}
A Forrásolvasó létrehozása után nem válthat a szinkron és az aszinkron mód között.
Ha aszinkron módban szeretne adatokat lekérni, hívja meg a ReadSample metódust, de az utolsó négy paramétert állítsa a NULL , ahogyan az az alábbi példában is látható.
// Request the first sample.
hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, NULL, NULL, NULL, NULL);
Amikor a ReadSample metódus aszinkron módon fejeződik be, a forrásolvasó meghívja a IMFSourceReaderCallback::OnReadSample metódust. Ez a metódus öt paramétert használ:
- hrStatus: Egy HRESULT értéket tartalmaz. Ez ugyanaz az érték, amelyet a ReadSampleszinkron módban ad vissza. Ha hrStatus hibakódot tartalmaz, figyelmen kívül hagyhatja a többi paramétert.
- dwStreamIndex, dwStreamFlags, llTimestamp és pSample: Ez a három paraméter egyenértékű ReadSampleutolsó három paraméterével. Ezek tartalmazzák a stream számát, az állapotjelzőket és a IMFSample mutatót.
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample);
A visszahívási felület emellett két másik módszert is meghatároz:
- OnEvent. Értesíti az alkalmazást, ha bizonyos események történnek a médiaforrásban, például pufferelés vagy hálózati kapcsolati események.
- OnFlush. A Flush metódus befejeződésekor hívjuk.
A Visszahívási felület implementálása
A visszahívási felületnek szálbiztosnak kell lennie, mert OnReadSample és a többi visszahívási metódust munkavégző szálakból hívjuk meg.
A visszahívás implementálása során számos különböző megközelítést alkalmazhat. Elvégezheti például az összes munkát a visszahíváson belül, vagy a visszahívással értesítheti az alkalmazást (például egy eseménykezelő jelzésével), majd elvégezheti a munkát az alkalmazásszálból.
Az OnReadSample metódust a rendszer minden olyan híváshoz meghívja, amelyet az IMFSourceReader::ReadSample metódushoz intéz. A következő minta lekéréséhez hívja meg újra ReadSample. Hiba esetén a OnReadSample meghívása a hrStatus paraméter hibakódjával történik.
Az alábbi példa a visszahívási felület minimális implementációját mutatja be. Először is itt van egy osztály deklarálása, amely megvalósítja a felületet.
#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;
};
Ebben a példában minket nem érdekelnek az OnEvent és OnFlush metódusok, így egyszerűen visszatérnek S_OK. Az osztály eseménykezelőt használ az alkalmazás jelzésére; ezt a fogópontot a konstruktor biztosítja.
Ebben a minimális példában az OnReadSample metódus egyszerűen kinyomtatja az időbélyeget a konzolablakba. Ezután tárolja az állapotkódot és a stream vége jelzőt, és jelzi az eseménykezelőt:
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;
}
Az alábbi kód azt mutatja, hogy az alkalmazás ezt a visszahívási osztályt használja az összes videokép beolvasásához egy médiafájlból:
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;
}
Kapcsolódó témakörök