控制外部裝置
[與此頁面 相關的功能 DirectShow是舊版功能。 它已被 MediaPlayer、 IMFMediaEngine和 Media Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayer、 IMFMediaEngine 和 音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議盡可能重寫使用舊版 API 的現有程式碼,以使用新的 API。]
若要控制 VTR) 裝置 (視訊磁帶錄製器,請使用 IAMExtTransport::p ut_Mode 方法。 使用 裝置傳輸狀態中列出的其中一個常數來指定新狀態。 例如,若要停止裝置,請使用下列專案:
pTransport->put_Mode(ED_MODE_STOP);
因為 VTR 是實體裝置,所以發出命令和命令完成時通常會有延遲。 您的應用程式應該會建立第二個背景工作執行緒,等候命令完成。 當命令完成時,執行緒可以更新使用者介面。 使用重要區段來序列化狀態變更。
某些 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 。
在背景工作執行緒結束之前,請使用 旗標呼叫 GetStatus 並ED_NOTIFY_HEVENT_RELEASE和控制碼的位址來釋放事件控制碼:
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;
}
相關主題