다음을 통해 공유


외부 디바이스 제어

[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngine 및 Media Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드가 DirectShow 대신 Media Foundation에서 MediaPlayer, IMFMediaEngine오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]

VTR(비디오 테이프 레코더) 디바이스를 제어하려면 IAMExtTransport::p ut_Mode 메서드를 사용합니다. 디바이스 전송 상태에 나열된 상수 중 하나를 사용하여 새 상태를 지정합니다. 예를 들어 디바이스를 중지하려면 다음을 사용합니다.

pTransport->put_Mode(ED_MODE_STOP); 

VTR은 물리적 디바이스이므로 일반적으로 명령 실행과 명령이 완료되는 시점 사이에 지연이 있습니다. 애플리케이션은 명령이 완료되기를 기다리는 두 번째 작업자 스레드를 만들어야 합니다. 명령이 완료되면 스레드가 사용자 인터페이스를 업데이트할 수 있습니다. 중요 섹션을 사용하여 상태 변경을 직렬화합니다.

일부 VTR은 디바이스의 전송 상태가 변경되면 애플리케이션에 알릴 수 있습니다. 디바이스가 이 기능을 지원하는 경우 작업자 스레드는 알림을 기다릴 수 있습니다. 그러나 1394 무역 협회의 "AV/C 테이프 레코더/플레이어 Subunit 사양"에 따르면 전송 상태 알림 명령은 선택 사항이므로 디바이스가 이를 지원할 필요가 없습니다. 디바이스가 알림을 지원하지 않는 경우 디바이스의 현재 상태에 대해 주기적으로 디바이스를 폴링해야 합니다.

이 섹션에서는 먼저 알림 메커니즘에 대해 설명한 다음 디바이스 폴링을 설명합니다.

전송 상태 알림 사용

전송 상태 알림은 전송이 새 상태로 전환할 때 드라이버가 이벤트를 신호하도록 하여 작동합니다. 애플리케이션에서 중요한 섹션, 이벤트 및 스레드 핸들을 선언합니다. 중요 섹션은 디바이스 상태를 동기화하는 데 사용됩니다. 이 이벤트는 애플리케이션이 종료되면 작업자 스레드를 중지하는 데 사용됩니다.

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

캡처 필터의 instance 만든 후 작업자 스레드를 만듭니다.

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

작업자 스레드에서 값이 ED_NOTIFY_HEVENT_GET IAMExtTransport::GetStatus 메서드를 호출하여 시작합니다. 이 호출은 작업이 완료될 때 신호를 받을 이벤트에 대한 핸들을 반환합니다.

// 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 전송 상태가 변경될 때마다 이벤트가 신호를 보냅니다. 이 시점에서 새 상태를 반영하도록 사용자 인터페이스를 업데이트할 수 있습니다. 다음 알림을 받으려면 이벤트 핸들을 다시 시작하고 ED_MODE_CHANGE_NOTIFY 사용하여 GetStatus 를 다시 호출합니다.

작업자 스레드가 종료되기 전에 플래그 ED_NOTIFY_HEVENT_RELEASE 및 핸들 주소를 사용하여 GetStatus 를 호출하여 이벤트 핸들을 해제합니다.

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

전송 상태 폴링

ED_MODE_CHANGE_NOTIFY 플래그를 사용하여 IAMExtTransport::GetStatus 를 호출하고 반환 값이 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 캠코더 제어