Merging audio and video Swift
Asked Answered
E

1

0

Im trying to merge N audio tracks, to an video file. The video is in MP4 format, and all audios are m4a.

All the preparation works well but when the export finishes, always fails. Heres my code :

func mixAudioAndVideo() {
    self.player?.pause()
    let mixComposition = AVMutableComposition()
    let paths = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    let docsDirect = paths[0]

    for audioTrack in self.audioTracks {
        let musicFile = docsDirect.URLByAppendingPathComponent(audioTrack.audioName)
        let audioAsset = AVURLAsset(URL: musicFile!, options: nil)
        let audioTimeRange = CMTimeRangeMake(audioTrack.audioTime!, audioAsset.duration)
        let compositionAudioTrack:AVMutableCompositionTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)
        do {
            try compositionAudioTrack.insertTimeRange(audioTimeRange, ofTrack: audioAsset.tracksWithMediaType(AVMediaTypeAudio).first!, atTime: audioTrack.audioTime!)
        } catch let error {
            print(error)
        }
    }

    let videoAsset = AVURLAsset(URL: video!.movieURL, options: nil)
    let videoTimeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
    let compositionVideoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

    do {
        try compositionVideoTrack.insertTimeRange(videoTimeRange, ofTrack: videoAsset.tracksWithMediaType(AVMediaTypeVideo).first!, atTime: kCMTimeZero)
    } catch let error {
        print(error)
    }

    let videoName = "video\(self.audioTracks.count).mov"
    let outputFilePath = docsDirect.URLByAppendingPathComponent(videoName)

    let assetExport = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
    assetExport!.outputFileType = AVFileTypeQuickTimeMovie
    assetExport!.outputURL = outputFilePath!

    assetExport?.exportAsynchronouslyWithCompletionHandler({
        dispatch_async(dispatch_get_main_queue()){
            print("finished exporting \(outputFilePath)")
            print("status \(assetExport?.status)")
            print("error \(assetExport?.error)")
            SVProgressHUD.dismiss()
        }
    })
}

And the code i get :

error Optional(Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo={NSLocalizedDescription=unknown error, NSUnderlyingError=0x170056140 {Error Domain=NSOSStatusErrorDomain Code=-12935 "(null)"}})
Eversion answered 3/11, 2016 at 14:51 Comment(0)
E
2

Swift: 3 first merge N audio tracks

var mergeAudioURL = NSURL()

func mergeAudioFiles(audioFileUrls: NSArray) {
        let composition = AVMutableComposition()

        for i in 0 ..< audioFileUrls.count {

            let compositionAudioTrack :AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())

            let asset = AVURLAsset(url: (audioFileUrls[i] as! NSURL) as URL)

            let track = asset.tracks(withMediaType: AVMediaTypeAudio)[0]

            let timeRange = CMTimeRange(start: CMTimeMake(0, 600), duration: track.timeRange.duration)

            try! compositionAudioTrack.insertTimeRange(timeRange, of: track, at: composition.duration)
        }

        let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL
        self.mergeAudioURL = documentDirectoryURL.appendingPathComponent("Merge Audio.m4a")! as URL as NSURL

        let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
        assetExport?.outputFileType = AVFileTypeAppleM4A
        assetExport?.outputURL = mergeAudioURL as URL
        removeFileAtURLIfExists(url: mergeAudioURL)
        assetExport?.exportAsynchronously(completionHandler:
            {
                switch assetExport!.status
                {
                case AVAssetExportSessionStatus.failed:
                    print("failed \(assetExport?.error)")
                case AVAssetExportSessionStatus.cancelled:
                    print("cancelled \(assetExport?.error)")
                case AVAssetExportSessionStatus.unknown:
                    print("unknown\(assetExport?.error)")
                case AVAssetExportSessionStatus.waiting:
                    print("waiting\(assetExport?.error)")
                case AVAssetExportSessionStatus.exporting:
                    print("exporting\(assetExport?.error)")
                default:
                    print("-----Merge audio exportation complete.\(self.mergeAudioURL)")
                }
        })
    }

then merge audio with video

var mergedAudioVideoURl = NSURL()

    func mergeMutableVideoWithAudio(videoUrl:NSURL, audioUrl:NSURL){
        let mixComposition : AVMutableComposition = AVMutableComposition()
        var mutableCompositionVideoTrack : [AVMutableCompositionTrack] = []
        var mutableCompositionAudioTrack : [AVMutableCompositionTrack] = []
        let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()
        //start merge
        let aVideoAsset : AVAsset = AVAsset(url: videoUrl as URL)
        let aAudioAsset : AVAsset = AVAsset(url: audioUrl as URL)
        mutableCompositionVideoTrack.append(mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid))
        mutableCompositionAudioTrack.append( mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid))
        let aVideoAssetTrack : AVAssetTrack = aVideoAsset.tracks(withMediaType: AVMediaTypeVideo)[0]
        let aAudioAssetTrack : AVAssetTrack = aAudioAsset.tracks(withMediaType: AVMediaTypeAudio)[0]
        do{
            try mutableCompositionVideoTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, aVideoAssetTrack.timeRange.duration), of: aVideoAssetTrack, at: kCMTimeZero)
            try mutableCompositionAudioTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, aVideoAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: kCMTimeZero)
        }catch{

        }
        totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,aVideoAssetTrack.timeRange.duration )
        let mutableVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition()
        mutableVideoComposition.frameDuration = CMTimeMake(1, 30)
        mutableVideoComposition.renderSize = CGSize(width: 1280, height: 720)
        mergedAudioVideoURl = NSURL(fileURLWithPath: NSHomeDirectory() + "/Documents/FinalVideo.mp4")
        let assetExport: AVAssetExportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)!
        assetExport.outputFileType = AVFileTypeMPEG4
        assetExport.outputURL = mergedAudioVideoURl as URL
        removeFileAtURLIfExists(url: mergedAudioVideoURl)
        assetExport.shouldOptimizeForNetworkUse = true
        assetExport.exportAsynchronously { () -> Void in
            switch assetExport.status {
            case AVAssetExportSessionStatus.completed:
                print("-----Merge mutable video with trimmed audio exportation complete.\(self.mergedAudioVideoURl)")
            case  AVAssetExportSessionStatus.failed:
                print("failed \(assetExport.error)")
            case AVAssetExportSessionStatus.cancelled:
                print("cancelled \(assetExport.error)")
            default:
                print("complete")
            }
        }
    }

    func removeFileAtURLIfExists(url: NSURL) {
        if let filePath = url.path {
            let fileManager = FileManager.default
            if fileManager.fileExists(atPath: filePath) {
                do{
                    try fileManager.removeItem(atPath: filePath)
                } catch let error as NSError {
                    print("-----Couldn't remove existing destination file: \(error)")
                }
            }
        }
    }
Erbes answered 11/3, 2017 at 6:11 Comment(1)
so my audio file is 3 seconds and i want to add it to a 30 seconds video before and after but it always shows up in the front, tried editng this to try mutableCompositionAudioTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, 3, preferredTimescale: 1)), of: aAudioAssetTrack, at: kCMTimeZero)Gaygaya

© 2022 - 2024 — McMap. All rights reserved.