2016-02-09 3 views
7

Я пытаюсь объединить изображение и видео. У меня есть их комбинирование и экспорт, но они вращаются по бокам.AVFoundation экспортная ориентация неправильная

Извините за скопированный код. Я видел ответы о применении преобразования к compositionVideoTrack.preferredTransform, но это ничего не делает. Добавление в AVMutableVideoCompositionInstruction ничего не делает.

Я чувствую, что в этой области все начинает идти не так. здесь:

// I feel like this loading here is the problem 
     let videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] 

     // because it makes our parentLayer and videoLayer sizes wrong 
     let videoSize  = videoTrack.naturalSize 

     // this is returning 1920x1080, so it is rotating the video 
     print("\(videoSize.width) , \(videoSize.height)") 

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

let aLayer = CALayer() 
    aLayer.contents = UIImage(named: "OverlayTestImageOverlay")?.CGImage 
    aLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height) 
    aLayer.opacity = 1 

Вот мой полный метод.

func combineImageVid() { 

     let path = NSBundle.mainBundle().pathForResource("SampleMovie", ofType:"MOV") 
     let fileURL = NSURL(fileURLWithPath: path!) 

     let videoAsset = AVURLAsset(URL: fileURL) 
     let mixComposition = AVMutableComposition() 

     let compositionVideoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid) 

     var clipVideoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo) 

     do { 
      try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: clipVideoTrack[0], atTime: kCMTimeZero) 
     } 
     catch _ { 
      print("failed to insertTimeRange") 
     } 


     compositionVideoTrack.preferredTransform = videoAsset.preferredTransform 

     // I feel like this loading here is the problem 
     let videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] 

     // because it makes our parentLayer and videoLayer sizes wrong 
     let videoSize  = videoTrack.naturalSize 

     // this is returning 1920x1080, so it is rotating the video 
     print("\(videoSize.width) , \(videoSize.height)") 

     let aLayer = CALayer() 
     aLayer.contents = UIImage(named: "OverlayTestImageOverlay")?.CGImage 
     aLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height) 
     aLayer.opacity = 1 


     let parentLayer  = CALayer() 
     let videoLayer  = CALayer() 

     parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height) 
     videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height) 

     parentLayer.addSublayer(videoLayer) 
     parentLayer.addSublayer(aLayer) 


     let videoComp = AVMutableVideoComposition() 
     videoComp.renderSize = videoSize 
     videoComp.frameDuration = CMTimeMake(1, 30) 
     videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer) 

     let instruction = AVMutableVideoCompositionInstruction() 

     instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration) 

     let mixVideoTrack = mixComposition.tracksWithMediaType(AVMediaTypeVideo)[0] 
     mixVideoTrack.preferredTransform = CGAffineTransformMakeRotation(CGFloat(M_PI * 90.0/180)) 

     let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: mixVideoTrack) 
     instruction.layerInstructions = [layerInstruction] 
     videoComp.instructions = [instruction] 


     // create new file to receive data 
     let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) 
     let docsDir: AnyObject = dirPaths[0] 
     let movieFilePath = docsDir.stringByAppendingPathComponent("result.mov") 
     let movieDestinationUrl = NSURL(fileURLWithPath: movieFilePath) 

     do { 
      try NSFileManager.defaultManager().removeItemAtPath(movieFilePath) 
     } 
     catch _ {} 


     // use AVAssetExportSession to export video 
     let assetExport = AVAssetExportSession(asset: mixComposition, presetName:AVAssetExportPresetHighestQuality) 
     assetExport?.videoComposition = videoComp 
     assetExport!.outputFileType = AVFileTypeQuickTimeMovie 
     assetExport!.outputURL = movieDestinationUrl 
     assetExport!.exportAsynchronouslyWithCompletionHandler({ 
      switch assetExport!.status{ 
      case AVAssetExportSessionStatus.Failed: 
       print("failed \(assetExport!.error)") 
      case AVAssetExportSessionStatus.Cancelled: 
       print("cancelled \(assetExport!.error)") 
      default: 
       print("Movie complete") 


       // play video 
       NSOperationQueue.mainQueue().addOperationWithBlock({() -> Void in 
        print(movieDestinationUrl) 
       }) 
      } 
     }) 
    } 

Это то, что я получаю на экспорт: enter image description here


Я попытался добавить эти два метода для того, чтобы повернуть видео:

class func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction { 

    let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track) 

    let assetTrack = asset.tracksWithMediaType(AVMediaTypeVideo)[0] 

    let transform = assetTrack.preferredTransform 
    let assetInfo = orientationFromTransform(transform) 
    var scaleToFitRatio = UIScreen.mainScreen().bounds.width/assetTrack.naturalSize.width 

    if assetInfo.isPortrait { 

     scaleToFitRatio = UIScreen.mainScreen().bounds.width/assetTrack.naturalSize.height 
     let scaleFactor = CGAffineTransformMakeScale(scaleToFitRatio, scaleToFitRatio) 
     instruction.setTransform(CGAffineTransformConcat(assetTrack.preferredTransform, scaleFactor), 
      atTime: kCMTimeZero) 
    } else { 

     let scaleFactor = CGAffineTransformMakeScale(scaleToFitRatio, scaleToFitRatio) 
     var concat = CGAffineTransformConcat(CGAffineTransformConcat(assetTrack.preferredTransform, scaleFactor), CGAffineTransformMakeTranslation(0, UIScreen.mainScreen().bounds.width/2)) 
     if assetInfo.orientation == .Down { 
      let fixUpsideDown = CGAffineTransformMakeRotation(CGFloat(M_PI)) 
      let windowBounds = UIScreen.mainScreen().bounds 
      let yFix = assetTrack.naturalSize.height + windowBounds.height 
      let centerFix = CGAffineTransformMakeTranslation(assetTrack.naturalSize.width, yFix) 
      concat = CGAffineTransformConcat(CGAffineTransformConcat(fixUpsideDown, centerFix), scaleFactor) 
     } 
     instruction.setTransform(concat, atTime: kCMTimeZero) 
    } 

    return instruction 
} 

