我的通用Windows应用程序中的相机手电筒仅工作一次

时间:2016-12-02 14:12:47

标签: c# uwp windows-10-mobile torch flashlight

我正在使用Windows 10平台上的通用Windows应用程序。该应用程序应该使用相机捕获条形码,并使用条形码做一些有用的事情。到目前为止,它可以很好地捕获和翻译条形码(使用ZXing Library)。我可以通过单击每个条形码一次按钮来一个接一个地捕获条形码。

但是我需要它在光线不足的情况下工作。我想让相机在光线不足的情况下自动打开手电筒(或手电筒)。我发现在我拍摄第一张照片之前,相机可以在光线不足的情况下自动打开手电筒(或手电筒)。不知何故,手电筒在第一张照片后自动关闭。只要用户仍然停留在我的应用程序的同一页面(并且只要环境昏暗),我希望它保持打开状态。请帮我解决这个问题。

到目前为止,我可以确定MediaCapture.CapturePhotoToStorageFileAsync()是关闭手电筒的命令。

以下是一个解决此问题的工作测试程序。

它是测试应用程序中的MainPage.xaml程序文件:

<Page
    x:Class="TestApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TestApp"
    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}">
      <CaptureElement Name="captureElement"
                      Stretch="UniformToFill"
                      Margin="32,-93,34.5,181.5"
                      d:LayoutOverrides="LeftPosition, RightPosition, TopPosition, BottomPosition" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" >
         <CaptureElement.RenderTransform>
            <CompositeTransform Rotation="90"/>
         </CaptureElement.RenderTransform>
      </CaptureElement>
      <Button x:Name="btnCapture" Content="Capture Barcode" HorizontalAlignment="Left" Margin="10,0,0,203" VerticalAlignment="Bottom" Height="64" BorderThickness="2,2,4,4" Background="#33FFFFFF" BorderBrush="Black" FontSize="20" FontWeight="Bold" Click="btnCapture_OnClick" Width="340"/>
      <Button x:Name="btnTerminateApp" Content="Terminate This App" HorizontalAlignment="Stretch" Height="66" Margin="10,0,10,42" VerticalAlignment="Bottom" Background="#33FFFFFF" BorderBrush="Black" BorderThickness="2,2,4,4" FontWeight="Bold" d:LayoutOverrides="LeftPosition, RightPosition" Click="btnTerminateApp_OnClick" FontSize="20"/>

    </Grid>
</Page>

它是测试应用程序中的MainPage.xaml.cs程序文件:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Graphics.Imaging;        // For BitmapDecoder.
using Windows.Media.Capture;           // For MediaCapture.
using Windows.Media.Devices;           // For FocusSettings, FocusMode, AutoFocusRange.
using Windows.Media.MediaProperties;   // For ImageEncodingProperties.
using Windows.Media.Playback;          // For MediaPlayer.Volume.
using Windows.Storage;                 // For StorageFile.
using Windows.Storage.Streams;         // For IRandomAccessStream.
using Windows.UI.Popups;               // For MessageDialog().
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;   // For WriteableBitmap.
using Windows.UI.Xaml.Navigation;
//using ZXing;                           // For BarcodeFormat.

