Kakapos全平台视频导出并添加滤镜工具

Kakapos 是一个视频添加过滤器工具,支持网络和本地网址以及相册视频。

同时可以简单的支持多种滤镜库,例如Harbeth、GPUImage、MetalPetal、CoreImage等等。

反正核心其实就是对CVPixelBuffer进行注入滤镜处理,然后再导出视频即可。

支持全平台系统,macOS、iOS、tvOS、watchOS


演示Demo

这边已经提供关于macOS和iOS两个端的演示Demo,需要的朋友请移步GitHub下载即可。

框架介绍

该架构主要涵盖4个文件:

  • Exporter:主要的转换文件,注入滤镜后导出视频。
/// 设置导出文件质量
public var presetName: String = AVAssetExportPresetHighestQuality {
    didSet {
        if !AVAssetExportSession.allExportPresets().contains(presetName) {
            presetName = AVAssetExportPresetMediumQuality
        }
    }
}
/// 根据视频链接初始化
public init(videoURL: URL, delegate: ExporterDelegate) {
    self.init(asset: AVAsset(url: videoURL), delegate: delegate)
}
/// 根据AVAsset初始化
public init(asset: AVAsset, delegate: ExporterDelegate) {
    self.asset = asset
    self.delegate = delegate
}
/// 添加滤镜然后导出视频,主要在`filtering`当中对`buffer`添加滤镜,最后协议反馈结果。
public func export(outputURL: URL, optimizeForNetworkUse: Bool = true, filtering: @escaping PixelBufferCallback) {
    ...
}
  • ExporterDelegate:导出协议,包括成功和失败两个协议方法。
public protocol ExporterDelegate: NSObjectProtocol {
    
    /// 导出成功的视频网址,相当于outputURL。
    func export(_ exporter: Exporter, success videoURL: URL)
    
    /// 视频导出失败。
    func export(_ exporter: Exporter, failed error: Exporter.Error)
}
  • Compositor:对视频帧进行处理,对缓冲进入注入滤镜处理,最后再还原回去finish(withComposedVideoFrame:)即可。
func startRequest(_ request: AVAsynchronousVideoCompositionRequest) {
    self.renderQueue.sync {
        guard let instruction = request.videoCompositionInstruction as? CompositionInstruction,
              let pixels = request.sourceFrame(byTrackID: instruction.trackID) else {
            return
        }
        let buffer = instruction.bufferCallback(pixels) ?? pixels
        request.finish(withComposedVideoFrame: buffer)
    }
}
  • CompositionInstruction:表示一个指令,决定每个timeRange内每个轨道的状态,配置视频组合的渲染尺寸、缩放、帧时长等,定义时间范围信息,以及每一帧的层级。
/// 接收回调信息,方便后续转换处理。
init(trackID: CMPersistentTrackID, bufferCallback: @escaping Exporter.PixelBufferCallback) {
    self.trackID = trackID
    self.bufferCallback = bufferCallback
    super.init()
    self.enablePostProcessing = true
}

关于AVMutableVideoCompositionInstruction更多介绍

参考:https://www.codersrc.com/archives/11663.html

该类还能拿来 完成对媒体裁剪 / 缩放 / 转场 / 过渡等操作

如何使用?

  • 设置转换视频存储沙盒链接。
/// 创建临时路径以保存转换的视频
let outputURL: URL = {
    let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let outputURL = documents.appendingPathComponent("condy_exporter_video.mp4")
    
    // Check if the file already exists then remove the previous file
    if FileManager.default.fileExists(atPath: outputURL.path) {
        do {
            try FileManager.default.removeItem(at: outputURL)
        } catch {
            //completionHandler(nil, error)
        }
    }
    return outputURL
}()
  • 创建视频导出器exporter实例。
let exporter = Exporter.init(videoURL: ``URL Link``, delegate: self)

或者

let exporter = Exporter.init(asset: ``AVAsset``, delegate: self)
  • 实现视频导出结果协议ExporterDelegate
/// Video export successed.
/// - Parameters:
///   - exporter: VideoExporter
///   - videoURL: Export the successful video url, Be equivalent to outputURL.
func export(_ exporter: Kakapos.Exporter, success videoURL: URL) {
    self.view.hideAllToasts()
    let player = AVPlayer(url: videoURL)
    let vc = AVPlayerViewController()
    vc.player = player
    self.present(vc, animated: true) {
        vc.player?.play()
    }
}

/// Video export failure.
/// - Parameters:
///   - exporter: VideoExporter
///   - error: Failure error message.
func export(_ exporter: Kakapos.Exporter, failed error: Kakapos.Exporter.Error) {
    // do someing..
}
  • 转换视频缓冲区CVPixelBuffer并添加过滤器。
let filters: [C7FilterProtocol] = [
    C7Flip(horizontal: true, vertical: false),
    C7ColorConvert(with: .gray),
    C7SoulOut(soul: 0.3),
    MPSGaussianBlur(radius: 5),
]
let exporter = Exporter.init(videoURL: ``URL Link``, delegate: self)
exporter.export(outputURL: outputURL) {
    let dest = BoxxIO(element: $0, filters: filters)
    return try? dest.output()
}

或者

let martix = C7ColorMatrix4x4(matrix: Matrix4x4.Color.gray)
let screen = C7SplitScreen(type: .two)

let exporter = Exporter.init(videoURL: ``URL Link``, delegate: self)
exporter.export(outputURL: outputURL) { $0 ->> martix ->> screen }

最后

✌️.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容