Controlar un dispositivo externo
[La característica asociada a esta página, DirectShow, es una característica heredada. Se ha reemplazado por MediaPlayer, IMFMediaEngine y Captura de audio/vídeo en Media Foundation. Esas características se han optimizado para Windows 10 y Windows 11. Microsoft recomienda encarecidamente que el nuevo código use MediaPlayer, IMFMediaEngine y Audio/Video Capture en Media Foundation en lugar de DirectShow, siempre que sea posible. Microsoft sugiere que el código existente que usa las API heredadas se reescriba para usar las nuevas API si es posible.
Para controlar un dispositivo de grabadora de cinta de vídeo (VTR), use el método IAMExtTransport::p ut_Mode . Especifique el nuevo estado mediante una de las constantes enumeradas en estado de transporte del dispositivo. Por ejemplo, para detener el dispositivo, use lo siguiente:
pTransport->put_Mode(ED_MODE_STOP);
Dado que el VTR es un dispositivo físico, normalmente hay un retraso entre emitir el comando y cuando se completa el comando. La aplicación debe crear un segundo subproceso de trabajo que espere a que finalice el comando. Cuando finalice el comando, el subproceso puede actualizar la interfaz de usuario. Use una sección crítica para serializar el cambio de estado.
Algunas VTR pueden notificar a la aplicación cuando el estado de transporte del dispositivo ha cambiado. Si el dispositivo admite esta característica, el subproceso de trabajo puede esperar la notificación. Según la especificación de subunidad av/C de la Asociación comercial de 1394, sin embargo, el comando de notificación de estado de transporte es opcional, lo que significa que los dispositivos no son necesarios para admitirlo. Si un dispositivo no admite notificaciones, debe sondear el dispositivo a intervalos periódicos para su estado actual.
En esta sección se describe primero el mecanismo de notificación y, a continuación, se describe el sondeo de dispositivos.
Uso de la notificación de estado de transporte
La notificación de estado de transporte funciona teniendo el controlador señalizando un evento cuando el transporte cambia a un nuevo estado. En la aplicación, declare una sección crítica, un evento y un identificador de subproceso. La sección crítica se usa para sincronizar el estado del dispositivo. El evento se usa para detener el subproceso de trabajo cuando se cierra la aplicación:
HANDLE hThread = 0;
HANDLE hThreadEnd = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hThreadEnd == NULL)
{
// Handle error.
}
CRITICAL_SECTION csIssueCmd;
InitializeCriticalSection(&cdIssueCmd);
Después de crear una instancia del filtro de captura, cree el subproceso de trabajo:
DWORD ThreadId;
hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
En el subproceso de trabajo, empiece llamando al método IAMExtTransport::GetStatus con el valor ED_NOTIFY_HEVENT_GET. Esta llamada devuelve un identificador a un evento que se indicará cuando se complete una operación:
// Get the handle to the notification event.
HANDLE hEvent = NULL;
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long*)&hNotify);
A continuación, vuelva a llamar a GetState y pase el valor ED_MODE_CHANGE_NOTIFY:
LONG State;
hr = pTransport->GetStatus(ED_MODE_CHANGE_NOTIFY, &State);
Si el dispositivo admite la notificación, el método devuelve el valor E_PENDING. (De lo contrario, debe sondear el dispositivo, como se describe en la sección siguiente). Suponiendo que el dispositivo admita la notificación, el evento se indicará siempre que cambie el estado del transporte de VTR. En este momento, puede actualizar la interfaz de usuario para reflejar el nuevo estado. Para obtener la siguiente notificación, restablezca el identificador de eventos y llame a GetStatus con ED_MODE_CHANGE_NOTIFY de nuevo.
Antes de que se cierre el subproceso de trabajo, libere el identificador de eventos mediante una llamada a GetStatus con la marca ED_NOTIFY_HEVENT_RELEASE y la dirección del identificador:
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long*)&hNotify)
En el código siguiente se muestra el procedimiento de subproceso completo. Se supone que la función UpdateTransportState es una función de aplicación que actualiza la interfaz de usuario. Tenga en cuenta que el subproceso espera dos eventos: el evento de notificación (hNotify) y el evento de terminación del subproceso (hThreadEnd). Tenga en cuenta también dónde se usa la sección crítica para proteger la variable de estado 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;
}
Use también la sección crítica al emitir comandos al dispositivo, como se indica a continuación:
// Issue the "stop" command.
EnterCriticalSection(&csIssueCmd);
if (SUCCEEDED(hr = pTransport->put_Mode(ED_MODE_STOP)))
{
UpdateTransportState(ED_MODE_STOP);
}
LeaveCriticalSection(&csIssueCmd);
Antes de que se cierre la aplicación, detenga el subproceso secundario estableciendo el evento de fin del subproceso:
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);
Sondeo del estado de transporte
Si llamas a IAMExtTransport::GetStatus con la marca ED_MODE_CHANGE_NOTIFY y el valor devuelto no es E_PENDING, significa que el dispositivo no admite la notificación. En ese caso, debe sondear el dispositivo para determinar su estado. El sondeo simplemente significa llamar a get_Mode a intervalos regulares para comprobar el estado de transporte. Debe seguir usando un subproceso secundario y una sección crítica, como se ha descrito anteriormente. El subproceso consulta el dispositivo por su estado a intervalos regulares. En el ejemplo siguiente se muestra una manera de implementar el subproceso:
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;
}
Temas relacionados