iOS音頻視頻的播放、剪輯、合成(仿秒拍給視頻添加背景音樂)

分析:
這是一個仿秒拍音視頻合成的demo。打開秒拍并拍攝一段視頻,然后進(jìn)入編輯界面,給拍攝的視頻添加背景音樂,可以選擇任何你喜歡的音樂作為背景音樂,還可以調(diào)節(jié)背景音樂的音量和拍攝視頻的原始音量,來進(jìn)行合成,但是音視頻的合成是需要時間的,而滑動調(diào)節(jié)音量和選擇音樂沒有卡頓,所以推測在這個編輯界面并沒有進(jìn)行合成操作,只是音視頻一起循環(huán)播放;而合成操作是在點擊發(fā)布按鈕后才進(jìn)行的。

分析完畢,實現(xiàn)功能:
1,根據(jù)拍攝視頻的長短來剪輯音頻的長度
2,實現(xiàn)音視頻的同步循環(huán)播放
3,音視頻合成

1、剪輯音頻

根據(jù)時間生成輸出路徑

#pragma mark - 輸出路徑
+ (NSURL *)exporterPath {
    
    NSInteger nowInter = (long)[[NSDate date] timeIntervalSince1970];
    NSString *fileName = [NSString stringWithFormat:@"output%ld.mp4",(long)nowInter];
    
    NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
   
    NSString *outputFilePath =[documentsDirectory stringByAppendingPathComponent:fileName];
    
    if([[NSFileManager defaultManager]fileExistsAtPath:outputFilePath]){
        
        [[NSFileManager defaultManager]removeItemAtPath:outputFilePath error:nil];
    }
    
    return [NSURL fileURLWithPath:outputFilePath];
}

根據(jù)拍攝視頻的長短,來剪輯音頻的長度,從而實現(xiàn)音視頻同步播放

#pragma mark - 音視頻剪輯,如果是視頻(mp4格式)剪輯把下面的兩個地方類型換為AVFileTypeAppleM4V
/**
 音視頻剪輯

 @param assetURL  音視頻資源路徑
 @param startTime 開始剪輯的時間
 @param endTime 結(jié)束剪輯的時間
 @param completionHandle 剪輯完成后的回調(diào)
 */
+ (void)cutAudioVideoResourcePath:(NSURL *)assetURL startTime:(CGFloat)startTime endTime:(CGFloat)endTime complition:(void (^)(NSURL *outputPath,BOOL isSucceed)) completionHandle{
    //    素材
    AVAsset *asset = [AVAsset assetWithURL:assetURL];

    //    導(dǎo)出素材
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetAppleM4A];
    
    //剪輯(設(shè)置導(dǎo)出的時間段)
    CMTime start = CMTimeMakeWithSeconds(startTime, asset.duration.timescale);
    CMTime duration = CMTimeMakeWithSeconds(endTime - startTime,asset.duration.timescale);
    exporter.timeRange = CMTimeRangeMake(start, duration);
    
//    輸出路徑
    NSURL *outputPath = [self exporterPath];
    exporter.outputURL = [self exporterPath];
    
//    輸出格式
    exporter.outputFileType = AVFileTypeAppleM4A;
    
    exporter.shouldOptimizeForNetworkUse= YES;
    
//    合成后的回調(diào)
    [exporter exportAsynchronouslyWithCompletionHandler:^{
        switch ([exporter status]) {
            case AVAssetExportSessionStatusFailed: {
                NSLog(@"合成失?。?@",[[exporter error] description]);
                completionHandle(outputPath,NO);
            } break;
            case AVAssetExportSessionStatusCancelled: {
                completionHandle(outputPath,NO);
            } break;
            case AVAssetExportSessionStatusCompleted: {
                completionHandle(outputPath,YES);
            } break;
            default: {
                completionHandle(outputPath,NO);
            } break;
        }
    }];
}

2、音視頻的循環(huán)同步播放

下面注釋已經(jīng)很清楚了,不多說了

//    添加播放層
    UIView *playView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 400)];
    [self.view addSubview:playView];
    
