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

本文关键字:摄像头 网络 之间 CaptureElement MediaCapture 使用 | 更新日期: 2023-09-27 18:22:21

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

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

XAML:

<CaptureElement
    x:Name="captureElement"
    Stretch="UniformToFill" />

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()中,尽管相机预览一直在刷新)。

其他时候,它的工作方式是显示第一台设备的预览,但对另一台设备没有,当我回到第一台设备时,它再次工作正常。

Bugs?

任何地方似乎都没有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

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

我没有平板电脑,而且我几乎没有地铁经验。。。但我确实有很多异步编程的经验。

您必须注意的一件事是异步程序不能很好地处理状态。你本身没有比赛条件,但你必须考虑重返赛场。在这样的例子中,可重入性可能会导致一种单线程的"竞争条件"。

如果我是对的,避免事件重入的一个简单方法是使用布尔型"重入保护"变量(我在下面称之为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);

我认为问题一定出在Developer Preview版本上。我在消费者预览版中的3个网络摄像头之间切换没有问题。