【问题标题】:Video capturing in iOS with MonoTouch使用 MonoTouch 在 iOS 中捕获视频
【发布时间】:2011-05-10 16:42:46
【问题描述】:

我有在 Objective-C 中创建、配置和启动视频捕获会话的代码,运行时没有问题。我将示例移植到 C# 和 MonoTouch 4.0.3 并且遇到了一些问题,代码如下:

    void Initialize ()
    {   
        // Create notifier delegate class 
        captureVideoDelegate = new CaptureVideoDelegate(this);

        // Create capture session
        captureSession = new AVCaptureSession();
        captureSession.SessionPreset = AVCaptureSession.Preset640x480;

        // Create capture device
        captureDevice = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);

        // Create capture device input
        NSError error;
        captureDeviceInput = new AVCaptureDeviceInput(captureDevice, out error);
        captureSession.AddInput(captureDeviceInput);

        // Create capture device output
        captureVideoOutput = new AVCaptureVideoDataOutput();
        captureSession.AddOutput(captureVideoOutput);
        captureVideoOutput.VideoSettings.PixelFormat = CVPixelFormatType.CV32BGRA;
        captureVideoOutput.MinFrameDuration = new CMTime(1, 30);
        //
        // ISSUE 1
        // In the original Objective-C code I was creating a dispatch_queue_t object, passing it to
        // setSampleBufferDelegate:queue message and worked, here I could not find an equivalent to 
        // the queue mechanism. Also not sure if the delegate should be used like this).
        //
        captureVideoOutput.SetSampleBufferDelegatequeue(captureVideoDelegate, ???????);

        // Create preview layer
        previewLayer = AVCaptureVideoPreviewLayer.FromSession(captureSession);
        previewLayer.Orientation = AVCaptureVideoOrientation.LandscapeRight;
        //
        // ISSUE 2:
        // Didn't find any VideoGravity related enumeration in MonoTouch (not sure if string will work)
        //
        previewLayer.VideoGravity = "AVLayerVideoGravityResizeAspectFill";
        previewLayer.Frame = new RectangleF(0, 0, 1024, 768);
        this.View.Layer.AddSublayer(previewLayer);

        // Start capture session
        captureSession.StartRunning();

    }

    #endregion

    public class CaptureVideoDelegate : AVCaptureVideoDataOutputSampleBufferDelegate
    {
        private VirtualDeckViewController mainViewController;

        public CaptureVideoDelegate(VirtualDeckViewController viewController)
        {
            mainViewController = viewController;
        }

        public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
        {
            // TODO: Implement - see: http://go-mono.com/docs/index.aspx?link=T%3aMonoTouch.Foundation.ModelAttribute

        }
    }

问题 1: 不确定如何在 SetSampleBufferDelegatequeue 方法中正确使用委托。也没有找到在 Objective-C 中可以正常工作的 dispatch_queue_t 对象的等效机制来传递第二个参数。

问题 2: 我在 MonoTouch 库中没有找到任何 VideoGravity 枚举,不确定传递具有常量值的字符串是否有效。

我已经寻找任何线索来解决这个问题,但周围没有明确的样本。任何有关如何在 MonoTouch 中执行相同操作的示例或信息都将受到高度赞赏。

非常感谢。