//    將資源路徑添加到AVPlayerItem上
    AVPlayerItem *playItem = [[AVPlayerItem alloc]initWithURL:[self filePathName:@"abc.mp4"]];
    
//    AVPlayer播放需要添加AVPlayerItem
    self.player = [[AVPlayer alloc]initWithPlayerItem:playItem];
    self.player.volume = 0.5;//默認(rèn)音量設(shè)置為0.5,取值范圍0-1
    
//    播放視頻需要在AVPlayerLayer上進(jìn)行顯示
    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    playerLayer.frame = playView.frame;//必須要設(shè)置playerLayer的frame
    [playView.layer addSublayer:playerLayer];//將AVPlayerLayer添加到播放層的layer上
    
//    添加一個循環(huán)播放的通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(repeatPlay) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    
    
//    背景音樂剪輯播放
//    計算視頻的長度,從而進(jìn)行相應(yīng)的音頻剪輯
    AVAsset *asset = [AVAsset assetWithURL:[self filePathName:@"abc.mp4"]];
    CMTime duration = asset.duration;
    CGFloat videoDuration = duration.value / (float)duration.timescale;
    NSLog(@"%f",videoDuration);
    
//    音頻剪輯,調(diào)用第一步的音頻剪輯工具類
    typeof(self) weakSelf = self;
    [EditAudioVideo cutAudioVideoResourcePath:[self filePathName:@"123.mp3"] startTime:0 endTime:videoDuration complition:^(NSURL *outputPath, BOOL isSucceed) {
        
//        音頻剪輯成功后,拿到剪輯后的音頻路徑
        NSError *error;
        weakSelf.BGMPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:outputPath error:&error];
        
        if (error == nil) {
            weakSelf.BGMPlayer.numberOfLoops = -1;//循環(huán)播放
            weakSelf.BGMPlayer.volume = 0.5;
            
            [weakSelf.BGMPlayer prepareToPlay];//預(yù)先加載音頻到內(nèi)存,播放更流暢
            
//            播放音頻,同時調(diào)用視頻播放,實現(xiàn)同步播放
            [weakSelf.BGMPlayer play];
            [weakSelf.player play];
        }else{
            NSLog(@"%@",error);
        }
        
    }];

3、音視頻的合成

1、加載音頻和視頻資源

//    素材
    AVAsset *asset = [AVAsset assetWithURL:assetURL];
    AVAsset *audioAsset = [AVAsset assetWithURL:BGMPath];

2、分離素材
音頻只有音頻軌道,而我們錄制的視頻有兩個軌道:音頻軌道、視頻軌道。需要把音視頻資源分離成各個軌道來進(jìn)行編輯。

    //    分離素材
    AVAssetTrack *videoAssetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0];//視頻素材
    AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0];//音頻素材

3、編輯各個軌道需要在AVMutableComposition類的環(huán)境中進(jìn)行

    //    編輯視頻環(huán)境
    AVMutableComposition *composition = [[AVMutableComposition alloc]init];

視頻素材加入視頻軌道

    AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

將視頻軌道插入到編輯環(huán)境中

    [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];

背景音樂音頻素材加入音頻軌道

    AVMutableCompositionTrack *audioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

將背景音樂的音頻軌道插入到編輯環(huán)境中

    [audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:audioAssetTrack atTime:kCMTimeZero error:nil];

是否加入視頻原聲,根據(jù)需要選擇添加,原理同上

    AVMutableCompositionTrack *originalAudioCompositionTrack = nil;
    if (needOriginalVoice) {
        AVAssetTrack *originalAudioAssetTrack = [[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0];
        originalAudioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
        [originalAudioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:originalAudioAssetTrack atTime:kCMTimeZero error:nil];
    }

將配置好的AVMutableComposition環(huán)境添加到導(dǎo)出類

//    導(dǎo)出素材
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];

這里有一個坑,AVMutableCompositionTrack軌道中控制音量preferredVolume屬性不起作用,不知道為什么,知道的大神指正下。
所以只能用音頻混合調(diào)節(jié)音量

