How to switch between webcams when using CaptureElement/MediaCapture? [closed]

谁说我不能喝 提交于 2020-01-02 18:54:07

问题


I am trying to provide an option to switch between webcams used to display preview using CaptureElement/MediaCapture. Unfortunately I tried multiple combinations of call sequences and the preview only shows up for the first device I use.

This is what I have been trying to do:

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);
        }
    }
}

It seems like it does work to switch to second camera once in a while (below 10% of times) and then stays all black when I go back to the first one.

Sometimes the app hangs after I try to switch the camera once or twice (it stops responding to input, it is stuck in App.Run(), though the camera preview keeps refreshing).

Other times - it works in a way that it shows preview from the first device, but does not for the other one and when I go back to the first one - it works fine with it again.

Bugs?

There does not seem to be a Dispose or Uninitialize method anywhere. These are the properties I see (it's Samsung's Build 2011 tablet):

* 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

回答1:


I don't have a tablet, and I have very little Metro experience... but I do have a lot of asynchronous programming experience.

One thing you have to be aware of is that async programs don't play well with state. You don't have race conditions per se, but you do have to consider reentrancy. In examples like this, reentrancy can cause a sort of single-threaded "race condition".

If I'm right, one easy way to avoid reentrancy on events is to have a boolean "reentrancy guard" variable (which I call switchingMedia below). Try this:

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;
}

I also recommend that you have your async methods return Task rather than void (unless they're event handlers, of course). This allows them to be composable. e.g., if InitializeWebCam returns Task, then you can put the reentrancy guard code in the event handler instead:

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;
    }
}

By defining all your async methods to return Task by default, you have more composability options.




回答2:


after getting all list of devices try like below

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



回答3:


I think the problem must have been with the Developer Preview build. I have no problem switching between 3 web cams in the Consumer Preview one.



来源:https://stackoverflow.com/questions/9136634/how-to-switch-between-webcams-when-using-captureelement-mediacapture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!