You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS中如何用AVAudioRecorder在通话时录制静音以保证录制时长准确

iOS 实现通话期间录制静音以保证时长准确

你的需求是在电话中断期间保持录音时长连续,用静音填充通话时段,这需要绕开iOS音频会话中断的限制——因为系统在通话时会抢占音频会话,无法直接持续录制,所以我们可以通过记录中断时长+追加静音数据的方式实现。

修改中断处理逻辑

首先,添加变量记录中断开始时间,替换原有的暂停恢复逻辑:

// 头文件或类扩展中添加属性
@property (nonatomic, strong) NSDate *interruptionStartDate;
@property (nonatomic, assign) NSTimeInterval totalInterruptionDuration; // 累计中断时长,用于多次中断场景

- (void)handleAudioSessionInterruption:(NSNotification *)notification
{
    NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];

    switch (interruptionType.unsignedIntegerValue) {
        case AVAudioSessionInterruptionTypeBegan:{
            _currentAudioSessionMode = EXAVAudioSessionModeInactive;
            if (_audioRecorder.recording) {
                [_audioRecorder pause];
                _interruptionStartDate = [NSDate date]; // 记录中断起始时间
                [self demoteAudioSessionIfPossible];
            }
        } break;
        case AVAudioSessionInterruptionTypeEnded:{
            _currentAudioSessionMode = EXAVAudioSessionModeActive;
            if (_audioRecorder && !_audioRecorder.recording) {
                if (_allowsAudioRecording) {
                    // 计算本次中断时长
                    NSTimeInterval interruptionDuration = [[NSDate date] timeIntervalSinceDate:_interruptionStartDate];
                    _totalInterruptionDuration += interruptionDuration; // 累计时长
                    _interruptionStartDate = nil;
                    
                    [self promoteAudioSessionIfNecessary];
                    [_audioRecorder record];
                }
            }
        } break;
        default:
            break;
    }
}

实现静音数据追加

在录音结束时,使用AVFoundation的合成API,将累计中断时长的静音片段追加到录音文件末尾,保证总时长准确:

// 录音结束时调用此方法,追加累计静音时长
- (void)appendTotalSilenceToFinalRecording
{
    if (_totalInterruptionDuration <= 0) return;
    
    NSURL *originalFileURL = _audioRecorder.url;
    if (!originalFileURL) return;
    
    // 1. 创建音频合成对象
    AVMutableComposition *composition = [AVMutableComposition composition];
    
    // 2. 导入原录音文件
    AVURLAsset *originalAsset = [AVURLAsset assetWithURL:originalFileURL];
    AVAssetTrack *originalAudioTrack = [originalAsset tracksWithMediaType:AVMediaTypeAudio].firstObject;
    if (!originalAudioTrack) return;
    
    CMTimeRange originalTimeRange = CMTimeRangeMake(kCMTimeZero, originalAsset.duration);
    NSError *insertError;
    [composition insertTimeRange:originalTimeRange
                         ofTrack:originalAudioTrack
                          atTime:kCMTimeZero
                           error:&insertError];
    if (insertError) {
        NSLog(@"导入原录音失败: %@", insertError.localizedDescription);
        return;
    }
    
    // 3. 添加静音片段
    AVMutableCompositionTrack *silenceTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    CMTime silenceCMTime = CMTimeMakeWithSeconds(_totalInterruptionDuration, originalAsset.duration.timescale);
    CMTimeRange silenceTimeRange = CMTimeRangeMake(originalAsset.duration, silenceCMTime);
    [silenceTrack insertEmptyTimeRange:silenceTimeRange];
    
    // 4. 导出合并后的文件
    NSURL *tempOutputURL = [self temporaryAudioFileURL]; // 生成临时输出路径
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];
    exportSession.outputURL = tempOutputURL;
    exportSession.outputFileType = AVFileTypeAppleM4A;
    exportSession.shouldOptimizeForNetworkUse = YES;
    
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            if (exportSession.status == AVAssetExportSessionStatusCompleted) {
                // 替换原录音文件
                NSError *fileError;
                NSFileManager *fileManager = [NSFileManager defaultManager];
                [fileManager removeItemAtURL:originalFileURL error:&fileError];
                if (!fileError) {
                    [fileManager moveItemAtURL:tempOutputURL toURL:originalFileURL error:&fileError];
                    if (fileError) {
                        NSLog(@"替换原文件失败: %@", fileError.localizedDescription);
                    }
                } else {
                    NSLog(@"删除原文件失败: %@", fileError.localizedDescription);
                }
                // 重置累计时长
                _totalInterruptionDuration = 0;
            } else {
                NSLog(@"导出静音合成文件失败: %@", exportSession.error.localizedDescription);
            }
        });
    }];
}

// 生成临时音频文件路径
- (NSURL *)temporaryAudioFileURL
{
    NSString *tempDir = NSTemporaryDirectory();
    NSString *fileName = [NSString stringWithFormat:@"temp_silence_merge_%@.m4a", [[NSUUID UUID] UUIDString]];
    return [NSURL fileURLWithPath:[tempDir stringByAppendingPathComponent:fileName]];
}

注意事项

  1. 文件格式兼容性:上述代码针对M4A格式(AVFileTypeAppleM4A),若使用其他录音格式,需对应修改导出的outputFileType。
  2. 性能优化:音频合成导出是异步操作,若录音时长较长,建议在后台线程处理,避免阻塞主线程。
  3. 权限与文件访问:确保App拥有录音权限,且录音文件路径有可写权限,避免文件操作失败。

内容的提问来源于stack exchange,提问作者Grzegorz Brzęczyszczykiewicz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:15:38