美文网首页iOS音视频处理
iOS音视频录音、播放、剪辑、合成

iOS音视频录音、播放、剪辑、合成

作者: 一誠 | 来源:发表于2017-10-30 20:57 被阅读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)];
    

    参考:
    什么是音频比特率、视频比特率、音频采样率?
    音频剪辑、合成

    相关文章

      网友评论

        本文标题:iOS音视频录音、播放、剪辑、合成

        本文链接:https://www.haomeiwen.com/subject/gyqfpxtx.html