Controlando um dispositivo externo
[O recurso associado a esta página, DirectShow, é um recurso herdado. Ele foi substituído por MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo na Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda fortemente que o novo código use MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo no Media Foundation em vez de DirectShow, quando possível. A Microsoft sugere que o código existente que usa as APIs herdadas seja reescrito para usar as novas APIs, se possível.]
Para controlar um dispositivo de gravador de vídeo (VTR), use o método IAMExtTransport::p ut_Mode . Especifique o novo estado usando uma das constantes listadas no Estado de Transporte do Dispositivo. Por exemplo, para interromper o dispositivo, use o seguinte:
pTransport->put_Mode(ED_MODE_STOP);
Como a VTR é um dispositivo físico, normalmente há um retardo entre emitir o comando e quando o comando é concluído. Seu aplicativo deve criar um segundo thread de trabalho que aguarda a conclusão do comando. Quando o comando for concluído, o thread poderá atualizar a interface do usuário. Use uma seção crítica para serializar a alteração de estado.
Algumas VTRs podem notificar o aplicativo quando o estado de transporte do dispositivo for alterado. Se o dispositivo der suporte a esse recurso, o thread de trabalho poderá aguardar a notificação. De acordo com a "Especificação de Subunidade de Gravador de Fita de AV/C/Player" da Associação Comercial de 1394, no entanto, o comando de notificação de estado de transporte é opcional, o que significa que os dispositivos não são necessários para dar suporte a ele. Se um dispositivo não der suporte à notificação, você deverá sondar o dispositivo em intervalos periódicos para seu estado atual.
Esta seção descreve primeiro o mecanismo de notificação e, em seguida, descreve a sondagem do dispositivo.
Usando a notificação de estado de transporte
A notificação de estado de transporte funciona fazendo com que o driver sinalize um evento quando o transporte alterna para um novo estado. Em seu aplicativo, declare uma seção crítica, um evento e um identificador de thread. A seção crítica é usada para sincronizar o estado do dispositivo. O evento é usado para interromper o thread de trabalho quando o aplicativo é encerrado:
HANDLE hThread = 0;
HANDLE hThreadEnd = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hThreadEnd == NULL)
{
// Handle error.
}
CRITICAL_SECTION csIssueCmd;
InitializeCriticalSection(&cdIssueCmd);
Depois de criar uma instância do filtro de captura, crie o thread de trabalho:
DWORD ThreadId;
hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
No thread de trabalho, comece chamando o método IAMExtTransport::GetStatus com o valor ED_NOTIFY_HEVENT_GET. Essa chamada retorna um identificador para um evento que será sinalizado quando uma operação for concluída:
// Get the handle to the notification event.
HANDLE hEvent = NULL;
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long*)&hNotify);
Em seguida, chame GetState novamente e passe o valor ED_MODE_CHANGE_NOTIFY:
LONG State;
hr = pTransport->GetStatus(ED_MODE_CHANGE_NOTIFY, &State);
Se o dispositivo der suporte à notificação, o método retornará o valor E_PENDING. (Caso contrário, você deve sondar o dispositivo, conforme descrito na próxima seção.) Supondo que o dispositivo dê suporte à notificação, o evento será sinalizado sempre que o estado do transporte VTR for alterado. Neste ponto, você pode atualizar a interface do usuário para refletir o novo estado. Para obter a próxima notificação, redefina o identificador de evento e chame GetStatus com ED_MODE_CHANGE_NOTIFY novamente.
Antes que o thread de trabalho seja encerrado, solte o identificador de evento chamando GetStatus com o sinalizador ED_NOTIFY_HEVENT_RELEASE e o endereço do identificador:
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long*)&hNotify)
O código a seguir mostra o procedimento de thread completo. A função UpdateTransportState é considerada uma função de aplicativo que atualiza a interface do usuário. Observe que o thread aguarda dois eventos: o evento de notificação (hNotify) e o evento de encerramento de thread (hThreadEnd). Observe também onde a seção crítica é usada para proteger a variável de estado do 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;
}
Use também a seção crítica ao emitir comandos para o dispositivo, da seguinte maneira:
// Issue the "stop" command.
EnterCriticalSection(&csIssueCmd);
if (SUCCEEDED(hr = pTransport->put_Mode(ED_MODE_STOP)))
{
UpdateTransportState(ED_MODE_STOP);
}
LeaveCriticalSection(&csIssueCmd);
Antes que o aplicativo seja encerrado, interrompa o thread secundário definindo o 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);
Sondando o estado do transporte
Se você chamar IAMExtTransport::GetStatus com o sinalizador ED_MODE_CHANGE_NOTIFY e o valor retornado não for E_PENDING, isso significa que o dispositivo não dá suporte à notificação. Nesse caso, você deve sondar o dispositivo para determinar seu estado. Sondar significa simplesmente chamar get_Mode em intervalos regulares para marcar o estado de transporte. Você ainda deve usar um thread secundário e uma seção crítica, conforme descrito anteriormente. O thread consulta o dispositivo por seu estado em um intervalo regular. O exemplo a seguir mostra uma maneira de implementar o 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;
}
Tópicos relacionados