【AVFoundation】AVComposition媒体组合

490 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

AVFoundation 是Apple iOS和OS X系统中用于处理基于时间的媒体数据的高级框架,通过开发所需的工具提供了强大的功能集,让开发者能够基于苹果平台创建当下最先进的媒体应用程序,其针对64位处理器设计,充分利用了多核硬件优势,会自动提供硬件加速操作,确保大部分设备能以最佳性能运行,是iOS开发接触音视频开发必学的框架之一

参与掘金日新计划,持续记录AVFoundation学习,Demo学习地址,里面封装了一些工具类,可以直接使用,这篇文章主要讲述AVComposition将多个媒体资源组合成一个新的媒体,其他类的相关用法可查看我的其他文章。

媒体编辑

将多个音频、视频组合、分开、修建等是视频剪辑App必不可少的功能,AVFoundation给出了一些API来创建非线性、无损的编辑工具和应用程序,以下是AVFoundation媒体编辑用到的几个核心类。

1.png

AVComposition

AVFoundation有关资源组合的功能源于AVAsset的子类AVComposition。 一个组合就是将其他几种媒体资源组合成一个自定义的临时排列,再将这个临时排列视为一个可以呈现或处理的独立媒体。

  • AVComposition组合相当于包含了一个或多个给定类型的媒体轨道的容器。
  • AVComposition中的轨道都是AVAssetTrack的子类AVCompositionTrack。一个组合轨道本身由一个或多个媒体片段AVCompositionTrackSegment组成,代表这个组合中的实际媒体区域。

其包含情况如图所示

2.png

  • 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))