Controllo di un dispositivo esterno
[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stata sostituita da MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation. Queste funzionalità sono state ottimizzate per Windows 10 e Windows 11. Microsoft consiglia vivamente che il nuovo codice usi MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation invece di DirectShow, quando possibile. Microsoft suggerisce che il codice esistente che usa le API legacy venga riscritto per usare le nuove API, se possibile.
Per controllare un dispositivo videoregistratore (VTR), usare il metodo IAMExtTransport::p ut_Mode . Specificare il nuovo stato usando una delle costanti elencate in Stato trasporto dispositivi. Ad esempio, per arrestare il dispositivo, usare quanto segue:
pTransport->put_Mode(ED_MODE_STOP);
Poiché la VTR è un dispositivo fisico, si verifica in genere un ritardo tra l'esecuzione del comando e il completamento del comando. L'applicazione deve creare un secondo thread di lavoro che attende il completamento del comando. Al termine del comando, il thread può aggiornare l'interfaccia utente. Usare una sezione critica per serializzare la modifica dello stato.
Alcuni VTR possono notificare all'applicazione quando lo stato del trasporto del dispositivo è cambiato. Se il dispositivo supporta questa funzionalità, il thread di lavoro può attendere la notifica. Secondo la specifica "AV/C Tape Recorder/Player Subunit Specification" dell'Associazione commerciale del 1394, tuttavia, il comando di notifica dello stato del trasporto è facoltativo, ovvero i dispositivi non sono necessari per supportarlo. Se un dispositivo non supporta la notifica, è consigliabile eseguire il polling del dispositivo a intervalli periodici per lo stato corrente.
Questa sezione descrive innanzitutto il meccanismo di notifica e quindi descrive il polling dei dispositivi.
Uso della notifica dello stato del trasporto
La notifica dello stato del trasporto funziona facendo in modo che il driver segnali un evento quando il trasporto passa a un nuovo stato. Nell'applicazione dichiarare una sezione critica, un evento e un handle di thread. La sezione critica viene usata per sincronizzare lo stato del dispositivo. L'evento viene usato per interrompere il thread di lavoro quando l'applicazione viene chiusa:
HANDLE hThread = 0;
HANDLE hThreadEnd = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hThreadEnd == NULL)
{
// Handle error.
}
CRITICAL_SECTION csIssueCmd;
InitializeCriticalSection(&cdIssueCmd);
Dopo aver creato un'istanza del filtro di acquisizione, creare il thread di lavoro:
DWORD ThreadId;
hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
Nel thread di lavoro iniziare chiamando il metodo IAMExtTransport::GetStatus con il valore ED_NOTIFY_HEVENT_GET. Questa chiamata restituisce un handle a un evento che verrà segnalato al termine di un'operazione:
// Get the handle to the notification event.
HANDLE hEvent = NULL;
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long*)&hNotify);
Chiamare quindi di nuovo GetState e passare il valore ED_MODE_CHANGE_NOTIFY:
LONG State;
hr = pTransport->GetStatus(ED_MODE_CHANGE_NOTIFY, &State);
Se il dispositivo supporta la notifica, il metodo restituisce il valore E_PENDING. In caso contrario, è necessario eseguire il polling del dispositivo, come descritto nella sezione successiva. Supponendo che il dispositivo supporti la notifica, l'evento verrà segnalato ogni volta che lo stato del trasporto VTR cambia. A questo punto, è possibile aggiornare l'interfaccia utente in modo da riflettere il nuovo stato. Per ottenere la notifica successiva, reimpostare l'handle eventi e chiamare nuovamente GetStatus con ED_MODE_CHANGE_NOTIFY.
Prima dell'uscita del thread di lavoro, rilasciare l'handle dell'evento chiamando GetStatus con il flag ED_NOTIFY_HEVENT_RELEASE e l'indirizzo dell'handle:
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long*)&hNotify)
Nel codice seguente viene illustrata la procedura di thread completa. Si presuppone che la funzione UpdateTransportState sia una funzione dell'applicazione che aggiorna l'interfaccia utente. Si noti che il thread attende due eventi: l'evento di notifica (hNotify) e l'evento di terminazione del thread (hThreadEnd). Si noti anche dove viene usata la sezione critica per proteggere la variabile di stato del dispositivo.
DWORD WINAPI ThreadProc(void *pParam)
{
HRESULT hr;
HANDLE EventHandles[2];
HANDLE hNotify = NULL;
DWORD WaitStatus;
LONG State;
// Get the notification event handle. This event will be signaled when
// the next state-change operation completes.
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long*)&hNotify);
while (hThread && hNotify && hThreadEnd)
{
EnterCriticalSection(&csIssueCmd);
// Ask the device to notify us when the state changes.
hr = pTransport->GetStatus(ED_MODE_CHANGE_NOTIFY, &State);
LeaveCriticalSection(&csIssueCmd);
if(hr == E_PENDING) // The device supports notification.
{
// Wait for the notification.
EventHandles[0] = hNotify;
EventHandles[1] = hThreadEnd;
WaitStatus = WaitForMultipleObjects(2, EventHandles, FALSE, INFINITE);
if(WAIT_OBJECT_0 == WaitStatus)
{
// We got notified. Query for the new state.
EnterCriticalSection(&csIssueCmd);
hr = m_pTransport->get_Mode(State);
UpdateTransportState(State); // Update the UI.
LeaveCriticalSection(&m_csIssueCmd);
ResetEvent(hNotify);
}
else {
break; // End this thread.
}
}
else {
// The device does not support notification.
PollDevice();
}
} // while
// Cancel notification.
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long*)&hNotify);
return 1;
}
Usare anche la sezione critica quando si emettono comandi al dispositivo, come indicato di seguito:
// Issue the "stop" command.
EnterCriticalSection(&csIssueCmd);
if (SUCCEEDED(hr = pTransport->put_Mode(ED_MODE_STOP)))
{
UpdateTransportState(ED_MODE_STOP);
}
LeaveCriticalSection(&csIssueCmd);
Prima dell'uscita dall'applicazione, arrestare il thread secondario impostando l'evento thread-end:
if (hThread)
{
// Signaling this event will cause the thread to end.
if (SetEvent(hThreadEnd))
{
// Wait for it to end.
WaitForSingleObjectEx(hThread, INFINITE, FALSE);
}
}
CloseHandle(hThreadEnd);
CloseHandle(hThread);
Polling dello stato del trasporto
Se chiami IAMExtTransport::GetStatus con il flag ED_MODE_CHANGE_NOTIFY e il valore restituito non è E_PENDING, significa che il dispositivo non supporta la notifica. In tal caso, è necessario eseguire il polling del dispositivo per determinarne lo stato. Il polling significa semplicemente chiamare get_Mode a intervalli regolari per controllare lo stato del trasporto. È comunque consigliabile usare un thread secondario e una sezione critica, come descritto in precedenza. Il thread esegue una query sul dispositivo per il relativo stato a intervalli regolari. L'esempio seguente illustra un modo per implementare il thread:
DWORD WINAPI ThreadProc(void *pParam)
{
HRESULT hr;
LONG State;
DWORD WaitStatus;
while (hThread && hThreadEnd)
{
EnterCriticalSection(&csIssueCmd);
State = 0;
hr = pTransport->get_Mode(&State);
LeaveCriticalSection(&csIssueCmd);
UpdateTransportState(State);
// Wait for a while, or until the thread ends.
WaitStatus = WaitForSingleObjectEx(hThreadEnd, 200, FALSE);
if (WaitStatus == WAIT_OBJECT_0)
{
break; // Exit thread now.
}
// Otherwise, the wait timed out. Time to poll again.
}
return 1;
}
Argomenti correlati