My CaptureElements are showing strange behavior. When I set a instantiated MediaCapture as the CaptureElements Source and then call MediaCapture.StartPreviewAsync() the CaptureElement doesn't show anything.
I have one Application (main-app) with a functional BarcodeScanner on the LoginPage.
-> Works!
Then I wanted to copy the same code to the SettingsPage with small modifications so in case of several attached cameras, the default one can be set.
-> Doesn't work
Then I tried to run the main-app with the help of the remote debugger on other windows tablets with same Windows 10 Version as my machine (keep in mind, that the BarcodeScanner on the Login-Screen works on my machine).
-> doesn't work
Because of these failures I copied the running code from the main-apps LoginPage to a completely new solution (lets call it test-app) with the same settings as the original one. I even experimented with referencing the same Dlls, implementing the same design pattern etc.
-> doesn't work
My Machine:
Win 10 Pro
Version 1809
Build 17763.652
DevEnv:
MS Visual Studio 2019 Pro
Vers. 16.1.6
EDIT: As minimum required Windows Version I selected Build 16229 and
my target Version is Build 17763 (my systems Win version)
The "Allow apps to access your camera"-Option in the Widows Settings is switched to ON, so all apps are allowed to access the camera.
Xaml
<Page
x:Class="QrCodeTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:QrCodeTest"
xmlns:vm="using:QrCodeTest.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.DataContext>
<vm:TestViewModel x:Name="ViewModel" />
</Page.DataContext>
<ScrollViewer>
<StackPanel>
<Button Content="Start Preview" HorizontalAlignment="Center" Click="Button_Click" Margin="5" />
<CaptureElement x:Name="capturePreview" HorizontalAlignment="Center" Stretch="Uniform" Width="0" Height="0" Margin="10" />
<Button Content="Stop Preview" HorizontalAlignment="Center" Click="Button_Click_1" Margin="5" />
<TextBlock Text="{Binding Etikett, Mode=TwoWay}" HorizontalAlignment="Center" Margin="5" />
</StackPanel>
</ScrollViewer>
</Page>
CodeBehind
private BarcodeScanner scanner { get; set; }
private ClaimedBarcodeScanner claimedScanner { get; set; }
private MediaCapture captureManager { get; set; }
internal async Task StartScannerAsync () {
capturePreview.Visibility = Visibility.Visible;
capturePreview.Width = 400; capturePreview.Height = 300;
scanner = null;
scanner = await DeviceHelpers.GetFirstDeviceAsync(BarcodeScanner.GetDeviceSelector(connectionTypes), async (id) => await BarcodeScanner.FromIdAsync(id));
if (scanner != null) {
captureManager = new MediaCapture();
claimedScanner = await scanner.ClaimScannerAsync();
if (claimedScanner != null) {
claimedScanner.ReleaseDeviceRequested += claimedScanner_ReleaseDeviceRequested;
claimedScanner.DataReceived += claimedScanner_DataReceived;
claimedScanner.IsDecodeDataEnabled = true;
IReadOnlyList<uint> supportedSymbologies = await scanner.GetSupportedSymbologiesAsync();
foreach (uint symbology in supportedSymbologies) {
listOfSymbologies.Add(new SymbologyListEntry(symbology));
}
await claimedScanner.EnableAsync();
MediaCaptureInitializationSettings _captureInitSettings = new MediaCaptureInitializationSettings {
VideoDeviceId = scanner.VideoDeviceId,
StreamingCaptureMode = StreamingCaptureMode.AudioAndVideo,
PhotoCaptureSource = PhotoCaptureSource.VideoPreview
};
await captureManager.InitializeAsync(_captureInitSettings);
capturePreview.Source = captureManager;
try {
// Change to false, in case you wanna compare different methods of doing the same
bool Like_MP_PAT_UWP = false;
if (Like_MP_PAT_UWP) {
await capturePreview.Source.StartPreviewAsync();
await claimedScanner.StartSoftwareTriggerAsync();
} else {
LocalDataContext.Etikett = "await captureManager.StartPreviewAsync();";
await captureManager.StartPreviewAsync();
await claimedScanner.StartSoftwareTriggerAsync();
Thread.Sleep(2000);
await claimedScanner.StopSoftwareTriggerAsync();
await captureManager.StopPreviewAsync();
LocalDataContext.Etikett = "await capturePreview.Source.StartPreviewAsync();";
await capturePreview.Source.StartPreviewAsync();
await claimedScanner.StartSoftwareTriggerAsync();
Thread.Sleep(2000);
await claimedScanner.StopSoftwareTriggerAsync();
await capturePreview.Source.StopPreviewAsync();
LocalDataContext.Etikett = "await claimedScanner.ShowVideoPreviewAsync();";
await claimedScanner.ShowVideoPreviewAsync();
await claimedScanner.StartSoftwareTriggerAsync();
Thread.Sleep(2000);
await claimedScanner.StopSoftwareTriggerAsync();
claimedScanner.HideVideoPreview();
}
} catch (Exception e) {
Exception x = e; displayRequest.RequestRelease();
} finally {
LocalDataContext.Etikett = string.Empty;
}
}
}
}
ViewModel:
public class TestViewModel: INotifyPropertyChanged {
public static TestViewModel Instance { get; set; }
private string _Etikett;
public string Etikett { get { return _Etikett; } set { _Etikett = value; NotifyPropertyChanged(); } }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged ([CallerMemberName] String propertyName = "") {
//PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
if (PropertyChanged != null) {
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I've wasted already 4 working days just to compare the solutions, the codes etc. The above code was copied from the test-app but it's mostly identical to the one on the main-apps LoginPage (except for the "if (Like_MP_PAT_UWP) {...}".
Every hint is welcome.
Thank you in advance.
The Problem was Kaspersky Endpoint Security's "advanced Threat Protection/host intrusion prevention"-setting. It prevented ALL apps outside from our dev-harddrive (i. e. on our tablets or from our network-drive) to access the camera (Dev-Drive = "Trusted Zone").
It was necessary to reconfigure that feature in Kaspersky Endpoint Security for the whole environment (declare necessary locations/clients as a trusted zone).
Hope, this might help someone with a similar problem or at least give a hint to someone.
Just spit-balling here, but I'd suggest attempting to reduce your test to just code that controls the MediaCapture object as much as possible, as that seems to be the symptom where you describe the main issue.
After that, try lowering the SharingMode of the camera to just read-only, in case another app is using the camera with exclusive access. Also, you can reduce the pop up consent check to just the camera, and not have the microphone consent. Sometimes if you accidentally don't agree during the consent pop-up, the app will be denied access to the camera until you allow it again from the system settings (Settings -> Privacy -> Camera).
Below is a sub-optimal and boiled down version of what you described above, but all parts included. I tried to segregate your starting a barcode session from disposing things. Using the MS samples as a guide will be far more reliable than this one. Nonetheless, there's lots more trace points to add, but below has a few traces around where the MediaCapture fails, and other points in the barcode scanner enable section. Hope it helps.
using System;
using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Devices.PointOfService;
using System.Threading.Tasks;
using Windows.Media.Capture;
using Windows.Devices.Enumeration;
using System.Diagnostics;
using Windows.Storage.Streams;
namespace StackOverflowQrTest
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
if (claimedScanner == null)
{
await StartScannerAsync();
}
}
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
await StopScannerAsync();
}
private BarcodeScanner scanner { get; set; }
private ClaimedBarcodeScanner claimedScanner { get; set; }
private MediaCapture captureManager { get; set; }
internal async Task StartScannerAsync()
{
capturePreview.Visibility = Visibility.Visible;
capturePreview.Width = 400; capturePreview.Height = 300;
scanner = await DeviceHelpers.GetFirstDeviceAsync(BarcodeScanner.GetDeviceSelector(), async (id) => await BarcodeScanner.FromIdAsync(id));
if (scanner != null)
{
claimedScanner = await scanner.ClaimScannerAsync();
if (claimedScanner != null)
{
claimedScanner.ReleaseDeviceRequested += ClaimedScanner_ReleaseDeviceRequested;
claimedScanner.DataReceived += ClaimedScanner_DataReceived;
claimedScanner.IsDecodeDataEnabled = true;
await claimedScanner.EnableAsync();
try
{
bool haveAssociatedCamera = !string.IsNullOrEmpty(scanner.VideoDeviceId);
if (haveAssociatedCamera)
{
captureManager = new MediaCapture();
captureManager.Failed += CaptureManager_Failed;
MediaCaptureInitializationSettings _captureInitSettings = new MediaCaptureInitializationSettings
{
VideoDeviceId = scanner.VideoDeviceId,
SharingMode = MediaCaptureSharingMode.SharedReadOnly, // share
StreamingCaptureMode = StreamingCaptureMode.Video // just video
};
await captureManager.InitializeAsync(_captureInitSettings);
capturePreview.Source = captureManager;
}
UpdateMessage("waiting..." + (!haveAssociatedCamera ? "But scanner not camera type" : ""));
if (captureManager != null) await captureManager.StartPreviewAsync();
await claimedScanner.StartSoftwareTriggerAsync();
}
catch (Exception e)
{
UpdateMessage(e.Message);
Debug.WriteLine("EXCEPTION: " + e.Message);
}
}
else
{
UpdateMessage("Could not claim barcode scanner");
}
}
else
{
UpdateMessage("No barcode scanners found");
}
}
private void CaptureManager_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs)
{
string msg = string.Format("MediaCapture_Failed: (0x{0:X}) {1}", errorEventArgs.Code, errorEventArgs.Message);
UpdateMessage(msg);
}
internal async Task StopScannerAsync()
{
if (captureManager != null)
{
if (captureManager.CameraStreamState == Windows.Media.Devices.CameraStreamState.Streaming)
{
await captureManager.StopPreviewAsync();
}
captureManager.Dispose();
captureManager = null;
}
if (claimedScanner != null)
{
claimedScanner.Dispose();
claimedScanner = null;
}
if (scanner != null)
{
scanner.Dispose();
scanner = null;
}
}
private void ClaimedScanner_DataReceived(ClaimedBarcodeScanner sender, BarcodeScannerDataReceivedEventArgs args)
{
var scanDataLabelReader = DataReader.FromBuffer(args.Report.ScanDataLabel);
string barcode = scanDataLabelReader.ReadString(args.Report.ScanDataLabel.Length);
UpdateMessage(barcode);
}
private void ClaimedScanner_ReleaseDeviceRequested(object sender, ClaimedBarcodeScanner e)
{
UpdateMessage("Another process is requesting barcode scanner device.");
e.RetainDevice();
}
private async void UpdateMessage (string message)
{
await LastBarcodeRead.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
LastBarcodeRead.Text = message;
});
}
}
public partial class DeviceHelpers
{
// We use a DeviceWatcher instead of DeviceInformation.FindAllAsync because
// the DeviceWatcher will let us see the devices as they are discovered,
// whereas FindAllAsync returns results only after discovery is complete.
public static async Task<T> GetFirstDeviceAsync<T>(string selector, Func<string, Task<T>> convertAsync)
where T : class
{
var completionSource = new TaskCompletionSource<T>();
var pendingTasks = new List<Task>();
DeviceWatcher watcher = DeviceInformation.CreateWatcher(selector);
watcher.Added += (DeviceWatcher sender, DeviceInformation device) =>
{
Func<string, Task> lambda = async (id) =>
{
T t = await convertAsync(id);
if (t != null)
{
completionSource.TrySetResult(t);
}
};
pendingTasks.Add(lambda(device.Id));
};
watcher.EnumerationCompleted += async (DeviceWatcher sender, object args) =>
{
// Wait for completion of all the tasks we created in our "Added" event handler.
await Task.WhenAll(pendingTasks);
// This sets the result to "null" if no task was able to produce a device.
completionSource.TrySetResult(null);
};
watcher.Start();
// Wait for enumeration to complete or for a device to be found.
T result = await completionSource.Task;
watcher.Stop();
return result;
}
}
}
Where main xaml is...
<Page
x:Class="StackOverflowQrTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:StackOverflowQrTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer>
<StackPanel>
<Button Content="Start Preview" HorizontalAlignment="Center" Click="Button_Click" Margin="5" />
<CaptureElement x:Name="capturePreview" HorizontalAlignment="Center" Stretch="Uniform" Width="0" Height="0" Margin="10" />
<Button Content="Stop Preview" HorizontalAlignment="Center" Click="Button_Click_1" Margin="5" />
<TextBox Header="LastBarcode" Name="LastBarcodeRead" IsReadOnly="True" HorizontalAlignment="Center" Margin="5" />
</StackPanel>
</ScrollViewer>
</Page>
Related
I'm trying to setup a simple connection over SPI, but when trying to get a SpiDevice, the SpiDevice.FromIdAsync method never finishes. This happens almost always, with a few rare exceptions that seem random.
spi = await SpiDevice.FromIdAsync(dis[0].Id, settings);
The code used to setup the connection comes from https://learn.microsoft.com/en-us/windows/iot-core/learn-about-hardware/pinmappings/pinmappingsrpi#spi-bus.
In the example, I output a single byte to a LED driver working as a shift register. I use a Raspberry Pi 3 as target device and Visual Studio Community 2017 as my IDE.
MainPage.xaml.cs
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Devices.Gpio;
using Windows.Devices.Spi;
using Windows.Devices.Enumeration;
using System.Threading.Tasks;
namespace SPI_Test
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
try
{
InitGPIO();
InitSpi().Wait();
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Tick += Timer_Tick;
}
catch (Exception)
{
}
}
GpioPin enablePin;
SpiDevice spi;
private void InitGPIO()
{
var gpio = GpioController.GetDefault();
//create pin to control the output of the LED driver
//the outputs are active when the pin is low
enablePin = gpio.OpenPin(12);
enablePin.Write(GpioPinValue.High); //disable outputs at first
enablePin.SetDriveMode(GpioPinDriveMode.Output);
}
private async Task InitSpi()
{
try
{
// Use chip select line CS0
var settings = new SpiConnectionSettings(0);
// Set clock to 10MHz
settings.ClockFrequency = 10000000;
// Get a selector string that will return our wanted SPI controller
string aqs = SpiDevice.GetDeviceSelector("SPI0");
// Find the SPI bus controller devices with our selector string
var dis = await DeviceInformation.FindAllAsync(aqs);
spi = await SpiDevice.FromIdAsync(dis[0].Id, settings); /* Create an SpiDevice with our bus controller and SPI settings */
}
/* If initialization fails, display the exception and stop running */
catch (Exception ex)
{
throw new Exception("SPI Initialization Failed", ex);
}
}
private DispatcherTimer timer;
private byte value = 0;
private void Timer_Tick(object sender, object e)
{
enablePin.Write(GpioPinValue.High); //disable outputs
spi.Write(new byte[] { value++ }); //send the current value to the LEDs and increase by 1
enablePin.Write(GpioPinValue.Low); //re-enable outputs
}
private void Button_Click(object sender, RoutedEventArgs e)
{
timer.Start();
}
}
}
MainPage.xaml
<Page
x:Class="SPI_Test.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SPI_Test"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Click="Button_Click" Content="Start" Margin="15" />
</StackPanel>
</Grid>
</Page>
You are experiencing a deadlock because of the mixing of blocking calls .Wait() and asynchronous calls. I would suggest moving that logic to an event handler. Like Loaded
public MainPage() {
this.InitializeComponent();
this.Loaded += async (sender, e) => {
try {
InitGPIO();
await InitSpi();
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Tick += Timer_Tick;
} catch (Exception) {
}
};
}
OVERVIEW
I'm new to WPF, I have simple window with a textarea, a button and a textblock. On click of the button i want to update the textblock to say "started.. " or something, run a routine that takes a few minutes and at the end update it to "done".
PROBLEM:
Currently my textblock only updates with the "Done" message. :(
CODE:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string myValue;
public string MyValue
{
get { return myValue; }
set
{
myValue = value;
RaisePropertyChanged("MyValue");
}
}
private void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 50; i++)
{
System.Threading.Thread.Sleep(100);
MyValue = i.ToString();
}
});
MyValue = "Scripting, please wait..";
String text = DBObjectsTextArea.Text;
String[] args = text.Split(' ');
SQLScripter scripter = new SQLScripter();
scripter.script(args);
MyValue = "Done!";
}
private void Window_Closed(object sender, EventArgs e)
{
Application.Current.Shutdown();
}
}
XAML:
<TextBox Height="109" HorizontalAlignment="Left" Margin="45,12,0,0" Name="DBObjectsTextArea" VerticalAlignment="Top" Width="418" />
<Button Content="Script To File" Height="23" HorizontalAlignment="Left" Margin="173,145,0,0" Name="ScriptToFileButton" VerticalAlignment="Top" Width="168" Click="ScriptToFileButton_Click" />
<TextBlock Height="56" HorizontalAlignment="Left" Margin="45,197,0,0" Name="StatusTextBlock" Text="{Binding Path=MyValue}" VerticalAlignment="Top" Width="409" />
SIMILAR LINK:
I based my code off of this:
How do I refresh visual control properties (TextBlock.text) set inside a loop?
You can change your event handler to this...
private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
{
String text = DBObjectsTextArea.Text;
Task t = new Task(() => PerformOnDispatcher(() => { MyValue = "Scripting, please wait.."; }));
ManualResetEvent mre = new ManualResetEvent(false);
t.ContinueWith((x) =>
{
// scripting code goes here...
mre.Set();
}, TaskContinuationOptions.LongRunning);
t.ContinueWith((y) =>
{
mre.WaitOne();
PerformOnDispatcher(() => { MyValue = "Scripting, please wait.."; });
});
t.Start();
}
This segregates the work into three phases: telling the user that scripting has started, then doing the work, then telling the user that the work is done. Synchronization is controlled by the "ContinueWith" method. This method waits until the previous method has completed before starting.
Because it's in a Task, changes to the UI are marshalled back via the dispatcher, which uses a separate scheduler and thus will not be blocked.
The last statement, t.Start() kicks off the sequence of events, so the user experience of clicking the button is not hampered by a blocked UI. The event handler returns almost immediately.
If the work happens really quickly, you still may only see the "Done" because the previous messages were overwritten before you had a chance to read them. So the best way to debug these things is to anchor the debugger on this statement:
PropertyChanged(this, new PropertyChangedEventArgs(propName));
This will let you observe what is being piped out to the binding engine.
Finally, to make the event handler less cluttered so as to focus on the question, I used a convenience method...
private void PerformOnDispatcher(Action a)
{
Dispatcher.InvokeAsync(a);
}
For industrial strength apps, this would be an extension method or done within the property setter.
If you wish the task to run on a background thread you must account for updating the WPF UI on the UI thread. This post from Dr. Xu explains it. Dispatcher.InvokeAsync was added with .Net Framework 4.5 and is preferred. Similar functionality can also be had in earlier versions of .Net with Dispatcher.BeginInvoke. Don't forget to clean up your task and determine how or if the app should close if the task is incomplete.
// MainWindow...
Task t; // keep for cleanup
// Update on the UI thread
private void SetMyValueAsync(string value)
{
Application.Current.Dispatcher.BeginInvoke((Action)(() => MyValue = value));
}
private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
{
t = Task.Factory.StartNew(() =>
{
SetMyValueAsync("Scripting, please wait..");
// If the following lines will update the UI
// they should do so on the UI thread per Dr. Xu's post.
// String text = DBObjectsTextArea.Text;
// String[] args = text.Split(' ');
// SQLScripter scripter = new SQLScripter();
//scripter.script(args);
for (int i = 0; i < 50; i++)
{
System.Threading.Thread.Sleep(100);
SetMyValueAsync(i.ToString());
}
SetMyValueAsync("Done!");
});
}
protected override void OnClosing(CancelEventArgs e)
{
if (t != null)
{
try { t.Dispose(); }
catch (System.InvalidOperationException)
{
e.Cancel = true;
MessageBox.Show("Cannot close. Task is not complete.");
}
}
base.OnClosing(e);
}
I got this working by using the following:
private void ScriptToFileButton_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
for (var i = 0; i < 50; i++)
{
Thread.Sleep(100);
MyValue = i.ToString(CultureInfo.InvariantCulture);
}
})
.ContinueWith(s =>
{
MyValue = "Scripting, please wait..";
String text = DBObjectsTextArea.Text;
String[] args = text.Split(' ');
SQLScripter scripter = new SQLScripter();
scripter.script(args);
Thread.Sleep(3000); // This sleep is only used to simulate scripting
})
.ContinueWith(s =>
{
MyValue = "Done!";
});
}
You need to create task continuations. The reason why you are only seeing "Done" is because you are setting MyValue directly after you start the Task. You are not waiting for the Task to complete it's initial processing.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I want to develop a metro app for scanning bar-codes. Is there existing default library for bar-code scanning? Please provide me the guidelines and sample apps
Check out ZXing.Net on CodePlex and NuGet. The CodePlex source code has sample of WinRT app.
MainPage.xaml
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Canvas Width="640" Height="360">
<CaptureElement x:Name="VideoCapture" Width="640" Height="360" />
<Image x:Name="CaptureImage" Width="640" Height="360" Visibility="Collapsed" />
</Canvas>
<TextBlock x:Name="Error" VerticalAlignment="Bottom" FontSize="32" Width="640" TextAlignment="Center" Margin="363,0,363,37" />
<TextBlock x:Name="ScanResult" VerticalAlignment="Bottom" TextAlignment="Center" FontSize="32" Width="640"/>
</Grid>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
private readonly MediaCapture _mediaCapture = new MediaCapture();
private Result _result;
public MainPage()
{
InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
try
{
var cameras = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
if (cameras.Count < 1)
{
Error.Text = "No camera found, decoding static image";
await DecodeStaticResource();
return;
}
MediaCaptureInitializationSettings settings;
if (cameras.Count == 1)
{
settings = new MediaCaptureInitializationSettings { VideoDeviceId = cameras[0].Id }; // 0 => front, 1 => back
}
else
{
settings = new MediaCaptureInitializationSettings { VideoDeviceId = cameras[1].Id }; // 0 => front, 1 => back
}
await _mediaCapture.InitializeAsync(settings);
VideoCapture.Source = _mediaCapture;
await _mediaCapture.StartPreviewAsync();
while (_result == null)
{
var photoStorageFile = await KnownFolders.PicturesLibrary.CreateFileAsync("scan.jpg", CreationCollisionOption.GenerateUniqueName);
await _mediaCapture.CapturePhotoToStorageFileAsync(ImageEncodingProperties.CreateJpeg(), photoStorageFile);
var stream = await photoStorageFile.OpenReadAsync();
// initialize with 1,1 to get the current size of the image
var writeableBmp = new WriteableBitmap(1, 1);
writeableBmp.SetSource(stream);
// and create it again because otherwise the WB isn't fully initialized and decoding
// results in a IndexOutOfRange
writeableBmp = new WriteableBitmap(writeableBmp.PixelWidth, writeableBmp.PixelHeight);
stream.Seek(0);
writeableBmp.SetSource(stream);
_result = ScanBitmap(writeableBmp);
await photoStorageFile.DeleteAsync(StorageDeleteOption.PermanentDelete);
}
await _mediaCapture.StopPreviewAsync();
VideoCapture.Visibility = Visibility.Collapsed;
CaptureImage.Visibility = Visibility.Visible;
ScanResult.Text = _result.Text;
}
catch (Exception ex)
{
Error.Text = ex.Message;
}
}
private async System.Threading.Tasks.Task DecodeStaticResource()
{
var file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(#"Assets\1.jpg");
var stream = await file.OpenReadAsync();
// initialize with 1,1 to get the current size of the image
var writeableBmp = new WriteableBitmap(1, 1);
writeableBmp.SetSource(stream);
// and create it again because otherwise the WB isn't fully initialized and decoding
// results in a IndexOutOfRange
writeableBmp = new WriteableBitmap(writeableBmp.PixelWidth, writeableBmp.PixelHeight);
stream.Seek(0);
writeableBmp.SetSource(stream);
CaptureImage.Source = writeableBmp;
VideoCapture.Visibility = Visibility.Collapsed;
CaptureImage.Visibility = Visibility.Visible;
_result = ScanBitmap(writeableBmp);
if (_result != null)
{
ScanResult.Text += _result.Text;
}
return;
}
private Result ScanBitmap(WriteableBitmap writeableBmp)
{
var barcodeReader = new BarcodeReader
{
TryHarder = true,
AutoRotate = true
};
var result = barcodeReader.Decode(writeableBmp);
if (result != null)
{
CaptureImage.Source = writeableBmp;
}
return result;
}
protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
await _mediaCapture.StopPreviewAsync();
}
}
I am testing a WP8 app and it's image viewer to show many images, I found app's memory consumption is raising and want to find out how to solve it.
I've read some articles from web, however the solutions provided by those articles are not working on my app, please read the history below.
First, I found the article "Image Tips for Windows Phone 7" and download its sample to do clean image cache testing, it's working with 1 image.
And then for testing purposes, I make this app compiled with 15 offline images inside the app, and set as "Content", please download test app from here.
My testing steps are:
(1) Launch app
(2) Go to Image Caching page
(3) Enable checkbox "Avoid Image Caching"
(4) Continuously tapping button Show/Clear
(5) Keep watching the memory status textblock at the bottom
When I'm testing my app, the memory is raising, like 16.02MB => Show(19.32MB) => Clear(16.15MB) => Show(20.18MB) => Clear (17.03MB)...etc
And memory won't be freed even leaving caching page and go to caching page again.
It seems the solution of article "Image Tips for Windows Phone 7" is working for only 1 image.
Here comes the xaml and code-behind of solution by "Image Tips for Windows Phone 7".
[Caching.xaml]
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<ToggleButton Content="Show" Width="150" Checked="ShowImageClicked" Unchecked="ClearImageClicked"/>
<CheckBox x:Name="cbAvoidCache" Content="Avoid Image Caching"/>
</StackPanel>
<Image x:Name="img" Grid.Row="2" Width="256" Height="192"/>
<TextBlock x:Name="tbMemory" Grid.Row="2" Text="Memory: " VerticalAlignment="Bottom" Style="{StaticResource PhoneTextLargeStyle}"/>
</Grid>
[Caching.xaml.cs]
public partial class Caching : PhoneApplicationPage
{
public Caching()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Start();
timer.Tick += delegate
{
GC.Collect();
tbMemory.Text = string.Format("Memory: {0} bytes", DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage"));
};
}
private int nIndex = 1;
BitmapImage bitmapImageFromUri = new BitmapImage();
private void ShowImageClicked(object sender, RoutedEventArgs e)
{
string strImage = string.Format("../ImagesAsContent/{0:D2}.jpg", nIndex);
bitmapImageFromUri.UriSource = new Uri(strImage, UriKind.Relative);
img.Source = bitmapImageFromUri;
nIndex++;
if (nIndex > 15)
{
nIndex = 1;
}
(sender as ToggleButton).Content = "Clear";
}
private void ClearImageClicked(object sender, RoutedEventArgs e)
{
if (cbAvoidCache.IsChecked == true)
{
// set the UriSource to null in order to delete the image cache
BitmapImage bitmapImageFromUri = img.Source as BitmapImage;
bitmapImageFromUri.UriSource = null;
}
img.Source = null;
(sender as ToggleButton).Content = "Show";
}
}
I also tried to search any other solutions, some testing results are as below.
(1) Article "[wpdev] Memory leak with BitmapImage": It provides 2 solutions, one is DisposeImage API, another is to set BitmapImage source to null as below. Also the article let us know we must be careful about event handler attach/dettach, however my testing app doesn't have event handler in caching page.
[DisposeImage]
private void DisposeImage(BitmapImage image)
{
if (image != null)
{
try
{
using (var ms = new MemoryStream(new byte[] { 0x0 }))
{
image.SetSource(ms);
}
}
catch (Exception)
{
}
}
}
[Set null]
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
(2) Article "Windows phone: listbox with images out-of-memory": It provides an API "DisposeImage" with little difference than (1)'s as below, but this also doesn't work, I still got the memory raising symptom.
public static void DisposeImage(BitmapImage image)
{
Uri uri= new Uri("oneXone.png", UriKind.Relative);
StreamResourceInfo sr=Application.GetResourceStream(uri);
try
{
using (Stream stream=sr.Stream)
{
image.DecodePixelWidth=1; //This is essential!
image.SetSource(stream);
}
}
catch
{}
}
(3) Article "Cannot find the memory leak": It provides the same 2 solutions as above mentioned, also it mentioned the issue cannot repro for isolated storage's images, however my testing app's images are from isolated storage.
(4) I also tried for 1000 images, the testing result is app crash when the app showed around 190 images sequentially, please refer to the Windows Phone Application Analysis Graphics for memory below.
Finally, thanks for your patience to read my question and history, I've been working on this to find solution for many days.
If you have any clue or solution, please kindly let me know.
Thanks.
I was dealing with the same problem and I think, in the end, that actually I've found a workaround, I am not a pro programmer but here is my solution:
public Task ReleaseSingleImageMemoryTask(MyImage myImage, object control)
{
Pivot myPivot = control as Pivot;
Task t = Task.Factory.StartNew(() =>
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (myImage.img.UriSource != null)
{
myImage.img.UriSource = null;
DisposeImage(myImage.img);
}
PivotItem it = (PivotItem)(myPivot.ItemContainerGenerator.ContainerFromIndex(myImage.number % 10));
Image img = FindFirstElementInVisualTree<Image>(it);
if (img != null)
{
img.Source = null;
GC.Collect();
}
});
myImage.released = true;
});
return t;
}
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
{
return (T)child;
}
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
private void DisposeImage(BitmapImage img)
{
if (img != null)
{
try
{
using (var ms = new MemoryStream(new byte[] { 0x0 }))
{
img = new BitmapImage();
img.SetSource(ms);
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("ImageDispose FAILED " + e.Message);
}
}
}
Hope this help :)
This code is written to get best (maximum) resolution for cam and then take the photo for that but it is taking photo only for VGA while cam has 8 mega pixel resolution. Please correct me what is the wrong with this code. thanks
<Page
x:Class="win8appbycrs.BlankPage2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:win8appbycrs"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Image x:Name="image" HorizontalAlignment="Left" Height="424" Margin="254,45,0,0" VerticalAlignment="Top" Width="619"/>
<Button Content="Set" HorizontalAlignment="Left" Margin="978,293,0,0" VerticalAlignment="Top" Click="Button_Click"/>
<CaptureElement x:Name="ce" HorizontalAlignment="Left" Height="268" Margin="97,490,0,0" VerticalAlignment="Top" Width="421" Stretch="Fill"/>
<ComboBox x:Name="ComboBox1" HorizontalAlignment="Left" Margin="659,474,0,0" VerticalAlignment="Top" Width="120"/>
<Button Content="Capture" HorizontalAlignment="Left" Margin="840,499,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
<Image x:Name="Img" HorizontalAlignment="Left" Height="225" Margin="254,133,0,0" VerticalAlignment="Top" Width="356"/>
</Grid>
C# Code:
public BlankPage2()
{
InitializeComponent();
mediaCaptureMgr = new MediaCapture();
StartPreview();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
int max_res = 0;
int selected = 0;
string sCameraName;
var interfaces = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Devices.Enumeration.DeviceClass.VideoCapture);
foreach (DeviceInformation deviceInterface in interfaces)
{
sCameraName = deviceInterface.Name.ToString();
MediaCaptureInitializationSettings captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
captureInitSettings.VideoDeviceId = deviceInterface.Id;
captureInitSettings.PhotoCaptureSource = PhotoCaptureSource.VideoPreview;
//enumerate each camera for supported configurations
MediaCapture mediaCaptureMgr = new Windows.Media.Capture.MediaCapture();
await mediaCaptureMgr.InitializeAsync(captureInitSettings);
//System.Collections.Generic.IReadOnlyList<IMediaEncodingProperties> res = mediaCaptureMgr.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord);
res = mediaCaptureMgr.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoPreview);
//if no settings available, bail
if (res.Count < 1) return;
//list the number of setting combinations for the device
string sCount = sCameraName + ": " + res.Count.ToString();
// ComboBox1.Items.Add(sCount);
//list the different format settings
for (int i = 0; i < res.Count; i++)
{
VideoEncodingProperties vp = (VideoEncodingProperties)res[i];
if (vp.Width * vp.Height > max_res)
{
resolutionMax=vp;
max_res = (int)vp.Width;
selected = i;
}
}
}
setres(selected);
}
async void StartPreview()
{
try
{
await mediaCaptureMgr.InitializeAsync();
ce.Source = mediaCaptureMgr;
await mediaCaptureMgr.StartPreviewAsync();
}
catch (Exception ex)
{
mediaCaptureMgr = null;
GC.Collect();
}
}
private async void setres(int maxres)
{
await mediaCaptureMgr.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview , res[maxres]);
}
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
var captureSettings = new ImageEncodingProperties();
captureSettings.Height = resolutionMax.Height;
captureSettings.Width = resolutionMax.Width;
captureSettings.Subtype = "Jpeg";
// Requires documentsLibrary capability
var folder = ApplicationData.Current.RoamingFolder;
var folder1 = KnownFolders.PicturesLibrary;
var file = await folder.CreateFileAsync(#"Captured.jpg", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
await mediaCaptureMgr.CapturePhotoToStorageFileAsync(captureSettings, file);
Img.Source = new BitmapImage(new Uri(#file.Path));
await file.CopyAsync(folder1);
}
Just taking a guess here - instead of using VideoPreview to get your resolutions, you should try using Photo:
//res = mediaCaptureMgr.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoPreview);
res = mediaCaptureMgr.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.Photo);