Горизонтальное вращение AVCaptureVideoPreviewLayer
Я пытаюсь отключить любое распознаваемое вращение ориентации на AVCaptureVideoPreviewLayer, сохраняя при этом вращение для любых подзонов. AVCaptureVideoPreviewLayer имеет свойство ориентации, и его изменение позволяет правильному отображению слоя для любой ориентации. Тем не менее, поворот включает в себя некоторое фанковое вращение AVCaptureVideoPreviewLayer, а не оставаться гладким, как в приложении Camera.
Вот как я получил ориентацию для правильной работы, за вычетом поворота:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
_captureVideoPreviewLayer.frame = self.view.bounds;
_captureVideoPreviewLayer.orientation = [[UIDevice currentDevice] orientation];
}
Как мне заставить этот слой действовать как приложение "Камера", сохраняя при этом поворота для своих подзонов?
Кроме того, как в стороне, я видел, что свойство ориентации обесценивается на AVCaptureVideoPreviewLayer, и вместо этого следует использовать свойство videoOrientation для AVCaptureConnection, но я не думаю, что у меня есть AVCaptureConnection для доступа сюда (я просто отображая камеру на экране). Как я должен настроить это для доступа к видеооценке?
Ответы
Ответ 1
Использование аффинного преобразования в представлении, содержащем слой предварительного просмотра, создает плавный переход:
- (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;
}
}
Функция willAnimateRotationToInterfaceOrientation
вызывается внутри блока анимации вращения, поэтому здесь изменяются свойства анимации вместе с остальными анимациями вращения. Эта функция была поэтапно отключена в iOS 6 и заменена новыми методами обработки вращения устройства, так что это работает сейчас, но это не лучшее решение.
Ответ 2
Это старый вопрос, но поскольку у него не было принятого ответа, и я сам занимался этой проблемой в течение последних нескольких дней, я решил, что отправлю ответ.
Трюк для поворота видео на основе изменений ориентации устройства - это NOT ROTATE AVCaptureVideoPreviewLayer
или AVCaptureConnection
вообще.
Изменение ориентации в ориентации AVCaptureConnection
(AVCaptureVideoPreviewLayer
было устаревшим) ВСЕГДА приводит к уродливому анимированному изменению. И после поворота видео вам все равно придется преобразовать предварительный просмотр слой.
Правильный способ сделать это - использовать AVAssetWriter
для записи видео и аудио данных.. а затем применить преобразование на AVAssetWriterInput
в момент начала записи.
Вот рекламный ролик от Apple об этом -
Получение повернутых CVPixelBuffers из AVCaptureVideoDataOutput
Чтобы запросить вращение буфера, клиент вызывает -setVideoOrientation: on AVCaptureVideoDataOutput видео AVCaptureConnection. Обратите внимание, что физически вращающиеся буферы действительно имеют стоимость исполнения, поэтому только просьба вращение если необходимо. Если, например, вы хотите повернутое видео, записанное в видеофайл QuickTime с использованием AVAssetWriter, предпочтительнее установить свойство -transform на AVAssetWriterInput, а не физически вращать буферы в AVCaptureVideoDataOutput.
Применение преобразования аналогично приведенному выше коду.
- Вы регистрируетесь для изменений ориентации устройства.
- Отслеживайте преобразование для применения на основе новой ориентации устройства.
- Когда нажата запись, настройте AVAssetWriterInput и примените к нему преобразование.
Надеюсь, это поможет тому, кто ищет ответ.
Ответ 3
Я смог выполнить это со следующим кодом:
Настройте, когда вам нужна камера:
preview = [[self videoPreviewWithFrame:CGRectMake(0, 0, 320, 480)] retain];
[self.view addSubview:preview];
Функция 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;
}
Ответ 4
Я хотел, чтобы AVCaptureVideoPreviewLayer красиво следил за всеми поворотами. Вот как я это сделал (overlayView
- это просто представление, которое имеет правильные границы). Он только анимируется без зависания, когда вы помещаете свои вызовы в супер точно, где они находятся в коде:
- (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];
}
Ответ 5
Вы должны использовать CATransform3DMakeRotation
, который предназначен для слоев, как это было упомянуто в комментариях:
Например:
float degreesToRotate = 90.0; //Degrees you want to rotate
self.previewLayer.transform = CATransform3DMakeRotation(degreesToRotate / 180.0 * M_PI, 0.0, 0.0, 1.0);
В моем случае, будучи "self.previewLayer", слой должен вращаться. Имейте в виду, что после этого вы должны привязать к нему границы контейнера:
self.previewLayer.frame = self.view.bounds;
.
,
.
РЕДАКТИРОВАТЬ: Если вы собираетесь вращаться в результате поворота устройства (willAnimateRotationToInterfaceOrientation
), вы должны сделать такое преобразование следующим образом:
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
//**Perform Transformation here**
[CATransaction commit];
Таким образом, слой будет вращаться по мере просмотра.
Ответ 6
Вот как я это делаю:
- (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];
}
}
Ответ 7
В быстрой я использую:
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
}
}
Ответ 8
Так как AVCaptureVideoPreviewLayer
является CALayer, то любые изменения будут анимированы, http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html. Чтобы остановить анимацию, просто [previewLayer removeAllAnimations]
.