AVCaptureVideoPreviewLayer lisse rotation d'orientation

J'essaye de désactiver n'importe quelle rotation discernable d'orientation à un AVCaptureVideoPreviewLayer tout en maintenant la rotation pour toutes les sous-vues. AVCaptureVideoPreviewLayer possède une propriété d'orientation, et en la modifiant, le calque peut s'afficher correctement pour toute orientation. Cependant, la rotation implique une rotation géniale de AVCaptureVideoPreviewLayer, plutôt que de restr lisse comme dans l'application Camera.

C'est ainsi que j'ai eu l'orientation pour fonctionner correctement, less l'accroc dans la rotation:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { _captureVideoPreviewLayer.frame = self.view.bounds; _captureVideoPreviewLayer.orientation = [[UIDevice currentDevice] orientation]; } 

Comment puis-je faire en sorte que cette couche agisse comme l'application Appareil photo, tout en conservant des rotations pour ses sous-vues?

Aussi, en aparté, j'ai vu que la propriété orientation est dépréciée sur AVCaptureVideoPreviewLayer, et la propriété videoOrientation de AVCaptureConnection devrait être utilisée à la place, mais je ne pense pas avoir un AVCaptureConnection pour accéder ici (je montre simplement le camera sur l'écran). Comment dois-je configurer ceci pour accéder à videoOrientation?

L'utilisation d'une transformation affine sur la vue contenant la couche de prévisualisation crée une transition en douceur:

 - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { if (cameraPreview) { if (toInterfaceOrientation==UIInterfaceOrientationPortrait) { cameraPreview.transform = CGAffineTransformMakeRotation(0); } else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft) { cameraPreview.transform = CGAffineTransformMakeRotation(M_PI/2); } else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight) { cameraPreview.transform = CGAffineTransformMakeRotation(-M_PI/2); } cameraPreview.frame = self.view.bounds; } } 

La fonction willAnimateRotationToInterfaceOrientation est appelée dans le bloc d'animation de rotation afin que les propriétés changeantes ici s'animent avec le rest des animations de rotation. Cette fonction a été supprimée dans iOS 6 et remplacée par de nouvelles methods pour gérer la rotation des périphériques. Cela fonctionne donc pour l'instant, mais ce n'est pas la meilleure solution.

C'est une vieille question, mais comme il n'y avait pas de réponse acceptée et que j'ai moi-même traité de cette question depuis plusieurs jours, je me suis dit que je postrais la réponse.

L'astuce pour faire tourner la video en fonction des changements d'orientation de l'appareil est de ne PAS AVCaptureVideoPreviewLayer ou AVCaptureConnection .

Modification de l'orientation sur AVCaptureConnection (l'orientation de AVCaptureConnection était obsolète) TOUJOURS entraîne une modification animée laide. Et après la rotation de la video, vous devez toujours transformer la couche de prévisualisation.

La manière correcte d'y parvenir est d'utiliser AVAssetWriter pour écrire datatables video et audio. Ensuite, appliquez une transformation sur AVAssetWriterInput au moment où vous commencez l'logging.

Voici un blurb d'Apple à ce sujet –

Réception de CVPixelBuffers tournés à partir de AVCaptureVideoDataOutput

Pour requestr la rotation du tampon, un client appelle -setVideoOrientation: sur la video AVCaptureConnection d'AVCaptureVideoDataOutput. Notez que les tampons à rotation physique ont un coût de performance, donc seulement requestr une rotation si nécessaire. Si, par exemple, vous souhaitez que la video pivotée soit écrite dans un file video QuickTime à l'aide de AVAssetWriter, il est préférable de définir la propriété -transform sur AVAssetWriterInput plutôt que de faire tourner physiquement les tampons dans AVCaptureVideoDataOutput.

