Udostępnij przez


Kontrolowanie urządzenia zewnętrznego

[Funkcja skojarzona z tą stroną, DirectShow, jest starszą funkcją. Zostało zastąpione przez MediaPlayer, IMFMediaEngineoraz Audio/Video Capture w Media Foundation. Te funkcje zostały zoptymalizowane pod kątem systemów Windows 10 i Windows 11. Firma Microsoft zdecydowanie zaleca, aby nowy kod używał MediaPlayer, IMFMediaEngine i Audio/Video Capture w programie Media Foundation zamiast DirectShow, jeśli to możliwe. Firma Microsoft sugeruje, że istniejący kod, który używa starszych interfejsów API, należy przepisać go do korzystania z nowych interfejsów API, jeśli to możliwe.]

Aby kontrolować urządzenie rejestratora taśm wideo (VTR), użyj metody IAMExtTransport::put_Mode. Określ nowy stan przy użyciu jednej ze stałych wymienionych w Stan Transportu Urządzenia. Aby na przykład zatrzymać urządzenie, użyj następującego polecenia:

pTransport->put_Mode(ED_MODE_STOP); 

Ponieważ VTR jest urządzeniem fizycznym, zazwyczaj występuje opóźnienie między wydawaniem polecenia a ukończeniem polecenia. Aplikacja powinna utworzyć drugi wątek procesu roboczego, który czeka na zakończenie polecenia. Po zakończeniu polecenia wątek może zaktualizować interfejs użytkownika. Użyj sekcji krytycznej, aby serializować zmianę stanu.

Niektóre VTR mogą powiadamiać aplikację o zmianie stanu działania urządzenia. Jeśli urządzenie obsługuje tę funkcję, wątek procesu roboczego może poczekać na powiadomienie. Zgodnie z dokumentem "AV/C Tape Recorder/Player Subunit Specification" stowarzyszenia 1394 Trade Association, polecenie powiadomienia o stanie transportu jednakże jest opcjonalne, co oznacza, że urządzenia nie muszą go obsługiwać. Jeśli urządzenie nie obsługuje powiadomienia, należy sondować urządzenie w okresowych odstępach czasu dla bieżącego stanu.

W tej sekcji najpierw opisano mechanizm powiadamiania, a następnie opisano sondowanie urządzeń.

Korzystanie z powiadomienia o stanie transportu

Powiadomienie o stanie transportu działa, sygnalizując zdarzenie, gdy transport przełącza się do nowego stanu. W aplikacji zadeklaruj sekcję krytyczną, zdarzenie i uchwyt wątku. Sekcja krytyczna służy do synchronizowania stanu urządzenia. Zdarzenie jest używane do zatrzymania wątku roboczego po zakończeniu działania aplikacji:

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

Po utworzeniu wystąpienia filtru przechwytywania utwórz wątek roboczy.

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

W wątku roboczym zacznij od wywołania metody IAMExtTransport::GetStatus z wartością ED_NOTIFY_HEVENT_GET. To wywołanie zwraca dojście do zdarzenia, które będzie sygnalizowane po zakończeniu operacji:

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

Następnie ponownie wywołaj GetState i przekaż wartość ED_MODE_CHANGE_NOTIFY:

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

Jeśli urządzenie obsługuje powiadomienie, metoda zwraca wartość E_PENDING. (W przeciwnym razie należy monitorować urządzenie, zgodnie z opisem w następnej sekcji). Zakładając, że urządzenie obsługuje powiadomienie, zdarzenie będzie sygnalizowane za każdym razem, gdy zmieni się stan transportu VTR. W tym momencie możesz zaktualizować interfejs użytkownika, aby odzwierciedlał nowy stan. Aby uzyskać następne powiadomienie, zresetuj uchwyt zdarzenia i wywołaj GetStatus ponownie za pomocą ED_MODE_CHANGE_NOTIFY.

Przed zamknięciem wątku roboczego zwolnij uchwyt zdarzenia, wywołując GetStatus z flagą ED_NOTIFY_HEVENT_RELEASE i adresem uchwytu:

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

Poniższy kod przedstawia kompletną procedurę wątku. Przyjmuje się, że funkcja UpdateTransportState jest funkcją aplikacji, która aktualizuje interfejs użytkownika. Należy pamiętać, że wątek czeka na dwa zdarzenia: zdarzenie powiadomienia (hNotify) i zdarzenie zakończenia wątku (hThreadEnd). Należy również pamiętać, gdzie sekcja krytyczna jest używana do ochrony zmiennej stanu urządzenia.

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

Użyj również sekcji krytycznej podczas wydawania poleceń do urządzenia w następujący sposób:

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

Przed zamknięciem aplikacji zatrzymaj wątek pomocniczy, ustawiając zdarzenie zakończenia wątku.

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

Sondowanie stanu transportu

Jeśli wywołasz IAMExtTransport::GetStatus z flagą ED_MODE_CHANGE_NOTIFY, a wartość zwracana nie jest E_PENDING, oznacza to, że urządzenie nie obsługuje powiadomienia. W takim przypadku należy sondować urządzenie, aby określić jego stan. sondowanie po prostu oznacza wywoływanie get_Mode w regularnych odstępach czasu w celu sprawdzenia stanu transportu. Nadal należy używać wątku pomocniczego i sekcji krytycznej zgodnie z wcześniejszym opisem. Wątek wysyła zapytanie do urządzenia o jego stan w regularnych odstępach czasu. W poniższym przykładzie pokazano jeden ze sposobów implementacji wątku:

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

Kontrolowanie kamkordera DV