如何基于现有视频指定CMTimeRange创建仅含轨迹帧的新AVAsset视频
实现轨迹帧过滤导出的方案
你可以根据你的使用场景选择以下两种实现方式:
方案1:离线处理本地已有视频(推荐,稳定性更高)
这种方式适合处理已经录制完成的本地视频文件,实现逻辑如下:
- 初始化
AVAssetReader读取原视频的帧数据,同时初始化AVAssetWriter配置和原视频一致的输出参数(分辨率、编码格式、帧率等),减少不必要的转码损耗。 - 按顺序读取原视频的每一个
CMSampleBuffer,拿到帧对应的CMTime,送入VNDetectTrajectoriesRequest执行轨迹检测,检测逻辑可以完全复用你现有回调中的实现。 - 每次检测拿到结果后,收集所有
VNDetectedTrajectoryObservation返回的CMTimeRange,将重叠或连续的时间范围提前合并。 - 二次遍历原视频的所有帧(或者在首次检测时同步判断),如果当前帧的时间落在已合并的有效时间范围内,就将该帧原封不动写入
AVAssetWriter,写入时保留帧的原始时间戳,避免音画不同步。 - 如果原视频带有音轨,需要同步筛选出落在有效时间范围内的音频帧写入
AVAssetWriter,或者在视频轨道处理完成后拼接对应时间段的音频。
如果不需要自定义编码逻辑,你可以用更简单的实现替代手动帧读写:
完成全视频的轨迹检测、合并完所有有效时间范围后,创建
AVMutableComposition,仅将原视频中对应有效时间段的视频、音频轨道插入到合成器中,最后直接用AVAssetExportSession导出合成资源即可,代码量更少,系统兼容性更好。
方案2:实时拍摄时边检测边导出
这种方式适合在摄像头录制的同时直接生成过滤后的视频,可直接复用你现有回调逻辑:
- 提前初始化好
AVAssetWriter以及对应视频、音频写入输入。 - 在
cameraViewController(_:didReceiveBuffer:orientation:)回调中,当检测到detectTrajectoryRequest.results不为空时,直接将当前回调收到的CMSampleBuffer写入AVAssetWriter即可。 - 注意将写入逻辑放到和轨迹检测同个串行队列执行,避免检测耗时高于帧间隔导致的时间戳错乱、丢帧问题。
关键实现提示
你可以在现有的processTrajectoryObservations方法中新增时间范围收集逻辑,不需要改动原有检测流程:
// 全局维护的已合并有效时间范围集合 var mergedValidTimeRanges: [CMTimeRange] = [] func processTrajectoryObservations(_ controller: CameraViewController, _ results: [VNObservation]) { let trajectoryRanges = results.compactMap { $0 as? VNDetectedTrajectoryObservation }.map(\.timeRange) // 合并新的有效时间范围到全局集合,可自行实现CMTimeRange合并逻辑 mergedValidTimeRanges = mergeOverlappingRanges(mergedValidTimeRanges + trajectoryRanges) }
内容的提问来源于stack exchange,提问作者Curiosity
相关产品推荐
相关产品推荐

