Cette rubrique explique comment utiliser le lecteur source en mode asynchrone. En mode asynchrone, l’application fournit une interface de rappel, qui est utilisée pour informer l’application que les données sont disponibles.
Le lecteur source fonctionne en mode synchrone ou en mode asynchrone. L’exemple de code présenté dans la section précédente suppose que le lecteur source utilise le mode synchrone, qui est la valeur par défaut. En mode synchrone, la méthode IMFSourceReader::ReadSample bloque tandis que la source multimédia produit l’exemple suivant. Une source multimédia acquiert généralement des données à partir d’une source externe (comme un fichier local ou une connexion réseau), de sorte que la méthode peut bloquer le thread appelant pendant un laps de temps notable.
En mode asynchrone, le ReadSample retourne immédiatement et le travail est effectué sur un autre thread. Une fois l’opération terminée, le lecteur source appelle l’application via l’interface de rappel IMFSourceReaderCallback . Pour utiliser le mode asynchrone, vous devez fournir un pointeur de rappel lors de la première création du lecteur source, comme suit :
Créez un magasin d’attributs en appelant la fonction MFCreateAttributes .
Lorsque vous créez le lecteur source, passez le magasin d’attributs à la fonction de création dans le paramètre pAttributes . Toutes les fonctions permettant de créer le lecteur source ont ce paramètre.
Après avoir créé le lecteur source, vous ne pouvez pas basculer les modes entre synchrone et asynchrone.
Pour obtenir des données en mode asynchrone, appelez la méthode ReadSample , mais définissez les quatre derniers paramètres sur NULL, comme illustré dans l’exemple suivant.
// Request the first sample.
hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, NULL, NULL, NULL, NULL);
hrStatus : contient une valeur HRESULT . Il s’agit de la même valeur que ReadSample retournerait en mode synchrone. Si hrStatus contient un code d’erreur, vous pouvez ignorer les paramètres restants.
dwStreamIndex, dwStreamFlags, llTimestamp et pSample : ces trois paramètres sont équivalents aux trois derniers paramètres de ReadSample. Ils contiennent respectivement le numéro de flux, status indicateurs et le pointeur IMFSample.
En outre, l’interface de rappel définit deux autres méthodes :
OnEvent. Avertit l’application lorsque certains événements se produisent dans la source multimédia, tels que les événements de mise en mémoire tampon ou de connexion réseau.
L’interface de rappel doit être thread-safe, car OnReadSample et les autres méthodes de rappel sont appelées à partir de threads de travail.
Il existe plusieurs approches différentes que vous pouvez adopter lorsque vous implémentez le rappel. Par exemple, vous pouvez effectuer tout le travail à l’intérieur du rappel, ou vous pouvez utiliser le rappel pour notifier l’application (par exemple, en signalant un handle d’événement), puis travailler à partir du thread d’application.
La méthode OnReadSample sera appelée une fois pour chaque appel que vous effectuez à la méthode IMFSourceReader::ReadSample . Pour obtenir l’exemple suivant, appelez à nouveau ReadSample . Si une erreur se produit, OnReadSample est appelé avec un code d’erreur pour le paramètre hrStatus .
L’exemple suivant montre une implémentation minimale de l’interface de rappel. Tout d’abord, voici la déclaration d’une classe qui implémente l’interface.
Dans cet exemple, les méthodes OnEvent et OnFlush ne nous intéressent pas. Elles retournent simplement S_OK. La classe utilise un handle d’événements pour signaler l’application ; ce handle est fourni par le biais du constructeur.
Dans cet exemple minimal, la méthode OnReadSample imprime simplement l’horodatage dans la fenêtre de console. Ensuite, il stocke le code status et l’indicateur de fin de flux, et signale le handle d’événement :
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;
}
Le code suivant montre que l’application utiliserait cette classe de rappel pour lire toutes les images vidéo d’un fichier multimédia :
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;
}
En tant que développeur, vous faites généralement partie des scénarios d’intégration de données pendant et après une implémentation d’applications de finances et d’opérations. Ce module explore les interfaces de programmation d’application (API) web disponibles pour les applications de finances et d’opérations et vous aide à comprendre les principales différences entre les intégrations synchrones et asynchrones pour l’application.