#pragma mark - 調(diào)節(jié)合成的音量
+ (AVAudioMix *)buildAudioMixWithVideoTrack:(AVCompositionTrack *)videoTrack VideoVolume:(float)videoVolume BGMTrack:(AVCompositionTrack *)BGMTrack BGMVolume:(float)BGMVolume controlVolumeRange:(CMTime)volumeRange {
    
//    創(chuàng)建音頻混合類
    AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
    
//    拿到視頻聲音軌道設(shè)置音量
    AVMutableAudioMixInputParameters *Videoparameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:videoTrack];
    [Videoparameters setVolume:videoVolume atTime:volumeRange];
    
//    設(shè)置背景音樂音量
    AVMutableAudioMixInputParameters *BGMparameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:BGMTrack];
    [BGMparameters setVolume:BGMVolume atTime:volumeRange];
    
//    加入混合數(shù)組
    audioMix.inputParameters = @[Videoparameters,BGMparameters];
    
    return audioMix;
}

拿到視頻、音頻資源路徑,取到設(shè)置的音視頻對應(yīng)音量,進(jìn)行合成
完整合成代碼如下

/**
 音視頻合成

 @param assetURL 原始視頻路徑
 @param BGMPath 背景音樂路徑
 @param needOriginalVoice 是否添加原視頻的聲音
 @param videoVolume 視頻音量
 @param BGMVolume 背景音樂音量
 @param completionHandle 合成后的回調(diào)
 */
+ (void)editVideoSynthesizeVieoPath:(NSURL *)assetURL BGMPath:(NSURL *)BGMPath  needOriginalVoice:(BOOL)needOriginalVoice videoVolume:(CGFloat)videoVolume BGMVolume:(CGFloat)BGMVolume complition:(void (^)(NSURL *outputPath,BOOL isSucceed)) completionHandle{
    //    素材
    AVAsset *asset = [AVAsset assetWithURL:assetURL];
    AVAsset *audioAsset = [AVAsset assetWithURL:BGMPath];
    
    //    分離素材
    AVAssetTrack *videoAssetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0];//視頻素材
    AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0];//音頻素材
    
    //    編輯視頻環(huán)境
    AVMutableComposition *composition = [[AVMutableComposition alloc]init];
    
    //    視頻素材加入視頻軌道
    AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];
    
    //    音頻素材加入音頻軌道
    AVMutableCompositionTrack *audioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:audioAssetTrack atTime:kCMTimeZero error:nil];
    
    //    是否加入視頻原聲
    AVMutableCompositionTrack *originalAudioCompositionTrack = nil;
    if (needOriginalVoice) {
        AVAssetTrack *originalAudioAssetTrack = [[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0];
        originalAudioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
        [originalAudioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:originalAudioAssetTrack atTime:kCMTimeZero error:nil];
    }
    
    //    導(dǎo)出素材
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
    
//    音量控制
    exporter.audioMix = [self buildAudioMixWithVideoTrack:originalAudioCompositionTrack VideoVolume:videoVolume BGMTrack:audioCompositionTrack BGMVolume:BGMVolume controlVolumeRange:kCMTimeZero];
    
//    設(shè)置輸出路徑
    NSURL *outputPath = [self exporterPath];
    exporter.outputURL = [self exporterPath];
    exporter.outputFileType = AVFileTypeMPEG4;//指定輸出格式
    
    [exporter exportAsynchronouslyWithCompletionHandler:^{
        switch ([exporter status]) {
            case AVAssetExportSessionStatusFailed: {
                NSLog(@"合成失?。?@",[[exporter error] description]);
                completionHandle(outputPath,NO);
            } break;
            case AVAssetExportSessionStatusCancelled: {
                completionHandle(outputPath,NO);
            } break;
            case AVAssetExportSessionStatusCompleted: {
                completionHandle(outputPath,YES);
            } break;
            default: {
                completionHandle(outputPath,NO);
            } break;
        }
    }];

GitHub代碼下載https://github.com/D-james/AudioVideoEdit,文件比較大,因為把要合成的音頻、視頻也傳上去了。
swift版https://github.com/D-james/EditAudioVideo-swift

參考資料:《AV Foundation開發(fā)秘籍:實踐掌握iOS & OS X 應(yīng)用的視聽處理技術(shù)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容