Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym temacie opisano sposób używania czytnika źródła w trybie asynchronicznym. W trybie asynchronicznym aplikacja udostępnia interfejs wywołania zwrotnego, który służy do powiadamiania aplikacji, że dane są dostępne.
Ten temat zakłada, że znasz już temat Używanie czytnika źródła do przetwarzania danych multimedialnych.
Używanie trybu asynchronicznego
Czytnik źródła działa w trybie synchronicznym lub asynchronicznym. W przykładzie kodu pokazanym w poprzedniej sekcji przyjęto założenie, że czytelnik źródła korzysta z trybu synchronicznego, który jest domyślny. W trybie synchronicznym metoda IMFSourceReader::ReadSample blokuje, gdy źródło multimediów generuje następną próbkę. Źródło multimediów zwykle uzyskuje dane z określonego źródła zewnętrznego (takiego jak plik lokalny lub połączenie sieciowe), dzięki czemu metoda może zablokować wątek wywołujący przez zauważalny czas.
W trybie asynchronicznym ReadSample jest zwracany natychmiast, a praca jest wykonywana w innym wątku. Po zakończeniu operacji czytnik źródła wywołuje aplikację za pośrednictwem interfejsu wywołania zwrotnego IMFSourceReaderCallback. Aby użyć trybu asynchronicznego, należy podać wskaźnik wywołania zwrotnego podczas tworzenia czytnika źródła w następujący sposób:
- Utwórz magazyn atrybutów, wywołując funkcję MFCreateAttributes.
- Ustaw atrybut MF_SOURCE_READER_ASYNC_CALLBACK w sklepie atrybutów. Wartość atrybutu jest wskaźnikiem do obiektu wywołania zwrotnego.
- Podczas tworzenia czytnika źródła przekaż magazyn atrybutów do funkcji tworzenia w parametrze pAttributes. Wszystkie funkcje do utworzenia czytnika źródła mają ten parametr.
W poniższym przykładzie przedstawiono te kroki.
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;
}
Po utworzeniu czytnika źródła nie można przełączać trybów między synchronicznym i asynchronicznym.
Aby pobrać dane w trybie asynchronicznym, wywołaj metodę ReadSample, ale ustaw cztery ostatnie parametry na null, jak pokazano w poniższym przykładzie.
// Request the first sample.
hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, NULL, NULL, NULL, NULL);
Gdy metoda ReadSample zakończy się asynchronicznie, czytnik źródła wywołuje metodę IMFSourceReaderCallback::OnReadSample. Ta metoda ma pięć parametrów:
- hrStatus: zawiera wartość HRESULT. Jest to ta sama wartość, którą ReadSample zwróciłby w trybie synchronicznym. Jeśli hrStatus zawiera kod błędu, możesz zignorować pozostałe parametry.
- dwStreamIndex, dwStreamFlags, llTimestamp i pSample : te trzy parametry są równoważne z trzema ostatnimi parametrami ReadSample. Zawierają one odpowiednio numer strumienia, flagi stanu i wskaźniki IMFSample.
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample);
Ponadto interfejs wywołania zwrotnego definiuje dwie inne metody:
- OnEvent. Powiadamia aplikację o wystąpieniu niektórych zdarzeń w źródle multimediów, takich jak buforowanie lub zdarzenia połączenia sieciowego.
- OnFlush. Wywoływane po zakończeniu metody Flush.
Implementowanie interfejsu wywołania zwrotnego
Interfejs wywołania zwrotnego musi być bezpieczny dla wątków, ponieważ OnReadSample oraz inne metody wywołania zwrotnego są wywoływane z wątków roboczych.
Istnieje kilka różnych podejść, które można wykonać podczas implementowania wywołania zwrotnego. Można na przykład wykonać całą pracę wewnątrz wywołania zwrotnego lub użyć wywołania zwrotnego, aby powiadomić aplikację (na przykład, sygnałem obsługi zdarzeń), a następnie wykonać zadania w wątku aplikacji.
Metoda OnReadSample będzie wywoływana raz dla każdego wywołania wykonywanego do metody IMFSourceReader::ReadSample. Aby uzyskać następny przykład, ponownie wywołaj ReadSample. Jeśli wystąpi błąd, OnReadSample jest wywoływana z kodem błędu dla parametru hrStatus.
Poniższy przykład przedstawia minimalną implementację interfejsu wywołania zwrotnego. Po pierwsze, oto deklaracja klasy, która implementuje interfejs.
#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;
};
W tym przykładzie nie interesuje nas OnEvent i metody OnFlush, więc po prostu zwracają S_OK. Klasa używa uchwytu zdarzenia, aby zasygnalizować aplikację; ten uchwyt jest przekazywany za pośrednictwem konstruktora.
W tym minimalnym przykładzie metoda OnReadSample po prostu wyświetla sygnaturę czasową w oknie konsoli. Następnie przechowuje kod stanu i flagę końca strumienia, i sygnalizuje uchwyt zdarzenia.
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;
}
Poniższy kod pokazuje, że aplikacja będzie używać tej klasy wywołania zwrotnego do odczytywania wszystkich ramek wideo z pliku multimedialnego:
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;
}
Tematy pokrewne