Поделиться через


Управление внешним устройством

[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать в новом коде MediaPlayer, IMFMediaEngine и аудио/видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, в котором используются устаревшие API, чтобы по возможности использовать новые API.]

Для управления устройством записи видео (VTR) используйте метод IAMExtTransport::p ut_Mode . Укажите новое состояние с помощью одной из констант, перечисленных в разделе Состояние транспорта устройства. Например, чтобы остановить устройство, используйте следующую команду:

pTransport->put_Mode(ED_MODE_STOP); 

Так как VTR является физическим устройством, обычно между выполнением команды и выполнением команды возникает задержка. Приложение должно создать второй рабочий поток, который ожидает завершения команды. По завершении выполнения команды поток может обновить пользовательский интерфейс. Используйте критический раздел для сериализации изменения состояния.

Некоторые виртуальные машины могут уведомлять приложение об изменении состояния транспорта устройства. Если устройство поддерживает эту функцию, рабочий поток может ожидать уведомления. Однако согласно спецификации "AV/C Tape Recorder/Player Subunit Specification" (Av/C Tape Recorder/Player Subunit Specification) торговой ассоциации 1394, команда уведомления о состоянии транспорта является необязательной, то есть устройства не требуют ее поддержки. Если устройство не поддерживает уведомления, следует периодически опрашивает устройство на наличие его текущего состояния.

В этом разделе сначала описывается механизм уведомления, а затем описывается опрос устройств.

Использование уведомления о состоянии транспорта

Уведомление о состоянии транспорта работает за счет того, что драйвер сигнализирует о событии, когда транспорт переключается в новое состояние. В приложении объявите критический раздел, событие и дескриптор потока. Критический раздел используется для синхронизации состояния устройства. Событие используется для остановки рабочего потока при выходе из приложения:

HANDLE hThread = 0;
HANDLE hThreadEnd = CreateEvent(NULL, TRUE, FALSE, NULL); 
if (hThreadEnd == NULL)
{
    // Handle error.
}
CRITICAL_SECTION csIssueCmd;
InitializeCriticalSection(&cdIssueCmd);

После создания экземпляра фильтра отслеживания создайте рабочий поток:

DWORD ThreadId;
hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);

В рабочем потоке начните с вызова метода IAMExtTransport::GetStatus со значением ED_NOTIFY_HEVENT_GET. Этот вызов возвращает дескриптор события, которое будет сигнализировать по завершении операции:

// Get the handle to the notification event.
HANDLE hEvent = NULL;
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long*)&hNotify);

Затем снова вызовите GetState и передайте значение ED_MODE_CHANGE_NOTIFY:

LONG State;
hr = pTransport->GetStatus(ED_MODE_CHANGE_NOTIFY, &State);

Если устройство поддерживает уведомление, метод возвращает значение E_PENDING. (В противном случае необходимо опросить устройство, как описано в следующем разделе.) Если устройство поддерживает уведомление, событие будет сигнализировать при каждом изменении состояния транспорта VTR. На этом этапе можно обновить пользовательский интерфейс, чтобы он отражал новое состояние. Чтобы получить следующее уведомление, сбросьте дескриптор события и снова вызовите GetStatus с ED_MODE_CHANGE_NOTIFY.

Перед выходом рабочего потока отпустите дескриптор события, вызвав GetStatus с флагом ED_NOTIFY_HEVENT_RELEASE и адресом дескриптора:

hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long*)&hNotify)

В следующем коде показана полная процедура потока. Предполагается, что функция UpdateTransportState является функцией приложения, которая обновляет пользовательский интерфейс. Обратите внимание, что поток ожидает двух событий: события уведомления (hNotify) и события завершения потока (hThreadEnd). Также обратите внимание, где критический раздел используется для защиты переменной состояния устройства.

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

Кроме того, используйте критический раздел при выполнении команд на устройстве, как показано ниже.

// Issue the "stop" command.
EnterCriticalSection(&csIssueCmd); 
if (SUCCEEDED(hr = pTransport->put_Mode(ED_MODE_STOP)))
{
    UpdateTransportState(ED_MODE_STOP);
}
LeaveCriticalSection(&csIssueCmd); 

Перед завершением работы приложения остановите вторичный поток, задав событие конца потока:

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

Опрос состояния транспорта

Если вы вызываете IAMExtTransport::GetStatus с флагом ED_MODE_CHANGE_NOTIFY, а возвращаемое значение не E_PENDING, это означает, что устройство не поддерживает уведомление. В этом случае необходимо опросить устройство, чтобы определить его состояние. Опрос просто означает вызов get_Mode через регулярные интервалы времени, чтобы проверка состояние транспорта. По-прежнему следует использовать дополнительный поток и критический раздел, как описано ранее. Поток регулярно запрашивает у устройства его состояние. В следующем примере показан один из способов реализации потока:

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

Управление dv camcorder