iphone avcapturesession avcapturedevice

iphone - ¿Por qué AVCaptureSession produce una orientación incorrecta?



avcapturedevice (10)

Entonces, seguí las instrucciones de Apple para capturar una sesión de video usando AVCaptureSession : http://developer.apple.com/iphone/library/qa/qa2010/qa1702.html . Un problema al que me enfrento es que, aunque la orientación de la cámara / dispositivo iPhone es vertical (y el AVCaptureVideoPreviewLayer muestra una secuencia de cámara vertical), la imagen de salida parece estar en el modo horizontal. Verifiqué el ancho y la altura de imageBuffer dentro de imageFromSampleBuffer: del código de ejemplo, y obtuve 640px y 480px respectivamente. ¿Alguien sabe por qué este es el caso?

¡Gracias!


Aquí hay una secuencia correcta:

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];


Echa un vistazo a la cabecera AVCaptureSession.h. Existe una definición para una enumeración llamada AVCaptureVideoOrientation que define varias orientaciones de video. En el objeto AVCaptureConnection hay una propiedad llamada videoOrientation que es AVCaptureVideoOrientation . Debería poder configurar esto para cambiar la orientación del video. Probablemente desee AVCaptureVideoOrientationLandscapeRight o AVCaptureVideoOrientationLandscapeLeft .

Puede encontrar AVCaptureConnections para la sesión mirando los resultados de la sesión. Las salidas tienen una propiedad de conexiones que es una matriz de conexiones para esa salida.


El problema de orientación es con la cámara frontal, por lo tanto, verifique el tipo de dispositivo y genere una nueva imagen, definitivamente resolverá el problema de orientación:

-(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); } }]; }


En primer lugar, en la configuración de su salida de video, coloque estas líneas:

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

Luego, configure su Target para que sea compatible solo con Portait, deseleccionando los modos Paisaje en la configuración General.

( Source )


Hice una modificación simple de una línea en imageFromSampleBuffer para corregir el problema de orientación (vea mi comentario en el código en "Modifiqué ..."). Espero que ayude a alguien porque pasé demasiado tiempo en esto.

// 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); }


Para aquellas personas que necesitan trabajar con CIImage y la orientación desde el búfer es incorrecta, utilicé esta corrección.

Tan fácil como eso. Por cierto, los números 3,1,6,8 son de aquí https://developer.apple.com/reference/imageio/kcgimagepropertyorientation

Y no me preguntes por qué 3,1,6,8 es la combinación correcta. Usé el método de la fuerza bruta para encontrarlo. Si sabes por qué dejar la explicación en un comentario por favor ...

- (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]; } // .... }


Por ejemplo:

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

El valor predeterminado parece ser AVCaptureVideoOrientationLandscapeRight .

Consulte también QA1744: Configuración de la orientación del video con AV Foundation .


Puedes probar esto:

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() }


Si la orientación de AVCaptureVideoPreviewLayer es correcta, simplemente puede establecer la orientación antes de capturar la imagen.

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

Además, es importante tener en cuenta que UIImageOrientation y AVCaptureVideoOrientation son diferentes. UIImageOrientationUp refiere al modo horizontal con los controles de volumen hacia el suelo ( no arriba si piensa usar los controles de volumen como un botón de obturador).

Por lo tanto, la orientación vertical con el botón de encendido que apunta al cielo ( AVCaptureVideoOrientationPortrait ) es en realidad UIImageOrientationLeft .


Todos ustedes están haciendo esto difícil.

En DidOutputSampleBuffer, simplemente cambie la orientación antes de tomar la imagen. Es mono, pero tu tienes

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

en objC es este método

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