1

Ich mache Video von einem Array von UIImages. Ich tat das erfolgreich & alle Bilder werden auf dem Video gezeigt. Ich verwende AVAssetExportSession, um das Video zu exportieren, das auch funktioniert, außer wenn ich AVAssetExportSession videoComposition -Eigenschaft verwende, zeigt das Video nur das erste Bild an. Hier ist mein Code:AVAssetExportSession videoComposition zeigt nicht alle Frames im Video

func mergeAudioVideoFiles(videoUrl:NSURL, audioUrl:NSURL)->NSURL 
{ 
    let mixComposition : AVMutableComposition = AVMutableComposition() 
    var mutableCompositionVideoTrack : [AVMutableCompositionTrack] = [] 
    var mutableCompositionAudioTrack : [AVMutableCompositionTrack] = [] 
    let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction() 


    //start merge 

    let aVideoAsset : AVAsset = AVAsset(URL: videoUrl) 
    let aAudioAsset : AVAsset = AVAsset(URL: audioUrl) 

    mutableCompositionVideoTrack.append(mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)) 
    mutableCompositionAudioTrack.append(mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)) 

    let aVideoAssetTrack : AVAssetTrack = aVideoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] 
    let aAudioAssetTrack : AVAssetTrack = aAudioAsset.tracksWithMediaType(AVMediaTypeAudio)[0] 
    do{ 
     try mutableCompositionVideoTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, aVideoAssetTrack.timeRange.duration), ofTrack: aVideoAssetTrack, atTime: kCMTimeZero) 

     try mutableCompositionAudioTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, aVideoAssetTrack.timeRange.duration), ofTrack: aAudioAssetTrack, atTime: kCMTimeZero)  
    }catch{ 

    } 
    print("\nslide duraition:\(CMTimeGetSeconds(aVideoAssetTrack.timeRange.duration))\n") 
    totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,aVideoAssetTrack.timeRange.duration) 

    let mutableVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition(propertiesOfAsset: aVideoAsset) 
    mutableVideoComposition.frameDuration = aVideoAssetTrack.timeRange.duration 
    mutableVideoComposition.renderSize = CGSizeMake(1280,720) 

    //find your video on this URl 
    let savePathUrl : NSURL = NSURL(fileURLWithPath: documentsPath.stringByAppendingPathComponent("pandorarofinalist.mov")) 

    // 4. Add subtitles (we call it theme) 
    let insertTime = kCMTimeZero 
    //let endTime = aVideoAssetTrack.timeRange.duration 
    //let range = self.totalFrameDuration 
    //let themeVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition(propertiesOfAsset: aVideoAsset) 
    // 4.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation. 

    let videolayerInstruction : AVMutableVideoCompositionLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: aVideoAssetTrack) 
    totalVideoCompositionInstruction.layerInstructions = NSArray(array: [videolayerInstruction]) as! [AVVideoCompositionLayerInstruction] 
    mutableVideoComposition.instructions = NSArray(array: [totalVideoCompositionInstruction]) as! [AVVideoCompositionInstructionProtocol] 

    //mutableCompositionAudioTrack[0].preferredTransform 
    videolayerInstruction.setTransform(mutableCompositionVideoTrack[0].preferredTransform, atTime: insertTime) 
    //videolayerInstruction.setOpacity(0.0, atTime: endTime) 

    // 4.3 - Add instructions 


    // mutableVideoComposition.renderScale = 1.0 
    //themeVideoComposition.renderSize = CGSizeMake(aVideoAssetTrack.naturalSize.width, aVideoAssetTrack.naturalSize.height) 
    //themeVideoComposition.frameDuration = self.totalFrameDuration 

    // add text 

    let title = String("my video") 

    let titleLayer = CATextLayer() 
    titleLayer.string = title 
    titleLayer.frame = CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: 100) 
    let fontName: CFStringRef = "Helvetica-Bold" 
    let fontSize = CGFloat(50) 
    titleLayer.font = CTFontCreateWithName(fontName, fontSize, nil) 
    titleLayer.alignmentMode = kCAAlignmentCenter 
    titleLayer.foregroundColor = UIColor.orangeColor().CGColor 

    let backgroundLayer = CALayer() 
    backgroundLayer.frame = CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: aVideoAssetTrack.naturalSize.height) 
    backgroundLayer.masksToBounds = true 
    backgroundLayer.addSublayer(titleLayer) 

    // 2. set parent layer and video layer 

    let parentLayer = CALayer() 
    let videoLayer = CALayer() 
    parentLayer.frame = CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: aVideoAssetTrack.naturalSize.height) 
    videoLayer.frame = CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: aVideoAssetTrack.naturalSize.height) 
    parentLayer.addSublayer(videoLayer) 
    parentLayer.addSublayer(backgroundLayer) 

    //backgroundLayer.opacity = 1.0 

    // 3. make animation 

    mutableVideoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer) 

    // Remove the file if it already exists (merger does not overwrite) 

    do{ 
     let fileManager = NSFileManager.defaultManager() 
     try fileManager.removeItemAtURL(savePathUrl) 
    }catch{ 
    } 

    let assetExport: AVAssetExportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)! 
    assetExport.outputFileType = AVFileTypeMPEG4 
    assetExport.outputURL = savePathUrl 
    assetExport.shouldOptimizeForNetworkUse = true 
    assetExport.videoComposition = mutableVideoComposition 

    assetExport.exportAsynchronouslyWithCompletionHandler {() -> Void in 
     switch assetExport.status { 

     case AVAssetExportSessionStatus.Completed: 

      PHPhotoLibrary.sharedPhotoLibrary().performChanges({ 
       PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(savePathUrl) 
      }) { success, error in 
       if !success { 
        print("Error saving video: \(error)") 
       } 
      } 

      //Uncomment this if u want to store your video in asset 

      //let assetsLib = ALAssetsLibrary() 
      //assetsLib.writeVideoAtPathToSavedPhotosAlbum(savePathUrl, completionBlock: nil) 

      print("success") 
     case AVAssetExportSessionStatus.Failed: 
      print("failed \(assetExport.error)") 
     case AVAssetExportSessionStatus.Cancelled: 
      print("cancelled \(assetExport.error)") 
     default: 
      print("complete") 
     } 
    } 

    return savePathUrl 
} 

