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;
}

Controlando uma gravação de DV