Microsoft DirectX 9.0

Device Control

To control a camcorder device, use the IAMExtTransport::put_Mode method. Specify the new state by using one of the constants listed in the previous section. For example, to stop the device, use the following:

MyDevCap.pTransport->put_Mode(ED_MODE_STOP); 

Because the camcorder is a physical device, there might be a lag between when the command is issued and when it completes. Your application should create a secondary thread that waits for the command to finish. When the command finishes, the thread can update the user interface. Use a critical section to serialize the state change.

Some camcorders can notify the application when the state of the device changes. If the device supports this feature, the secondary thread can wait for the notification. If not, the application must poll the device at periodic intervals.

Notification

First declare a critical section variable, an event, and a thread handle. The critical section is used to synchronize the device state. The event is used to end the secondary thread when the application exits:

CRITICAL_SECTION csIssueCmd;
HANDLE           hThreadEndEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
HANDLE           hThread = 0;

After you create an instance of the capture filter, create the secondary thread:

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

Within the secondary thread, make two calls to the IAMExtTransport::GetStatus method. In the first call, pass the value ED_NOTIFY_HEVENT_GET. This call retrieves a handle to an event that will be signaled when an operation completes:

HANDLE hEvent = NULL;
hr = MyDevCap.pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long *)&hEvent);

In the second call, pass the value ED_MODE_CHANGE_NOTIFY, along with a pointer to a variable that will receive the device state:

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

If the device supports notification, the return value from this call is E_PENDING. Otherwise, you must poll device, as described in the next section.

Assuming that the device does support notification, wait for the event to be signaled. When the next operation completes, the event is signaled and the value of State specifies the new device state. At this point, you can update the user interface to reflect the new state. Release the event handle by calling GetStatus with the flag ED_NOTIFY_HEVENT_RELEASE and the address of the handle.

The following code shows the complete thread procedure. The function UpdateTransportState is assumed to be an application-defined function that updates the user interface. Note that the thread waits for two events: the notification event (hEvent) and the thread-termination event (hThreadEndEvent). Also note where the critical section is used to protect the device state variable.

DWORD WINAPI ThreadProc(void *pParam)
{
    HRESULT hr;
    HANDLE  EventHandles[2];
    HANDLE  hEvent = NULL;
    DWORD   WaitStatus;
    LONG    State;

    // Get an event, which will be signaled when the next operation completes.   
    hr = MyDevCap.pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long *) &hEvent);

    while (hThread && hEvent && hThreadEndEvent) {
        EnterCriticalSection(&csIssueCmd);  
            State = 0;
            hr = MyDevCap.pTransport->GetStatus(ED_MODE_CHANGE_NOTIFY, &State);
        LeaveCriticalSection(&csIssueCmd); 

        if(hr == E_PENDING) {             // Device supports notification.
            EventHandles[0] = hEvent;
            EventHandles[1] = hThreadEndEvent;
            WaitStatus = WaitForMultipleObjects(2, EventHandles, FALSE, INFINITE);
            if(WAIT_OBJECT_0 == WaitStatus) {
                UpdateTransportState(State);
            } 
            else {
                break;  // End this thread.
            }
        } 
        else {          
            break;  // End this thread. (Device does not support notification.)
        } 

    } // while

    // Cancel notification.
    hr = MyDevCap.pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long *) &hEvent);
    return 1; 
}

Also use the critical section when you issue commands to the device, as follows:

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

Before the application exits, end the secondary thread:

if (hThread) 
{
    // Signaling this event will cause the thread to end.    
    if (SetEvent(ThreadEndEvent)) {
        // Wait for it to end.
        WaitForSingleObjectEx(hThread, INFINITE, FALSE);
    }
}
CloseHandle(hThreadEndEvent);
CloseHandle(hThread);

Polling the Device

If the device does not support notification, then calling IAMExtTransport::GetStatus with the ED_MODE_CHANGE_NOTIFY flag returns a value other than E_PENDING. In that case, you must poll the device to determine its state.

You should still use a secondary thread and a critical section, as described earlier. The thread queries the device for its state at a regular interval. The following example shows one way to implement the thread:

DWORD WINAPI ThreadProc(void *pParam)
{
    HRESULT hr;
    LONG State;
    DWORD WaitStatus;

    while (hThread && hThreadEndEvent) 
    {
        EnterCriticalSection(&csIssueCmd);  
            State = 0;
            hr = MyDevCap.pTransport->get_Mode(&State);
        LeaveCriticalSection(&csIssueCmd); 
        UpdateTransportState(State);

        // Wait for a while, or until the thread ends. 
        WaitStatus = WaitForSingleObjectEx(hThreadEndEvent, 200, FALSE); 
        if (WaitStatus == WAIT_OBJECT_0)
            break; // Exit thread now. 
                   // Otherwise, the wait timed out. Time to poll again.
    }
    return 1;
}