Created
February 7, 2017 11:27
-
-
Save SunboX/16381f40afd34aa4d2f31deb9de7ed15 to your computer and use it in GitHub Desktop.
UWP ZXing with preview
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using System.Runtime.InteropServices.WindowsRuntime; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Windows.Graphics.Imaging; | |
using Windows.Media.Capture; | |
using Windows.Media.MediaProperties; | |
using Windows.UI.Core; | |
using Windows.UI.Xaml; | |
using Windows.UI.Xaml.Controls; | |
using Windows.UI.Xaml.Media.Imaging; | |
using Windows.UI.Xaml.Navigation; | |
using ZXing; | |
using ZXing.Common; | |
namespace IoT_WebCam_Test | |
{ | |
public sealed partial class MainPage : Page | |
{ | |
// Rotation metadata to apply to the preview stream and recorded videos (MF_MT_VIDEO_ROTATION) | |
// Reference: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868174.aspx | |
private static readonly Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); | |
private readonly BarcodeReader _reader = new BarcodeReader | |
{ | |
Options = new DecodingOptions | |
{ | |
PossibleFormats = new[] | |
{ | |
BarcodeFormat.QR_CODE, | |
BarcodeFormat.EAN_13, | |
BarcodeFormat.EAN_8, | |
BarcodeFormat.UPC_A, | |
BarcodeFormat.UPC_E, | |
BarcodeFormat.UPC_EAN_EXTENSION | |
}, | |
TryHarder = true | |
} | |
}; | |
private bool _isPreviewing; | |
private bool _isRecording; | |
private MediaCapture _mediaCapture; | |
public MainPage() | |
{ | |
InitializeComponent(); | |
InitCamera(); | |
} | |
protected override async void OnNavigatedTo(NavigationEventArgs e) | |
{ | |
base.OnNavigatedTo(e); | |
} | |
protected override void OnNavigatedFrom(NavigationEventArgs e) | |
{ | |
Cleanup(); | |
base.OnNavigatedFrom(e); | |
} | |
private async void InitCamera() | |
{ | |
try | |
{ | |
if (_mediaCapture != null) | |
{ | |
// Cleanup MediaCapture object | |
if (_isPreviewing) | |
{ | |
await _mediaCapture.StopPreviewAsync(); | |
_isPreviewing = false; | |
} | |
if (_isRecording) | |
{ | |
await _mediaCapture.StopRecordAsync(); | |
_isRecording = false; | |
} | |
_mediaCapture.Dispose(); | |
_mediaCapture = null; | |
} | |
status.Text = "Initializing camera to capture audio and video..."; | |
// Use default initialization | |
_mediaCapture = new MediaCapture(); | |
await _mediaCapture.InitializeAsync(); | |
// get available resolutions | |
var resolutions = _mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.Photo).ToList(); | |
for (var i = 0; i < resolutions.Count; i++) | |
{ | |
var prop = resolutions[i] as VideoEncodingProperties; | |
if (prop != null) | |
{ | |
Debug.WriteLine($"Size {i}: {prop.Width}, {prop.Height}"); | |
} | |
} | |
/* | |
_mediaCapture.VideoDeviceController.FocusControl.Configure(new FocusSettings | |
{ | |
Mode = FocusMode.Auto, | |
AutoFocusRange = AutoFocusRange.Macro | |
}); | |
*/ | |
// set used resolution | |
await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.Photo, resolutions[40]); | |
// Set callbacks for failure and recording limit exceeded | |
status.Text = "Device successfully initialized for video recording!"; | |
_mediaCapture.Failed += mediaCapture_Failed; | |
// Start Preview | |
previewElement.Source = _mediaCapture; | |
await _mediaCapture.StartPreviewAsync(); | |
var props = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview); | |
//props.Properties.Add(RotationKey, 90); | |
await _mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview, props, null); | |
_isPreviewing = true; | |
status.Text += "Camera preview succeeded"; | |
var cancellationTokenSource = new CancellationTokenSource(); | |
await Task.Factory.StartNew(async () => | |
{ | |
for (;;) | |
{ | |
if (cancellationTokenSource.IsCancellationRequested) | |
{ | |
break; | |
} | |
await TakePhoto(); | |
} | |
}, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); | |
} | |
catch (Exception ex) | |
{ | |
status.Text = "Unable to initialize camera for audio/video mode: " + ex.Message; | |
status.Visibility = Visibility.Visible; | |
} | |
} | |
private async void Cleanup() | |
{ | |
if (_mediaCapture != null) | |
{ | |
// Cleanup MediaCapture object | |
if (_isPreviewing) | |
{ | |
await _mediaCapture.StopPreviewAsync(); | |
//playbackElement.Source = null; | |
_isPreviewing = false; | |
} | |
if (_isRecording) | |
{ | |
await _mediaCapture.StopRecordAsync(); | |
_isRecording = false; | |
//recordVideo.Content = "Start Video Record"; | |
//recordAudio.Content = "Start Audio Record"; | |
} | |
_mediaCapture.Dispose(); | |
_mediaCapture = null; | |
} | |
} | |
/// <summary> | |
/// Callback function for any failures in MediaCapture operations | |
/// </summary> | |
/// <param name="currentCaptureObject"></param> | |
/// <param name="currentFailure"></param> | |
private async void mediaCapture_Failed(MediaCapture currentCaptureObject, | |
MediaCaptureFailedEventArgs currentFailure) | |
{ | |
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => | |
{ | |
try | |
{ | |
status.Text = "MediaCaptureFailed: " + currentFailure.Message; | |
if (!_isRecording) | |
{ | |
return; | |
} | |
await _mediaCapture.StopRecordAsync(); | |
status.Text += "\n Recording Stopped"; | |
} | |
catch (Exception) | |
{ | |
// ignored | |
} | |
finally | |
{ | |
status.Text += "\nCheck if camera is diconnected. Try re-launching the app"; | |
} | |
}); | |
} | |
private async Task TakePhoto() | |
{ | |
try | |
{ | |
//await _mediaCapture.VideoDeviceController.FocusControl.FocusAsync(); | |
var lowLagCapture = | |
await | |
_mediaCapture.PrepareLowLagPhotoCaptureAsync( | |
ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Bgra8)); | |
var capturedPhoto = await lowLagCapture.CaptureAsync(); | |
var softwareBitmap = capturedPhoto.Frame.SoftwareBitmap; | |
await lowLagCapture.FinishAsync(); | |
if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 || softwareBitmap.BitmapAlphaMode != BitmapAlphaMode.Ignore) | |
{ | |
softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore); | |
} | |
var newBmp = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Gray8, BitmapAlphaMode.Ignore); | |
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => | |
{ | |
var bytes = EncodedBytes(newBmp); | |
TestPreview.Source = await SoftwareImageSourceFromBytes(bytes, newBmp.PixelWidth, newBmp.PixelHeight, BitmapPixelFormat.Gray8); | |
//Debug.WriteLine($"Size: {newBmp.PixelWidth}, {newBmp.PixelHeight}"); | |
//await Task.Delay(300); | |
var result = _reader.Decode(bytes, newBmp.PixelWidth, newBmp.PixelHeight, RGBLuminanceSource.BitmapFormat.Gray8); | |
if (result != null) | |
{ | |
Debug.WriteLine(result.Text); | |
} | |
}); | |
} | |
catch (Exception) | |
{ | |
// ignored | |
} | |
} | |
[ComImport] | |
[Guid("5b0d3235-4dba-4d44-865e-8f1d0e4fd04d")] | |
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | |
unsafe interface IMemoryBufferByteAccess | |
{ | |
void GetBuffer(out byte* buffer, out uint capacity); | |
} | |
private byte[] EncodedBytes(SoftwareBitmap soft) | |
{ | |
using (var buffer = soft.LockBuffer(BitmapBufferAccessMode.ReadWrite)) | |
using (var reference = buffer.CreateReference()) | |
{ | |
unsafe | |
{ | |
// Get a pointer to the pixel buffer | |
var data = default(byte*); | |
uint capacity = 0; | |
var memoryBufferByteAccess = reference as IMemoryBufferByteAccess; | |
if (memoryBufferByteAccess != null) | |
{ | |
memoryBufferByteAccess.GetBuffer(out data, out capacity); | |
} | |
var len = checked((int) capacity); | |
var bytes = new byte[len]; | |
Marshal.Copy((IntPtr)data, bytes, 0, len); | |
return bytes; | |
} | |
} | |
} | |
public static async Task<WriteableBitmap> ImageFromBytes(byte[] bytes, int width, int height) | |
{ | |
var wb = new WriteableBitmap(width, height); | |
using (var stream = wb.PixelBuffer.AsStream()) | |
{ | |
await stream.WriteAsync(bytes, 0, bytes.Length); | |
} | |
return wb; | |
} | |
public static async Task<SoftwareBitmapSource> SoftwareImageSourceFromBytes(byte[] bytes, int width, int height, BitmapPixelFormat bitmapPixelFormat) | |
{ | |
var grayImage = await ImageFromBytes(bytes, width, width); | |
var grayscaleBitmap = SoftwareBitmap.CreateCopyFromBuffer(grayImage.PixelBuffer, bitmapPixelFormat, width, height); | |
var uwpSoftwareBitmap = SoftwareBitmap.Convert(grayscaleBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore); | |
var source = new SoftwareBitmapSource(); | |
await source.SetBitmapAsync(uwpSoftwareBitmap); | |
return source; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment