为什么AVCaptureSession输出错误的方向?
所以,我遵循苹果的指示,使用AVCaptureSession
捕获video会议: http : //developer.apple.com/iphone/library/qa/qa2010/qa1702.html 。 我面临的一个问题是,即使相机/ iPhone设备的方向是垂直的(并且AVCaptureVideoPreviewLayer
显示垂直相机stream),输出图像似乎处于风景模式。 我检查了imageFromSampleBuffer:
示例代码中的imageBuffer的宽度和高度,我分别获得了640px和480px。 有谁知道这是为什么?
谢谢!
看看头文件AVCaptureSession.h。 有一个名为AVCaptureVideoOrientation
的枚举的定义,它定义了各种video方向。 在AVCaptureConnection对象上有一个名为videoOrientation的属性,它是一个AVCaptureVideoOrientation
。 你应该能够设置这个来改变video的方向。 您可能需要AVCaptureVideoOrientationLandscapeRight
或AVCaptureVideoOrientationLandscapeLeft
。
您可以通过查看会话的输出来find会话的AVCaptureConnections。 输出具有连接属性,该连接属性是该输出的连接数组。
我对imageFromSampleBuffer
进行了一个简单的单行修改,以更正方向问题(请参阅我在“I modified …”下的代码中的评论)。 希望它能帮助别人,因为我在这上面花了太多时间。
// 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); }
你们都这么做很难
在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
这是一个正确的序列:
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];
例如:
AVCaptureConnection *captureConnection = <a capture connection>; if ([captureConnection isVideoOrientationSupported]) { captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait; }
默认显示为AVCaptureVideoOrientationLandscapeRight
。
另请参阅QA1744:使用AV Foundation设置video的方向 。
如果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
指的是音量控制下降到地面的风景模式(如果考虑将音量控制用作快门button, 则不启动)。
因此,使用指向天空的电源button( AVCaptureVideoOrientationPortrait
)的纵向方向实际上是UIImageOrientationLeft
。
对于那些需要使用CIImage和从缓冲区定位的人是错误的,我使用了这个修正。
就这么简单。 BTW的数字3,1,6,8是从这里https://developer.apple.com/reference/imageio/kcgimagepropertyorientation
不要问我为什么3,1,6,8是正确的组合。 我用蛮力的方法来find它。 如果你知道为什么让评论中的解释请…
- (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]; } // .... }
方向问题与前置摄像头有关,所以检查设备types并生成新的图像,这一定会解决方向问题:
-(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); } }]; }
试试这个;)
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() }