在了解Cabbage之前可以查看github上的文档,里面有详细介绍AVFoundation,本文章就简单地过一下框架相关类。
在
AVFoundation中,视频和音频数据可以用AVAsset表示,AVAsset里面包含了AVAssetTrack数据,比如:一个视频文件里面包含了一个视频track和两个音频track。可以使用AVComposition对track进行裁剪和变速等操作,也可以把多段track拼接到AVComposition里面。
先看个例子,这是视频转场的代码:
class ViewController: UITableViewController {
func test() {
let playerItem: AVPlayerItem? = transitionPlayerItem()//AVPlayerItem 的时间轴驱动视频数据的获取。
let controller = AVPlayerViewController()
controller.player = AVPlayer.init(playerItem: playerItem)//视频播放器
navigationController?.pushViewController(controller, animated: true)
}
func transitionPlayerItem() -> AVPlayerItem? {
let bambooTrackItem: TrackItem = { ... }()
let overlayTrackItem: TrackItem = { ... }()
let seaTrackItem: TrackItem = { ... }()
let transitionDuration = CMTime(seconds: 2, preferredTimescale: 600)
bambooTrackItem.videoTransition = PushTransition(duration: transitionDuration)
bambooTrackItem.audioTransition = FadeInOutAudioTransition(duration: transitionDuration)
overlayTrackItem.videoTransition = BoundingUpTransition(duration: transitionDuration)
//Cabbage 核心类是 Timeline 和 CompositionGenerator
let timeline = Timeline()//Timeline用于往时间轴上添加数据片段,可以提供视频相关数据和音频相关数据。
timeline.videoChannel = [bambooTrackItem, overlayTrackItem, seaTrackItem]
timeline.audioChannel = [bambooTrackItem, seaTrackItem]
do {
try Timeline.reloadVideoStartTime(providers: timeline.videoChannel)
} catch { ... }
timeline.renderSize = CGSize(width: 1920, height: 1080)
/*CompositionGenerator 其实是 Timeline 和 AVFoundation 接口的桥接器。
CompositionGenerator 用于把 Timeline 的数据合成为 AVComposition、AVVideoComposition 和 AVAudioMix,
然后用这 3 个对象生成 AVPlayerItem、AVAssetImageGenerator 和 AVAssetExportSession 等用于处理视频的对象。*/
let compositionGenerator = CompositionGenerator(timeline: timeline)
let playerItem = compositionGenerator.buildPlayerItem()
return playerItem
}
}
Cabbage核心类是Timeline和CompositionGenerator。
Timeline用于往时间轴上添加数据片段,可以提供视频相关数据和音频相关数据。
CompositionGenerator其实是Timeline和AVFoundation接口的桥接器。 用于把Timeline的数据合成为AVComposition、AVVideoComposition和AVAudioMix,然后用这 3 个对象生成AVPlayerItem、AVAssetImageGenerator和AVAssetExportSession等用于处理视频的对象。
- 通过
CompositionGenerator生成相关类,先是AVComposition、video的AVCompositionTrack、audio的AVCompositionTrack:
public class CompositionGenerator {
...
public func buildPlayerItem() -> AVPlayerItem {
let composition = buildComposition()//创建AVComposition
let playerItem = AVPlayerItem(asset: composition)
playerItem.videoComposition = buildVideoComposition()//创建AVVideoComposition
playerItem.audioMix = buildAudioMix()
return playerItem
}
...
@discardableResult
public func buildComposition() -> AVComposition {
...
let composition = AVMutableComposition(urlAssetInitializationOptions: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
...
timeline.videoChannel.enumerated().forEach({ (offset, provider) in
for index in 0..<provider.numberOfVideoTracks() {
let trackID: Int32 = getVideoTrackID(for: index) + Int32((offset % 2 + 1) * 1000)
if let compositionTrack = provider.videoCompositionTrack(for: composition, at: index, preferredTrackID: trackID) { ... }
}
})
...
timeline.audioChannel.enumerated().forEach { (offset, provider) in
for index in 0..<provider.numberOfAudioTracks() {
let trackID: Int32 = getAudioTrackID(for: index) + Int32((offset % 2 + 1) * 1000)
if let compositionTrack = provider.audioCompositionTrack(for: composition, at: index, preferredTrackID: trackID) { ... }
}
...
}
...
return composition//AVComposition 对 track 进行裁剪和变速等操作,也可以把多段 track 拼接到 AVComposition 里面。
}
}
open class TrackItem: NSObject, NSCopying, TransitionableVideoProvider, TransitionableAudioProvider {
...
open func videoCompositionTrack(for composition: AVMutableComposition, at index: Int, preferredTrackID: Int32) -> AVCompositionTrack? {
let trackInfo = resource.trackInfo(for: .video, at: index)
let track = trackInfo.track
let compositionTrack: AVMutableCompositionTrack? = {
if let track = composition.track(withTrackID: preferredTrackID) {
return track
}
return composition.addMutableTrack(withMediaType: track.mediaType, preferredTrackID: preferredTrackID)
}()
if let compositionTrack = compositionTrack { ... }
return compositionTrack
}
...
open func audioCompositionTrack(for composition: AVMutableComposition, at index: Int, preferredTrackID: Int32) -> AVCompositionTrack? {
let trackInfo = resource.trackInfo(for: .audio, at: index)
let compositionTrack: AVMutableCompositionTrack? = {
if let track = composition.track(withTrackID: preferredTrackID) {
return track
}
return composition.addMutableTrack(withMediaType: trackInfo.track.mediaType, preferredTrackID: preferredTrackID)
}()
if let compositionTrack = compositionTrack { ... }
return compositionTrack
}
}
- 然后是
AVVideoComposition:
public class CompositionGenerator {
...
public func buildVideoComposition() -> AVVideoComposition? {
...
let videoComposition = AVMutableVideoComposition()
videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
videoComposition.renderSize = self.timeline.renderSize
videoComposition.instructions = instructions
videoComposition.customVideoCompositorClass = VideoCompositor.self
self.videoComposition = videoComposition
return videoComposition
}
...
}
AVVideoComposition可以用于设置帧率、画布大小、指定不同的video track应用何种编辑操作以及可以将视频画面嵌套在CALayer中。