AVAssetWriter录制视频后播放无声音且AVPlayerItemDidPlayToEndTime通知不触发
问题排查与修复方案
一、回放无声音问题
核心原因
当前代码最核心的问题是音频采样的时间戳未做偏移对齐:
你创建AVAssetWriter时调用了writer.startSession(atSourceTime: .zero),写入视频帧时做了时间偏移CMTime(seconds: timestamp - _time, preferredTimescale: CMTimeScale(600)),但写入音频帧时直接使用了原始采集的sampleBuffer,原始sampleBuffer的时间戳是系统采集的绝对时间,和writer的起始时间轴完全不匹配,导致音频数据被判定为无效、不会被写入到最终文件中。
修复代码
在写入音频时,需要对音频sampleBuffer做时间戳偏移调整:
} else if output == _audioOutput { if _assetWriterAudioInput?.isReadyForMoreMediaData == true { // 调整音频时间戳 let originalTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) let adjustedTime = CMTime(seconds: originalTime.seconds - _time, preferredTimescale: originalTime.timescale) // 生成偏移后的新sampleBuffer var adjustedSampleBuffer: CMSampleBuffer? var timingInfo = CMSampleTimingInfo(duration: CMSampleBufferGetDuration(sampleBuffer), presentationTimeStamp: adjustedTime, decodeTimeStamp: CMSampleBufferGetDecodeTimeStamp(sampleBuffer)) CMSampleBufferCreateCopyWithNewTiming(allocator: kCFAllocatorDefault, sampleBuffer: sampleBuffer, sampleTimingEntryCount: 1, sampleTimingArray: &timingInfo, sampleBufferOut: &adjustedSampleBuffer) if let adjustedBuffer = adjustedSampleBuffer { _assetWriterAudioInput?.append(adjustedBuffer) } } }
其他注意事项
- 建议去掉
AVAssetWriter初始化的try!,添加异常捕获逻辑,避免因音频/视频配置无效导致写入失败 - 写入完成后可以先校验输出文件的
AVAsset是否包含音频轨道,排查是否在写入阶段就丢失了音频数据
二、.AVPlayerItemDidPlayToEndTime通知无法触发问题
常见原因及修复
- 观察者未在主线程添加:该通知默认在主线程分发,如果你在子线程添加观察者,会出现收不到通知的情况,添加观察者时需确保在主线程执行:
DispatchQueue.main.async { NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: .AVPlayerItemDidPlayToEndTime, object: currentPlayerItem) }
- 播放文件时长无效:如果之前音频时间戳异常导致输出文件的总时长计算错误,播放器无法正确判定播放结束节点,修复音频写入问题后该问题大概率会同步解决
AVQueuePlayer配置问题:检查是否设置了player.actionAtItemEnd = .none,如果设置为.advance且队列中存在后续item,通知会被队列消费不会向外分发- 观察者提前释放:确保持有通知观察者的对象未被提前释放,更换播放item时需先移除旧item的观察者,再给新item添加观察者
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

