使用 AudioMixInputParameters AVFoundation 为每个视频轨道设置多个音量在 Swift iOS 中不起作用

Setting multiple Volumes to each Video tracks using AudioMixInputParameters AVFoundation is not working in Swift iOS

我正在 Swift 中基于 ApplicationVideo。根据要求,我必须 select 来自 Device Gallery 的多个 Videos,为每个 Video Asset 设置不同的 CIFilter 效果和 Volume 然后合并所有 Videos 并且必须保存 Final Video。作为输出,当我播放 Final VideoVideo 声音 volume 应该相应地改变。

我已经将所有 selected Video Assets 合并为一个具有不同 CIFilter 效果的一个,但我的问题是当我试图为每个 Volume 设置时Video Clips 那就不行了。我正在为我的 Final Video 获取默认值 Volume。这是我的代码:

func addFilerEffectAndVolumeToIndividualVideoClip(_ assetURL: URL, video: VideoFileModel, completion : ((_ session: AVAssetExportSession?, _ outputURL : URL?) -> ())?){

        let videoFilteredAsset = AVAsset(url: assetURL)

        print(videoFilteredAsset)
        createVideoComposition(myAsset: videoFilteredAsset, videos: video)

        let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]

        let url = URL(fileURLWithPath: documentDirectory).appendingPathComponent("\(video.fileID)_\("FilterVideo").mov")

        let filePath = url.path
        let fileManager = FileManager.default

        do {
            if fileManager.fileExists(atPath: filePath) {
                print("FILE AVAILABLE")
                try fileManager.removeItem(atPath:filePath)
            } else {
                print("FILE NOT AVAILABLE")
            }
        } catch _ {
        }

        let composition: AVMutableComposition = AVMutableComposition()
        let compositionVideo: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
        let compositionAudioVideo: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())


        //Add video to the final record
        do {
            try compositionVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoFilteredAsset.duration), of: videoFilteredAsset.tracks(withMediaType: AVMediaTypeVideo)[0], at: kCMTimeZero)
        } catch _ {
        }

        //Extract audio from the video and the music
        let audioMix: AVMutableAudioMix = AVMutableAudioMix()
        var audioMixParam: [AVMutableAudioMixInputParameters] = []

        let assetVideoTrack: AVAssetTrack = videoFilteredAsset.tracks(withMediaType: AVMediaTypeAudio)[0]

        let videoParam: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: assetVideoTrack)
        videoParam.trackID = compositionAudioVideo.trackID

        //Set final volume of the audio record and the music
        videoParam.setVolume(video.videoClipVolume, at: kCMTimeZero)

        //Add setting
        audioMixParam.append(videoParam)

        //Add audio on final record
        do {
            try compositionAudioVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoFilteredAsset.duration), of: assetVideoTrack, at: kCMTimeZero)

        } catch _ {
            assertionFailure()
        }

        //Fading volume out for background music
        let durationInSeconds = CMTimeGetSeconds(videoFilteredAsset.duration)

        let firstSecond = CMTimeRangeMake(CMTimeMakeWithSeconds(0, 1), CMTimeMakeWithSeconds(1, 1))
        let lastSecond = CMTimeRangeMake(CMTimeMakeWithSeconds(durationInSeconds-1, 1), CMTimeMakeWithSeconds(1, 1))

        videoParam.setVolumeRamp(fromStartVolume: 0, toEndVolume: video.videoClipVolume, timeRange: firstSecond)
        videoParam.setVolumeRamp(fromStartVolume: video.videoClipVolume, toEndVolume: 0, timeRange: lastSecond)

        //Add parameter
        audioMix.inputParameters = audioMixParam

        // Export part, left for facility
        let exporter = AVAssetExportSession(asset: videoFilteredAsset, presetName: AVAssetExportPresetHighestQuality)!
        exporter.videoComposition = videoFilterComposition
        exporter.outputURL = url
        exporter.outputFileType = AVFileTypeQuickTimeMovie

        exporter.audioMix = audioMix

        exporter.exportAsynchronously(completionHandler: { () -> Void in
            completion!(exporter, url)
        })

    }

在那之后,我再次使用一种方法使用 AVAssetExportSession 合并所有 Video Clips,我没有设置任何 AudioMixInputParameters

Note: 当我使用 AVAssetExportSession's AudioMixInputParameters 在最终合并方法中设置音量时,然后 Volume 正在为完整 Video 进行更改。

我的问题:是否可以为每个Video Clips设置多个volume。请建议。谢谢!

这是我的问题的有效解决方案:

