持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
AVFoundation 是Apple iOS和OS X系统中用于处理基于时间的媒体数据的高级框架,通过开发所需的工具提供了强大的功能集,让开发者能够基于苹果平台创建当下最先进的媒体应用程序,其针对64位处理器设计,充分利用了多核硬件优势,会自动提供硬件加速操作,确保大部分设备能以最佳性能运行,是iOS开发接触音视频开发必学的框架之一。
参与掘金日新计划,持续记录AVFoundation学习,Demo学习地址,里面封装了一些工具类,可以直接使用,这篇文章主要讲述AVComposition将多个媒体资源组合成一个新的媒体,其他类的相关用法可查看我的其他文章。
媒体编辑
将多个音频、视频组合、分开、修建等是视频剪辑App必不可少的功能,AVFoundation给出了一些API来创建非线性、无损的编辑工具和应用程序,以下是AVFoundation媒体编辑用到的几个核心类。
AVComposition
AVFoundation有关资源组合的功能源于AVAsset的子类AVComposition。 一个组合就是将其他几种媒体资源组合成一个自定义的临时排列,再将这个临时排列视为一个可以呈现或处理的独立媒体。
- AVComposition组合相当于包含了一个或多个给定类型的媒体轨道的容器。
- AVComposition中的轨道都是AVAssetTrack的子类AVCompositionTrack。一个组合轨道本身由一个或多个媒体片段AVCompositionTrackSegment组成,代表这个组合中的实际媒体区域。
其包含情况如图所示
- AVComposition是AVAsset的子类,所以可以直接用来播放、导出。
- AVComposition没有遵循NSCoding协议,如果要存储到硬盘,需要自己定义数据模型来保存其排列状态。
示例
将一段音乐和两段视频组合成一个新的视频
- 加载三个媒体资源
let optionl: [String: Bool] = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
let keys: [String] = ["tracks", "duration", "commonMetadata"]
// 音频资源1
let audioUrl: URL = Bundle.main.url(forResource: "02 Keep Going", withExtension: "m4a")!
let audioAsset1 = AVURLAsset(url: audioUrl, options: optionl)
audioAsset1.loadValuesAsynchronously(forKeys: keys)
// 视频资源1
guard let videoUrl1: URL = Bundle.main.url(forResource: "01_nebula", withExtension: "mp4") else { return }
let videoAsset1 = AVURLAsset(url: videoUrl1, options: optionl)
videoAsset1.loadValuesAsynchronously(forKeys: keys)
// 视频资源2
guard let videoUrl2: URL = Bundle.main.url(forResource: "02_blackhole", withExtension: "mp4") else { return }
let videoAsset2 = AVURLAsset(url: videoUrl2, options: optionl)
videoAsset2.loadValuesAsynchronously(forKeys: keys)
- 将两个视频资源的轨道添加进新组合里的的轨道,注意insertTimeRange的衔接时间
// 创建可变创作,添加视频轨道 音频轨道
let composition: AVMutableComposition = AVMutableComposition()
guard let videoTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else { return }
guard let audioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) else { return }
var cursorTime: CMTime = .zero
let videoDuration: CMTime = CMTimeMake(value: 5, timescale: 1)
let videoRange: CMTimeRange = CMTimeRange(start: .zero, duration: videoDuration)
// 从视频资源1获取轨道
guard let videoTrack1: AVAssetTrack = videoAsset1.tracks(withMediaType: .video).first else { return }
// 将视频资源1的轨道添加到视频轨道
try? videoTrack.insertTimeRange(videoRange, of: videoTrack1, at: cursorTime)
cursorTime = CMTimeAdd(cursorTime, videoDuration)
// 从视频资源2获取轨道
guard let videoTrack2: AVAssetTrack = videoAsset2.tracks(withMediaType: .video).first else { return }
// 将视频资源2的轨道添加到视频轨道
try? videoTrack.insertTimeRange(videoRange, of: videoTrack2, at: cursorTime)
- 将音频资源的轨道添加进新组合里的音频轨道
cursorTime = .zero
let audioRange: CMTimeRange = CMTimeRange(start: .zero, duration: composition.duration)
// 从音频资源1获取轨道
guard let audioTrack1: AVAssetTrack = audioAsset1.tracks(withMediaType: .audio).first else { return }
// 将音频资源1的轨道添加到音频轨道
try? audioTrack.insertTimeRange(audioRange, of: audioTrack1, at: cursorTime)
- 测试播放
player = AVPlayer(playerItem: AVPlayerItem(asset: composition))