You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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是正确的,避免插入时间计算错误;
  • 检查AVPlayerItemstatus,确保没有加载错误;
  • 如果涉及音频轨道,记得同步处理音频的合成(逻辑和视频类似)。

内容的提问来源于stack exchange,提问作者Hope

火山引擎 最新活动