2016-02-09 22 views
7

Sto tentando di combinare un'immagine e un video. Li ho combinando ed esportando tuttavia sono modi laterali ruotati.Orientamento esportazione AV errato

Ci scusiamo per il codice di massa incolla. Ho visto le risposte sull'applicazione di una trasformazione a compositionVideoTrack.preferredTransform ma questo non fa nulla. Aggiungendo a AVMutableVideoCompositionInstruction non fa nulla anche.

Mi sento come se questa zona fosse dove le cose iniziano a andare storte. qui:

// 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)") 

Quindi da qui le nostre dimensioni dei fotogrammi sono errate per il resto del metodo. Ora, quando cerchiamo di andare a creare il livello dell'immagine di sovrapposizione della cornice non è corretto:

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

Ecco il mio metodo completo.

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) 
       }) 
      } 
     }) 
    } 

Questo è quello che sto ottenendo esportato: enter image description here


Ho provato ad aggiungere questi due metodi al fine di ruotare il video:

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) 
} 

La versione aggiornata di mio metodo combineImageVid() aggiungendo questo in

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) 

che mi dà questo output:

enter image description here

Così mi sto avvicinando però sento che perché la pista è originariamente caricata nel modo sbagliato, ho bisogno di affrontare la questione lì. Inoltre, non so perché l'enorme scatola nera ci sia ora. Ho pensato che forse era dovuto al mio livello dell'immagine prendendo i limiti della risorsa video caricato qui:

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

Tuttavia la modifica che in qualche piccola larghezza/altezza non fare la differenza. Allora ho pensato di aggiungere una rec raccolto per sbarazzarsi del quadrato nero, ma che non ha funzionato neanche :(


seguito Allens suggerimenti di non usare questi due metodi:

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

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

Ma l'aggiornamento il mio metodo originale a guardare come questo:

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 

Stiamo ottenendo davvero vicino ma il problema ora sembra essere la modifica renderSize.Se lo cambio a qualcosa di diverso rispetto alla dimensione del paesaggio ottengo questo:

enter image description here

+0

Questo link qui potrebbe aiutare http://stackoverflow.com/questions/10034337/how-to- export-video-asset-via-avassetexportsession-in-portrait- –

+0

Ho provato anche questo :(Apprezzo il suggerimento però. – random

+0

Puoi provare a cambiare questo nel metodo combineImageVid() --- compositionVideoTrack.preferredTransform = CGAffineTransformMakeRotation (M_PI_2); –

risposta

14

Ecco il documento per l'orientamento di Apple:

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

se il video originale è stata presa in in modalità verticale iOS, la sua dimensione della natura sarà ancora orizzontale, ma viene fornita con un metadata di rotazione nel file mov. Al fine di ruotare il vostro video, è necessario apportare modifiche al tuo primo pezzo di codice con il seguente:

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 

Sì, si sono davvero vicino!

+0

Apprezzo l'aiuto! avvicinandosi molto comunque sembra che ci sia un problema quando si cambia 'renderSize'. Ho aggiornato qualsiasi domanda con un .gif di cosa sta succedendo. – random

+0

@ casuale, stai correndo in simulatore? quindi non farlo, eseguirlo in un vero dispositivo iOS, il simulatore non può fare bene la traduzione. – Allen

+0

sì, l'esecuzione nel simulatore era il problema! Sei fantastico, grazie mille per l'aiuto !! – random

0

Forse U dovrebbe verificare preferredTransform del VideoTrack modo per dare un RenderSize esatto e trasformare:

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

scusate, ho tradotto la spiegazione. @ luk2302 – JohnChen