// The Blank Page item template is documented
// at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace TestApp
   {
   /// <summary>
   /// An empty page that can be used on its own or navigated to within a Frame.
   /// </summary>
   public sealed partial class MainPage : Page
      {
      private MediaCapture captureMgr { get; set; }

      public MainPage()
         {
         this.InitializeComponent();
         this.InitCapture();
         }

      private void btnTerminateApp_OnClick(object sender, RoutedEventArgs e)
         {
         this.ReleaseCapture();
         Application.Current.Exit();
         }

      private async void btnCapture_OnClick( object sender, RoutedEventArgs e )
         // Capture the barcode photo and translate it into a barcode number. And then
         // use the barcode number to mark the piece as checked out. 
         {
         // Capture the barcode and translate it into a barcode number.

         //....Capture the barcode photo from the camera to a storage-file.
         ImageEncodingProperties fmtImage = ImageEncodingProperties.CreateJpeg();
         StorageFile storefile = await ApplicationData.Current.LocalFolder.CreateFileAsync
            (
            "BarcodePhoto.jpg",
            CreationCollisionOption.GenerateUniqueName
            );
         await this.captureMgr.CapturePhotoToStorageFileAsync( fmtImage, storefile );

         //....Convert the barcode photo in the storage file into a writeable-bitmap.
         IRandomAccessStream stream = await storefile.OpenAsync( FileAccessMode.Read );
         BitmapDecoder decoderBmp = await BitmapDecoder.CreateAsync( stream );
         WriteableBitmap bmp = new WriteableBitmap( (int)decoderBmp.PixelWidth,
                                                    (int)decoderBmp.PixelHeight );
         bmp.SetSource(stream);

         //....We are done with the temporary barcode image file. Delete it.
         await storefile.DeleteAsync();

         ////....Translate the barcode photo from the writeable-bitmap into a barcode number.
         //
         //ZXing.BarcodeReader bcodeReader = new ZXing.BarcodeReader();
         //
         //BarcodeFormat[] aAllowedFormat = new BarcodeFormat[] { BarcodeFormat.CODE_39 };
         //bcodeReader.Options.PossibleFormats = aAllowedFormat;
         //   // We only want it to deal with one barcode format. Hopefully this will reduce the
         //   // chance of reading the barcode number wrong, or speed up the decoding process.
         //   // Note that this option only works if we includes "Microphone" as a required
         //   // DeviceCapability of this app in Package.appmanifest. If we don't include
         //   // "Microphone", we will get an unhandled exception here.
         //
         //bcodeReader.Options.TryHarder = true;  // Try this option to see if we can reduce the
         //                                       //    chance of failing to translate the
         //                                       //    barcode into a number. So far no problem
         //                                       //    as of 11/21/2016.
         //
         //var result = bcodeReader.Decode( bmp );
         //if ( result == null )
         //   return;
         }

      private async void InitCapture()
         // Initialize everything about MediaCapture.
         {
         this.captureMgr = new MediaCapture();
         await this.captureMgr.InitializeAsync();

         // Skip the steps to set the photo resolution to the second lowest in order
         // not to make this test program too big.

         // Start the camera preview.
         captureElement.Source = this.captureMgr;
         await this.captureMgr.StartPreviewAsync();

         // Set the camera to auto-focus.
         var settings = new FocusSettings { Mode           = FocusMode.Continuous,
                                            AutoFocusRange = AutoFocusRange.FullRange };
         await this.captureMgr.VideoDeviceController.FocusControl.UnlockAsync();
         this.captureMgr.VideoDeviceController.FocusControl.Configure( settings );
         await this.captureMgr.VideoDeviceController.FocusControl.FocusAsync();

         // Turn on the flashlight in case the lighting is dim. Without enough
         // lighting, the auto-focus feature of the camera cannot work.
         var cameraFlashLight = this.captureMgr.VideoDeviceController.FlashControl;
         if ( cameraFlashLight.Supported )
            {
            if (cameraFlashLight.PowerSupported)
               cameraFlashLight.PowerPercent = 100;
            cameraFlashLight.Enabled = true;
            }
         // //////////////////////////
         // Tried replacing flashlight with torch. But get the same problem.
         // //////////////////////////
         //var cameraTorch = this.captureMgr.VideoDeviceController.TorchControl;
         //if ( cameraTorch.Supported )
         //   {
         //   if ( cameraTorch.PowerSupported )
         //      cameraTorch.PowerPercent = 100;
         //   cameraTorch.Enabled = true;
         //   }
         // //////////////////////////
         }

      private async void ReleaseCapture()
         {
         captureElement.Source = null;
         await this.captureMgr.StopPreviewAsync();
         this.captureMgr.Dispose();
         }
      }
   }

为了不强迫人们安装ZXing Library只是为了尝试上面的测试应用程序,我在测试应用程序中注释了与ZXing Library相关的所有内容。

