【问题标题】:Why AVCaptureSession output a wrong orientation?为什么 AVCaptureSession 输出错误的方向?
【发布时间】:2010-08-24 23:14:00
【问题描述】:

所以,我按照 Apple 的说明使用 AVCaptureSessionhttp://developer.apple.com/iphone/library/qa/qa2010/qa1702.html 捕获视频会话。我面临的一个问题是,即使相机/iPhone 设备的方向是垂直的(AVCaptureVideoPreviewLayer 显示垂直的相机流),输出图像似乎处于横向模式。我检查了示例代码的imageFromSampleBuffer:内的imageBuffer的宽度和高度,分别得到了640px和480px。有谁知道为什么会这样?

谢谢!

【问题讨论】:

    标签: iphone avcapturesession avcapturedevice


    【解决方案1】:

    查看头文件 AVCaptureSession.h。有一个名为AVCaptureVideoOrientation 的枚举定义,它定义了各种视频方向。在 AVCaptureConnection 对象上有一个名为 videoOrientation 的属性,它是一个AVCaptureVideoOrientation。您应该能够设置它来更改视频的方向。你可能想要AVCaptureVideoOrientationLandscapeRightAVCaptureVideoOrientationLandscapeLeft

    您可以通过查看会话的输出来找到会话的 AVCaptureConnections。输出有一个连接属性,该属性是该输出的连接数组。

    【讨论】:

    • Property 'videoOrientation' not found on object of type 'AVCaptureSession *'
    • @cheesus videoOrientation 在 AVCaptureConnection 上,而不是 AVCaptureSession
    • 但是我应该在AVCaptureConnection 的哪里设置这个属性?在委托方法里面?类似:- (void) captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { [connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
    • @rptwsthi 您可以在将输出添加到会话 captureSession.addOutput(videoOutput) let connection = videoOutput.connectionWithMediaType(AVFoundation.AVMediaTypeVideo) connection.videoOrientation = .Portrait captureSession.startRunning() 后立即设置
    【解决方案2】:

    你们都在做这件事。

    在 DidOutputSampleBuffer 中,只需在抓取图像之前更改方向即可。这是单声道,但你有

        public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate {    
            public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
            {
                try {
                    connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft;
    

    在 objC 中就是这个方法

    - ( void ) captureOutput: ( AVCaptureOutput * ) captureOutput
       didOutputSampleBuffer: ( CMSampleBufferRef ) sampleBuffer
          fromConnection: ( AVCaptureConnection * ) connection
    

    【讨论】:

    • 试过了,我得到了'AVCaptureConnection' does not have a member named 'VideoOrientation'
    • 这是一个大小写问题。应该是videoOrientation 所以编辑了答案。
    • 只有一件事我不明白。当我将相机与 ipad 风景一起使用时,我必须反转 videoOrientation。例如:如果我的 ipad 是 UIDeviceOrientationLeft 我必须做 connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight 和其他景观相同,否则图像会颠倒。多亏了 Apple,为什么必须在设备方向方面反转视频方向是另一件令人困惑的事情。
    • 有效!谢谢先生!
    • 有效。只要记住在主队列上异步设置方向,因为这个方法是在一个特殊的串行队列上调用的。
    【解决方案3】:

    我对@9​​87654322@ 进行了简单的一行修改以纠正方向问题(请参阅我在“我修改...”下的代码中的注释)。希望它对某人有所帮助,因为我在这方面花费了太多时间。

    // Create a UIImage from sample buffer data
    - (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer  {
        // Get a CMSampleBuffer's Core Video image buffer for the media data
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
        // Lock the base address of the pixel buffer
        CVPixelBufferLockBaseAddress(imageBuffer, 0); 
    
        // Get the number of bytes per row for the pixel buffer
        void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 
    
        // Get the number of bytes per row for the pixel buffer
        size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
        // Get the pixel buffer width and height
        size_t width = CVPixelBufferGetWidth(imageBuffer); 
        size_t height = CVPixelBufferGetHeight(imageBuffer); 
    
        // Create a device-dependent RGB color space
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    
        // Create a bitmap graphics context with the sample buffer data
        CGContextRef context1 = CGBitmapContextCreate(baseAddress, width, height, 8, 
                                                     bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    
        // Create a Quartz image from the pixel data in the bitmap graphics context
        CGImageRef quartzImage = CGBitmapContextCreateImage(context1); 
        // Unlock the pixel buffer
        CVPixelBufferUnlockBaseAddress(imageBuffer,0);
    
        // Free up the context and color space
        CGContextRelease(context1); 
        CGColorSpaceRelease(colorSpace);
    
        // Create an image object from the Quartz image
        //I modified this line: [UIImage imageWithCGImage:quartzImage]; to the following to correct the orientation:
        UIImage *image =  [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight]; 
    
        // Release the Quartz image
        CGImageRelease(quartzImage);
    
        return (image);
    }
    

    【讨论】:

    • 谢谢,对我有帮助。注意取自的方法:stackoverflow.com/questions/8924299/…
    • 你这个男人!我无法弄清楚这一点,因为我没有使用 AVCaptureConnection ......这成功了。谢谢!
    • @ReidBelton 您不能不使用 AVCaptureConnection,它会在您有输入和输出时自动创建,您只需检索它并更改方向。
    • 我有一个 CGBitmapContextCreateImage: invalid context 0x0 错误
    • 这不会重新压缩图像,降低其质量吗? EXIF 和嵌入的缩略图(如果有)也会出错。
    【解决方案4】:

    这是一个正确的顺序:

    AVCaptureVideoDataOutput *videoCaptureOutput = [[AVCaptureVideoDataOutput alloc] init];
    
    if([self.captureSession canAddOutput:self.videoCaptureOutput]){
        [self.captureSession addOutput:self.videoCaptureOutput];
    }else{
        NSLog(@"cantAddOutput");
    }
    
    // set portrait orientation
    AVCaptureConnection *conn = [self.videoCaptureOutput connectionWithMediaType:AVMediaTypeVideo];
    [conn setVideoOrientation:AVCaptureVideoOrientationPortrait];
    

    【讨论】:

    • 这对我有用。就像此代码所示,在更改这些属性之前将输出添加到捕获会话非常重要。带有 videoMirrored = true 的 AVCaptureVideoOrientationLandscapeRight 给了我与使用带有前置摄像头的标准相机应用程序相同的效果。
    • 这不只是改变了本地conn对象的方向,而不是属于self.videoCaptureOutput的对象吗?
    • @spfursich 你提到的东西非常非常不起眼但很重要。
    【解决方案5】:

    例如:

    AVCaptureConnection *captureConnection = <a capture connection>;
    if ([captureConnection isVideoOrientationSupported]) {
        captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
    }
    

    默认值似乎是AVCaptureVideoOrientationLandscapeRight

    另见QA1744: Setting the orientation of video with AV Foundation

    【讨论】:

    • 这对我有用,我将它添加到 myAVCaptureConnection 处理程序中。它按预期旋转了图像,但视频是镜像的。我看到 isMirrored 已被弃用,因此不清楚如何修复镜像内容 b/c 新的镜像方法没有关于使用的文档。
    • @Jim:由于AVCaptureVideoPreviewLayer 中的mirrored 已被弃用,您应该改用AVCaptureConnectionvideoMirrored 属性。至少在头文件中记录了这一点,其中mirrored 及其朋友还明确将您指向videoMirrored 和匹配的捕获连接方法。
    • 遗憾的是,该功能在 iOS 中不可用
    • @Jim:不可能,伙计!它也在在线文档中:developer.apple.com/library/ios/documentation/AVFoundation/…ʘ‿ʘ 是什么让您认为它在 iOS 上不可用?
    【解决方案6】:

    对于那些需要使用 CIImage 并且缓冲区方向错误的人,我使用了这个更正。

    就这么简单。顺便说一句,数字 3、1、6、8 来自这里https://developer.apple.com/reference/imageio/kcgimagepropertyorientation

    别问我为什么 3,1,6,8 是正确的组合。我使用蛮力方法找到它。如果您知道为什么请在评论中给出解释...

    - (void)captureOutput:(AVCaptureOutput *)captureOutput
        didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
               fromConnection:(AVCaptureConnection *)connection
    {
    
        // common way to get CIImage
    
        CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    
        CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate);
    
        CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer
                                                          options:(__bridge NSDictionary *)attachments];
    
        if (attachments) {
           CFRelease(attachments);
        }
    
        // fixing the orientation of the CIImage
    
        UIInterfaceOrientation curOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    
        if (curOrientation == UIInterfaceOrientationLandscapeLeft){
            ciImage = [ciImage imageByApplyingOrientation:3];
        } else if (curOrientation == UIInterfaceOrientationLandscapeRight){
            ciImage = [ciImage imageByApplyingOrientation:1];
        } else if (curOrientation == UIInterfaceOrientationPortrait){
            ciImage = [ciImage imageByApplyingOrientation:6];
        } else if (curOrientation == UIInterfaceOrientationPortraitUpsideDown){
            ciImage = [ciImage imageByApplyingOrientation:8];
        }
    
    
    
        // ....
    
    }
    

    【讨论】:

    【解决方案7】:

    如果AVCaptureVideoPreviewLayer 方向正确,您可以在捕获图像之前简单地设置方向。

    AVCaptureStillImageOutput *stillImageOutput;
    AVCaptureVideoPreviewLayer *previewLayer;
    NSData *capturedImageData;
    
    AVCaptureConnection *videoConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
    if ([videoConnection isVideoOrientationSupported]) {
        [videoConnection setVideoOrientation:previewLayer.connection.videoOrientation];
    }
    [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
        CFDictionaryRef exifAttachments =
                CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
        if (exifAttachments) {
            // Do something with the attachments.
        }
        // TODO need to manually add GPS data to the image captured
        capturedImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
        UIImage *image = [UIImage imageWithData:capturedImageData];
    }];
    

    另外,请注意UIImageOrientationAVCaptureVideoOrientation 是不同的。 UIImageOrientationUp 是指横向模式,音量控制向下朝向地面(不是如果您考虑将音量控制用作快门按钮)。

    因此,电源按钮指向天空的纵向 (AVCaptureVideoOrientationPortrait) 实际上是 UIImageOrientationLeft

    【讨论】:

    • 这应该被标记为正确答案。你也可以使用:connection.videoMirrored = true
    【解决方案8】:

    首先,在你的视频输出的配置中,把这些行:

    guard let connection = videoOutput.connection(withMediaType: 
    AVFoundation.AVMediaTypeVideo) else { return }
    guard connection.isVideoOrientationSupported else { return }
    guard connection.isVideoMirroringSupported else { return }
    connection.videoOrientation = .portrait
    connection.isVideoMirrored = position == .front
    

    然后,通过取消选中 General 配置中的 Landscape 模式,将 Target 配置为仅支持 Portait。

    (Source)

    【讨论】:

      【解决方案9】:

      方向问题是前置摄像头,所以检查设备类型并生成新图像,它肯定会解决方向问题:

      -(void)capture:(void(^)(UIImage *))handler{
      
      AVCaptureConnection *videoConnection = nil;
      for (AVCaptureConnection *connection in self.stillImageOutput.connections)
      {
          for (AVCaptureInputPort *port in [connection inputPorts])
          {
              if ([[port mediaType] isEqual:AVMediaTypeVideo] )
              {
                  videoConnection = connection;
                  break;
              }
          }
          if (videoConnection) { break; }
      }
      
      [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
      
          if (imageSampleBuffer != NULL) {
              NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
              **UIImage *capturedImage = [UIImage imageWithData:imageData];
              if (self.captureDevice == [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][1]) {
                  capturedImage = [[UIImage alloc] initWithCGImage:capturedImage.CGImage scale:1.0f orientation:UIImageOrientationLeftMirrored];
              }**
      
              handler(capturedImage);
          }
      }];
      }
      

      【讨论】:

        【解决方案10】:
        // #1
        AVCaptureVideoOrientation newOrientation = AVCaptureVideoOrientationLandscapeRight;
        if (@available(iOS 13.0, *)) {
            // #2
            for (AVCaptureConnection *connection in [captureSession connections]) {
                if ([connection isVideoOrientationSupported]) {
                    connection.videoOrientation = newOrientation;
                    break;
                }
            } // #3
        } else if ([previewLayer.connection isVideoOrientationSupported]) {
            previewLayer.connection.videoOrientation = newOrientation;
        }
        

        一旦您可以正确使用AVCaptureSession,您就可以设置视频方向。 这里对上面的代码进行详细说明。请记住,此代码必须在 [captureSession startRunning] 执行之后执行:

        1. 选择您喜欢的方向
        2. 对于 ios 版本 >= 13.0,您必须从 captureSession 检索活动连接。记住:只有视频连接支持videoOrientation
        3. 对于 ios 版本 previewLayer 的连接

        如果您的 viewController 没有固定方向,您可以在设备方向更改后为您的连接设置一个新的 videoOrientation

        【讨论】:

          【解决方案11】:

          你可以试试这个:

          private func startLiveVideo() {
          
              let captureSession = AVCaptureSession()
              captureSession.sessionPreset = .photo
              let captureDevice = AVCaptureDevice.default(for: .video)
          
              let input = try! AVCaptureDeviceInput(device: captureDevice!)
              let output = AVCaptureVideoDataOutput()
              captureSession.addInput(input)
              captureSession.addOutput(output)
          
              output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
              output.connection(with: .video)?.videoOrientation = .portrait
          
              let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
              previewLayer.frame = view.bounds
              view.layer.addSublayer(previewLayer)
          
              captureSession.startRunning()
          }
          

          【讨论】:

          • 你能解释一下为什么会这样吗?因为当我“尝试一下”时,它不会
          • 你调用了私有的 startLiveVideo() 函数吗?你遇到了什么问题?
          猜你喜欢
          • 1970-01-01
          • 2015-04-12
          • 1970-01-01
          • 2017-12-09
          • 2020-06-13
          • 1970-01-01
          • 2020-12-24
          • 2021-05-26
          • 2012-04-26
          相关资源
          最近更新 更多