【问题讨论】:

    标签: ios delegates xamarin.ios queue video-capture


    【解决方案1】:

    这是我的代码。好好利用它。我只是剪掉了重要的东西,所有的初始化都在那里,以及样本输出缓冲区的读取。

    然后我有从链接的自定义 ObjC 库处理 CVImageBuffer 的代码,如果您需要在 Monotouch 中处理它,那么您需要加倍努力并将其转换为 CGImage 或 UIImage。 Monotouch (AFAIK) 中没有此功能,因此您需要自己从普通的 ObjC 绑定它。 ObjC 中的示例在这里:how to convert a CVImageBufferRef to UIImage

    public void InitCapture ()
            {
                try
                {
                    // Setup the input
                    NSError error = new NSError ();
                    captureInput = new AVCaptureDeviceInput (AVCaptureDevice.DefaultDeviceWithMediaType (AVMediaType.Video), out error); 
    
                    // Setup the output
                    captureOutput = new AVCaptureVideoDataOutput (); 
                    captureOutput.AlwaysDiscardsLateVideoFrames = true; 
                    captureOutput.SetSampleBufferDelegateAndQueue (avBufferDelegate, dispatchQueue);
                    captureOutput.MinFrameDuration = new CMTime (1, 10);
    
                    // Set the video output to store frame in BGRA (compatible across devices)
                    captureOutput.VideoSettings = new AVVideoSettings (CVPixelFormatType.CV32BGRA);
    
                    // Create a capture session
                    captureSession = new AVCaptureSession ();
                    captureSession.SessionPreset = AVCaptureSession.PresetMedium;
                    captureSession.AddInput (captureInput);
                    captureSession.AddOutput (captureOutput);
    
                    // Setup the preview layer
                    prevLayer = new AVCaptureVideoPreviewLayer (captureSession);
                    prevLayer.Frame = liveView.Bounds;
                    prevLayer.VideoGravity = "AVLayerVideoGravityResize"; // image may be slightly distorted, but red bar position will be accurate
    
                    liveView.Layer.AddSublayer (prevLayer);
    
                    StartLiveDecoding ();
                }
                catch (Exception ex)
                {
                    Console.WriteLine (ex.ToString ());
                }
            }
    
    public void DidOutputSampleBuffer (AVCaptureOutput captureOutput, MonoTouch.CoreMedia.CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
            {   
                Console.WriteLine ("DidOutputSampleBuffer: enter");
    
                if (isScanning) 
                {
                    CVImageBuffer imageBuffer = sampleBuffer.GetImageBuffer (); 
    
                    Console.WriteLine ("DidOutputSampleBuffer: calling decode");
    
                    //      NSLog(@"got image w=%d h=%d bpr=%d",CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer), CVPixelBufferGetBytesPerRow(imageBuffer));
                    // call the decoder
                    DecodeImage (imageBuffer);
                }
                else
                {
                    Console.WriteLine ("DidOutputSampleBuffer: not scanning");
                }
    
                Console.WriteLine ("DidOutputSampleBuffer: quit");
            } 
    

    【讨论】:

    • Init结束时的StartLiveDecoding函数做的不多,只是调用//开始视频捕获captureSession.StartRunning();
    • 谢谢,这意味着 MonoTouch 支持解决方案。问题 #2 的答案在那里,但仍然不知道您的 dispatchQueue 是如何创建的。我猜 avBufferDelegate 是从委托类派生的类的一个实例。剩下的问题与 dispatchQueue 有关。非常感谢Pavel,缓冲区转换没问题。
    • 托管创建捕获会话工作,问题之一是尝试使用 SetSampleBufferDelegatequeue 而不是 SetSampleBufferDelegateAndQueue(不确定有什么区别)。但是现在我遇到了一个问题,预览中的图像在几帧后冻结,但是如果我在 DidOutputSampleBuffer 中放置一个断点,则图像在预览层中继续显示良好,而在该断点处停止执行。我想它必须按照我创建调度队列的方式进行。关于如何正确设置调度队列的任何线索?感谢您的帮助。
    【解决方案2】:

    所有问题都解决了,最后工作正常,发生了冻结,因为在我的测试中,我还没有在方法 DidOutputSampleBuffer 中处理 sampleBuffer。我的观点的最终代码在这里:

    更新 1:更改了 VideoSettings CVPixelFormat 的分配,不正确,会导致 sampleBuffer 中的 BytesPerPixel 错误。

    public partial class VirtualDeckViewController : UIViewController
    {   
        public CaptureVideoDelegate captureVideoDelegate;
    
        public AVCaptureVideoPreviewLayer previewLayer;
        public AVCaptureSession captureSession;
        public AVCaptureDevice captureDevice;
        public AVCaptureDeviceInput captureDeviceInput;
        public AVCaptureVideoDataOutput captureVideoOutput;
    

    ...

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();
    
            SetupVideoCaptureSession();
        }
    
        public void SetupVideoCaptureSession()
        {
            // Create notifier delegate class 
            captureVideoDelegate = new CaptureVideoDelegate();
    
            // Create capture session
            captureSession = new AVCaptureSession();
            captureSession.BeginConfiguration();
            captureSession.SessionPreset = AVCaptureSession.Preset640x480;
    
            // Create capture device
            captureDevice = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);
    
            // Create capture device input
            NSError error;
            captureDeviceInput = new AVCaptureDeviceInput(captureDevice, out error);
            captureSession.AddInput(captureDeviceInput);
    
            // Create capture device output
            captureVideoOutput = new AVCaptureVideoDataOutput();
            captureVideoOutput.AlwaysDiscardsLateVideoFrames = true;
                        // UPDATE: Wrong videosettings assignment
            //captureVideoOutput.VideoSettings.PixelFormat = CVPixelFormatType.CV32BGRA;
                        // UPDATE Correct videosettings assignment
                        captureVideoOutput.VideoSettings = new AVVideoSettings(CVPixelFormatType.CV32BGRA);
            captureVideoOutput.MinFrameDuration = new CMTime(1, 30);
            DispatchQueue dispatchQueue = new DispatchQueue("VideoCaptureQueue");
            captureVideoOutput.SetSampleBufferDelegateAndQueue(captureVideoDelegate, dispatchQueue);
            captureSession.AddOutput(captureVideoOutput);
    
            // Create preview layer
            previewLayer = AVCaptureVideoPreviewLayer.FromSession(captureSession);
            previewLayer.Orientation = AVCaptureVideoOrientation.LandscapeLeft;
            previewLayer.VideoGravity = "AVLayerVideoGravityResizeAspectFill";
            previewLayer.Frame = new RectangleF(0, 0, 1024, 768);
            this.View.Layer.AddSublayer(previewLayer);
    
            // Start capture session
            captureSession.CommitConfiguration();
            captureSession.StartRunning();  
        }
    
        public class CaptureVideoDelegate : AVCaptureVideoDataOutputSampleBufferDelegate
        {   
            public CaptureVideoDelegate() : base()
            {   
            }
    
            public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
            {
                // TODO: Implement buffer processing
    
                // Very important (buffer needs to be disposed or it will freeze)
                sampleBuffer.Dispose();
            }
        }
    

    我终于在这里找到的 Miguel de Icaza 样本回答了最后的难题:link

    感谢米格尔和帕维尔

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-17
      • 1970-01-01
      • 1970-01-01
      • 2013-08-15
      • 2017-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多