J’ai donc suivi les instructions d’Apple pour capturer une session vidéo en utilisant AVCaptureSession
: http://developer.apple.com/iphone/library/qa/qa2010/qa1702.html . L’un des problèmes auxquels je suis confronté est que même si l’orientation de l’appareil photo / iPhone est verticale (et que AVCaptureVideoPreviewLayer
affiche un stream de caméra vertical), l’image de sortie semble être en mode paysage. J’ai vérifié la largeur et la hauteur de imageBuffer dans imageFromSampleBuffer:
du code exemple et j’ai obtenu respectivement 640px et 480px. Est-ce que quelqu’un sait pourquoi c’est le cas?
Merci!
Jetez un oeil à l’en-tête AVCaptureSession.h. Il existe une définition pour un enum appelé AVCaptureVideoOrientation
qui définit diverses orientations vidéo. Sur l’object AVCaptureConnection, il existe une propriété appelée videoOrientation qui est une AVCaptureVideoOrientation
. Vous devriez être en mesure de définir ceci pour changer l’orientation de la vidéo. Vous voulez probablement AVCaptureVideoOrientationLandscapeRight
ou AVCaptureVideoOrientationLandscapeLeft
.
Vous pouvez trouver AVCaptureConnections pour la session en regardant les sorties de la session. Les sorties ont une propriété de connexions qui est un tableau de connexions pour cette sortie.
J’ai apporté une simple modification d’une ligne à l’ imageFromSampleBuffer
pour corriger le problème d’orientation (voir mon commentaire dans le code sous “J’ai modifié …”). J’espère que ça aide quelqu’un parce que j’ai passé trop de temps là-dessus.
// 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); }
Vous faites tout cela difficile.
Dans DidOutputSampleBuffer, modifiez simplement l’orientation avant de saisir l’image. C’est mono, mais vous avez
public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate { public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection) { try { connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft;
en objC c’est cette méthode
- ( void ) captureOutput: ( AVCaptureOutput * ) captureOutput didOutputSampleBuffer: ( CMSampleBufferRef ) sampleBuffer fromConnection: ( AVCaptureConnection * ) connection
Voici une séquence correcte:
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];
Par exemple:
AVCaptureConnection *captureConnection = ; if ([captureConnection isVideoOrientationSupported]) { captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait; }
La valeur par défaut semble être AVCaptureVideoOrientationLandscapeRight
.
Voir aussi QA1744: Définition de l’orientation de la vidéo avec AV Foundation .
Pour les personnes qui ont besoin de travailler avec CIImage et l’orientation à partir du tampon est incorrecte, j’ai utilisé cette correction.
Aussi simple que ça. BTW les nombres 3,1,6,8 sont d’ici https://developer.apple.com/reference/imageio/kcgimagepropertyorientation
Et ne me demandez pas pourquoi 3,1,6,8 est la bonne combinaison. J’ai utilisé la méthode de la force brute pour le trouver. Si vous savez pourquoi laisser l’explication dans un commentaire s’il vous plaît …
- (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]; } // .... }
Si l’orientation AVCaptureVideoPreviewLayer
est correcte, vous pouvez simplement définir l’orientation avant de capturer l’image.
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]; }];
En outre, il est important de noter que UIImageOrientation
et AVCaptureVideoOrientation
sont différents. UIImageOrientationUp
fait référence au mode paysage avec les commandes de volume vers le sol ( pas vers le haut si vous envisagez d’utiliser les commandes de volume comme déclencheur).
Ainsi, l’orientation portrait avec le bouton d’alimentation pointant vers le ciel ( AVCaptureVideoOrientationPortrait
) est en réalité UIImageOrientationLeft
.
Problème d’orientation est avec la caméra frontale, donc vérifier le type d’appareil et générer une nouvelle image, il va certainement résoudre le problème d’orientation:
-(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); } }]; }
Vous pouvez essayer ceci:
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() }