iOS音视频录音、播放、剪辑、合成
2017-10-30 本文已影响244人
一誠
一、录制
1、检测AVAudioSessionRecordPermission,用户是否有权限使用录音器
[AVAudioSession sharedInstance].recordPermission == AVAudioSessionRecordPermissionGranted //授权
// AVAudioSessionRecordPermissionUndetermined 没有决定
// AVAudioSessionRecordPermissionDenied 用户禁止
2、请求授权
AVAudioSession *session = [AVAudioSession sharedInstance];
if ([session respondsToSelector:@selector(requestRecordPermission:)]) {
[session performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
if (granted) {
// 用户授权
[self safeRecorder];
} else {
[self userDeniedRecordRequest];
}
}];
}
3、开始录音
创建音频会话
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *sessionError;
//录音会话的几种模式
AVAudioSessionCategoryPlayAndRecord :边录音边播放
AVAudioSessionCategoryAmbient :混音播放,可以与其他音频应用同时播放
AVAudioSessionCategorySoloAmbient :独占播放
AVAudioSessionCategoryPlayback :后台播放,也是独占的
AVAudioSessionCategoryRecord :用于需要录音的应用,除了来电铃声,闹钟或日历提醒之外的其它系统声音都不会被播放,只提供单纯录音功能.
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
if (audioSession != nil) {
NSLog(@"Error creating sessing:%@", [sessionError description]);
} else {
//关闭其他音频播放,把自己设为活跃状态
[audioSession setActive:YES error:&sessionError];
NSLog(@"Error creating sessing:%@", [sessionError description]);
}
录音设置
//编码格式
NSMutableDictionary*settings = [[NSMutableDictionary alloc]init];
//设置录音格式
// 1、kAudioFormatMPEG4AAC压缩格式能在显著减小文件的同时,保证音频的质量。同时Android也支持aac音频格式。
[settings setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC]forKey:AVFormatIDKey];
//设置录音采样率(Hz)如:AVSampleRateKey==8000/44100/96000(影响音频的质量)
[settings setValue:[NSNumber numberWithFloat:44100.0f]forKey:AVSampleRateKey];
//录音通道数1或2 单声道 立体声
[settings setValue:[NSNumber numberWithInt:1]forKey:AVNumberOfChannelsKey];
//线性采样位数8、16、24、32
//[settings setValue:[NSNumber numberWithInt:16]forKey:AVEncoderBitRateKey];
// 创建录音器
NSURL * recorderUrl = [NSURL fileURLWithPath:self.audioRecordPath];
NSError * error = nil;
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:recorderUrl settings:settings error:&error];
if (error) {
//Error Domain=NSOSStatusErrorDomain Code=1718449215 --- 录音格式错误
NSLog(@"创建录音器 - %@",error.description);
}
//设置代理
self.audioRecorder.delegate = self;
//开启音频测量
self.audioRecorder.meteringEnabled = YES;
//准备记录录音
[_audioRecorder prepareToRecord];
//开启仪表计数功能,必须开启这个功能,才能检测音频值
[_audioRecorder setMeteringEnabled:YES];
//启动或者恢复记录的录音文件
[_audioRecorder record];
// 检测音频频率
kWeakSelf
[self.timer startWithEventHandler:^{//定时器每秒调用一次
[weakSelf meterAudio];
}];
// 注册通知 检测录音过程是否被打断
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(audioSessionDidInterrupte:) name:AVAudioSessionInterruptionNotification object:nil];
这里通过计时器检测音频强度
- (void)meterAudio{
[self.audioRecorder updateMeters];
if (self.audioRecorder.recording) {
//float num = [self.audioRecorder peakPowerForChannel:0];
float power = [self.audioRecorder averagePowerForChannel:0];//音频强度范围时-160到0
//customLog(@"获取频率--- %f - %f",num,power);
CGFloat value = 1 - fabs(power / 160.0);
customLog(@"强度值 --- %f",value);
}
}
当录音过程被打断时调用
///录音被打断了
- (void)audioSessionDidInterrupte:(NSNotification *)notification{
customLog(@"录音被中断了");
NSArray *allKeys = notification.userInfo.allKeys;
// 判断事件类型
if([allKeys containsObject:AVAudioSessionInterruptionTypeKey]){
AVAudioSessionInterruptionType audioInterruptionType = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
switch (audioInterruptionType) {
case AVAudioSessionInterruptionTypeBegan:
break;
case AVAudioSessionInterruptionTypeEnded:
break;
}
}
// 判断中断的音频录制是否可恢复录制
if([allKeys containsObject:AVAudioSessionInterruptionOptionKey]){
AVAudioSessionInterruptionOptions shouldResume = [[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] integerValue];
if(shouldResume){
}
}
}
AVAudioRecorderDelegate
#pragma mark AVAudioRecorderDelegate
// AVAudioRecorder INTERRUPTION NOTIFICATIONS ARE DEPRECATED - Use AVAudioSession instead. 注册通知
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
customLog(@"录制完成");
}
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError * __nullable)error{
customLog(@"录制出错 %@",error);
}
二、播放
// 播放器
NSURL * audioUrl = [NSURL fileURLWithPath:url];
NSError * error = nil;
_audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:audioUrl error:&error];
if (error) {
customLog(@"播放错误 --- %@",error.description);
}
[_audioPlayer prepareToPlay];
_audioPlayer.delegate = self;
[_audioPlayer prepareToPlay];
[_audioPlayer play];
三、剪辑
//AVURLAsset是AVAsset的子类,AVAsset类专门用于获取多媒体的相关信息,包括获取多媒体的画面、声音等信息.而AVURLAsset子类的作用则是根据NSURL来初始化AVAsset对象.
AVURLAsset *videoAsset = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:self.recordUrl]];
//音频输出会话
//AVAssetExportPresetAppleM4A: This export option will produce an audio-only .m4a file with appropriate iTunes gapless playback data(输出音频,并且是.m4a格式)
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:videoAsset presetName:AVAssetExportPresetAppleM4A];
self.exportSession = exportSession;
//设置输出路径 / 文件类型 / 截取时间段
exportSession.outputURL = [NSURL fileURLWithPath:self.outputUrl];
exportSession.outputFileType = AVFileTypeAppleM4A;
CMTime time = videoAsset.duration;
exportSession.timeRange = CMTimeRangeFromTimeToTime(CMTimeMake(1 * time.timescale, time.timescale), CMTimeMake(5 * time.timescale, time.timescale));
[exportSession exportAsynchronouslyWithCompletionHandler:^{
/**
AVContentKeyRequestStatusRequestingResponse,
AVContentKeyRequestStatusReceivedResponse,
AVContentKeyRequestStatusRenewed,
AVContentKeyRequestStatusRetried,
AVContentKeyRequestStatusCancelled,
AVContentKeyRequestStatusFailed
*/
customLog(@"%ld",(long)exportSession.status);
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
customLog(@"导出完成");
}
}];
// 通过CADisplayLink检测导出进度 过程一般很快
[self.timer startDisplayLinkWithTarget:self selector:@selector(updateProgress)];
- (void)updateProgress{
customLog(@"%f",self.exportSession.progress);
if (self.exportSession.progress >= 1) {
[self.timer stopDisplayLink];
// 播放
[self.play playWithUrl:self.outputUrl];
}
}
四、合成
//AVURLAsset子类的作用则是根据NSURL来初始化AVAsset对象.
AVURLAsset *audioAsset1 = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:self.recordUrl] options:nil];
AVURLAsset *audioAsset2 = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:self.outputUrl] options:nil];
//音频轨迹(一般视频至少有2个轨道,一个播放声音,一个播放画面.音频有一个)
NSArray * audioAssets1 = [audioAsset1 tracksWithMediaType:AVMediaTypeAudio];//AVMediaTypeVideo视频
AVAssetTrack *assetTrack1 = [audioAssets1 objectAtIndex:0];
NSArray * audioAssets2 = [audioAsset2 tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *assetTrack2 = [audioAssets2 objectAtIndex:0];
// 音频合成
AVMutableComposition *composition = [AVMutableComposition composition];
//AVMutableComposition用来合成视频或音频
AVMutableCompositionTrack *compositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 把第二段录音添加到第一段后面
[compositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset1.duration) ofTrack:assetTrack1 atTime:kCMTimeZero error:nil];
[compositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset2.duration) ofTrack:assetTrack2 atTime:audioAsset2.duration error:nil];
//输出
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:composition presetName:AVAssetExportPresetAppleM4A];
self.compositionSession = exportSession;
exportSession.outputFileType = AVFileTypeAppleM4A;
exportSession.outputURL = [NSURL fileURLWithPath:self.compositionUrl];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
//exporeSession.status
customLog(@"%ld",(long)exportSession.status);
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
customLog(@"合成完成");
}
}];
// 检测进度
[self.timer startDisplayLinkWithTarget:self selector:@selector(updateCompositionProgress)];