這篇文章會教你如何連接一個或多個遠端攝影機,並取得一個MediaFrameSourceGroup物件,讓你能讀取每個攝影機的影格。 欲了解更多媒體來源讀取框架的資訊,請參閱 使用 MediaFrameReader 處理媒體框架。 欲了解更多與裝置配對的資訊,請參見 「配對裝置」。
Note
本文所討論的功能自 Windows 10 版本 1903 起開始可用。
建立一個 DeviceWatcher 類別來監控可用的遠端攝影機
DeviceWatcher 類別會監控應用程式可用的裝置,並在裝置新增或移除時通知應用程式。 透過呼叫 DeviceInformation.CreateWatcher,並傳入一個先進查詢語法(AQS)字串,以識別你想監控的裝置類型,來取得 DeviceWatcher 實例。 指定網路攝影機設備的 AQS 字串如下:
@"System.Devices.InterfaceClassGuid:=""{B8238652-B500-41EB-B4F3-4234F7F5AE99}"" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True"
Note
輔助方法 MediaFrameSourceGroup.GetDeviceSelector 會回傳一個 AQS 字串,用以監控本地連接及遠端網路攝影機。 如果只監控網路攝影機,應該使用上面顯示的 AQS 字串。
當您呼叫 Start 方法來啟動傳回的 DeviceWatcher 時,它會針對目前可用的每一部網路攝影機引發 Added 事件。 除非您呼叫 Stop 來停止監看程式,否則當新的網路攝影機裝置可供使用時,將會引發 Added 事件;而當攝影機裝置變得無法使用時,將會引發 Removed 事件。
傳遞至 Added 和 Removed 事件處理常式的事件引數,分別為 DeviceInformation 物件和 DeviceInformationUpdate 物件。 這些物件各自有一個 Id 屬性,該屬性是事件觸發的網路攝影機識別碼。 將這個 ID 輸入 MediaFrameSourceGroup.FromIdAsync 方法,取得一個 MediaFrameSourceGroup 物件,可以用來從攝影機擷取影格。
遠端攝影機配對輔助類別
以下範例展示了一個輔助類別,該類別使用 DeviceWatcher 建立並更新 MediaFrameSourceGroup 物件的 ObservableCollection,以支援對攝影機清單的資料綁定。 一般應用程式會將 MediaFrameSourceGroup 包裝成自訂模型類別。 請注意,helper 類別會維護對應用程式 DispatcherQueue 的參考,並更新 UI 執行緒中的攝影機集合。
此外,這個範例除了處理 Added 和 Removed 事件外,也會處理 DeviceWatcher.Updated 事件。 在 更新 處理程式中,相關的遠端攝影機裝置會被移除,然後再加入集合。
class RemoteCameraPairingHelper : IDisposable
{
private DispatcherQueue _dispatcherQueue;
private DeviceWatcher _watcher;
private ObservableCollection<MediaFrameSourceGroup> _remoteCameraCollection;
public RemoteCameraPairingHelper(DispatcherQueue uiDispatcherQueue)
{
_dispatcherQueue = uiDispatcherQueue;
_remoteCameraCollection = new ObservableCollection<MediaFrameSourceGroup>();
var remoteCameraAqs = @"System.Devices.InterfaceClassGuid:=""{B8238652-B500-41EB-B4F3-4234F7F5AE99}"" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";
_watcher = DeviceInformation.CreateWatcher(remoteCameraAqs);
_watcher.Added += Watcher_Added;
_watcher.Removed += Watcher_Removed;
_watcher.Updated += Watcher_Updated;
_watcher.Start();
}
public void Dispose()
{
_watcher.Stop();
_watcher.Updated -= Watcher_Updated;
_watcher.Removed -= Watcher_Removed;
_watcher.Added -= Watcher_Added;
}
public IReadOnlyList<MediaFrameSourceGroup> FrameSourceGroups
{
get { return _remoteCameraCollection; }
}
private async void Watcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)
{
await RemoveDevice(args.Id);
await AddDeviceAsync(args.Id);
}
private async void Watcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
await RemoveDevice(args.Id);
}
private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
await AddDeviceAsync(args.Id);
}
private async Task AddDeviceAsync(string id)
{
var group = await MediaFrameSourceGroup.FromIdAsync(id);
if (group != null)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () => {
_remoteCameraCollection.Add(group);
});
}
}
private async Task RemoveDevice(string id)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
var existing = _remoteCameraCollection.FirstOrDefault(item => item.Id == id);
if (existing != null)
{
_remoteCameraCollection.Remove(existing);
}
});
}
}
struct RemoteCameraPairingHelper
{
RemoteCameraPairingHelper(DispatcherQueue uiDispatcher) :
m_dispatcherQueue(uiDispatcher)
{
m_remoteCameraCollection = winrt::single_threaded_observable_vector<MediaFrameSourceGroup>();
auto remoteCameraAqs =
LR"(System.Devices.InterfaceClassGuid:="{B8238652-B500-41EB-B4F3-4234F7F5AE99}")"
LR"(AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True)";
m_watcher = DeviceInformation::CreateWatcher(remoteCameraAqs, nullptr);
m_watcherAddedAutoRevoker = m_watcher.Added(winrt::auto_revoke, { this, &RemoteCameraPairingHelper::Watcher_Added });
m_watcherRemovedAutoRevoker = m_watcher.Removed(winrt::auto_revoke, { this, &RemoteCameraPairingHelper::Watcher_Removed });
m_watcherUpdatedAutoRevoker = m_watcher.Updated(winrt::auto_revoke, { this, &RemoteCameraPairingHelper::Watcher_Updated });
m_watcher.Start();
}
~RemoteCameraPairingHelper()
{
m_watcher.Stop();
}
IObservableVector<MediaFrameSourceGroup> FrameSourceGroups()
{
return m_remoteCameraCollection;
}
winrt::fire_and_forget Watcher_Added(DeviceWatcher /* sender */, DeviceInformation args)
{
co_await AddDeviceAsync(args.Id());
}
winrt::fire_and_forget Watcher_Removed(DeviceWatcher /* sender */, DeviceInformationUpdate args)
{
co_await RemoveDevice(args.Id());
}
winrt::fire_and_forget Watcher_Updated(DeviceWatcher /* sender */, DeviceInformationUpdate args)
{
co_await RemoveDevice(args.Id());
co_await AddDeviceAsync(args.Id());
}
Windows::Foundation::IAsyncAction AddDeviceAsync(winrt::hstring id)
{
auto group = co_await MediaFrameSourceGroup::FromIdAsync(id);
if (group)
{
//co_await m_dispatcherQueue;
co_await wil::resume_foreground(m_dispatcherQueue);
m_remoteCameraCollection.Append(group);
}
}
Windows::Foundation::IAsyncAction RemoveDevice(winrt::hstring id)
{
//co_await m_dispatcherQueue;
co_await wil::resume_foreground(m_dispatcherQueue);
uint32_t ix{ 0 };
for (auto const&& item : m_remoteCameraCollection)
{
if (item.Id() == id)
{
m_remoteCameraCollection.RemoveAt(ix);
break;
}
++ix;
}
}
private:
DispatcherQueue m_dispatcherQueue{ nullptr };
DeviceWatcher m_watcher{ nullptr };
IObservableVector<MediaFrameSourceGroup> m_remoteCameraCollection;
DeviceWatcher::Added_revoker m_watcherAddedAutoRevoker;
DeviceWatcher::Removed_revoker m_watcherRemovedAutoRevoker;
DeviceWatcher::Updated_revoker m_watcherUpdatedAutoRevoker;
};