Swift 4中insertTimeRange添加多视频轨道仅首轨可播放问题
这个问题在AVFoundation开发里太常见了!你已经成功把多个视频轨道添加到AVMutableComposition里了,但之所以只显示第一条,核心原因是你没告诉系统该怎么渲染这些重叠的视频轨道——AVMutableComposition只是负责把媒体数据组合起来,而视频的显示布局、层级、位置这些是由AVMutableVideoComposition单独控制的,默认情况下系统只会渲染第一条轨道的内容。
下面分两种场景给你具体的解决方案:
场景1:你想让多个视频同时显示(分屏/画中画/叠加)
这种情况必须配置AVMutableVideoComposition来定义每个轨道的渲染区域,步骤如下:
1. 先确保轨道插入的时间范围正确
比如你要让所有视频同时播放,那每个轨道的插入时间都是kCMTimeZero(这部分你已经做对了)。
2. 添加视频合成配置
修改你的mergeVideos()函数,在创建完mixComposition后,加上以下逻辑:
// 先把添加的视频轨道存起来,方便后续处理 var compositionVideoTracks: [AVMutableCompositionTrack] = [] // (这里是你原来添加轨道的代码,记得把每个创建的compositionTrack加入数组) // 创建AVMutableVideoComposition,控制视频渲染 let videoComposition = AVMutableVideoComposition() // 取第一个视频的尺寸作为渲染尺寸,也可以自定义 guard let firstVideoAsset = AVURLAsset(url: videos.first!), let firstVideoTrack = firstVideoAsset.tracks(withMediaType: .video).first else { // 处理错误 return } videoComposition.renderSize = firstVideoTrack.naturalSize videoComposition.frameDuration = firstVideoTrack.minFrameDuration // 创建合成指令,覆盖整个视频时长 let mainInstruction = AVMutableVideoCompositionInstruction() mainInstruction.timeRange = CMTimeRange(start: .zero, duration: mixComposition.duration) // 为每个轨道创建图层指令,设置位置和大小 var layerInstructions: [AVMutableVideoCompositionLayerInstruction] = [] for (index, track) in compositionVideoTracks.enumerated() { let layerInst = AVMutableVideoCompositionLayerInstruction(assetTrack: track) // 示例:左右分屏显示,你可以根据需求修改transform var transform = CGAffineTransform.identity if index == 0 { // 左半屏:缩放宽度为原来的0.5 transform = transform.scaledBy(x: 0.5, y: 1.0) } else { // 右半屏:缩放后平移到右侧 transform = transform.scaledBy(x: 0.5, y: 1.0) transform = transform.translatedBy(x: firstVideoTrack.naturalSize.width/2, y: 0) } // 设置变换效果 layerInst.setTransform(transform, at: .zero) // 可选:设置图层透明度,比如叠加时可以调透明 layerInst.setOpacity(1.0, at: .zero) layerInstructions.append(layerInst) } // 绑定指令到视频合成 mainInstruction.layerInstructions = layerInstructions videoComposition.instructions = [mainInstruction] // 关键!把视频合成绑定到PlayerItem let playerItem = AVPlayerItem(asset: mixComposition) playerItem.videoComposition = videoComposition
这样系统就知道该怎么渲染每一条视频轨道了,你可以根据需求修改transform来实现画中画、上下分屏等效果。
场景2:你想让视频按顺序前后拼接
如果你的需求是把视频依次播放,那问题出在插入时间范围的参数上——你可能把所有轨道都插在了kCMTimeZero的位置,导致后面的轨道被前面的覆盖了。修改插入逻辑即可:
var currentPlaybackTime = CMTime.zero for videoURL in videos { let asset = AVURLAsset(url: videoURL) guard let videoTrack = asset.tracks(withMediaType: .video).first else { continue } let compositionTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)! // 插入到当前播放时间的末尾,而不是kCMTimeZero try! compositionTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: videoTrack, at: currentPlaybackTime) // 更新当前播放时间 currentPlaybackTime = CMTimeAdd(currentPlaybackTime, asset.duration) }
这种情况下不需要额外配置AVMutableVideoComposition,轨道会按时间顺序依次播放。
额外检查点
- 确认每个视频的
asset.duration是正确的,避免插入时间计算错误; - 检查
AVPlayerItem的status,确保没有加载错误; - 如果涉及音频轨道,记得同步处理音频的合成(逻辑和视频类似)。
内容的提问来源于stack exchange,提问作者Hope




