Горизонтальное вращение 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
    }
}