Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ZXing Barcode Scanner WinRT can't start StartPreviewAsync()

I run the following lines to get a preview of my webcam:

...
public MediaCapture VideoCaptureSource { get; set; }
var cameras = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);

if (cameras.Count == 1)
{
  settings = new MediaCaptureInitializationSettings { VideoDeviceId = cameras[0].Id };
}
else
{
  settings = new MediaCaptureInitializationSettings { VideoDeviceId = cameras[1].Id };
}

await _mediaCapture.InitializeAsync(settings);
VideoCaptureSource = _mediaCapture;
await VideoCaptureSource.StartPreviewAsync();
...

Here's my XAML:

<Canvas Width="640" Height="360">
  <CaptureElement Source="{Binding VideoCaptureSource}" Visibility="{Binding VideoCaptureVisibility}" Width="640" Height="360" />
  <Image Source="{Binding CaptureImageSource}" Visibility="{Binding CaptureImageVisibility, FallbackValue=Collapsed}" Width="640" Height="360" />
</Canvas>

Everything works fine until I call the StartPreviewAsyn()-Method. Unfortunately, I only get a not-so-useful error message: "An unexpected error has occurred in the operation requested." I tried to run the Code on my local machine as well as on the simulator. Has anyone an idea what I do wrong?

Environment is Windows 8.1, Caliburn.Micro, ZXing.Net 0.12.0.0, VS 2013

like image 510
muetzenflo Avatar asked Oct 21 '22 21:10

muetzenflo


1 Answers

I recently ran into the same problem. It seems that binding the video source is quite tricky, because you need to initialise, set and start the preview stream in the right order. I fixed the issue using an attached property:

/// <summary>
/// This utility class provides an attached property that enables us to
/// bind the source of a <see cref="CaptureElement"/>.
/// </summary>
public static class VideoSourceBinding {

    public static MediaCapture GetVideoSource(DependencyObject obj) {
        return (MediaCapture) obj.GetValue(VideoSourceProperty);
    }

    public static void SetVideoSource(DependencyObject obj,
            MediaCapture value) {
        obj.SetValue(VideoSourceProperty, value);
    }

    public static readonly DependencyProperty VideoSourceProperty
        = DependencyProperty.RegisterAttached("VideoSource",
        typeof(MediaCapture),
        typeof(VideoSourceBinding),
        new PropertyMetadata(null, onVideoSourcePropertyChanged));

    private static async void onVideoSourcePropertyChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e) {
        Debug.Assert(d is CaptureElement);
        Debug.Assert(e.Property == VideoSourceBinding.VideoSourceProperty);

        CaptureElement preview = d as CaptureElement;

        if (d != null) {
            if (preview.Source != null) {
                // If another camera was attached before, stop it.
                await preview.Source.StopPreviewAsync();
            }

            try {
                preview.Source = (MediaCapture) e.NewValue;
            } catch {
                // The property change occurred before the the video source
                // was properly initialised. In this case, we ignore the
                // change and wait for the source to fire the event again
                // once the initialisation was completed.
                // Important: The source must actually change in order for
                // the event to be fired (the attached property will detect
                // if the same object was re-set) and ignore this.
                preview.Source = null;
            }

            if (preview.Source != null) {
                // Start the preview stream.
                await preview.Source.StartPreviewAsync();
            }
        }
    }
}

The video source is then bound like this:

<CaptureElement u:VideoSourceBinding.VideoSource="{Binding VideoCapture}" />

Note that the order in which you do things in the view model is important:

public class ViewModel : Screen {
    // Note: The sample uses a Caliburn Micro base class - this is, however, not
    // a requirement.

    public MediaCapture VideoCapture {
        get;
        private set;
    }

    /// <summary>
    /// Starts the video preview.
    /// </summary>
    /// <remarks>
    /// Call this method whenever it is necessary to (re-) start the preview, eg
    /// if the page is activated or if the settings have changed.
    /// </remarks>
    private async void startVideo() {
        var captureSettings = new MediaCaptureInitializationSettings() {
            StreamingCaptureMode = StreamingCaptureMode.Video
        };
        // Set a NEW MediaCapture! Do not re-use the old one, because the property 
        // change event of the attached property will not fire otherwise.
        this.VideoCapture = new MediaCapture();
        await this.videoCapture.InitializeAsync(captureSettings);
        // Notify the UI about a new video source AFTER the initialisation completed. It 
        // is important to await the initialisation here.
        this.NotifyOfPropertyChange(() => this.VideoCapture);
    }
}
like image 200
Christoph Avatar answered Oct 23 '22 19:10

Christoph