分析:
這是一個仿秒拍音視頻合成的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ù)》