Вращающееся видео с AVMutableVideoCompositionLayerInstruction

Я снимаю видео на iPhone 4 с фронтальной камерой и комбинирую видео с некоторыми другими медиа-активами. Я бы хотел, чтобы это видео было ориентировано на портрет - ориентация по умолчанию для всего видео пейзажа, и в некоторых случаях вам нужно управлять этим вручную.

Я использую AVFoundation и, в частности, AVAssetExportSession с AVMutableVideoComposition. Основываясь на видео WWDC, ясно, что я должен сам "фиксировать" ориентацию, когда я совмещаю видео в новую композицию.

Итак, я создал AVMutableVideoCompositionLayerInstruction, подключенный к моему AVMutableVideoCompositionInstruction, и я использую метод setTransform: atTime: для установки преобразования, предназначенного для поворота видео:

    AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
CGAffineTransform portraitRotationTransform = CGAffineTransformMakeRotation(degreesToRadians(90.0));
[passThroughLayer setTransform:portraitRotationTransform atTime:kCMTimeZero];

Проблема заключается в том, что когда я просматриваю видео, которое экспортируется, на экране не отображается ни одно фактическое содержимое. Если я уменьшу угол поворота на 45 градусов, я вижу часть видео на экране - это почти так, как если бы он не вращался в центральной точке. Я включаю изображения ниже, чтобы сделать это более понятным, о чем я говорю.

Естественный размер видео возвращается как 480x360. Я попытался изменить это на 360x480, и это не влияет на основную проблему.

0 Степень вращения:

enter image description here

45 градусов вращения:

enter image description here

Вращение на 90 градусов только зеленое.

В любом случае, я надеюсь, кто-то, кто сделал это раньше, может указать мне в правильном направлении. Я не могу найти какие-либо документы по некоторым более продвинутым темам в композициях и экспорте AVFoundation.

Ответ 1

Попробуйте следующее:

AVMutableVideoCompositionLayerInstruction *passThroughLayer = AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(degreesToRadians(90.0));
CGAffineTransform rotateTranslate = CGAffineTransformTranslate(rotateTransform,320,0);
[passThroughLayer setTransform:rotateTranslate atTime:kCMTimeZero];

По существу идея состоит в создании матрицы вращения и перевода. Вы поворачиваете его в правильную ориентацию, а затем переводите в вид. Я не видел никакого способа указать точку центра, пока я просматривал API.

Ответ 2

Основываясь на ответах. Я нашел очень хороший способ отладки и выяснения, что пошло не так с вашими преобразованиями. Используя доступные методы рампы, вы можете анимировать преобразования, упрощая просмотр того, что делает ваше преобразование.

В большинстве случаев я обнаружил, что у меня есть преобразования, которые, казалось, ничего не делали, пока я не понял, что только использование свойства preferredTransform видеодорожки в одиночку может привести к тому, что видеопоток будет перемещен из экрана рендеринга.

AVMutableVideoCompositionLayerInstruction *videoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];

[videoLayerInstruction setTransformRampFromStartTransform:CGAffineTransformIdentity
toEndTransform:videoTrack.preferredTransform 
timeRange:CMTimeRangeMake(projectClipStart, projectClipDuration)];

В конце концов, я обнаружил, что в некоторых случаях мне нужно было применить перевод, чтобы вернуть повернутое видео в экран рендеринга.

CGAffineTransformConcat(videoTrack.preferredTransform, CGAffineTransformMakeTranslation(0, renderSize.height))

Примечание. Ваши значения перевода могут отличаться.

Ответ 3

AVAssetTrack *videoAssetTrack= [[videoAsset tracksWithMediaType:AVMediaTypeVideo] lastObject];

AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:&error];

videoCompositionTrack.preferredTransform = videoAssetTrack.preferredTransform;

Ответ 4

Также может быть актуальным, но есть ошибка в предпочтительномTransform, устанавливаемом при использовании фронтальной камеры. см. здесь, например, где ребята из проекта SDAVAssetExportSession закодировали обход:

https://github.com/rs/SDAVAssetExportSession/pull/70

Ответ 5

Я нашел решение в проекте плагина Flutter.

- (CGAffineTransform)fixTransform:(AVAssetTrack*)videoTrack {
  CGAffineTransform transform = videoTrack.preferredTransform;

  if (transform.tx == 0 && transform.ty == 0) {
      NSInteger rotationDegrees = (NSInteger)round(radiansToDegrees(atan2(transform.b, transform.a)));
      NSLog(@"TX and TY are 0. Rotation: %ld. Natural width,height: %f, %f", (long)rotationDegrees,
      videoTrack.naturalSize.width, videoTrack.naturalSize.height);
      if (rotationDegrees == 90) {
        NSLog(@"Setting transform tx");
        transform.tx = videoTrack.naturalSize.height;
        transform.ty = 0;
      } else if (rotationDegrees == 270) {
        NSLog(@"Setting transform ty");
        transform.tx = 0;
        transform.ty = videoTrack.naturalSize.width;
     }
  }
  return transform;
}

// set layerInstruction
[firstVideoLayerInstruction setTransform:[self fixTransform:firstVideoAssetTrack] atTime:kCMTimeZero];

Flutter VideoPlayerPlugin