| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072 |
- import Foundation
- import UIKit
- import CoreVideo
- import CoreImage
- import QuartzCore
- import Metal
- import MetalKit
- import simd
- @objc public protocol LNHWDMetalViewDelegate: AnyObject {
- @objc(onMetalViewUnavailable)
- func onMetalViewUnavailable()
- }
- @objc public protocol LNVAPMetalViewDelegate: AnyObject {
- @objc(onMetalViewUnavailable)
- func onMetalViewUnavailable()
- }
- @objc public protocol LNHWDMP4OpenGLViewDelegate: AnyObject {
- @objc(onViewUnavailableStatus)
- func onViewUnavailableStatus()
- }
- private enum LNLegacyRuntime {
- static func cls(_ name: String) -> AnyClass? {
- NSClassFromString(name) ?? NSClassFromString("QGVAPlayer.\(name)")
- }
- static func instantiate(_ className: String) -> NSObject? {
- guard let t = cls(className) as? NSObject.Type else { return nil }
- return t.init()
- }
- static func set(_ obj: NSObject, _ key: String, _ value: Any?) {
- let setter = "set\(key.prefix(1).uppercased())\(key.dropFirst()):"
- let sel = NSSelectorFromString(setter)
- guard obj.responds(to: sel) else { return }
- obj.setValue(value, forKey: key)
- }
- static func callInitWithFrame(_ obj: NSObject, frame: CGRect) -> AnyObject? {
- let sel = NSSelectorFromString("initWithFrame:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return nil }
- typealias Fn = @convention(c) (AnyObject, Selector, CGRect) -> AnyObject
- return unsafeBitCast(imp, to: Fn.self)(obj, sel, frame)
- }
- static func callInitWithFrameBlend(_ obj: NSObject, frame: CGRect, blend: Int) -> AnyObject? {
- let sel = NSSelectorFromString("initWithFrame:blendMode:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return nil }
- typealias Fn = @convention(c) (AnyObject, Selector, CGRect, Int) -> AnyObject
- return unsafeBitCast(imp, to: Fn.self)(obj, sel, frame, blend)
- }
- static func callInitWithMetalLayer(_ obj: NSObject, layer: AnyObject) -> AnyObject? {
- let sel = NSSelectorFromString("initWithMetalLayer:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return nil }
- typealias Fn = @convention(c) (AnyObject, Selector, AnyObject) -> AnyObject
- return unsafeBitCast(imp, to: Fn.self)(obj, sel, layer)
- }
- static func callInitWithMetalLayerBlend(_ obj: NSObject, layer: AnyObject, blend: Int) -> AnyObject? {
- let sel = NSSelectorFromString("initWithMetalLayer:blendMode:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return nil }
- typealias Fn = @convention(c) (AnyObject, Selector, AnyObject, Int) -> AnyObject
- return unsafeBitCast(imp, to: Fn.self)(obj, sel, layer, blend)
- }
- static func callRenderPixelBuffer(_ obj: NSObject, pixelBuffer: CVPixelBuffer?, layer: AnyObject?) {
- let sel = NSSelectorFromString("renderPixelBuffer:metalLayer:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return }
- typealias Fn = @convention(c) (AnyObject, Selector, CVPixelBuffer?, AnyObject?) -> Void
- unsafeBitCast(imp, to: Fn.self)(obj, sel, pixelBuffer, layer)
- }
- static func callRenderPixelBufferMerge(_ obj: NSObject, pixelBuffer: CVPixelBuffer?, layer: AnyObject?, infos: NSArray?) {
- let sel = NSSelectorFromString("renderPixelBuffer:metalLayer:mergeInfos:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return }
- typealias Fn = @convention(c) (AnyObject, Selector, CVPixelBuffer?, AnyObject?, NSArray?) -> Void
- unsafeBitCast(imp, to: Fn.self)(obj, sel, pixelBuffer, layer, infos)
- }
- static func callDisplay(_ obj: NSObject, pixelBuffer: CVPixelBuffer?) {
- let sel = NSSelectorFromString("display:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return }
- typealias Fn = @convention(c) (AnyObject, Selector, CVPixelBuffer?) -> Void
- unsafeBitCast(imp, to: Fn.self)(obj, sel, pixelBuffer)
- }
- static func callDisplayMerge(_ obj: NSObject, pixelBuffer: CVPixelBuffer?, infos: NSArray?) {
- let sel = NSSelectorFromString("display:mergeInfos:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return }
- typealias Fn = @convention(c) (AnyObject, Selector, CVPixelBuffer?, NSArray?) -> Void
- unsafeBitCast(imp, to: Fn.self)(obj, sel, pixelBuffer, infos)
- }
- static func callDisplayPixelBuffer(_ obj: NSObject, pixelBuffer: CVPixelBuffer?) {
- let sel = NSSelectorFromString("displayPixelBuffer:")
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return }
- typealias Fn = @convention(c) (AnyObject, Selector, CVPixelBuffer?) -> Void
- unsafeBitCast(imp, to: Fn.self)(obj, sel, pixelBuffer)
- }
- static func callNoArgs(_ obj: NSObject, _ selector: String) {
- let sel = NSSelectorFromString(selector)
- guard obj.responds(to: sel), let imp = obj.method(for: sel) else { return }
- typealias Fn = @convention(c) (AnyObject, Selector) -> Void
- unsafeBitCast(imp, to: Fn.self)(obj, sel)
- }
- }
- private final class LNPixelBufferLayerRenderer {
- private let ciContext = CIContext(options: nil)
- func render(pixelBuffer: CVPixelBuffer, into layer: CALayer) {
- let image = CIImage(cvPixelBuffer: pixelBuffer)
- guard let cgImage = ciContext.createCGImage(image, from: image.extent) else { return }
- DispatchQueue.main.async {
- layer.contents = cgImage
- }
- }
- }
- @available(iOS 13.0, *)
- private final class LNHWDMetalCoreRenderer {
- private struct LNColorParameters {
- var matrix: simd_float3x3
- var offset: simd_float2
- }
- private static let kVertexFunctionName = "hwd_vertexShader"
- private static let kFragmentFunctionName = "hwd_yuvFragmentShader"
- private static let verticesByBlendMode: [[[Float]]] = [
- // alpha left
- [
- [-1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0],
- [-1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 0.0, 0.0],
- [ 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.5, 1.0],
- [ 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.5, 0.0]
- ],
- // alpha right
- [
- [-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.5, 1.0],
- [-1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0],
- [ 1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 1.0, 1.0],
- [ 1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0]
- ],
- // alpha top
- [
- [-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.5],
- [-1.0, 1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0],
- [ 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.5],
- [ 1.0, 1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 0.0]
- ],
- // alpha bottom
- [
- [-1.0, -1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0],
- [-1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5],
- [ 1.0, -1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 1.0],
- [ 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5]
- ]
- ]
- private static let matrix601Full = simd_float3x3(
- SIMD3<Float>(1.0, 1.0, 1.0),
- SIMD3<Float>(0.0, -0.34413, 1.772),
- SIMD3<Float>(1.402, -0.71414, 0.0)
- )
- private static let matrix709Full = simd_float3x3(
- SIMD3<Float>(1.0, 1.0, 1.0),
- SIMD3<Float>(0.0, -0.18732, 1.8556),
- SIMD3<Float>(1.57481, -0.46813, 0.0)
- )
- private let device: MTLDevice
- private let commandQueue: MTLCommandQueue
- private let pipelineState: MTLRenderPipelineState
- private var textureCache: CVMetalTextureCache?
- private var vertexBuffer: MTLBuffer?
- private var yuvMatrixBuffer: MTLBuffer?
- private var currentColorMatrix = matrix601Full
- init?(metalLayer: CAMetalLayer, blendMode: QGHWDTextureBlendMode) {
- guard let device = MTLCreateSystemDefaultDevice(),
- let queue = device.makeCommandQueue(),
- let library = device.makeDefaultLibrary(),
- let vertexFunc = library.makeFunction(name: Self.kVertexFunctionName),
- let fragmentFunc = library.makeFunction(name: Self.kFragmentFunctionName) else {
- return nil
- }
- let desc = MTLRenderPipelineDescriptor()
- desc.vertexFunction = vertexFunc
- desc.fragmentFunction = fragmentFunc
- desc.colorAttachments[0].pixelFormat = metalLayer.pixelFormat
- guard let pipelineState = try? device.makeRenderPipelineState(descriptor: desc) else {
- return nil
- }
- self.device = device
- self.commandQueue = queue
- self.pipelineState = pipelineState
- metalLayer.device = device
- metalLayer.framebufferOnly = false
- let cacheStatus = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
- guard cacheStatus == kCVReturnSuccess else { return nil }
- updateBlendMode(blendMode)
- updateYuvMatrixBuffer(matrix: Self.matrix601Full)
- }
- func updateBlendMode(_ blendMode: QGHWDTextureBlendMode) {
- let modeIndex = max(0, min(Int(blendMode.rawValue), Self.verticesByBlendMode.count - 1))
- let flat = Self.verticesByBlendMode[modeIndex].flatMap { $0 }
- vertexBuffer = device.makeBuffer(bytes: flat, length: flat.count * MemoryLayout<Float>.size, options: .storageModeShared)
- }
- func render(pixelBuffer: CVPixelBuffer, metalLayer: CAMetalLayer) {
- guard metalLayer.bounds.width > 0,
- metalLayer.bounds.height > 0,
- let drawable = metalLayer.nextDrawable(),
- let commandBuffer = commandQueue.makeCommandBuffer(),
- let descriptor = currentRenderPassDescriptor(texture: drawable.texture),
- let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor),
- let vertexBuffer,
- let yuvMatrixBuffer else {
- return
- }
- updateYuvMatrixIfNeeded(pixelBuffer: pixelBuffer)
- guard let yTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 0, format: .r8Unorm),
- let uvTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 1, format: .rg8Unorm) else {
- return
- }
- encoder.setRenderPipelineState(pipelineState)
- encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
- encoder.setFragmentBuffer(yuvMatrixBuffer, offset: 0, index: 0)
- encoder.setFragmentTexture(yTexture, index: 0)
- encoder.setFragmentTexture(uvTexture, index: 1)
- encoder.drawPrimitives(type: MTLPrimitiveType.triangleStrip, vertexStart: 0, vertexCount: 4)
- encoder.endEncoding()
- commandBuffer.present(drawable)
- commandBuffer.commit()
- }
- func dispose() {
- if let textureCache {
- CVMetalTextureCacheFlush(textureCache, 0)
- }
- textureCache = nil
- vertexBuffer = nil
- yuvMatrixBuffer = nil
- }
- private func makeTexture(pixelBuffer: CVPixelBuffer, plane: Int, format: MTLPixelFormat) -> MTLTexture? {
- guard let textureCache else { return nil }
- let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, plane)
- let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, plane)
- var cvTexture: CVMetalTexture?
- let status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
- textureCache,
- pixelBuffer,
- nil,
- format,
- width,
- height,
- plane,
- &cvTexture)
- guard status == kCVReturnSuccess, let cvTexture else { return nil }
- return CVMetalTextureGetTexture(cvTexture)
- }
- private func currentRenderPassDescriptor(texture: MTLTexture) -> MTLRenderPassDescriptor? {
- let descriptor = MTLRenderPassDescriptor()
- descriptor.colorAttachments[0].texture = texture
- descriptor.colorAttachments[0].loadAction = .clear
- descriptor.colorAttachments[0].storeAction = .store
- descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
- return descriptor
- }
- private func updateYuvMatrixIfNeeded(pixelBuffer: CVPixelBuffer) {
- let matrixAttachment = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, nil)?.takeUnretainedValue()
- let matrix = (matrixAttachment != nil && CFEqual(matrixAttachment, kCVImageBufferYCbCrMatrix_ITU_R_709_2)) ? Self.matrix709Full : Self.matrix601Full
- guard matrix != currentColorMatrix else { return }
- currentColorMatrix = matrix
- updateYuvMatrixBuffer(matrix: matrix)
- }
- private func updateYuvMatrixBuffer(matrix: simd_float3x3) {
- let params = LNColorParameters(matrix: matrix, offset: simd_float2(0.5, 0.5))
- yuvMatrixBuffer = device.makeBuffer(bytes: [params], length: MemoryLayout<LNColorParameters>.stride, options: .storageModeShared)
- }
- }
- @available(iOS 13.0, *)
- private final class LNVAPMetalCoreRenderer {
- private struct LNColorParameters {
- var matrix: simd_float3x3
- var offset: simd_float2
- }
- private struct LNMaskParameters {
- var weightMatrix: simd_float3x3
- var coreSize: Int32
- var texelOffset: Float
- }
- private static let matrix601Full = simd_float3x3(
- SIMD3<Float>(1.0, 1.0, 1.0),
- SIMD3<Float>(0.0, -0.34413, 1.772),
- SIMD3<Float>(1.402, -0.71414, 0.0)
- )
- private static let matrix709Full = simd_float3x3(
- SIMD3<Float>(1.0, 1.0, 1.0),
- SIMD3<Float>(0.0, -0.18732, 1.8556),
- SIMD3<Float>(1.57481, -0.46813, 0.0)
- )
- private let device: MTLDevice
- private let pixelFormat: MTLPixelFormat
- private let commandQueue: MTLCommandQueue
- private let shaderLoader: LNVAPMetalShaderFunctionLoader
- private var textureCache: CVMetalTextureCache?
- private var defaultMainPipelineState: MTLRenderPipelineState?
- private var mainPipelineStateForMask: MTLRenderPipelineState?
- private var mainPipelineStateForMaskBlur: MTLRenderPipelineState?
- private var attachmentPipelineState: MTLRenderPipelineState?
- private var vertexBuffer: MTLBuffer?
- private var yuvMatrixBuffer: MTLBuffer?
- private var maskBlurBuffer: MTLBuffer?
- private var maskTexture: MTLTexture?
- private var currentColorMatrix = matrix601Full
- var commonInfo: LNVAPCommonInfo? {
- didSet {
- updateMainVertexBuffer()
- }
- }
- var maskInfo: LNVAPMaskInfo? {
- didSet {
- updateMainVertexBuffer()
- updateMaskTexture()
- }
- }
- init?(metalLayer: CAMetalLayer) {
- guard let device = MTLCreateSystemDefaultDevice(),
- let queue = device.makeCommandQueue() else {
- return nil
- }
- self.device = device
- self.pixelFormat = metalLayer.pixelFormat
- self.commandQueue = queue
- self.shaderLoader = LNVAPMetalShaderFunctionLoader(device: device)
- metalLayer.device = device
- metalLayer.framebufferOnly = false
- let cacheStatus = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
- guard cacheStatus == kCVReturnSuccess else { return nil }
- updateYuvMatrixBuffer(matrix: Self.matrix601Full)
- }
- func render(pixelBuffer: CVPixelBuffer, metalLayer: CAMetalLayer, mergeInfos: [LNVAPMergedInfo]) {
- guard metalLayer.bounds.width > 0,
- metalLayer.bounds.height > 0,
- let drawable = metalLayer.nextDrawable(),
- let commandBuffer = commandQueue.makeCommandBuffer(),
- let descriptor = currentRenderPassDescriptor(texture: drawable.texture),
- let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
- return
- }
- updateYuvMatrixIfNeeded(pixelBuffer: pixelBuffer)
- guard let yTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 0, format: .r8Unorm),
- let uvTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 1, format: .rg8Unorm) else {
- return
- }
- drawBackground(encoder: encoder, yTexture: yTexture, uvTexture: uvTexture)
- drawMergedAttachments(encoder: encoder, yTexture: yTexture, uvTexture: uvTexture, mergeInfos: mergeInfos)
- encoder.endEncoding()
- commandBuffer.present(drawable)
- commandBuffer.commit()
- }
- func dispose() {
- if let textureCache {
- CVMetalTextureCacheFlush(textureCache, 0)
- }
- textureCache = nil
- vertexBuffer = nil
- yuvMatrixBuffer = nil
- maskBlurBuffer = nil
- maskTexture = nil
- defaultMainPipelineState = nil
- mainPipelineStateForMask = nil
- mainPipelineStateForMaskBlur = nil
- attachmentPipelineState = nil
- }
- private func drawBackground(encoder: MTLRenderCommandEncoder, yTexture: MTLTexture, uvTexture: MTLTexture) {
- guard let vertexBuffer,
- let yuvMatrixBuffer else {
- return
- }
- if let maskInfo,
- let maskTexture {
- if maskInfo.blurLength > 0 {
- guard let pipelineState = mainPipelineStateForMaskBlur ?? makeMaskBlurPipelineState(),
- let maskBlurBuffer = maskBlurBuffer ?? makeMaskBlurBuffer() else {
- return
- }
- encoder.setRenderPipelineState(pipelineState)
- encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
- encoder.setFragmentBuffer(yuvMatrixBuffer, offset: 0, index: 0)
- encoder.setFragmentBuffer(maskBlurBuffer, offset: 0, index: 1)
- encoder.setFragmentTexture(yTexture, index: 0)
- encoder.setFragmentTexture(uvTexture, index: 1)
- encoder.setFragmentTexture(maskTexture, index: 2)
- encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
- return
- }
- guard let pipelineState = mainPipelineStateForMask ?? makeMaskPipelineState() else {
- return
- }
- encoder.setRenderPipelineState(pipelineState)
- encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
- encoder.setFragmentBuffer(yuvMatrixBuffer, offset: 0, index: 0)
- encoder.setFragmentTexture(yTexture, index: 0)
- encoder.setFragmentTexture(uvTexture, index: 1)
- encoder.setFragmentTexture(maskTexture, index: 2)
- encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
- return
- }
- guard let pipelineState = defaultMainPipelineState ?? makeMainPipelineState() else {
- return
- }
- encoder.setRenderPipelineState(pipelineState)
- encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
- encoder.setFragmentBuffer(yuvMatrixBuffer, offset: 0, index: 0)
- encoder.setFragmentTexture(yTexture, index: 0)
- encoder.setFragmentTexture(uvTexture, index: 1)
- encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
- }
- private func drawMergedAttachments(encoder: MTLRenderCommandEncoder, yTexture: MTLTexture, uvTexture: MTLTexture, mergeInfos: [LNVAPMergedInfo]) {
- guard !mergeInfos.isEmpty,
- let commonInfo,
- let pipelineState = attachmentPipelineState ?? makeAttachmentPipelineState(),
- let yuvMatrixBuffer else {
- return
- }
- for mergeInfo in mergeInfos {
- guard let sourceTexture = mergeInfo.source?.texture,
- let colorParamsBuffer = mergeInfo.source?.colorParamsBuffer,
- let mergeVertexBuffer = mergeInfo.vertexBuffer(containerSize: commonInfo.size,
- maskContianerSize: commonInfo.videoSize,
- device: device) else {
- continue
- }
- encoder.setRenderPipelineState(pipelineState)
- encoder.setVertexBuffer(mergeVertexBuffer, offset: 0, index: 0)
- encoder.setFragmentBuffer(yuvMatrixBuffer, offset: 0, index: 0)
- encoder.setFragmentBuffer(colorParamsBuffer, offset: 0, index: 1)
- encoder.setFragmentTexture(yTexture, index: 0)
- encoder.setFragmentTexture(uvTexture, index: 1)
- encoder.setFragmentTexture(sourceTexture, index: 2)
- encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
- }
- }
- private func makeMainPipelineState() -> MTLRenderPipelineState? {
- guard let state = createPipelineState(vertexFunction: LNVAPMetalUtil.vapVertexFunctionName,
- fragmentFunction: LNVAPMetalUtil.vapYUVFragmentFunctionName) else {
- return nil
- }
- defaultMainPipelineState = state
- return state
- }
- private func makeMaskPipelineState() -> MTLRenderPipelineState? {
- guard let state = createPipelineState(vertexFunction: LNVAPMetalUtil.vapVertexFunctionName,
- fragmentFunction: LNVAPMetalUtil.vapMaskFragmentFunctionName) else {
- return nil
- }
- mainPipelineStateForMask = state
- return state
- }
- private func makeMaskBlurPipelineState() -> MTLRenderPipelineState? {
- guard let state = createPipelineState(vertexFunction: LNVAPMetalUtil.vapVertexFunctionName,
- fragmentFunction: LNVAPMetalUtil.vapMaskBlurFragmentFunctionName) else {
- return nil
- }
- mainPipelineStateForMaskBlur = state
- return state
- }
- private func makeAttachmentPipelineState() -> MTLRenderPipelineState? {
- guard let state = createPipelineState(vertexFunction: LNVAPMetalUtil.vapAttachmentVertexFunctionName,
- fragmentFunction: LNVAPMetalUtil.vapAttachmentFragmentFunctionName) else {
- return nil
- }
- attachmentPipelineState = state
- return state
- }
- private func createPipelineState(vertexFunction: String, fragmentFunction: String) -> MTLRenderPipelineState? {
- guard let vertexProgram = shaderLoader.loadFunction(withName: vertexFunction),
- let fragmentProgram = shaderLoader.loadFunction(withName: fragmentFunction) else {
- return nil
- }
- let descriptor = MTLRenderPipelineDescriptor()
- descriptor.vertexFunction = vertexProgram
- descriptor.fragmentFunction = fragmentProgram
- descriptor.colorAttachments[0].pixelFormat = pixelFormat
- descriptor.colorAttachments[0].isBlendingEnabled = true
- descriptor.colorAttachments[0].rgbBlendOperation = .add
- descriptor.colorAttachments[0].alphaBlendOperation = .add
- descriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
- descriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
- descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
- descriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
- return try? device.makeRenderPipelineState(descriptor: descriptor)
- }
- private func updateMainVertexBuffer() {
- guard let commonInfo else {
- vertexBuffer = nil
- return
- }
- let vertices = LNVAPMetalUtil.vapMTLVerticesIdentity
- let rgbCoordinates = LNVAPMetalUtil.genTextureCoordinates(rect: commonInfo.rgbAreaRect, containerSize: commonInfo.videoSize, reverse: false, degree: 0)
- let alphaCoordinates = LNVAPMetalUtil.genTextureCoordinates(rect: commonInfo.alphaAreaRect, containerSize: commonInfo.videoSize, reverse: false, degree: 0)
- let maskCoordinates: [Float]
- if let maskInfo {
- maskCoordinates = LNVAPMetalUtil.genTextureCoordinates(rect: maskInfo.sampleRect, containerSize: maskInfo.dataSize, reverse: false, degree: 0)
- } else {
- maskCoordinates = [Float](repeating: 0, count: 8)
- }
- var vertexData = [Float](repeating: 0, count: 40)
- var index = 0
- for i in 0..<16 {
- vertexData[index] = vertices[i]
- index += 1
- if i % 4 == 3 {
- let row = i / 4
- vertexData[index] = rgbCoordinates[row * 2]
- index += 1
- vertexData[index] = rgbCoordinates[row * 2 + 1]
- index += 1
- vertexData[index] = alphaCoordinates[row * 2]
- index += 1
- vertexData[index] = alphaCoordinates[row * 2 + 1]
- index += 1
- vertexData[index] = maskCoordinates[row * 2]
- index += 1
- vertexData[index] = maskCoordinates[row * 2 + 1]
- index += 1
- }
- }
- vertexBuffer = device.makeBuffer(bytes: vertexData, length: vertexData.count * MemoryLayout<Float>.size, options: .storageModeShared)
- }
- private func updateMaskTexture() {
- guard let maskInfo else {
- maskTexture = nil
- return
- }
- let width = Int(maskInfo.dataSize.width)
- let height = Int(maskInfo.dataSize.height)
- guard width > 0,
- height > 0,
- !maskInfo.data.isEmpty else {
- maskTexture = nil
- return
- }
- let descriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r8Unorm, width: width, height: height, mipmapped: false)
- descriptor.usage = [.shaderRead]
- descriptor.storageMode = .shared
- guard let texture = device.makeTexture(descriptor: descriptor) else {
- maskTexture = nil
- return
- }
- maskInfo.data.withUnsafeBytes { bytes in
- guard let base = bytes.baseAddress else { return }
- texture.replace(region: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0, withBytes: base, bytesPerRow: width)
- }
- maskTexture = texture
- }
- private func makeMaskBlurBuffer() -> MTLBuffer? {
- let weight = simd_float3x3(
- SIMD3<Float>(0.0625, 0.125, 0.0625),
- SIMD3<Float>(0.125, 0.25, 0.125),
- SIMD3<Float>(0.0625, 0.125, 0.0625)
- )
- let params = LNMaskParameters(weightMatrix: weight, coreSize: 3, texelOffset: 0.01)
- let buffer = device.makeBuffer(bytes: [params], length: MemoryLayout<LNMaskParameters>.stride, options: .storageModeShared)
- maskBlurBuffer = buffer
- return buffer
- }
- private func makeTexture(pixelBuffer: CVPixelBuffer, plane: Int, format: MTLPixelFormat) -> MTLTexture? {
- guard let textureCache else { return nil }
- let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, plane)
- let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, plane)
- var cvTexture: CVMetalTexture?
- let status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
- textureCache,
- pixelBuffer,
- nil,
- format,
- width,
- height,
- plane,
- &cvTexture)
- guard status == kCVReturnSuccess, let cvTexture else { return nil }
- return CVMetalTextureGetTexture(cvTexture)
- }
- private func currentRenderPassDescriptor(texture: MTLTexture) -> MTLRenderPassDescriptor? {
- let descriptor = MTLRenderPassDescriptor()
- descriptor.colorAttachments[0].texture = texture
- descriptor.colorAttachments[0].loadAction = .clear
- descriptor.colorAttachments[0].storeAction = .store
- descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
- return descriptor
- }
- private func updateYuvMatrixIfNeeded(pixelBuffer: CVPixelBuffer) {
- let matrixAttachment = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, nil)?.takeUnretainedValue()
- let matrix = (matrixAttachment != nil && CFEqual(matrixAttachment, kCVImageBufferYCbCrMatrix_ITU_R_709_2)) ? Self.matrix709Full : Self.matrix601Full
- guard matrix != currentColorMatrix else { return }
- currentColorMatrix = matrix
- updateYuvMatrixBuffer(matrix: matrix)
- }
- private func updateYuvMatrixBuffer(matrix: simd_float3x3) {
- let params = LNColorParameters(matrix: matrix, offset: simd_float2(0.5, 0.5))
- yuvMatrixBuffer = device.makeBuffer(bytes: [params], length: MemoryLayout<LNColorParameters>.stride, options: .storageModeShared)
- }
- }
- private final class LNMetalUnavailableBridge: NSObject {
- var callback: (() -> Void)?
- @objc func onMetalViewUnavailable() {
- callback?()
- }
- @objc func onViewUnavailableStatus() {
- callback?()
- }
- }
- private enum LNVAPLegacyMapper {
- static func toLegacyCommonInfo(_ info: LNVAPCommonInfo?) -> NSObject? {
- guard let info, let obj = LNLegacyRuntime.instantiate("QGVAPCommonInfo") else { return nil }
- LNLegacyRuntime.set(obj, "version", info.version)
- LNLegacyRuntime.set(obj, "framesCount", info.framesCount)
- LNLegacyRuntime.set(obj, "size", NSValue(cgSize: info.size))
- LNLegacyRuntime.set(obj, "videoSize", NSValue(cgSize: info.videoSize))
- LNLegacyRuntime.set(obj, "targetOrientaion", info.targetOrientaion.rawValue)
- LNLegacyRuntime.set(obj, "fps", info.fps)
- LNLegacyRuntime.set(obj, "isMerged", info.isMerged)
- LNLegacyRuntime.set(obj, "alphaAreaRect", NSValue(cgRect: info.alphaAreaRect))
- LNLegacyRuntime.set(obj, "rgbAreaRect", NSValue(cgRect: info.rgbAreaRect))
- return obj
- }
- static func toLegacyMaskInfo(_ info: LNVAPMaskInfo?) -> NSObject? {
- guard let info, let obj = LNLegacyRuntime.instantiate("QGVAPMaskInfo") else { return nil }
- LNLegacyRuntime.set(obj, "data", info.data)
- LNLegacyRuntime.set(obj, "dataSize", NSValue(cgSize: info.dataSize))
- LNLegacyRuntime.set(obj, "positionInVideoRect", NSValue(cgRect: info.sampleRect))
- return obj
- }
- static func toLegacySourceInfo(_ info: LNVAPSourceInfo?) -> NSObject? {
- guard let info, let obj = LNLegacyRuntime.instantiate("QGVAPSourceInfo") else { return nil }
- LNLegacyRuntime.set(obj, "contentTag", info.contentTag)
- LNLegacyRuntime.set(obj, "contentTagValue", info.contentTagValue)
- LNLegacyRuntime.set(obj, "color", info.color)
- LNLegacyRuntime.set(obj, "size", NSValue(cgSize: info.size))
- LNLegacyRuntime.set(obj, "sourceImage", info.sourceImage)
- LNLegacyRuntime.set(obj, "texture", info.texture)
- LNLegacyRuntime.set(obj, "colorParamsBuffer", info.colorParamsBuffer)
- return obj
- }
- static func toLegacyMergedInfos(_ infos: [LNVAPMergedInfo]) -> NSArray {
- let out = NSMutableArray(capacity: infos.count)
- for info in infos {
- guard let obj = LNLegacyRuntime.instantiate("QGVAPMergedInfo") else { continue }
- LNLegacyRuntime.set(obj, "source", toLegacySourceInfo(info.source))
- LNLegacyRuntime.set(obj, "renderIndex", info.renderIndex)
- LNLegacyRuntime.set(obj, "renderRect", NSValue(cgRect: info.renderRect))
- LNLegacyRuntime.set(obj, "needMask", info.needMask)
- LNLegacyRuntime.set(obj, "maskRect", NSValue(cgRect: info.maskRect))
- LNLegacyRuntime.set(obj, "maskRotation", info.maskRotation)
- out.add(obj)
- }
- return out
- }
- }
- @objcMembers
- public final class LNHWDMetalRenderer: NSObject {
- public var blendMode: QGHWDTextureBlendMode {
- didSet {
- if #available(iOS 13.0, *), let swiftRenderer = swiftRenderer as? LNHWDMetalCoreRenderer {
- swiftRenderer.updateBlendMode(blendMode)
- }
- if let legacy = legacyRenderer {
- LNLegacyRuntime.set(legacy, "blendMode", blendMode.rawValue)
- }
- }
- }
- private let fallback = LNPixelBufferLayerRenderer()
- private let fallbackLayer = CALayer()
- private let legacyRenderer: NSObject?
- private let swiftRenderer: AnyObject?
- @objc(initWithMetalLayer:blendMode:)
- public init(metalLayer: AnyObject, blendMode: QGHWDTextureBlendMode) {
- self.blendMode = blendMode
- if #available(iOS 13.0, *), let metalLayer = metalLayer as? CAMetalLayer {
- self.swiftRenderer = LNHWDMetalCoreRenderer(metalLayer: metalLayer, blendMode: blendMode)
- } else {
- self.swiftRenderer = nil
- }
- if self.swiftRenderer == nil, let rendererObj = LNLegacyRuntime.instantiate("QGHWDMetalRenderer") {
- let created = LNLegacyRuntime.callInitWithMetalLayerBlend(rendererObj, layer: metalLayer, blend: Int(blendMode.rawValue))
- self.legacyRenderer = (created as? NSObject) ?? rendererObj
- } else {
- self.legacyRenderer = nil
- }
- super.init()
- }
- @objc(renderPixelBuffer:metalLayer:)
- public func renderPixelBuffer(_ pixelBuffer: CVPixelBuffer?, metalLayer: AnyObject) {
- if #available(iOS 13.0, *),
- let pixelBuffer,
- let metalLayer = metalLayer as? CAMetalLayer,
- let swiftRenderer = swiftRenderer as? LNHWDMetalCoreRenderer {
- swiftRenderer.render(pixelBuffer: pixelBuffer, metalLayer: metalLayer)
- return
- }
- if let legacy = legacyRenderer {
- LNLegacyRuntime.callRenderPixelBuffer(legacy, pixelBuffer: pixelBuffer, layer: metalLayer)
- return
- }
- guard let pixelBuffer else { return }
- let target = (metalLayer as? CALayer) ?? fallbackLayer
- fallback.render(pixelBuffer: pixelBuffer, into: target)
- }
- @objc(dispose)
- public func dispose() {
- if #available(iOS 13.0, *), let swiftRenderer = swiftRenderer as? LNHWDMetalCoreRenderer {
- swiftRenderer.dispose()
- }
- if let legacy = legacyRenderer {
- LNLegacyRuntime.callNoArgs(legacy, "dispose")
- }
- }
- }
- @objcMembers
- public final class LNHWDMetalView: UIView {
- public weak var delegate: LNHWDMetalViewDelegate?
- public weak var lnDelegate: LNHWDMetalViewDelegate? {
- get { delegate }
- set { delegate = newValue }
- }
- public var blendMode: QGHWDTextureBlendMode {
- didSet {
- fallbackRenderer.blendMode = blendMode
- }
- }
- private let unavailableBridge = LNMetalUnavailableBridge()
- private var renderLayer: CALayer = CALayer()
- private lazy var fallbackRenderer = LNHWDMetalRenderer(metalLayer: renderLayer, blendMode: blendMode)
- @objc(initWithFrame:blendMode:)
- public init(frame: CGRect, blendMode: QGHWDTextureBlendMode) {
- self.blendMode = blendMode
- super.init(frame: frame)
- setupRenderingLayer(frame: frame)
- }
- public required init?(coder: NSCoder) {
- self.blendMode = .alphaLeft
- super.init(coder: coder)
- setupRenderingLayer(frame: bounds)
- }
- private func setupRenderingLayer(frame: CGRect) {
- unavailableBridge.callback = { [weak self] in
- self?.delegate?.onMetalViewUnavailable()
- }
- if #available(iOS 13.0, *) {
- let metalLayer = CAMetalLayer()
- metalLayer.pixelFormat = .bgra8Unorm
- renderLayer = metalLayer
- } else {
- renderLayer = CALayer()
- }
- renderLayer.frame = frame
- renderLayer.contentsScale = UIScreen.main.scale
- renderLayer.contentsGravity = .resizeAspect
- layer.addSublayer(renderLayer)
- }
- public override func layoutSubviews() {
- super.layoutSubviews()
- renderLayer.frame = bounds
- }
- @objc(display:)
- public func display(_ pixelBuffer: CVPixelBuffer?) {
- fallbackRenderer.renderPixelBuffer(pixelBuffer, metalLayer: renderLayer)
- }
- @objc(dispose)
- public func dispose() {
- renderLayer.contents = nil
- }
- }
- @objcMembers
- public final class LNVAPMetalRenderer: NSObject {
- public var commonInfo: LNVAPCommonInfo? {
- didSet {
- if #available(iOS 13.0, *), let swiftRenderer = swiftRenderer as? LNVAPMetalCoreRenderer {
- swiftRenderer.commonInfo = commonInfo
- }
- guard let legacy = legacyRenderer else { return }
- LNLegacyRuntime.set(legacy, "commonInfo", LNVAPLegacyMapper.toLegacyCommonInfo(commonInfo))
- }
- }
- public var maskInfo: LNVAPMaskInfo? {
- didSet {
- if #available(iOS 13.0, *), let swiftRenderer = swiftRenderer as? LNVAPMetalCoreRenderer {
- swiftRenderer.maskInfo = maskInfo
- }
- guard let legacy = legacyRenderer else { return }
- LNLegacyRuntime.set(legacy, "maskInfo", LNVAPLegacyMapper.toLegacyMaskInfo(maskInfo))
- }
- }
- private let fallback = LNPixelBufferLayerRenderer()
- private let fallbackLayer = CALayer()
- private let legacyRenderer: NSObject?
- private let swiftRenderer: AnyObject?
- @objc(initWithMetalLayer:)
- public init(metalLayer: AnyObject) {
- if #available(iOS 13.0, *), let metalLayer = metalLayer as? CAMetalLayer {
- self.swiftRenderer = LNVAPMetalCoreRenderer(metalLayer: metalLayer)
- } else {
- self.swiftRenderer = nil
- }
- if let rendererObj = LNLegacyRuntime.instantiate("QGVAPMetalRenderer") {
- let created = LNLegacyRuntime.callInitWithMetalLayer(rendererObj, layer: metalLayer)
- self.legacyRenderer = (created as? NSObject) ?? rendererObj
- } else {
- self.legacyRenderer = nil
- }
- super.init()
- }
- @objc(renderPixelBuffer:metalLayer:mergeInfos:)
- public func renderPixelBuffer(_ pixelBuffer: CVPixelBuffer?, metalLayer: AnyObject, mergeInfos: [LNVAPMergedInfo]) {
- if #available(iOS 13.0, *),
- let pixelBuffer,
- let metalLayer = metalLayer as? CAMetalLayer,
- let swiftRenderer = swiftRenderer as? LNVAPMetalCoreRenderer {
- swiftRenderer.render(pixelBuffer: pixelBuffer, metalLayer: metalLayer, mergeInfos: mergeInfos)
- return
- }
- if let legacy = legacyRenderer {
- LNLegacyRuntime.set(legacy, "commonInfo", LNVAPLegacyMapper.toLegacyCommonInfo(commonInfo))
- LNLegacyRuntime.set(legacy, "maskInfo", LNVAPLegacyMapper.toLegacyMaskInfo(maskInfo))
- LNLegacyRuntime.callRenderPixelBufferMerge(legacy,
- pixelBuffer: pixelBuffer,
- layer: metalLayer,
- infos: LNVAPLegacyMapper.toLegacyMergedInfos(mergeInfos))
- return
- }
- guard let pixelBuffer else { return }
- let target = (metalLayer as? CALayer) ?? fallbackLayer
- fallback.render(pixelBuffer: pixelBuffer, into: target)
- }
- @objc(dispose)
- public func dispose() {
- if #available(iOS 13.0, *), let swiftRenderer = swiftRenderer as? LNVAPMetalCoreRenderer {
- swiftRenderer.dispose()
- }
- if let legacy = legacyRenderer {
- LNLegacyRuntime.callNoArgs(legacy, "dispose")
- }
- }
- }
- @objcMembers
- public final class LNVAPMetalView: UIView {
- public weak var delegate: LNVAPMetalViewDelegate?
- public weak var lnDelegate: LNVAPMetalViewDelegate? {
- get { delegate }
- set { delegate = newValue }
- }
- public var commonInfo: LNVAPCommonInfo?
- public var maskInfo: LNVAPMaskInfo?
- private let unavailableBridge = LNMetalUnavailableBridge()
- private var renderLayer: CALayer = CALayer()
- private lazy var fallbackRenderer = LNVAPMetalRenderer(metalLayer: renderLayer)
- public override init(frame: CGRect) {
- super.init(frame: frame)
- setupRenderingLayer(frame: frame)
- }
- public required init?(coder: NSCoder) {
- super.init(coder: coder)
- setupRenderingLayer(frame: bounds)
- }
- private func setupRenderingLayer(frame: CGRect) {
- unavailableBridge.callback = { [weak self] in
- self?.delegate?.onMetalViewUnavailable()
- }
- if #available(iOS 13.0, *) {
- let metalLayer = CAMetalLayer()
- metalLayer.pixelFormat = .bgra8Unorm
- renderLayer = metalLayer
- } else {
- renderLayer = CALayer()
- }
- renderLayer.frame = frame
- renderLayer.contentsScale = UIScreen.main.scale
- renderLayer.contentsGravity = .resizeAspect
- layer.addSublayer(renderLayer)
- }
- public override func layoutSubviews() {
- super.layoutSubviews()
- renderLayer.frame = bounds
- }
- @objc(display:mergeInfos:)
- public func display(_ pixelBuffer: CVPixelBuffer?, mergeInfos: [LNVAPMergedInfo]) {
- fallbackRenderer.commonInfo = commonInfo
- fallbackRenderer.maskInfo = maskInfo
- fallbackRenderer.renderPixelBuffer(pixelBuffer, metalLayer: renderLayer, mergeInfos: mergeInfos)
- }
- @objc(dispose)
- public func dispose() {
- renderLayer.contents = nil
- }
- }
- @objcMembers
- public final class LNHWDMP4OpenGLView: UIView {
- public weak var displayDelegate: LNHWDMP4OpenGLViewDelegate?
- public var glContext: AnyObject?
- public var blendMode: QGHWDTextureBlendMode = .alphaLeft {
- didSet {
- fallbackRenderer.blendMode = blendMode
- }
- }
- public var pause: Bool = false
- private var renderLayer: CALayer = CALayer()
- private lazy var fallbackRenderer = LNHWDMetalRenderer(metalLayer: renderLayer, blendMode: blendMode)
- public override init(frame: CGRect) {
- super.init(frame: frame)
- setupRenderingLayer(frame: frame)
- }
- public required init?(coder: NSCoder) {
- super.init(coder: coder)
- setupRenderingLayer(frame: bounds)
- }
- private func setupRenderingLayer(frame: CGRect) {
- if #available(iOS 13.0, *) {
- let metalLayer = CAMetalLayer()
- metalLayer.pixelFormat = .bgra8Unorm
- renderLayer = metalLayer
- } else {
- renderLayer = CALayer()
- }
- renderLayer.frame = frame
- renderLayer.contentsScale = UIScreen.main.scale
- renderLayer.contentsGravity = .resizeAspect
- layer.addSublayer(renderLayer)
- }
- public override func layoutSubviews() {
- super.layoutSubviews()
- renderLayer.frame = bounds
- }
- @objc(setupGL)
- public func setupGL() {
- renderLayer.frame = bounds
- }
- @objc(displayPixelBuffer:)
- public func displayPixelBuffer(_ pixelBuffer: CVPixelBuffer?) {
- if window == nil {
- displayDelegate?.onViewUnavailableStatus()
- return
- }
- guard !pause, let pixelBuffer else { return }
- fallbackRenderer.blendMode = blendMode
- fallbackRenderer.renderPixelBuffer(pixelBuffer, metalLayer: renderLayer)
- }
- @objc(dispose)
- public func dispose() {
- fallbackRenderer.dispose()
- renderLayer.contents = nil
- }
- @objc(updateBackingSize)
- public func updateBackingSize() {
- renderLayer.frame = bounds
- }
- }
|