用户可以通过离开该页面(并返回主菜单)然后返回到同一页面来解决问题。这会重置程序中的某些内容并使自动手电筒功能再次起作用。显然,这不是一个好的解决方法,因为用户需要为每个条形码拍摄做到这一点。请注意,上面显示的测试应用只有一页,没有主菜单。因此,您将无法使用上面的测试应用程序看到此解决方法。

我尝试通过在拍摄每张照片后重置MediaCapture来解决此问题。这是通过在我上面显示的测试应用程序中调用ReleaseCapture()和InitCapture()来完成的。不幸的是,这不仅减慢了条形码的每次捕获速度,而且还触发了关于该对象的System.ObjectDisposedException未初始化或类似的事情。无论如何,我更喜欢修复原始问题,而不是使用解决方法。

顺便说一下,在我的开发PC中,我有Windows 10 Professional和Visual Studio 2015 Professional。我使用的Windows手机是Microsoft Lumia 640 LTE,Windows 10 Moblile版本1511 OS-Built 10.0.10586.107。

请帮我解决这个问题。也欢迎任何建议。

提前致谢。

杰伊陈

1 个答案:

答案 0 :(得分:0)

结果解决方案非常简单。我需要做的就是首先释放它然后重新初始化它来重置CaptureElement,如下所示:

this.ReleaseCapture();
this.InitCapture();

ReleaseCapture()的棘手部分是我无法处理MediaCapture。否则,当我尝试重新初始化CaptureElement时,程序将崩溃。因此,ReleaseCapture()现在就像这样:

private async void ReleaseCapture()
   // Release the resources used for capturing photo.
   {
   try
      {
      captureElement.Source = null;
      await this.captureMgr.StopPreviewAsync();

      //////////////
      // Don't dispose it. Otherwise, when we re-initialize it right after we have released it, the program will
      // crash. We are better off don't do this here. When we are leaving the page, the page will release it
      // anyway.
      //////////////
      //this.captureMgr.Dispose();
      /////////////////////////////
      }
   catch( Exception ex )
      {
      String sErrMsg = String.Concat( "Fail to release resources related to the ",
                                      "use of the camera. The error message is: ",
                                      ex.Message );
      await new MessageDialog( sErrMsg, "Error" ).ShowAsync();
      }
   }

其他的事情是我在初始化CaptureElement时需要用火炬替换闪光灯的使用。原因是我更喜欢使用火炬来连续照明。不可否认,这与这个问题无关。但我提到这只是为了解释为什么这个版本的InitCapture()看起来与我原来的版本不同:

private async void InitCapture()
   // Initialize everything about MediaCapture.
   {
   this.captureMgr = new MediaCapture();
   await this.captureMgr.InitializeAsync();

   // Skip the steps to set the photo resolution to simplify
   // the sample program.

   // Start the camera preview.
   captureElement.Source = this.captureMgr;
   await this.captureMgr.StartPreviewAsync();

   // Ask the camera to auto-focus now.
   var focusControl = this.captureMgr.VideoDeviceController.FocusControl;
   var settings = new FocusSettings { Mode           = FocusMode.Continuous,
                                      AutoFocusRange = AutoFocusRange.FullRange };
   focusControl.Configure( settings );
   await focusControl.FocusAsync();       // Wait for the camera to focus

   // Turn on the torch in case the lighting is dim. Without enough
   // lighting, the auto-focus feature of the camera cannot work.
   var cameraTorch = this.captureMgr.VideoDeviceController.TorchControl;
   if ( cameraTorch.Supported )
      {
      if ( cameraTorch.PowerSupported )
         cameraTorch.PowerPercent = 100;
      cameraTorch.Enabled = true;
      }

   #region Error handling
   MediaCaptureFailedEventHandler handler = (sender, e) =>
      {
       System.Threading.Tasks.Task task = System.Threading.Tasks.Task.Run(async () =>
         {
         await new MessageDialog( "There was an error capturing the video from camera.", "Error" ).ShowAsync();
         });
      };

   this.captureMgr.Failed += handler;
   #endregion
   }

我在三款不同的Windows Mobile 10手机中尝试过此功能。他们都工作。

希望这有助于某人。

杰伊陈