使用CaptureElement / MediaCapture时如何在网络摄像头之间切换?

我试图提供一个选项,用于在使用CaptureElement / MediaCapture显示预览的网络摄像头之间切换。 不幸的是,我尝试了多种呼叫序列组合,预览只显示我使用的第一个设备。

这就是我一直在努力做的事情:

XAML:

 

C#:

 MediaCapture mediaCapture; DeviceInformationCollection devices; int currentDevice = 0; private async void LayoutRoot_Tapped(object sender, Windows.UI.Xaml.Input.TappedEventArgs e) { if (devices != null) { currentDevice = (currentDevice + 1) % devices.Count; InitializeWebCam(); } } private async void InitializeWebCam() { if (devices == null) { devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture); ListDeviceDetails(); } if (mediaCapture != null) { await mediaCapture.StopPreviewAsync(); this.captureElement.Source = null; } mediaCapture = new MediaCapture(); await mediaCapture.InitializeAsync( new MediaCaptureInitializationSettings { VideoDeviceId = devices[currentDevice].Id }); this.captureElement.Source = mediaCapture; await mediaCapture.StartPreviewAsync(); } private void ListDeviceDetails() { int i = 0; foreach (var device in devices) { Debug.WriteLine("* Device [{0}]", i++); Debug.WriteLine("EnclosureLocation.InDock: " + device.EnclosureLocation.InDock); Debug.WriteLine("EnclosureLocation.InLid: " + device.EnclosureLocation.InLid); Debug.WriteLine("EnclosureLocation.Panel: " + device.EnclosureLocation.Panel); Debug.WriteLine("Id: " + device.Id); Debug.WriteLine("IsDefault: " + device.IsDefault); Debug.WriteLine("IsEnabled: " + device.IsEnabled); Debug.WriteLine("Name: " + device.Name); Debug.WriteLine("IsDefault: " + device.IsDefault); foreach (var property in device.Properties) { Debug.WriteLine(property.Key + ": " + property.Value); } } } 

看起来它偶尔会切换到第二台相机(低于10%的时间),然后当我回到第一台时,它会全黑。

有时应用程序挂起后我尝试将相机切换一次或两次(它停止响应输入,它停留在App.Run(),虽然相机预览保持刷新)。

其他时候 – 它的工作方式是它显示第一个设备的预览,但不适用于另一个设备,当我回到第一个时 – 它再次正常工作。

错误?

似乎在任何地方都没有Dispose或Uninitialize方法。 这些是我看到的属性(它是三星的Build 2011平板电脑):

 * Device [0] EnclosureLocation.InDock: False EnclosureLocation.InLid: False EnclosureLocation.Panel: Front Id: \\?\USB#VID_2232&PID_1021&MI_00#7&2469C269&0&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\GLOBAL IsDefault: False IsEnabled: True Name: WebCam SC-20FHM11347N IsDefault: False System.ItemNameDisplay: WebCam SC-20FHM11347N System.Devices.DeviceInstanceId: USB\VID_2232&PID_1021&MI_00\7&2469C269&0&0000 System.Devices.Icon: C:\Windows\System32\DDORes.dll,-2068 System.Devices.InterfaceEnabled: True System.Devices.IsDefault: False * Device [1] EnclosureLocation.InDock: False EnclosureLocation.InLid: False EnclosureLocation.Panel: Back Id: \\?\USB#VID_2232&PID_1022&MI_00#7&27072759&0&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\GLOBAL IsDefault: False IsEnabled: True Name: WebCam SC-30H2L11449N IsDefault: False System.ItemNameDisplay: WebCam SC-30H2L11449N System.Devices.DeviceInstanceId: USB\VID_2232&PID_1022&MI_00\7&27072759&0&0000 System.Devices.Icon: C:\Windows\System32\DDORes.dll,-2068 System.Devices.InterfaceEnabled: True System.Devices.IsDefault: False 

我没有平板电脑,而且我的Metro体验很少……但我确实有很多异步编程经验。

你需要注意的一件事是异步程序与状态不匹配。 你本身没有竞争条件,但你必须考虑重入。 在这样的例子中,重入可能会导致一种单线程“竞争条件”。

如果我是对的,一种避免事件重入的简单方法是使用布尔“reentrancy guard”变量(我在下面称之为switchingMedia )。 试试这个:

 MediaCapture mediaCapture; DeviceInformationCollection devices; int currentDevice = 0; bool switchingMedia = false; private async void LayoutRoot_Tapped(object sender, Windows.UI.Xaml.Input.TappedEventArgs e) { if (devices != null) { InitializeWebCam(); } } private async void InitializeWebCam() { if (switchingMedia) return; switchingMedia = true; if (devices == null) { devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture); ListDeviceDetails(); } else { currentDevice = (currentDevice + 1) % devices.Count; } if (mediaCapture != null) { await mediaCapture.StopPreviewAsync(); this.captureElement.Source = null; } mediaCapture = new MediaCapture(); await mediaCapture.InitializeAsync( new MediaCaptureInitializationSettings { VideoDeviceId = devices[currentDevice].Id }); this.captureElement.Source = mediaCapture; await mediaCapture.StartPreviewAsync(); switchingMedia = false; } 

我还建议您使用async方法返回Task而不是void (当然,除非它们是事件处理程序)。 这使它们可以组合。 例如,如果InitializeWebCam返回Task ,那么您可以将重入保护代码放在事件处理程序中:

 private async void LayoutRoot_Tapped(object sender, Windows.UI.Xaml.Input.TappedEventArgs e) { if (devices != null && !switchingMedia) { currentDevice = (currentDevice + 1) % devices.Count; switchingMedia = true; await InitializeWebCam(); switchingMedia = false; } } 

通过定义所有async方法以默认返回Task ,您有更多可组合性选项。

获取所有设备列表后尝试如下

 var rearCamera = devices.FirstOrDefault(item => item.EnclosureLocation != null && item.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back); 

我认为问题一定是开发人员预览版本。 我在Consumer Preview中的3个网络摄像头之间切换没有问题。