func addVolumeToIndividualVideoClip(_ assetURL: URL, video: VideoFileModel, completion : ((_ session: AVAssetExportSession?, _ outputURL : URL?) -> ())?){

        //Create Asset from Url
        let filteredVideoAsset: AVAsset = AVAsset(url: assetURL)

        video.fileID = String(video.videoID)

        //Get the path of App Document Directory
        let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]

        let url = URL(fileURLWithPath: documentDirectory).appendingPathComponent("\(video.fileID)_\("FilterVideo").mov")

        let filePath = url.path
        let fileManager = FileManager.default

        do {
            if fileManager.fileExists(atPath: filePath) {
                print("FILE AVAILABLE")
                try fileManager.removeItem(atPath:filePath)
            } else {
                print("FILE NOT AVAILABLE")
            }
        } catch _ {
        }


        let composition: AVMutableComposition = AVMutableComposition()
        let compositionVideo: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
        let compositionAudioVideo: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())


        //Add video to the final record
        do {
             try compositionVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, filteredVideoAsset.duration), of: filteredVideoAsset.tracks(withMediaType: AVMediaTypeVideo)[0], at: kCMTimeZero)
        } catch _ {
        }

        //Extract audio from the video and the music
        let audioMix: AVMutableAudioMix = AVMutableAudioMix()
        var audioMixParam: [AVMutableAudioMixInputParameters] = []

        let assetVideoTrack: AVAssetTrack = filteredVideoAsset.tracks(withMediaType: AVMediaTypeAudio)[0]

        let videoParam: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: assetVideoTrack)
        videoParam.trackID = compositionAudioVideo.trackID

        //Set final volume of the audio record and the music
        videoParam.setVolume(video.videoVolume, at: kCMTimeZero)

        //Add setting
        audioMixParam.append(videoParam)

        //Add audio on final record
        //First: the audio of the record and Second: the music
        do {
            try compositionAudioVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, filteredVideoAsset.duration), of: assetVideoTrack, at: kCMTimeZero)
        } catch _ {
            assertionFailure()
        }

        //Fading volume out for background music
        let durationInSeconds = CMTimeGetSeconds(filteredVideoAsset.duration)

        let firstSecond = CMTimeRangeMake(CMTimeMakeWithSeconds(0, 1), CMTimeMakeWithSeconds(1, 1))
        let lastSecond = CMTimeRangeMake(CMTimeMakeWithSeconds(durationInSeconds-1, 1), CMTimeMakeWithSeconds(1, 1))

        videoParam.setVolumeRamp(fromStartVolume: 0, toEndVolume: video.videoVolume, timeRange: firstSecond)
        videoParam.setVolumeRamp(fromStartVolume: video.videoVolume, toEndVolume: 0, timeRange: lastSecond)

        //Add parameter
        audioMix.inputParameters = audioMixParam

        //Remove the previous temp video if exist
        let filemgr = FileManager.default
        do {
            if filemgr.fileExists(atPath: "\(video.fileID)_\("FilterVideo").mov") {
                try filemgr.removeItem(atPath: "\(video.fileID)_\("FilterVideo").mov")
            } else {
            }
        } catch _ {
        }

        //Exporte the final record’
        let exporter: AVAssetExportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)!
        exporter.outputURL = url
        exporter.outputFileType = AVFileTypeMPEG4
        exporter.audioMix = audioMix

        exporter.exportAsynchronously(completionHandler: { () -> Void in
            completion!(exporter, url)

           // self.saveVideoToLibrary(from: filePath)
        })
    }

我发现,导出预设为 AVAssetExportPresetPassthrough 的资产不会对输出量产生影响。当我尝试使用 AVAssetExportPresetLowQuality 时,成功应用了音量更改。

我希望它在某处有更好的记录:(

工作代码:

// Assume we have:
let composition: AVMutableComposition
var inputParameters = [AVAudioMixInputParameters]()

// We add a track
let trackComposition = composition.addMutableTrack(...)

// Configure volume for this track
let inputParameter = AVMutableAudioMixInputParameters(track: trackComposition)
inputParameter.setVolume(desiredVolume, at: startTime)

// It works even without setting the `trackID`
// inputParameter.trackID = trackComposition.trackID

inputParameters.append(inputParameter)

// Apply gathered `inputParameters` before exporting
let audioMix = AVMutableAudioMix()
audioMix.inputParameters = inputParameters

// I found it's not working, if using `AVAssetExportPresetPassthrough`,
// so try `AVAssetExportPresetLowQuality` first
let export = AVAssetExportSession(..., presetName: AVAssetExportPresetLowQuality)
export.audioMix = audioMix

使用多个 assetTrack 插入到同一个 compositionTrack 进行测试,为每个插入设置不同的音量。似乎有效。