class func orientationFromTransform(transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) { 
    var assetOrientation = UIImageOrientation.Up 
    var isPortrait = false 
    if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 { 
     assetOrientation = .Right 
     isPortrait = true 
    } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 { 
     assetOrientation = .Left 
     isPortrait = true 
    } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 { 
     assetOrientation = .Up 
    } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 { 
     assetOrientation = .Down 
    } 
    return (assetOrientation, isPortrait) 
} 

Обновленный мой combineImageVid() метод добавив его в

let instruction = AVMutableVideoCompositionInstruction() 

instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration) 

let mixVideoTrack = mixComposition.tracksWithMediaType(AVMediaTypeVideo)[0] 

//let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: mixVideoTrack) 
//layerInstruction.setTransform(videoAsset.preferredTransform, atTime: kCMTimeZero) 

let layerInstruction = videoCompositionInstructionForTrack(compositionVideoTrack, asset: videoAsset) 

Который дает мне этот выход:

enter image description here

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

aLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height) 

Однако изменения, что в некоторой малой ширине/высоте не делает разницы. Я тогда подумал о добавлении ЗАП урожая, чтобы избавиться от черного квадрата, но это не сработало :(


После Allens предложений не с помощью этих двух методов:

class func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction 

class func orientationFromTransform(transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) 

Но обновление мой оригинальный способ выглядеть следующим образом:

videoLayer.frame = CGRectMake(0, 0, videoSize.height, videoSize.width) //notice the switched width and height 
... 
videoComp.renderSize = CGSizeMake(videoSize.height,videoSize.width) //this make the final video in portrait 
... 
layerInstruction.setTransform(videoTrack.preferredTransform, atTime: kCMTimeZero) //important piece of information let composition know you want to rotate the original video in output 

Мы получаем очень близки, однако теперь проблема, кажется, редактирование renderSize.Если бы я изменить его на что-нибудь другое, чем размер ландшафтном я получаю это:

enter image description here

+0

Эта ссылка здесь может помочь вам http://stackoverflow.com/questions/10034337/how-to- export-video-asset-via-avassetexportsession-in-portrait-mode –

+0

Я тоже пробовал :(Я ценю предложение, хотя. – random

+0

Можете ли вы попробовать изменить это в методе combImageVid() --- compositionVideoTrack.preferredTransform = CGAffineTransformMakeRotation (M_PI_2); –

ответ

14

здесь является документом для ориентации на Apple:

https://developer.apple.com/library/ios/qa/qa1744/_index.html

если исходное видео было принято в портретный режим iOS, размер его природы по-прежнему будет ландшафтом, но он поставляется с метаданными вращения в файле mov. Для того, чтобы повернуть видео, необходимо внести изменения в 1-й части кода со следующим:

videoLayer.frame = CGRectMake(0, 0, videoSize.height, videoSize.width) //notice the switched width and height 
... 
videoComp.renderSize = CGSizeMake(videoSize.height,videoSize.width) //this make the final video in portrait 
... 
layerInstruction.setTransform(videoTrack.preferredTransform, atTime: kCMTimeZero) //important piece of information let composition know you want to rotate the original video in output 

Да, вы действительно близки!

+0

Я ценю помощь! очень близкое, но, похоже, проблема при изменении «renderSize». Я обновил любой вопрос с помощью .gif того, что происходит. – random

+0

@ random, вы работаете в симуляторе? то нет, запустите его в реальном устройстве iOS, симулятор не сможет хорошо сделать перевод. – Allen

+0

yup, запуск его в симуляторе был проблемой! Ты потрясающий, большое спасибо за помощь! – random

0

Может U следует проверить preferredTransform в videoTrack, с тем, чтобы дать ему точное RenderSize и преобразование:

CGAffineTransform transform = assetVideoTrack.preferredTransform; 
CGFloat rotation = [self rotationWithTransform:transform]; 
//if been rotated 
     if (rotation != 0) 
     { 
      //if rotation is 360° 
      if (fabs((rotation - M_PI * 2)) >= valueOfError) { 

       CGFloat m = rotation/M_PI; 
       CGAffineTransform t1; 
       //rotation is 90° or 270° 
       if (fabs(m - 1/2.0) < valueOfError || fabs(m - 3/2.0) < valueOfError) { 
        self.mutableVideoComposition.renderSize = CGSizeMake(assetVideoTrack.naturalSize.height,assetVideoTrack.naturalSize.width); 
        t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.height, 0); 
       } 
       //rotation is 180° 
       if (fabs(m - 1.0) < valueOfError) { 
        t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.width, assetVideoTrack.naturalSize.height); 
       } 
       CGAffineTransform t2 = CGAffineTransformRotate(t1,rotation); 
       //    CGAffineTransform transform = makeTransform(1.0, 1.0, 90, videoTrack.naturalSize.height, 0); 
       [passThroughLayer setTransform:t2 atTime:kCMTimeZero]; 
      } 
     } 

//convert transform to radian 
- (CGFloat)rotationWithTransform:(CGAffineTransform)t 
{ 
    return atan2f(t.b, t.a); 
} 
+0

извините, я перевел разложение. @ luk2302 – JohnChen