菜鸟教程小白 发表于 2022-12-11 22:09:07

ios - 锁定/解锁 iPhone 时 AVCapture session 卡住/卡住


                                            <p><p>我正在我的 xamarin.forms 应用程序中实现扫描仪功能,为此我正在使用 iOSnativeAVCaptureSession。但我的问题是在扫描或捕获 session 处于事件状态并且设备被锁定时,然后在解锁设备后卡住捕获 session ,这很奇怪。 </p>

<p>我尝试使用 UIApplication.DidEnterBackgroundNotification|UIApplication.WillEnterForegroundNotification 来处理它,我在此停止并再次启动捕获 session 。但卡住仍在发生。</p>

<pre><code>using System;
using AVFoundation;
using CoreFoundation;
using CoreGraphics;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

namespace MarginPointApp.iOS
{
    public class ScannerViewController : UIViewController
    {
      AVCaptureDevice captureDevice;
      AVCaptureVideoPreviewLayer videoPreviewLayer;
      AVCaptureSession captureSession;
      UIView viewfinderView;
      MetadataObjectsDelegate metadataObjectsDelegate;
      public event EventHandler&lt;String&gt; OnScanSuccess;
      UIView redLineCenter;
      UIView overlayView;
      UIView overlay;
      UIView bottomBarView;
      UIButton flashLightButton;
      UIButton cancelButton;
      UIButton cameraButton;
      UILabel bottomTextLabel;
      bool isCameraDismissed;
      NSObject interuptStartNoti, interuptEndNoti;
      AVMetadataObjectType metaTypes = AVMetadataObjectType.Code128Code |
                           AVMetadataObjectType.Code39Code | AVMetadataObjectType.Code39Mod43Code |
                               AVMetadataObjectType.DataMatrixCode | AVMetadataObjectType.EAN13Code | AVMetadataObjectType.EAN8Code |
                           AVMetadataObjectType.Interleaved2of5Code | AVMetadataObjectType.PDF417Code |
                           AVMetadataObjectType.QRCode | AVMetadataObjectType.UPCECode;
      public override void ViewDidLoad()
      {
            base.ViewDidLoad();
            UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString(&#34;statusBar&#34;)) as UIView;
            if (statusBar.RespondsToSelector(new ObjCRuntime.Selector(&#34;setBackgroundColor:&#34;)))
            {
                statusBar.BackgroundColor = UIColor.Black;
            }

            NavigationItem.Title = &#34;Scanner&#34;;
            this.View.BackgroundColor = UIColor.White;

            if (marginpoint.im.iOS.AppDelegate.camPosition)
            {
                captureDevice = GetCameraDevice(AVCaptureDevicePosition.Front);
            }
            else
            {
                captureDevice = GetCameraDevice(AVCaptureDevicePosition.Back);
            }
            CameraSetup();
      }

      public void CameraSetup()
      {

            NSError error = null;
            if (captureDevice != null)
            {
                try
                {
                  var input = new AVCaptureDeviceInput(captureDevice, out error);

                  captureSession = new AVCaptureSession();
                  if (captureSession == null) { return; }
                  if (captureSession.CanAddInput(input))
                        captureSession.AddInput(input);

                  var captureMetadataOutput = new AVCaptureMetadataOutput();
                  if (captureSession.CanAddOutput(captureMetadataOutput))
                  {
                        captureSession.AddOutput(captureMetadataOutput);
                        // captureMetadataOutput.MetadataObjectTypes = captureMetadataOutput.AvailableMetadataObjectTypes;
                        captureMetadataOutput.MetadataObjectTypes = metaTypes;
                  }

                  var metadataQueue = new DispatchQueue(&#34;com.AVCam.metadata&#34;);
                  metadataObjectsDelegate = new MetadataObjectsDelegate
                  {
                        DidOutputMetadataObjectsAction = DidOutputMetadataObjects
                  };
                  captureMetadataOutput.SetDelegate(metadataObjectsDelegate, metadataQueue);

                  videoPreviewLayer = new AVCaptureVideoPreviewLayer(session: captureSession);
                  videoPreviewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspectFill;
                  videoPreviewLayer.Frame = View.Layer.Bounds;
                  View.Layer.AddSublayer(videoPreviewLayer);
                }
                catch (Exception e)
                {
                  //Console.WriteLine(&#34;error device input&#34; + e.ToString());
                }
            }

            // Prepare device for configuration
            captureDevice.LockForConfiguration(out error);
            if (error != null)
            {
                // There has been an issue, abort
                //Console.WriteLine(&#34;Error: {0}&#34;, error.LocalizedDescription);
                captureDevice.UnlockForConfiguration();
                return;
            }
            addOverlayOnScreen();

            /*
            string reason = string.Empty;         
            if (interuptStartNoti == null)
            {
                interuptStartNoti = AVCaptureSession.Notifications.ObserveWasInterrupted((sender, e) =&gt;
                  {
                        reason = e.Notification.UserInfo?.ValueForKey(new NSString(&#34;AVCaptureSessionInterruptionReasonKey&#34;))?.ToString();
                        if (captureSession != null &amp;&amp; !reason.Equals(string.Empty) &amp;&amp; !reason.Equals(&#34;3&#34;))
                        {
                            captureSession.StopRunning();
                            //captureSession.Dispose();
                            //captureSession = null;
                        }                     
                  });
            }

            if (interuptEndNoti == null)
            {
                interuptEndNoti = AVCaptureSession.Notifications.ObserveInterruptionEnded((sender, e) =&gt;
                  {
                      Device.BeginInvokeOnMainThread(() =&gt;
                           {
                               if (marginpoint.im.iOS.AppDelegate.camPosition)
                               {
                                 captureDevice = GetCameraDevice(AVCaptureDevicePosition.Front);
                               }
                               else
                               {
                                 captureDevice = GetCameraDevice(AVCaptureDevicePosition.Back);
                               }
                               if (!reason.Equals(string.Empty) &amp;&amp; !reason.Equals(&#34;3&#34;))
                               {
                                 //CameraSetup();
                                 captureSession.StartRunning();
                               }
                              });
                  });
            }
            */

      }

      NSObject didEnterBackgroundNoti, willEnterForegroundNoti;
      public override void ViewWillAppear(bool animated)
      {
            base.ViewWillAppear(animated);
            if (didEnterBackgroundNoti == null)
            {
                didEnterBackgroundNoti = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidEnterBackgroundNotification, (obj) =&gt;
                  {
                      Device.BeginInvokeOnMainThread(() =&gt;
                      {
                        AddBlurEffect();
                        captureSession?.StopRunning();
                      });
                  });
            }
            willEnterForegroundNoti = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.WillEnterForegroundNotification, (obj) =&gt;
            {
                Device.BeginInvokeOnMainThread(() =&gt;
                {
                  RemoveBlurEffect();
                  CameraSetup();
                });
            });
      }

      public override void ViewWillDisappear(bool animated)
      {
            base.ViewWillDisappear(animated);

            NSNotificationCenter.DefaultCenter.RemoveObserver(didEnterBackgroundNoti);
            NSNotificationCenter.DefaultCenter.RemoveObserver(willEnterForegroundNoti);
      }

      private AVCaptureDevice GetCameraDevice(AVCaptureDevicePosition position)
      {
            AVCaptureDevice captureDevice = null;
            if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
            {
                captureDevice = AVCaptureDevice.GetDefaultDevice(AVCaptureDeviceType.BuiltInWideAngleCamera, AVMediaType.Video, position);//AVCaptureDevice.GetDefaultDevice(AVMediaTypes.Video);
            }
            else
            {
                var devices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video);
                foreach (var device in devices)
                {
                  if (device.Position == position)
                  {
                        captureDevice = device;
                  }
                }
            }
            return captureDevice;
      }

      UIVisualEffectView blurView;
      /// &lt;summary&gt;
      /// Adds the blur effect to camera preview.
      /// &lt;/summary&gt;
      void AddBlurEffect()
      {
            if (blurView == null)
            {
                var blur = UIBlurEffect.FromStyle(UIBlurEffectStyle.Light);
                blurView = new UIVisualEffectView(blur);
                blurView.Frame = View.Frame;
                blurView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth;
                View.AddSubview(blurView);
            }
      }

      void RemoveBlurEffect()
      {
            if (blurView != null)
            {
                blurView.RemoveFromSuperview();
                blurView = null;
            }
      }

      void addOverlayOnScreen()
      {
            overlayView = new UIView();
            overlayView.Frame = new CGRect(x: 0, y: 0, width: View.Frame.Width, height: View.Frame.Height);
            View.AddSubview(overlayView);
            View.BringSubviewToFront(overlayView);

            var overlayWidth = Application.Current.MainPage.Width * 0.7;
            overlay = new UIView();
            overlay.Layer.BorderColor = UIColor.Green.CGColor;
            overlay.Layer.BorderWidth = 4;
            overlay.Frame = new CGRect(x: View.Center.X - overlayWidth / 2, y: View.Center.Y - overlayWidth / 2, width: overlayWidth, height: overlayWidth);
            overlayView.AddSubview(overlay);
            overlayView.BringSubviewToFront(overlay);


            redLineCenter = new UIView();
            redLineCenter.BackgroundColor = UIColor.Red;
            redLineCenter.Alpha = 0.5f;
            redLineCenter.Frame = new CGRect(x: overlay.Frame.X + 4, y: overlay.Center.Y - 2, width: overlay.Frame.Width - 9, height: 4);
            overlayView.AddSubview(redLineCenter);
            overlayView.BringSubviewToFront(redLineCenter);


            // to find Qr code
            viewfinderView = new UIView();
            viewfinderView.Frame = new CGRect(x: overlay.Frame.X, y: overlay.Center.Y - 50, width: overlay.Frame.Width, height: 100);
            overlayView.AddSubview(viewfinderView);
            overlayView.BringSubviewToFront(viewfinderView);

            bottomBarView = new UIView();
            bottomBarView.BackgroundColor = UIColor.White;
            bottomBarView.Frame = new CGRect(x: 0, y: View.Frame.Height - 50, width: View.Frame.Width, height: 50);
            overlayView.AddSubview(bottomBarView);
            overlayView.BringSubviewToFront(bottomBarView);

            var centerPoint = (bottomBarView.Frame.Top - overlay.Frame.Bottom) / 2 - 15;
            bottomTextLabel = new UILabel
            {
                Frame = new CGRect(x: View.Frame.X, y: overlay.Frame.Bottom + centerPoint, width: View.Frame.Width, height: 30),
                Text = AppResources.ScanAutomatically,
                TextColor = UIColor.White,
                Font = UIFont.FromName(&#34;TitilliumWeb-Regular&#34;, 17),
                TextAlignment = UITextAlignment.Center
            };

            View.AddSubview(bottomTextLabel);
            View.BringSubviewToFront(bottomTextLabel);

            if (captureDevice.Position == AVCaptureDevicePosition.Back)
            {
                flashLightButton = new UIButton();
                flashLightButton.SetImage(new UIImage(filename: &#34;flash_white_light.png&#34;), UIControlState.Normal);
                flashLightButton.Frame = new CGRect(x: 0, y: 0, width: 50, height: 50);
                bottomBarView.AddSubview(flashLightButton);

                flashLightButton.TouchUpInside += async (object sender, EventArgs e) =&gt;
                {
                  NSError error = null;
                  if (captureDevice == null) return;
                  captureDevice.LockForConfiguration(out error);
                  if (error != null)
                  {
                        captureDevice.UnlockForConfiguration();
                        return;
                  }
                  else
                  {
                        if (!captureDevice.TorchAvailable)
                        {
                            var alert = new UIAlertView
                            {
                              Title = AppResources.MarginPoint,
                              Message = AppResources.CameraFlash
                            };
                            alert.AddButton(AppResources.OkButtonTitle);
                            alert.Show();

                            return;
                        }
                        if (captureDevice.TorchMode != AVCaptureTorchMode.On)
                        {
                            captureDevice.TorchMode = AVCaptureTorchMode.On;
                        }
                        else
                        {
                            captureDevice.TorchMode = AVCaptureTorchMode.Off;
                        }
                        captureDevice.UnlockForConfiguration();
                  }
                };
            }

            string blueColor = &#34;#1273B6&#34;;
            cancelButton = new UIButton();
            cancelButton.SetTitleColor(Color.FromHex(blueColor).ToUIColor(), UIControlState.Normal);
            cancelButton.SetTitle(AppResources.PickerCancelLabel, UIControlState.Normal);
            cancelButton.Font = UIFont.FromName(&#34;TitilliumWeb-Regular&#34;, 18);
            cancelButton.Frame = new CGRect(x: bottomBarView.Center.X - 50, y: 0, width: 100, height: 50);
            bottomBarView.AddSubview(cancelButton);
            cancelButton.TouchUpInside += (object sender, EventArgs e) =&gt;
            {
                if (captureSession != null)
                  captureSession.StopRunning();
                DismissViewController(true, null);
            };

            Device.BeginInvokeOnMainThread(() =&gt;
            {
                if (captureSession != null)
                  captureSession.StartRunning();
            });

            cameraButton = new UIButton();
            cameraButton.SetImage(new UIImage(filename: &#34;camera.png&#34;), UIControlState.Normal);
            cameraButton.Frame = new CGRect(x: bottomBarView.Frame.Width - 50, y: 0, width: 50, height: 50);
            bottomBarView.AddSubview(cameraButton);
            cameraButton.TouchUpInside += (object sender, EventArgs e) =&gt;
            {
                if (captureDevice.Position == AVCaptureDevicePosition.Back)
                {
                  if (captureDevice.TorchAvailable)
                        captureDevice.TorchMode = AVCaptureTorchMode.Off;
                  captureDevice = GetCameraDevice(AVCaptureDevicePosition.Front);
                  marginpoint.im.iOS.AppDelegate.camPosition = true;
                }
                else
                {
                  if (captureDevice.TorchAvailable)
                        captureDevice.TorchMode = AVCaptureTorchMode.Off;
                  captureDevice = GetCameraDevice(AVCaptureDevicePosition.Back);
                  marginpoint.im.iOS.AppDelegate.camPosition = false;
                }

                if (captureDevice != null)
                {
                  try
                  {
                        NSError error;
                        var input = new AVCaptureDeviceInput(captureDevice, out error);

                        captureSession = new AVCaptureSession();
                        if (captureSession == null) { return; }
                        if (captureSession.CanAddInput(input))
                            captureSession.AddInput(input);

                        var captureMetadataOutput = new AVCaptureMetadataOutput();
                        if (captureSession.CanAddOutput(captureMetadataOutput))
                        {
                            captureSession.AddOutput(captureMetadataOutput);
                            //captureMetadataOutput.MetadataObjectTypes = captureMetadataOutput.AvailableMetadataObjectTypes;

                            captureMetadataOutput.MetadataObjectTypes = metaTypes;
                        }
                        var metadataQueue = new DispatchQueue(&#34;com.AVCam.metadata&#34;);
                        metadataObjectsDelegate = new MetadataObjectsDelegate
                        {
                            DidOutputMetadataObjectsAction = DidOutputMetadataObjects
                        };
                        captureMetadataOutput.SetDelegate(metadataObjectsDelegate, metadataQueue);

                        captureSession.StartRunning();

                        videoPreviewLayer = new AVCaptureVideoPreviewLayer(session: captureSession);
                        videoPreviewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspectFill;
                        videoPreviewLayer.Frame = View.Layer.Bounds;
                        View.Layer.AddSublayer(videoPreviewLayer);
                  }
                  catch (Exception ex)
                  {
                        // Console.WriteLine(&#34;error device input&#34; + ex.ToString());
                  }

                  addOverlayOnScreen();
                }
            };
      }

      public void DidOutputMetadataObjects(AVCaptureOutput captureOutput,
                               AVMetadataObject[] metadataObjects,
                               AVCaptureConnection connection)
      {
            Device.BeginInvokeOnMainThread(() =&gt;
            {
                if (metadataObjects != null &amp;&amp; metadataObjects.Length == 0)
                {
                  //codeLabel.Text = &#34;No Data&#34;;
                  //displayScanResult(string.Empty);
                  return;
                }

                var metadataObject = metadataObjects as AVMetadataMachineReadableCodeObject;

                if (metadataObject == null) { return; }

                var visualCodeObject = videoPreviewLayer.GetTransformedMetadataObject(metadataObject);
                if (metadataObject.Type == AVMetadataObjectType.QRCode)
                {
                  if (viewfinderView.Frame.Contains(visualCodeObject.Bounds))
                  {
                        captureSession.StopRunning();
                        displayScanResult(metadataObject.StringValue);
                  }

                }
                else
                {
                  captureSession.StopRunning();
                  displayScanResult(metadataObject.StringValue);
                }
            });
      }

      private async void displayScanResult(string metadataObjectVal)
      {
            OnScanSuccess?.Invoke(this, string.IsNullOrWhiteSpace(metadataObjectVal) ? string.Empty : metadataObjectVal as String);
            captureSession.StopRunning();
            DismissViewController(true, null);
            //codeLabel.Text = metadataObject.StringValue;
      }

    }
    class MetadataObjectsDelegate : AVCaptureMetadataOutputObjectsDelegate
    {
      public Action&lt;AVCaptureMetadataOutput, AVMetadataObject[], AVCaptureConnection&gt; DidOutputMetadataObjectsAction;

      public override void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput, AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
      {
            if (DidOutputMetadataObjectsAction != null)
                DidOutputMetadataObjectsAction(captureOutput, metadataObjects, connection);
      }
    }
}


</code></pre>

<p>有没有人遇到过这个问题并找到了解决方法?</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>当用户进入前台时,不要每次都创建一个新的<code>captureSession</code>,而是尝试启动现有的<code>captureSession</code>。</p>

<pre><code>willEnterForegroundNoti =
NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.WillEnterForegroundNotification,
(obj) =&gt; {

          Device.BeginInvokeOnMainThread(() =&gt; {

            RemoveBlurEffect();
            captureSession?.startRunning();
          });
});
</code></pre></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 锁定/解锁 iPhone 时 AVCapturesession 卡住/卡住,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/54684301/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/54684301/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 锁定/解锁 iPhone 时 AVCapture session 卡住/卡住