Das Problem ist, dass Zeile assetExport.videoComposition = mutableVideoComposition Wenn ich diese Zeile der Ausgabe Video auslassen ist gut so. Aber wenn ich diese Zeile hinzufüge, zeigt das Ausgabevideo nur das erste Bild, das ich für das Video hinzugefügt habe. Ich muss videoComposition einstellen, weil ich Titeltext dem Video hinzufüge, das ich als CALayer hinzugefügt habe. Ich verwende swift 2.2 für mein Projekt. Irgendwelche Hilfe bitte? Danke im Voraus.

+0

Haben Sie eine Lösung gefunden? – Sam

+0

Dies ist ein Fehler mit AVVideoCompositionCoreAnimationTool Bitte überprüfen Sie diesen Beitrag und alle damit verbundenen Beiträge http://StackOverflow.com/search?Tab=Newest&q=Swift%20AVAssetWriter – Sam

+0

Allerdings habe ich es nicht geschafft, eine Lösung zu implementieren – Sam

Antwort

2

Ich glaube, das Problem ist diese Linie:

mutableVideoComposition.frameDuration = aVideoAssetTrack.timeRange.duration

frameDuration soll die Dauer eines einzelnen Rahmens im Video darstellen, nicht die Gesamtdauer des Videos. Die obige Zeile bewirkt, dass ein Frame des Videos die Dauer der ursprünglichen Videospur einhält. Sie sehen also nur einen Frame, als wäre es ein Standbild.

Für ein 30fps Video, sollten Sie frameDuration auf 1/30stel Sekunde, wie dies eingestellt:

mutableVideoComposition.frameDuration = CMTime(value: 1, timescale: 30)


Warnung: Seien Sie vorsichtig, nicht die andere init-Methode verwenden CMTime(seconds: 1.0, preferredTimescale: 30) , denn das wird deine frameDuration zu einer Sekunde machen.

Verwandte Themen