直接组合音频会有一些问题:
- 1、音乐轨道刚开始播放时音量就很大,在组合资源结束时突然停止。如果可以开始逐渐增加,结束的时候逐渐减小会提升体验。
- 2、画外音轨道的处理。音乐轨道声音完全父爱画外音的声音,几乎听不到画外音。
框架提供AVAudioMix
来解决上面的两个问题。
AVAudioMix
用来在组合音频轨道中进行自定义音频的处理。
AVAudioMix
所具有的音频处理方法是由它的输入参数集定义的。参数是AVAudioMixInputParameters
类型对象。
AVAudioMix
和AVAudioMixInputParameters
都是不可变对象,他们适用于AVPlayerItem和AVAssetExportSession之类的客户端提供相关数据,不过不能操作其状态。
如果需要创建自电影音频混合,需要用AVMutableAudioMix
和AVMutableAudioMixInputParameters
。
(一)、音量
一个组合资源播放或导出时,默认以最大音量或正常音量播放音频轨道。只有一个但音频轨道时,这样的方法比较容易接收。如果一个组合资源包含多个音频资源,对于多音频轨道,每个声音都在争夺空间,就会导致一些声音无法被听到。
AVFoundation将音量定义为一个标准化的浮点型数值:0.0(静音)-1.0(最大音量)。
(二)、添加设置
两种方式:
setVolume:(float)volume atTime:(CMTime)time
--在time这个时间点将该轨道音频音量调至volume。setVolumeRampFromStartVolume:(float)startVolume toEndVolume:(float)endVolume timeRange:(CMTimeRange)timeRange
--在timeRange这个时间范围内,将该轨道音频的音量由startVolume平缓变动到endVolume。
//创建新的AVMutableComposition并添加AVCompositionTrack对象。
self.composition = [AVMutableComposition composition];
[self addCompositionTrackOfType:AVMediaTypeVideo withMediaItems:self.timeline.videos];
[self addCompositionTrackOfType:AVMediaTypeAudio withMediaItems:self.timeline.voiceOvers];
AVMutableCompositionTrack *musicTrack = [self addCompositionTrackOfType:AVMediaTypeAudio withMediaItems:self.timeline.musicItems];
//创建实例,保存输入参数
self.audioMix = [AVMutableAudioMix audioMix];
//创建AVMutableAudioMixInputParameters实例
//将它与传递给方法的AVCompositionTrack进行关联
AVMutableAudioMixInputParameters *parameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack: musicTrack];
[parameters setVolumeRampFromStartVolume:automation.startVolume toEndVolume:automation.endVolume timeRange:automation.timeRange];
self.audioMix.inputParameters = @[parameters];
// 播放资源对象, 关联混合音频
- (AVPlayerItem *)makePlayable {
// Listing 10.2
//创建一个带有AVComposition实例的AVPlayerItem。
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:[self.composition copy]];
//设置audioMix对象作为播放器条目的audioMix属性。就可以在应用程序视频播放器中播放音频时应用音频处理
playerItem.audioMix = self.audioMix;
return playerItem;
}
//导出会话 关联混合音频
- (AVAssetExportSession *)makeExportable {
// Listing 10.2
NSString *preset = AVAssetExportPresetHighestQuality;
AVAssetExportSession *session = [AVAssetExportSession exportSessionWithAsset:[self.composition copy] presetName:preset];
session.audioMix = self.audioMix;
return session;
}
找到对应的音频轨道,修改该音频轨道的audioMix
。
这里的示例使用单音频轨道的视频,对于多音频轨道混合音频处理,还需要深入学习。
示例代码:
@interface THAudioMixCompositionBuilder ()
@property (strong, nonatomic) THTimeline *timeline;
@property (strong, nonatomic) AVMutableComposition *composition;
@end
@implementation THAudioMixCompositionBuilder
- (id)initWithTimeline:(THTimeline *)timeline {
self = [super init];
if (self) {
_timeline = timeline;
}
return self;
}
- (id <THComposition>)buildComposition {
// Listing 10.4
//创建新的AVMutableComposition并添加AVCompositionTrack对象。
self.composition = [AVMutableComposition composition];
[self addCompositionTrackOfType:AVMediaTypeVideo withMediaItems:self.timeline.videos];
[self addCompositionTrackOfType:AVMediaTypeAudio withMediaItems:self.timeline.voiceOvers];
AVMutableCompositionTrack *musicTrack = [self addCompositionTrackOfType:AVMediaTypeAudio withMediaItems:self.timeline.musicItems];
// 创建AVAudioMix实例,接收引用音量调整的轨道的引用
AVAudioMix *audioMix = [self buildAudioMixWithTrack:musicTrack];
//返回一个THAudioMixComposition,传递给组合和音频混合
return [THAudioMixComposition compositionWithComposition:self.composition audioMix:audioMix];;
}
- (AVAudioMix *)buildAudioMixWithTrack:(AVCompositionTrack *)track {
// Listing 10.5
// 从时间轴得到音乐轨道的THAudioItem实例。示例中止允许添加一个单独的音乐轨道,所以可以取第一个
THAudioItem *item = [self.timeline.musicItems firstObject];
if (item) {
//创建实例,保存输入参数
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
//创建AVMutableAudioMixInputParameters实例,将它与传递给方法的AVCompositionTrack进行关联
AVMutableAudioMixInputParameters *parameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:track];
for (THVolumeAutomation *automation in item.volumeAutomation) {
//遍历音频条目,对每个实例在parameters对象对象上定义一个音量渐变。
[parameters setVolumeRampFromStartVolume:automation.startVolume toEndVolume:automation.endVolume timeRange:automation.timeRange];
// [parameters setVolume:automation.endVolume atTime:CMTimeMake(3, 1)];
}
// 将parameters对象封装在NSArray中,设置他为音频混合的inputParameters
audioMix.inputParameters = @[parameters];
return audioMix;
}
return nil;
}
- (AVMutableCompositionTrack *)addCompositionTrackOfType:(NSString *)type // 5
withMediaItems:(NSArray *)mediaItems {
if (!THIsEmpty(mediaItems)) {
CMPersistentTrackID trackID = kCMPersistentTrackID_Invalid;
AVMutableCompositionTrack *compositionTrack =
[self.composition addMutableTrackWithMediaType:type
preferredTrackID:trackID];
// Set insert cursor to 0
CMTime cursorTime = kCMTimeZero;
for (THMediaItem *item in mediaItems) {
if (CMTIME_COMPARE_INLINE(item.startTimeInTimeline,
!=,
kCMTimeInvalid)) {
cursorTime = item.startTimeInTimeline;
}
AVAssetTrack *assetTrack =
[[item.asset tracksWithMediaType:type] firstObject];
[compositionTrack insertTimeRange:item.timeRange
ofTrack:assetTrack
atTime:cursorTime
error:nil];
// Move cursor to next item time
cursorTime = CMTimeAdd(cursorTime, item.timeRange.duration);
}
return compositionTrack;
}
return nil;
}