L'application de la transformation est similaire au code affiché ci-dessus.

  1. Vous vous enregistrez pour les changements d'orientation de l'appareil.
  2. Gardez une trace de la transformation à appliquer en fonction de l'orientation du nouveau périphérique.
  3. Lorsque vous appuyez sur logging, configurez AVAssetWriterInput et appliquez-lui la transformation.

J'espère que cela aide quiconque cherche une réponse.

J'ai été capable d'accomplir ceci avec le code suivant:

Configurer lorsque vous avez besoin de l'appareil photo:

 preview = [[self videoPreviewWithFrame:CGRectMake(0, 0, 320, 480)] retain]; [self.view addSubview:preview]; 

La fonction videoPreviewWithFrame .

 - (UIView *) videoPreviewWithFrame:(CGRect) frame { AVCaptureVideoPreviewLayer *tempPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]]; [tempPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; tempPreviewLayer.frame = frame; UIView* tempView = [[UIView alloc] init]; [tempView.layer addSublayer:tempPreviewLayer]; tempView.frame = frame; [tempPreviewLayer autorelease]; [tempView autorelease]; return tempView; } 

Je voulais que AVCaptureVideoPreviewLayer suive bien toutes les rotations. C'est comme ça que je l'ai fait ( overlayView est juste une vue qui a les bonnes limites). Il ne s'anime que si vous placez vos appels à super exactement où ils sont dans le code:

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; if ([[[self previewLayer] connection] isVideoOrientationSupported]) { [[[self previewLayer] connection] setVideoOrientation:toInterfaceOrientation]; } } - (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { CGRect layerRect = [[self overlayView] bounds]; [[self previewLayer] setBounds:layerRect]; [[self previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect), CGRectGetMidY(layerRect))]; [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration: duration]; } 

Vous devriez utiliser CATransform3DMakeRotation qui est destiné aux calques, comme cela a été mentionné dans les commentaires:

Par exemple:

 float degreesToRotate = 90.0; //Degrees you want to rotate self.previewLayer.transform = CATransform3DMakeRotation(degreesToRotate / 180.0 * M_PI, 0.0, 0.0, 1.0); 

Dans mon cas, étant "self.previewLayer" la couche à faire pivoter. Gardez à l'esprit que vous devez ensuite l'accrocher aux limites de la vue conteneur:

 self.previewLayer.frame = self.view.bounds; 

. . .

EDIT: Si vous allez effectuer une rotation à la suite de la rotation du périphérique ( willAnimateRotationToInterfaceOrientation ), vous devez effectuer la transformation suivante:

 [CATransaction begin]; [CATransaction setAnimationDuration:duration]; [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; //**Perform Transformation here** [CATransaction commit]; 

De cette façon, la couche va tourner comme le fait la vue.

C'est comme ça que je le fais:

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { if (tookFirstPicture) return; if (toInterfaceOrientation == UIInterfaceOrientationPortrait) { [previewLayer setOrientation:AVCaptureVideoOrientationPortrait]; } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { [previewLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft]; } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { [previewLayer setOrientation:AVCaptureVideoOrientationLandscapeRight]; } else { [previewLayer setOrientation:AVCaptureVideoOrientationPortraitUpsideDown]; } } 

En swift j'utilise:

 override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { if !UIDevice.currentDevice().model.contains("Simulator") { if (toInterfaceOrientation == .Portrait) { prevLayer?.transform = CATransform3DMakeRotation(0, 0, 0, 1) } else if (toInterfaceOrientation == .LandscapeLeft) { prevLayer?.transform = CATransform3DMakeRotation(CGFloat(M_PI)/2, 0, 0, 1) } else if (toInterfaceOrientation == .LandscapeRight) { prevLayer?.transform = CATransform3DMakeRotation(-CGFloat(M_PI)/2, 0, 0, 1) } prevLayer?.frame = self.scanView.frame } } 

Puisque AVCaptureVideoPreviewLayer est un CALayer, les changements seront animés, http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html . Pour arrêter l'animation seulement [previewLayer removeAllAnimations] .