54

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

谢谢!

4

11 回答 11

44

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

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

于 2010-08-25T02:06:15.737 回答
23

你们都让这变得困难。

在 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
于 2014-11-05T16:12:54.620 回答
22

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

// 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);
}
于 2012-07-21T19:31:11.957 回答
16

这是一个正确的顺序:

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];
于 2014-02-09T13:35:11.050 回答
13

例如:

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

默认似乎是AVCaptureVideoOrientationLandscapeRight.

另请参阅QA1744:使用 AV Foundation 设置视频方向

于 2013-04-10T07:29:29.357 回答
7

对于那些需要使用 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];
    }



    // ....

}
于 2017-05-24T15:34:09.610 回答
5

如果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];
}];

此外,重要的是要注意这一点,UIImageOrientation并且AVCaptureVideoOrientation是不同的。UIImageOrientationUp是指横向模式,音量控件向下朝向地面(如果您考虑将音量控件用作快门按钮,则不是向上)。

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

于 2015-06-15T18:24:13.457 回答
3

首先,在您的视频输出配置中,放置以下几行:

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。

来源

于 2018-10-21T00:15:58.423 回答
2

Orientation issue is with the front camera, so check device type and generate new image, it will definitely solve the orientation issue:

-(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);
    }
}];
}
于 2014-10-18T13:38:57.760 回答
1
// #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 版本 < 13.0,您可以使用来自previewLayer

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

于 2020-04-07T08:55:20.587 回答
-1

你可以试试这个:

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()
}
于 2017-11-16T21:14:21.753 回答