AVMutableAudioMixInputParameters:setVolume()不能处理音频文件iOS 9

时间:2015-10-26 13:41:28

标签: ios iphone swift avfoundation

我尝试将视频记录与设备的iPod库中的音频文件混合。

我想设置每个音频的音量(视频和音频文件的音频)。

我尝试将AVMutableAudioMixInputParameters对象与setVolume()方法一起使用。

我对视频的音量没有任何问题,但最终记录中音频文件的音量始终设置为最大值。我尝试使用视频更改音频文件以进行测试,并仅拍摄此视频的音频轨道,并且工作正常。

import UIKit
import AVFoundation

class AVTools: NSObject {

    /**
    volume: between 1.0 and 0.0
    */
    class func mergeVideoAndMusicWithVolume(videoURL: NSURL, audioURL: NSURL, startAudioTime: Float64, volumeVideo: Float, volumeAudio: Float, complete: (NSURL?) -> Void) -> Void {

        //The goal is merging a video and a music from iPod library, and set it a volume

        //Get the path of App Document Directory
        let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        let docsDir = dirPaths[0] as String

        //Create Asset from record and music
        let assetVideo: AVURLAsset = AVURLAsset(URL: videoURL)
        let assetMusic: AVURLAsset = AVURLAsset(URL: audioURL)

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

        //Add video to the final record
        do {
            try compositionVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, assetVideo.duration), ofTrack: assetVideo.tracksWithMediaType(AVMediaTypeVideo)[0], atTime: kCMTimeZero)
        } catch _ {
        }

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

        let assetVideoTrack: AVAssetTrack = assetVideo.tracksWithMediaType(AVMediaTypeAudio)[0]
        let assetMusicTrack: AVAssetTrack = assetMusic.tracksWithMediaType(AVMediaTypeAudio)[0]

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

        let musicParam: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: assetMusicTrack)
        musicParam.trackID = assetMusicTrack.trackID

        //Set final volume of the audio record and the music
        videoParam.setVolume(volumeVideo, atTime: kCMTimeZero)
        musicParam.setVolume(volumeAudio, atTime: kCMTimeZero) // <----- This doesn't work on audio file

        //Add setting
        audioMixParam.append(musicParam)
        audioMixParam.append(videoParam)

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

        do {
            try compositionAudioMusic.insertTimeRange(CMTimeRangeMake(CMTimeMake(Int64(startAudioTime * 10000), 10000), assetVideo.duration), ofTrack: assetMusicTrack, atTime: kCMTimeZero)
        } catch _ {
            assertionFailure()
        }

        //Add parameter
        audioMix.inputParameters = audioMixParam

        //Remove the previous temp video if exist
        let filemgr = NSFileManager.defaultManager()
        do {
            if filemgr.fileExistsAtPath("\(docsDir)/movie-merge-music.mov") {
                try filemgr.removeItemAtPath("\(docsDir)/movie-merge-music.mov")
            } else {
            }
        } catch _ {
        }

        //Exporte the final record’
        let completeMovie = "\(docsDir)/movie-merge-music.mov"
        let completeMovieUrl = NSURL(fileURLWithPath: completeMovie)
        let exporter: AVAssetExportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)!
        exporter.outputURL = completeMovieUrl
        exporter.outputFileType = AVFileTypeMPEG4
        exporter.audioMix = audioMix

        exporter.exportAsynchronouslyWithCompletionHandler({
            switch exporter.status{
            case  AVAssetExportSessionStatus.Failed:
                print("failed \(exporter.error)")
                complete(nil)
            case AVAssetExportSessionStatus.Cancelled:
                print("cancelled \(exporter.error)")
                complete(nil)
            default:
                print("complete")
                complete(completeMovieUrl)
            }
        })
    }
}

1 个答案:

答案 0 :(得分:8)

好的,我发现了问题。 问题是我分配资产的trackID而不是组合的trackID。 要修复它,只需替换:

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

    let musicParam: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: assetMusicTrack)
    musicParam.trackID = assetMusicTrack.trackID

为:

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

    let musicParam: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: assetMusicTrack)
    musicParam.trackID = compositionAudioMusic.trackID

最终结果:

    /**
 volume: between 1.0 and 0.0
 */
class func mergeVideoAndMusicWithVolume(videoURL: NSURL, audioURL: NSURL, startAudioTime: Float64, volumeVideo: Float, volumeAudio: Float, complete: (NSURL?) -> Void) -> Void {

    //The goal is merging a video and a music from iPod library, and set it a volume

    //Get the path of App Document Directory
    let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    let docsDir = dirPaths[0] as String

    //Create Asset from record and music
    let assetVideo: AVURLAsset = AVURLAsset(URL: videoURL)
    let assetMusic: AVURLAsset = AVURLAsset(URL: audioURL)

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

    //Add video to the final record
    do {
        try compositionVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, assetVideo.duration), ofTrack: assetVideo.tracksWithMediaType(AVMediaTypeVideo)[0], atTime: kCMTimeZero)
    } catch _ {
    }

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

    let assetVideoTrack: AVAssetTrack = assetVideo.tracksWithMediaType(AVMediaTypeAudio)[0]
    let assetMusicTrack: AVAssetTrack = assetMusic.tracksWithMediaType(AVMediaTypeAudio)[0]

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

    let musicParam: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: assetMusicTrack)
    musicParam.trackID = compositionAudioMusic.trackID

    //Set final volume of the audio record and the music
    videoParam.setVolume(volumeVideo, atTime: kCMTimeZero)
    musicParam.setVolume(volumeAudio, atTime: kCMTimeZero)

    //Add setting
    audioMixParam.append(musicParam)
    audioMixParam.append(videoParam)

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

    do {
        try compositionAudioMusic.insertTimeRange(CMTimeRangeMake(CMTimeMake(Int64(startAudioTime * 10000), 10000), assetVideo.duration), ofTrack: assetMusicTrack, atTime: kCMTimeZero)
    } catch _ {
        assertionFailure()
    }

    //Add parameter
    audioMix.inputParameters = audioMixParam

    //Remove the previous temp video if exist
    let filemgr = NSFileManager.defaultManager()
    do {
        if filemgr.fileExistsAtPath("\(docsDir)/movie-merge-music.mov") {
            try filemgr.removeItemAtPath("\(docsDir)/movie-merge-music.mov")
        } else {
        }
    } catch _ {
    }

    //Exporte the final record’
    let completeMovie = "\(docsDir)/movie-merge-music.mov"
    let completeMovieUrl = NSURL(fileURLWithPath: completeMovie)
    let exporter: AVAssetExportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)!
    exporter.outputURL = completeMovieUrl
    exporter.outputFileType = AVFileTypeMPEG4
    exporter.audioMix = audioMix

    exporter.exportAsynchronouslyWithCompletionHandler({
        switch exporter.status{
        case  AVAssetExportSessionStatus.Failed:
            print("failed \(exporter.error)")
            complete(nil)
        case AVAssetExportSessionStatus.Cancelled:
            print("cancelled \(exporter.error)")
            complete(nil)
        default:
            print("complete")
            complete(completeMovieUrl)
        }
    })
}