外部デバイスの制御
[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、および Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayer、IMFMediaEngine、Audio/Video Capture を使用することを強くお勧めします。 Microsoft は、従来の API を使用する既存のコードを、可能であれば新しい API を使用するように書き直すよう提案しています。]
ビデオ テープ レコーダー (VTR) デバイスを制御するには、 IAMExtTransport::p ut_Mode メソッドを使用します。 デバイス トランスポート状態に記載されている定数のいずれかを使用して、新しい状態を指定します。 たとえば、デバイスを停止するには、次のコマンドを使用します。
pTransport->put_Mode(ED_MODE_STOP);
VTR は物理デバイスであるため、通常、コマンドの発行とコマンドの完了までの間に遅延があります。 アプリケーションでは、コマンドの完了を待機する 2 つ目のワーカー スレッドを作成する必要があります。 コマンドが完了すると、スレッドはユーザー インターフェイスを更新できます。 状態の変更をシリアル化するには、クリティカル セクションを使用します。
一部の VTR は、デバイスのトランスポート状態が変更されたときにアプリケーションに通知できます。 デバイスがこの機能をサポートしている場合、ワーカー スレッドは通知を待機できます。 ただし、1394 貿易協会の "AV/C テープ レコーダー/プレーヤー サブユニット仕様" によると、トランスポート状態通知コマンドは省略可能です。つまり、デバイスでサポートする必要はありません。 デバイスが通知をサポートしていない場合は、デバイスの現在の状態を定期的にポーリングする必要があります。
このセクションでは、まず通知メカニズムについて説明し、次にデバイスのポーリングについて説明します。
トランスポート状態通知の使用
トランスポート状態通知は、トランスポートが新しい状態に切り替わるときに、ドライバーがイベントを通知することで機能します。 アプリケーションで、クリティカル セクション、イベント、スレッド ハンドルを宣言します。 重要なセクションは、デバイスの状態を同期するために使用されます。 イベントは、アプリケーションが終了したときにワーカー スレッドを停止するために使用されます。
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 トランスポートの状態が変更されるたびにイベントが通知されます。 この時点で、新しい状態を反映するようにユーザー インターフェイスを更新できます。 次の通知を取得するには、イベント ハンドルをリセットし、ED_MODE_CHANGE_NOTIFYを使用して GetStatus をもう一度呼び出します。
ワーカー スレッドが終了する前に、フラグ ED_NOTIFY_HEVENT_RELEASEとハンドルのアドレスを指定して GetStatus を 呼び出して、イベント ハンドルを解放します。
hr = pTransport->GetStatus(ED_NOTIFY_HEVENT_RELEASE, (long*)&hNotify)
次のコードは、完全なスレッド プロシージャを示しています。 UpdateTransportState 関数は、ユーザー インターフェイスを更新するアプリケーション関数であると見なされます。 スレッドは、通知イベント (hNotify) とスレッド終了イベント (hThreadEnd) の 2 つのイベントを待機します。 また、デバイス状態変数を保護するためにクリティカル セクションが使用される場所にも注意してください。
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を呼び出してトランスポート状態をチェックすることを意味します。 前に説明したように、セカンダリ スレッドとクリティカル セクションを引き続き使用する必要があります。 スレッドは、デバイスの状態を定期的に照会します。 次の例は、スレッドを実装する 1 つの方法を示しています。
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;
}
関連トピック