iOS - swift - merge videos using AVFoundation

Marco picture Marco · Aug 20, 2014 · Viewed 8.2k times · Source

I'm trying to merge some videos in a unique output.mov. The exported file isn't playable and I don't now why. Can someone help me?

func exportVideo2(path:String, outputPath:String, nMovie:Int) -> Bool{
    var composition = AVMutableComposition()
    let track:AVMutableCompositionTrack = composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
    var insertTime = kCMTimeZero

    var movie = movieOfProject(path) 
    if movie.count == nMovie{
        for (index,mov) in enumerate(movie){
            let moviePath = path.stringByAppendingPathComponent(mov)
            // moviePath = path to the .mov file 
            println(moviePath)
            let moviePathUrl = NSURL(fileURLWithPath: moviePath)
            let sourceAsset = AVURLAsset(URL: moviePathUrl, options: nil)
            println(sourceAsset)
            let tracks = sourceAsset.tracksWithMediaType(AVMediaTypeVideo)
            println(sourceAsset.playable) // print true
            println(sourceAsset.exportable) // print true
            println(sourceAsset.readable) // print true
            if tracks.count > 0{
                let assetTrack:AVAssetTrack = tracks[0] as AVAssetTrack
                track.insertTimeRange(CMTimeRangeMake(kCMTimeZero,sourceAsset.duration), ofTrack: assetTrack, atTime: insertTime, error: nil)
                insertTime = CMTimeAdd(insertTime, sourceAsset.duration)
            }
        }
        let completeMovie = outputPath.stringByAppendingPathComponent("movie.mov")
        let completeMovieUrl = NSURL(fileURLWithPath: completeMovie)
        var exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)
        exporter.outputURL = completeMovieUrl
        exporter.outputFileType = AVFileTypeMPEG4
        exporter.exportAsynchronouslyWithCompletionHandler(nil)
        let ass = AVURLAsset(URL: completeMovieUrl, options: nil)
        println(ass.readable) // print false
        println(ass.exportable) // print false
        println(ass.playable) // print false
        return true
    }else{
        return false
    }
} 

The .mov files that I have to merge are all readable so I think the problem is in the last part, where the output video is exported.

Answer

Marco picture Marco · Aug 21, 2014

Resolved.

func exportVideo3(path:String, outputPath:String, nMovie:Int) -> Bool{
    var composition = AVMutableComposition()
    let trackVideo:AVMutableCompositionTrack = composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
    let trackAudio:AVMutableCompositionTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
    var insertTime = kCMTimeZero

    var movie = movieOfProject(path)
    if movie.count == nMovie{
        for (index,mov) in enumerate(movie){
            let moviePath = path.stringByAppendingPathComponent(mov)
            let moviePathUrl = NSURL(fileURLWithPath: moviePath)
            let sourceAsset = AVURLAsset(URL: moviePathUrl, options: nil)

            let tracks = sourceAsset.tracksWithMediaType(AVMediaTypeVideo)
            let audios = sourceAsset.tracksWithMediaType(AVMediaTypeAudio)

            if tracks.count > 0{
                let assetTrack:AVAssetTrack = tracks[0] as AVAssetTrack
                trackVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero,sourceAsset.duration), ofTrack: assetTrack, atTime: insertTime, error: nil)
                   let assetTrackAudio:AVAssetTrack = audios[0] as AVAssetTrack
                  trackAudio.insertTimeRange(CMTimeRangeMake(kCMTimeZero,sourceAsset.duration), ofTrack: assetTrackAudio, atTime: insertTime, error: nil)
                insertTime = CMTimeAdd(insertTime, sourceAsset.duration)
            }
        }

        let completeMovie = outputPath.stringByAppendingPathComponent("movie.mov")
        let completeMovieUrl = NSURL(fileURLWithPath: completeMovie)
        var exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)
        exporter.outputURL = completeMovieUrl
        exporter.outputFileType = AVFileTypeMPEG4 //AVFileTypeQuickTimeMovie
        exporter.exportAsynchronouslyWithCompletionHandler({
            switch exporter.status{
            case  AVAssetExportSessionStatus.Failed:
                println("failed \(exporter.error)")
            case AVAssetExportSessionStatus.Cancelled:
                println("cancelled \(exporter.error)")
            default:
                println("complete")
            }
        })
        return true
    }else{
        return false
    }
}