デバイス制御
カムコーダ デバイスを制御するには、IAMExtTransport::put_Mode メソッドを使う。上述の一覧の中のいずれかの定数を使って、新しい状態を指定する。たとえば、デバイスを停止するには、以下のコードを使う。
MyDevCap.pTransport->put_Mode(ED_MODE_STOP);
カムコーダは物理デバイスなので、コマンドを発行してからそれが完了するまでにある程度時間がかかる。そのため、アプリケーションはコマンドの完了を待つ二次スレッドを作成しておく必要がある。コマンドが完了すると、そのスレッドがユーザー インターフェイスを更新できる。状態変化が順番に行われるように、クリティカル セクションを使う。
カムコーダの中には、デバイスの状態が変化したときにアプリケーションに通知できるものがある。デバイスがこの機能をサポートしている場合、二次スレッドはその通知を待つことができる。サポートしていない場合、アプリケーションはデバイスを定期的にポーリングしなければならない。
通知
最初に、クリティカル セクションの変数、イベント、スレッド ハンドルを宣言する。クリティカル セクションは、デバイスの状態を同期するために使う。イベントは、アプリケーションが終了したときに二次スレッドを終了させるために使う。
CRITICAL_SECTION csIssueCmd;
HANDLE hThreadEndEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE hThread = 0;
キャプチャ フィルタのインスタンスを生成した後、二次スレッドを作成する。
DWORD ThreadId;
hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
二次スレッドの中で、IAMExtTransport::GetStatus メソッドを 2 回呼び出す。最初の呼び出しで、値 ED_NOTIFY_HEVENT_GET を渡す。この呼び出しでは、処理が完了すると通知済みになるイベントのハンドルを取得する。
HANDLE hEvent = NULL;
hr = MyDevCap.pTransport->GetStatus(ED_NOTIFY_HEVENT_GET, (long *)&hEvent);
2 回目の呼び出しでは、デバイスの状態を受け取る変数へのポインタと共に、値 ED_MODE_CHANGE_NOTIFY を渡す。
LONG State;
hr = MyDevCap.pTransport->GetStatus(ED_MODE_CHANGE_NOTIFY, &State);
デバイスが通知をサポートしている場合、この呼び出しの戻り値は E_PENDING である。サポートしていない場合は、以下で説明するようにデバイスをポーリングする必要がある。
デバイスが通知をサポートしていると仮定すると、イベントが通知済み状態になるまで待機すること。次の処理が完了すると、イベントが通知済み状態になり、State の値でデバイスの新しい状態が指定される。ここで、新しい状態を反映するようにユーザー インターフェイスを更新できる。フラグ ED_NOTIFY_HEVENT_RELEASE とハンドルのアドレスを指定して GetStatus を呼び出し、イベント ハンドルを解放する。
以下のコードは、完全なスレッドの処理を示したものである。ユーザー インターフェイスを更新する UpdateTransportState 関数は、アプリケーションで定義されているものとする。スレッドは、通知イベント (hEvent) と、スレッド終了イベント (hThreadEndEvent) の、2 つのイベントを待つことに注意してほしい。また、デバイス状態の変数を保護するために、クリティカル セクションを使っている部分にも注意すること。
DWORD WINAPI ThreadProc(void *pParam)
{
HRESULT hr;
HANDLE EventHandles[2];
HANDLE hEvent = NULL;
DWORD WaitStatus;
LONG State;
// 次の処理が完了すると通知済みになるイベントを取得する。
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) { // デバイスは通知をサポートする。
EventHandles[0] = hEvent;
EventHandles[1] = hThreadEndEvent;
WaitStatus = WaitForMultipleObjects(2, EventHandles, FALSE, INFINITE);
if(WAIT_OBJECT_0 == WaitStatus) {
UpdateTransportState(State);
}
else {
break; // このスレッドを終了する。
}
}
else {
break; // このスレッドを終了する (デバイスは通知をサポートしない)。
}
} // while
// 通知を取り消す。
hr = MyDevCap.pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long *) &hEvent);
return 1;
}
以下のように、デバイスにコマンドを発行するときにもクリティカル セクションを使う。
// "stop" コマンドを発行する。
EnterCriticalSection(&csIssueCmd);
if(hr = MyDevCap.pTransport->put_Mode(ED_MODE_STOP), SUCCEEDED(hr))
UpdateTransportState(ED_MODE_STOP);
LeaveCriticalSection(&csIssueCmd);
アプリケーションが終了する前に、二次スレッドを終了させる。
if (hThread)
{
// このイベントを通知すると、スレッドは終了する。
if (SetEvent(ThreadEndEvent)) {
// 終了するまで待機する。
WaitForSingleObjectEx(hThread, INFINITE, FALSE);
}
}
CloseHandle(hThreadEndEvent);
CloseHandle(hThread);
デバイスのポーリング
デバイスが通知をサポートしていない場合は、値 ED_MODE_CHANGE_NOTIFY フラグで IAMExtTransport::GetStatus を呼び出すと、E_PENDING 以外の値が返される。その場合は、デバイスをポーリングして状態を判定しなければならない。
前に説明したように、ここでも二次スレッドとクリティカル セクションを使うべきである。このスレッドは、一定間隔でデバイスに状態を問い合わせる。以下の例は、スレッドの実装方法の 1 つを示したものである。
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);
// 少しの間、またはスレッドが終了するまで待機する。
WaitStatus = WaitForSingleObjectEx(hThreadEndEvent, 200, FALSE);
if (WaitStatus == WAIT_OBJECT_0)
break; // スレッドをすぐに終了する。
// それ以外の場合、待機はタイムアウトになり、
// もう一度ポーリングする時間になる。
}
return 1;
}