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 }

最後

✌️.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章