LNControllers.swift 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907
  1. import Foundation
  2. import UIKit
  3. import AVFoundation
  4. import VideoToolbox
  5. import CoreMedia
  6. import CoreVideo
  7. @objcMembers
  8. public final class LNAnimatedImageDecodeConfig: NSObject {
  9. public var threadCount: Int = 1
  10. public var bufferCount: Int = 5
  11. @objc(defaultConfig)
  12. public static func defaultConfig() -> LNAnimatedImageDecodeConfig {
  13. let config = LNAnimatedImageDecodeConfig()
  14. config.threadCount = 1
  15. config.bufferCount = 5
  16. return config
  17. }
  18. }
  19. @objcMembers
  20. public final class LNAnimatedImageDecodeThread: Thread {
  21. public var occupied: Bool = false
  22. public var sequenceDec: String {
  23. #if DEBUG
  24. return description
  25. #else
  26. return description
  27. #endif
  28. }
  29. }
  30. @objcMembers
  31. public final class LNAnimatedImageDecodeThreadPool: NSObject {
  32. public static let shared = LNAnimatedImageDecodeThreadPool()
  33. private let lock = NSLock()
  34. private var threads: [LNAnimatedImageDecodeThread] = []
  35. @objc(sharedPool)
  36. public static func sharedPool() -> LNAnimatedImageDecodeThreadPool {
  37. shared
  38. }
  39. @objc(getDecodeThread)
  40. public func getDecodeThread() -> LNAnimatedImageDecodeThread {
  41. lock.lock()
  42. defer { lock.unlock() }
  43. if let freeThread = threads.first(where: { !$0.occupied }) {
  44. return freeThread
  45. }
  46. let thread = LNAnimatedImageDecodeThread(target: self, selector: #selector(run), object: nil)
  47. thread.start()
  48. threads.append(thread)
  49. return thread
  50. }
  51. @objc private func run() {
  52. autoreleasepool {
  53. let runLoop = RunLoop.current
  54. runLoop.add(Port(), forMode: .default)
  55. while !Thread.current.isCancelled {
  56. runLoop.run(mode: .default, before: .distantFuture)
  57. }
  58. }
  59. }
  60. }
  61. @objcMembers
  62. public final class LNAnimatedImageBufferManager: NSObject {
  63. public var buffers: NSMutableArray
  64. private let config: LNAnimatedImageDecodeConfig
  65. public init(config: LNAnimatedImageDecodeConfig) {
  66. self.config = config
  67. self.buffers = NSMutableArray(capacity: max(config.bufferCount, 0))
  68. super.init()
  69. }
  70. @objc(getBufferedFrame:)
  71. public func getBufferedFrame(_ frameIndex: Int) -> LNBaseAnimatedImageFrame? {
  72. if buffers.count == 0 { return nil }
  73. let bufferIndex = frameIndex % buffers.count
  74. if bufferIndex > buffers.count - 1 { return nil }
  75. guard let frame = buffers.object(at: bufferIndex) as? LNBaseAnimatedImageFrame else { return nil }
  76. guard frame.frameIndex == frameIndex else { return nil }
  77. return frame
  78. }
  79. @objc(popVideoFrame)
  80. public func popVideoFrame() -> LNBaseAnimatedImageFrame? {
  81. guard buffers.count > 0 else { return nil }
  82. guard let frame = buffers.firstObject as? LNBaseAnimatedImageFrame else { return nil }
  83. buffers.removeObject(at: 0)
  84. return frame
  85. }
  86. @objc(isBufferFull)
  87. public func isBufferFull() -> Bool {
  88. if buffers.count < config.bufferCount { return false }
  89. for case let obj in buffers {
  90. if !(obj is LNBaseAnimatedImageFrame) {
  91. return false
  92. }
  93. }
  94. return true
  95. }
  96. }
  97. @objc public protocol LNAnimatedImageDecoderDelegate: AnyObject {
  98. @objc(decoderClassForManager:)
  99. func decoderClass(for manager: LNAnimatedImageDecodeManager) -> AnyClass
  100. @objc optional func shouldSetupAudioPlayer() -> Bool
  101. @objc optional func decoderDidFinishDecode(_ decoder: LNBaseDecoder)
  102. @objc optional func decoderDidFailDecode(_ decoder: LNBaseDecoder?, error: NSError)
  103. }
  104. public let kLNVAPDecoderSeekStart = "kLNVAPDecoderSeekStart"
  105. public let kLNVAPDecoderSeekFinish = "kLNVAPDecoderSeekFinish"
  106. public let kQGVAPDecoderSeekStart = kLNVAPDecoderSeekStart
  107. public let kQGVAPDecoderSeekFinish = kLNVAPDecoderSeekFinish
  108. @objcMembers
  109. open class LNBaseDecoder: NSObject {
  110. public dynamic var currentDecodeFrame: Int = -1
  111. public private(set) var fileInfo: LNBaseDFileInfo
  112. public var initializationError: NSError?
  113. public required init(fileInfo: LNBaseDFileInfo) {
  114. self.fileInfo = fileInfo
  115. super.init()
  116. self.fileInfo.occupiedCount += 1
  117. }
  118. @objc(initWith:error:)
  119. public convenience init(fileInfo: LNBaseDFileInfo, error: NSErrorPointer) {
  120. self.init(fileInfo: fileInfo)
  121. if let err = initializationError {
  122. error?.pointee = err
  123. }
  124. }
  125. open func decodeFrame(_ frameIndex: Int, buffers: NSMutableArray) {}
  126. open func shouldStopDecode(_ nextFrameIndex: Int) -> Bool { false }
  127. open func isFrameIndexBeyondEnd(_ frameIndex: Int) -> Bool { false }
  128. }
  129. @objc public enum LNMP4HWDErrorCode: Int {
  130. case fileNotExist = 10000
  131. case invalidMP4File = 10001
  132. case canNotGetStreamInfo = 10002
  133. case canNotGetStream = 10003
  134. case errorCreateVTBDesc = 10004
  135. case errorCreateVTBSession = 10005
  136. }
  137. @objcMembers
  138. public final class LNMP4FrameHWDecoder: LNBaseDecoder {
  139. public static let errorDomain = "LNMP4HWDErrorDomain"
  140. private var buffers: NSMutableArray?
  141. private var parser: LNMP4ParserProxy?
  142. private var decodeQueue = DispatchQueue(label: "com.ln.vap.decode")
  143. private var decodeSession: VTDecompressionSession?
  144. private var formatDescription: CMFormatDescription?
  145. private var isFinish = false
  146. private var finishFrameIndex: Int = -1
  147. private var invalidRetryCount = 0
  148. private var spsData: Data?
  149. private var ppsData: Data?
  150. private var vpsData: Data?
  151. private var lastDecodeFrame: Int = -1
  152. @objc(errorDescriptionForCode:)
  153. public static func errorDescription(for code: LNMP4HWDErrorCode) -> String {
  154. switch code {
  155. case .fileNotExist: return "文件不存在"
  156. case .invalidMP4File: return "非法文件格式"
  157. case .canNotGetStreamInfo: return "无法获取视频流信息"
  158. case .canNotGetStream: return "无法获取视频流"
  159. case .errorCreateVTBDesc: return "VTB创建desc失败"
  160. case .errorCreateVTBSession: return "VTB创建session失败"
  161. }
  162. }
  163. public required init(fileInfo: LNBaseDFileInfo) {
  164. super.init(fileInfo: fileInfo)
  165. parser = (fileInfo as? LNMP4HWDFileInfo)?.mp4Parser
  166. if !onInputStart() {
  167. isFinish = true
  168. }
  169. }
  170. public override func decodeFrame(_ frameIndex: Int, buffers: NSMutableArray) {
  171. if frameIndex == currentDecodeFrame { return }
  172. currentDecodeFrame = frameIndex
  173. self.buffers = buffers
  174. decodeQueue.async { [weak self] in
  175. guard let self else { return }
  176. if frameIndex != self.lastDecodeFrame + 1 { return }
  177. self.decodeFrameInner(frameIndex, drop: false)
  178. }
  179. }
  180. public override func shouldStopDecode(_ nextFrameIndex: Int) -> Bool {
  181. isFinish
  182. }
  183. public override func isFrameIndexBeyondEnd(_ frameIndex: Int) -> Bool {
  184. if finishFrameIndex > 0 {
  185. return frameIndex >= finishFrameIndex
  186. }
  187. return false
  188. }
  189. deinit {
  190. onInputEnd()
  191. fileInfo.occupiedCount -= 1
  192. }
  193. private func onInputStart() -> Bool {
  194. guard !fileInfo.filePath.isEmpty else {
  195. initializationError = NSError(domain: Self.errorDomain, code: LNMP4HWDErrorCode.fileNotExist.rawValue, userInfo: ["location": fileInfo.filePath])
  196. return false
  197. }
  198. guard FileManager.default.fileExists(atPath: fileInfo.filePath) else {
  199. initializationError = NSError(domain: Self.errorDomain, code: LNMP4HWDErrorCode.fileNotExist.rawValue, userInfo: ["location": fileInfo.filePath])
  200. return false
  201. }
  202. isFinish = false
  203. spsData = nil
  204. ppsData = nil
  205. vpsData = nil
  206. return initPPSAndSPS()
  207. }
  208. private func initPPSAndSPS() -> Bool {
  209. guard let parser else { return false }
  210. spsData = parser.spsData
  211. ppsData = parser.ppsData
  212. vpsData = parser.vpsData
  213. guard let spsData, let ppsData else {
  214. initializationError = NSError(domain: Self.errorDomain, code: LNMP4HWDErrorCode.canNotGetStreamInfo.rawValue, userInfo: ["location": fileInfo.filePath])
  215. return false
  216. }
  217. if parser.videoCodecID == .h264 {
  218. guard spsData.count > 0, ppsData.count > 0 else { return false }
  219. let spsPtr = spsData.withUnsafeBytes { $0.baseAddress!.assumingMemoryBound(to: UInt8.self) }
  220. let ppsPtr = ppsData.withUnsafeBytes { $0.baseAddress!.assumingMemoryBound(to: UInt8.self) }
  221. var parameterSetPointers: [UnsafePointer<UInt8>] = [spsPtr, ppsPtr]
  222. var parameterSetSizes: [Int] = [spsData.count, ppsData.count]
  223. let status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
  224. allocator: kCFAllocatorDefault,
  225. parameterSetCount: 2,
  226. parameterSetPointers: &parameterSetPointers,
  227. parameterSetSizes: &parameterSetSizes,
  228. nalUnitHeaderLength: 4,
  229. formatDescriptionOut: &formatDescription
  230. )
  231. if status != noErr {
  232. initializationError = NSError(domain: Self.errorDomain, code: LNMP4HWDErrorCode.errorCreateVTBDesc.rawValue, userInfo: ["location": fileInfo.filePath])
  233. return false
  234. }
  235. } else if parser.videoCodecID == .h265 {
  236. guard #available(iOS 11.0, *), let vpsData else {
  237. initializationError = NSError(domain: Self.errorDomain, code: LNMP4HWDErrorCode.canNotGetStreamInfo.rawValue, userInfo: ["location": fileInfo.filePath])
  238. return false
  239. }
  240. guard vpsData.count > 0, spsData.count > 0, ppsData.count > 0 else { return false }
  241. let vpsPtr = vpsData.withUnsafeBytes { $0.baseAddress!.assumingMemoryBound(to: UInt8.self) }
  242. let spsPtr = spsData.withUnsafeBytes { $0.baseAddress!.assumingMemoryBound(to: UInt8.self) }
  243. let ppsPtr = ppsData.withUnsafeBytes { $0.baseAddress!.assumingMemoryBound(to: UInt8.self) }
  244. var parameterSetPointers: [UnsafePointer<UInt8>] = [vpsPtr, spsPtr, ppsPtr]
  245. var parameterSetSizes: [Int] = [vpsData.count, spsData.count, ppsData.count]
  246. let status = CMVideoFormatDescriptionCreateFromHEVCParameterSets(
  247. allocator: kCFAllocatorDefault,
  248. parameterSetCount: 3,
  249. parameterSetPointers: &parameterSetPointers,
  250. parameterSetSizes: &parameterSetSizes,
  251. nalUnitHeaderLength: 4,
  252. extensions: nil,
  253. formatDescriptionOut: &formatDescription
  254. )
  255. if status != noErr {
  256. initializationError = NSError(domain: Self.errorDomain, code: LNMP4HWDErrorCode.errorCreateVTBDesc.rawValue, userInfo: ["location": fileInfo.filePath])
  257. return false
  258. }
  259. }
  260. return createDecompressionSession()
  261. }
  262. private func createDecompressionSession() -> Bool {
  263. guard let formatDescription else { return false }
  264. let pixelFormat: UInt32 = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
  265. let attrs = [
  266. kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: pixelFormat)
  267. ] as NSDictionary
  268. let status = VTDecompressionSessionCreate(
  269. allocator: kCFAllocatorDefault,
  270. formatDescription: formatDescription,
  271. decoderSpecification: nil,
  272. imageBufferAttributes: attrs,
  273. outputCallback: nil,
  274. decompressionSessionOut: &decodeSession
  275. )
  276. if status != noErr {
  277. initializationError = NSError(domain: Self.errorDomain, code: LNMP4HWDErrorCode.errorCreateVTBSession.rawValue, userInfo: ["location": fileInfo.filePath])
  278. return false
  279. }
  280. return true
  281. }
  282. private func decodeFrameInner(_ frameIndex: Int, drop: Bool) {
  283. guard !isFinish else { return }
  284. guard let buffers else { return }
  285. guard let parser else { return }
  286. guard let _ = spsData, let _ = ppsData else { return }
  287. guard let packetData = parser.readPacket(ofSample: frameIndex), !packetData.isEmpty else {
  288. finishFrameIndex = frameIndex
  289. onInputEnd()
  290. return
  291. }
  292. let currentPts: UInt64 = {
  293. guard frameIndex >= 0, frameIndex < parser.videoSamples.count else { return 0 }
  294. return parser.videoSamples[frameIndex].pts
  295. }()
  296. let startDate = Date()
  297. var blockBuffer: CMBlockBuffer?
  298. var status = CMBlockBufferCreateWithMemoryBlock(
  299. allocator: kCFAllocatorDefault,
  300. memoryBlock: nil,
  301. blockLength: packetData.count,
  302. blockAllocator: kCFAllocatorDefault,
  303. customBlockSource: nil,
  304. offsetToData: 0,
  305. dataLength: packetData.count,
  306. flags: 0,
  307. blockBufferOut: &blockBuffer
  308. )
  309. guard status == kCMBlockBufferNoErr, let blockBuffer else { return }
  310. packetData.withUnsafeBytes { rawPtr in
  311. if let base = rawPtr.baseAddress {
  312. CMBlockBufferReplaceDataBytes(with: base, blockBuffer: blockBuffer, offsetIntoDestination: 0, dataLength: packetData.count)
  313. }
  314. }
  315. var sampleBuffer: CMSampleBuffer?
  316. var sampleSizeArray = [packetData.count]
  317. status = CMSampleBufferCreateReady(
  318. allocator: kCFAllocatorDefault,
  319. dataBuffer: blockBuffer,
  320. formatDescription: formatDescription,
  321. sampleCount: 1,
  322. sampleTimingEntryCount: 0,
  323. sampleTimingArray: nil,
  324. sampleSizeEntryCount: 1,
  325. sampleSizeArray: &sampleSizeArray,
  326. sampleBufferOut: &sampleBuffer
  327. )
  328. guard status == noErr, let sampleBuffer, let decodeSession else { return }
  329. var flagOut: VTDecodeInfoFlags = []
  330. let decodeStatus = VTDecompressionSessionDecodeFrame(
  331. decodeSession,
  332. sampleBuffer: sampleBuffer,
  333. flags: [],
  334. infoFlagsOut: &flagOut
  335. ) { [weak self] status, _, imageBuffer, _, _ in
  336. guard let self else { return }
  337. self.handleDecodePixelBuffer(
  338. imageBuffer,
  339. frameIndex: frameIndex,
  340. currentPts: currentPts,
  341. startDate: startDate,
  342. status: status,
  343. needDrop: drop,
  344. buffers: buffers,
  345. fps: parser.fps
  346. )
  347. }
  348. if decodeStatus == kVTInvalidSessionErr {
  349. invalidRetryCount += 1
  350. if invalidRetryCount >= 3 { return }
  351. resetDecoder()
  352. findKeyFrameAndDecodeToCurrent(frameIndex)
  353. } else {
  354. invalidRetryCount = 0
  355. }
  356. }
  357. private func handleDecodePixelBuffer(
  358. _ pixelBuffer: CVImageBuffer?,
  359. frameIndex: Int,
  360. currentPts: UInt64,
  361. startDate: Date,
  362. status: OSStatus,
  363. needDrop: Bool,
  364. buffers: NSMutableArray,
  365. fps: Int
  366. ) {
  367. lastDecodeFrame = frameIndex
  368. if status != noErr { return }
  369. if needDrop { return }
  370. guard let pixelBuffer = pixelBuffer else { return }
  371. let newFrame = LNMP4AnimatedImageFrame()
  372. newFrame.pixelBuffer = pixelBuffer
  373. newFrame.frameIndex = frameIndex
  374. newFrame.decodeTime = Date().timeIntervalSince(startDate) * 1000.0
  375. newFrame.defaultFps = Int32(fps)
  376. newFrame.pts = currentPts
  377. buffers.add(newFrame)
  378. let sorted = (buffers as? [LNMP4AnimatedImageFrame] ?? []).sorted { $0.pts < $1.pts }
  379. buffers.removeAllObjects()
  380. sorted.forEach { buffers.add($0) }
  381. }
  382. private func resetDecoder() {
  383. if let decodeSession {
  384. VTDecompressionSessionWaitForAsynchronousFrames(decodeSession)
  385. VTDecompressionSessionInvalidate(decodeSession)
  386. self.decodeSession = nil
  387. }
  388. _ = createDecompressionSession()
  389. }
  390. private func findKeyFrameAndDecodeToCurrent(_ frameIndex: Int) {
  391. NotificationCenter.default.post(name: NSNotification.Name(kLNVAPDecoderSeekStart), object: self)
  392. guard let parser else { return }
  393. let keyframeIndexes = parser.videoSyncSampleIndexes
  394. var index = keyframeIndexes.first?.intValue ?? 0
  395. for num in keyframeIndexes {
  396. if num.intValue < frameIndex {
  397. index = num.intValue
  398. continue
  399. }
  400. break
  401. }
  402. while index < frameIndex {
  403. decodeFrameInner(index, drop: true)
  404. index += 1
  405. }
  406. decodeFrameInner(frameIndex, drop: false)
  407. NotificationCenter.default.post(name: NSNotification.Name(kLNVAPDecoderSeekFinish), object: self)
  408. }
  409. private func onInputEnd() {
  410. if isFinish { return }
  411. isFinish = true
  412. if let decodeSession {
  413. VTDecompressionSessionWaitForAsynchronousFrames(decodeSession)
  414. VTDecompressionSessionInvalidate(decodeSession)
  415. self.decodeSession = nil
  416. }
  417. formatDescription = nil
  418. spsData = nil
  419. ppsData = nil
  420. vpsData = nil
  421. }
  422. }
  423. @objcMembers
  424. public final class LNAnimatedImageDecodeManager: NSObject {
  425. public weak var decoderDelegate: LNAnimatedImageDecoderDelegate?
  426. private let config: LNAnimatedImageDecodeConfig
  427. private let fileInfo: LNBaseDFileInfo
  428. private var decoders: [LNBaseDecoder] = []
  429. private var bufferManager: LNAnimatedImageBufferManager
  430. private var audioPlayer: AVAudioPlayer?
  431. public init(fileInfo: LNBaseDFileInfo, config: LNAnimatedImageDecodeConfig, delegate: LNAnimatedImageDecoderDelegate?) {
  432. self.fileInfo = fileInfo
  433. self.config = config
  434. self.decoderDelegate = delegate
  435. self.bufferManager = LNAnimatedImageBufferManager(config: config)
  436. super.init()
  437. createDecoders(by: config)
  438. initializeBuffers(fromIndex: 0)
  439. setupAudioPlayerIfNeed()
  440. }
  441. @objc(initWith:config:delegate:)
  442. public convenience init(_ fileInfo: LNBaseDFileInfo, config: LNAnimatedImageDecodeConfig, delegate: LNAnimatedImageDecoderDelegate?) {
  443. self.init(fileInfo: fileInfo, config: config, delegate: delegate)
  444. }
  445. @objc(consumeDecodedFrame:)
  446. public func consumeDecodedFrame(_ frameIndex: Int) -> LNBaseAnimatedImageFrame? {
  447. objc_sync_enter(self)
  448. defer { objc_sync_exit(self) }
  449. if frameIndex == 0 && bufferManager.buffers.count < config.bufferCount {
  450. return nil
  451. }
  452. let decodeFinished = checkIfDecodeFinish(frameIndex)
  453. let frame = bufferManager.popVideoFrame()
  454. if let frame {
  455. frame.frameIndex = frameIndex
  456. decodeFrame(frameIndex + config.bufferCount)
  457. return frame
  458. }
  459. if !decodeFinished {
  460. guard !decoders.isEmpty else { return nil }
  461. let decoderIndex = decoders.count == 1 ? 0 : frameIndex % decoders.count
  462. let decoder = decoders[decoderIndex]
  463. if decoder.shouldStopDecode(frameIndex) {
  464. decoderDelegate?.decoderDidFinishDecode?(decoder)
  465. return nil
  466. }
  467. initializeBuffers(fromIndex: frameIndex)
  468. }
  469. return nil
  470. }
  471. @objc(tryToStartAudioPlay)
  472. public func tryToStartAudioPlay() {
  473. audioPlayer?.play()
  474. }
  475. @objc(tryToStopAudioPlay)
  476. public func tryToStopAudioPlay() {
  477. audioPlayer?.stop()
  478. }
  479. @objc(tryToPauseAudioPlay)
  480. public func tryToPauseAudioPlay() {
  481. audioPlayer?.pause()
  482. }
  483. @objc(tryToResumeAudioPlay)
  484. public func tryToResumeAudioPlay() {
  485. audioPlayer?.play()
  486. }
  487. @objc(containsThisDeocder:)
  488. public func containsThisDeocder(_ decoder: Any) -> Bool {
  489. decoders.contains { $0 === (decoder as AnyObject) }
  490. }
  491. private func checkIfDecodeFinish(_ frameIndex: Int) -> Bool {
  492. guard !decoders.isEmpty else { return true }
  493. let decoderIndex = decoders.count == 1 ? 0 : frameIndex % decoders.count
  494. let decoder = decoders[decoderIndex]
  495. if decoder.isFrameIndexBeyondEnd(frameIndex) {
  496. decoderDelegate?.decoderDidFinishDecode?(decoder)
  497. return true
  498. }
  499. return false
  500. }
  501. private func decodeFrame(_ frameIndex: Int) {
  502. guard !decoders.isEmpty else { return }
  503. let decoderIndex = decoders.count == 1 ? 0 : frameIndex % decoders.count
  504. let decoder = decoders[decoderIndex]
  505. if decoder.shouldStopDecode(frameIndex) { return }
  506. decoder.decodeFrame(frameIndex, buffers: bufferManager.buffers)
  507. }
  508. private func createDecoders(by config: LNAnimatedImageDecodeConfig) {
  509. guard let decoderDelegate else { return }
  510. for _ in 0..<max(config.threadCount, 1) {
  511. guard let cls = decoderDelegate.decoderClass(for: self) as? LNBaseDecoder.Type else { continue }
  512. let decoder = cls.init(fileInfo: fileInfo)
  513. if let error = decoder.initializationError {
  514. decoderDelegate.decoderDidFailDecode?(nil, error: error)
  515. break
  516. }
  517. decoders.append(decoder)
  518. }
  519. }
  520. private func initializeBuffers(fromIndex start: Int) {
  521. for i in 0..<max(config.bufferCount, 0) {
  522. decodeFrame(start + i)
  523. }
  524. }
  525. private func setupAudioPlayerIfNeed() {
  526. if decoderDelegate?.shouldSetupAudioPlayer?() == false {
  527. return
  528. }
  529. guard let fileInfo = fileInfo as? LNMP4HWDFileInfo,
  530. let mp4Parser = fileInfo.mp4Parser,
  531. mp4Parser.audioTrackBox != nil else {
  532. audioPlayer = nil
  533. return
  534. }
  535. let url = URL(fileURLWithPath: self.fileInfo.filePath)
  536. audioPlayer = try? AVAudioPlayer(contentsOf: url)
  537. }
  538. }
  539. @objc public protocol LNVAPConfigDelegate: AnyObject {
  540. @objc(onVAPConfigResourcesLoaded:error:)
  541. func onVAPConfigResourcesLoaded(_ config: LNVAPConfigModel, error: NSError?)
  542. @objc optional func vap_contentForTag(_ tag: String, resource: LNVAPSourceInfo) -> String?
  543. @objc optional func vap_loadImageWithURL(_ urlStr: String, context: [AnyHashable: Any], completion: @escaping (UIImage?, NSError?, String) -> Void)
  544. }
  545. @objcMembers
  546. public final class LNVAPConfigManager: NSObject {
  547. public weak var delegate: LNVAPConfigDelegate?
  548. public var hasValidConfig: Bool = false
  549. public var model: LNVAPConfigModel = .init()
  550. private let fileInfo: LNMP4HWDFileInfo
  551. public init(fileInfo: LNMP4HWDFileInfo) {
  552. self.fileInfo = fileInfo
  553. super.init()
  554. setupConfig()
  555. }
  556. @objc(initWith:)
  557. public convenience init(_ fileInfo: LNMP4HWDFileInfo) {
  558. self.init(fileInfo: fileInfo)
  559. }
  560. @objc(loadConfigResources)
  561. public func loadConfigResources() {
  562. if model.resources.isEmpty {
  563. delegate?.onVAPConfigResourcesLoaded(model, error: nil)
  564. return
  565. }
  566. if let contentProvider = delegate?.vap_contentForTag {
  567. model.resources.forEach { resource in
  568. resource.contentTagValue = contentProvider(resource.contentTag ?? "", resource)
  569. }
  570. }
  571. guard let loadImage = delegate?.vap_loadImageWithURL else {
  572. // Keep parity with OC: if no image loader delegate, return directly.
  573. return
  574. }
  575. let group = DispatchGroup()
  576. var loadError: NSError?
  577. for resource in model.resources {
  578. guard resource.type == LNVAPAttachmentConstants.sourceTypeImg,
  579. resource.loadType == LNVAPAttachmentConstants.sourceLoadTypeNet,
  580. let url = resource.contentTagValue else {
  581. if resource.type == LNVAPAttachmentConstants.sourceTypeText,
  582. resource.loadType == LNVAPAttachmentConstants.sourceLoadTypeLocal {
  583. resource.sourceImage = LNVAPTextureLoader.drawingImage(
  584. forText: resource.contentTagValue,
  585. color: resource.color,
  586. size: resource.size,
  587. bold: resource.style == LNVAPAttachmentConstants.sourceStyleBoldText
  588. )
  589. }
  590. continue
  591. }
  592. group.enter()
  593. loadImage(url, ["resource": resource]) { image, error, imageURL in
  594. if image == nil || error != nil {
  595. loadError = loadError ?? error ?? NSError(domain: "loadImageError:\(imageURL)", code: -1)
  596. }
  597. resource.sourceImage = image
  598. group.leave()
  599. }
  600. }
  601. group.notify(queue: .main) { [weak self] in
  602. guard let self else { return }
  603. self.delegate?.onVAPConfigResourcesLoaded(self.model, error: loadError)
  604. }
  605. }
  606. @objc(loadMTLTextures:)
  607. public func loadMTLTextures(_ device: MTLDevice) {
  608. model.resources.forEach { resource in
  609. resource.texture = LNVAPTextureLoader.loadTexture(with: resource.sourceImage, device: device)
  610. resource.sourceImage = nil
  611. }
  612. }
  613. @objc(loadMTLBuffers:)
  614. public func loadMTLBuffers(_ device: MTLDevice) {
  615. model.resources.forEach { resource in
  616. resource.colorParamsBuffer = LNVAPTextureLoader.loadVapColorFillBuffer(with: resource.color, device: device)
  617. }
  618. }
  619. private func setupConfig() {
  620. guard let vapc = fileInfo.mp4Parser?.rootBox?.subBox(ofType: .vapc) else {
  621. hasValidConfig = false
  622. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "config can not find vapc box")
  623. return
  624. }
  625. hasValidConfig = true
  626. guard let vapcData = fileInfo.mp4Parser?.readData(of: vapc, length: Int(vapc.length - 8), offset: 8),
  627. let dict = (try? JSONSerialization.jsonObject(with: vapcData)) as? [String: Any] else {
  628. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "fail to parse config as dictionary")
  629. return
  630. }
  631. parseConfigDictionary(dict)
  632. }
  633. private func parseConfigDictionary(_ configDic: [String: Any]) {
  634. guard let commonInfoDic = lnDictionary(configDic["info"]) else {
  635. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "has no commonInfoDic")
  636. return
  637. }
  638. let sourcesArr = lnArray(configDic["src"])
  639. let framesArr = lnArray(configDic["frame"])
  640. let configModel = LNVAPConfigModel()
  641. self.model = configModel
  642. let commonInfo = LNVAPCommonInfo()
  643. commonInfo.version = lnInt(commonInfoDic["v"])
  644. commonInfo.framesCount = lnInt(commonInfoDic["f"])
  645. commonInfo.size = CGSize(width: lnFloat(commonInfoDic["w"]), height: lnFloat(commonInfoDic["h"]))
  646. commonInfo.videoSize = CGSize(width: lnFloat(commonInfoDic["videoW"]), height: lnFloat(commonInfoDic["videoH"]))
  647. commonInfo.targetOrientaion = LNVAPOrientation(rawValue: lnInt(commonInfoDic["orien"])) ?? .none
  648. commonInfo.fps = lnInt(commonInfoDic["fps"])
  649. commonInfo.isMerged = lnInt(commonInfoDic["isVapx"]) == 1
  650. commonInfo.alphaAreaRect = lnRect(commonInfoDic["aFrame"])
  651. commonInfo.rgbAreaRect = lnRect(commonInfoDic["rgbFrame"])
  652. configModel.info = commonInfo
  653. fileInfo.mp4Parser?.fps = commonInfo.fps
  654. var sources: [String: LNVAPSourceInfo] = [:]
  655. for sourceObj in sourcesArr {
  656. guard let sourceDic = lnDictionary(sourceObj) else {
  657. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "sourceDic is not dic")
  658. continue
  659. }
  660. let sourceID = lnString(sourceDic["srcId"])
  661. guard !sourceID.isEmpty else {
  662. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "has no sourceID")
  663. continue
  664. }
  665. let sourceInfo = LNVAPSourceInfo()
  666. sourceInfo.type = lnString(sourceDic["srcType"])
  667. sourceInfo.loadType = lnString(sourceDic["loadType"])
  668. sourceInfo.contentTag = lnString(sourceDic["srcTag"])
  669. sourceInfo.style = lnString(sourceDic["style"])
  670. sourceInfo.fitType = lnString(sourceDic["fitType"])
  671. sourceInfo.size = CGSize(width: lnFloat(sourceDic["w"]), height: lnFloat(sourceDic["h"]))
  672. let colorHex = lnString(sourceDic["color"])
  673. if !colorHex.isEmpty {
  674. sourceInfo.color = UIColor.lnColor(hex: colorHex)
  675. }
  676. sources[sourceID] = sourceInfo
  677. }
  678. configModel.resources = Array(sources.values)
  679. var mergedConfig: [NSNumber: [LNVAPMergedInfo]] = [:]
  680. for frameObj in framesArr {
  681. guard let frameMergedDic = lnDictionary(frameObj) else {
  682. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "frameMergedDic is not dic")
  683. continue
  684. }
  685. let frameIndex = lnInt(frameMergedDic["i"])
  686. let mergedObjs = lnArray(frameMergedDic["obj"])
  687. var mergedInfos: [LNVAPMergedInfo] = []
  688. for mergeObj in mergedObjs {
  689. guard let mergeInfoDic = lnDictionary(mergeObj) else {
  690. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "mergeInfoDic is not dic")
  691. continue
  692. }
  693. let sourceID = lnString(mergeInfoDic["srcId"])
  694. guard let sourceInfo = sources[sourceID] else {
  695. LNVAPLogger.log(level: 3, file: #file, line: #line, func: #function, module: "LNVAPConfigManager", message: "sourceInfo is nil")
  696. continue
  697. }
  698. let mergeInfo = LNVAPMergedInfo()
  699. mergeInfo.source = sourceInfo
  700. mergeInfo.renderIndex = lnInt(mergeInfoDic["z"])
  701. mergeInfo.needMask = mergeInfoDic["mFrame"] != nil
  702. mergeInfo.renderRect = lnRect(mergeInfoDic["frame"])
  703. mergeInfo.maskRect = lnRect(mergeInfoDic["mFrame"])
  704. mergeInfo.maskRotation = lnInt(mergeInfoDic["mt"])
  705. mergedInfos.append(mergeInfo)
  706. }
  707. mergedConfig[NSNumber(value: frameIndex)] = mergedInfos.sorted(by: { $0.renderIndex < $1.renderIndex })
  708. }
  709. configModel.mergedConfig = mergedConfig
  710. }
  711. }
  712. private extension LNVAPConfigManager {
  713. func lnInt(_ value: Any?) -> Int {
  714. if let n = value as? NSNumber { return n.intValue }
  715. if let s = value as? NSString { return s.integerValue }
  716. if let s = value as? String { return Int(s) ?? 0 }
  717. return 0
  718. }
  719. func lnFloat(_ value: Any?) -> CGFloat {
  720. if let n = value as? NSNumber { return CGFloat(n.doubleValue) }
  721. if let s = value as? NSString { return CGFloat(s.doubleValue) }
  722. if let s = value as? String { return CGFloat(Double(s) ?? 0) }
  723. return 0
  724. }
  725. func lnString(_ value: Any?) -> String {
  726. if let s = value as? String { return s }
  727. if let s = value as? NSString { return s as String }
  728. if let n = value as? NSNumber { return n.stringValue }
  729. return ""
  730. }
  731. func lnArray(_ value: Any?) -> [Any] {
  732. if let arr = value as? [Any] { return arr }
  733. if let arr = value as? NSArray { return arr.compactMap { $0 } }
  734. return []
  735. }
  736. func lnDictionary(_ value: Any?) -> [String: Any]? {
  737. if let dict = value as? [String: Any] { return dict }
  738. if let dict = value as? NSDictionary {
  739. var out: [String: Any] = [:]
  740. for (k, v) in dict {
  741. if let key = k as? String {
  742. out[key] = v
  743. }
  744. }
  745. return out
  746. }
  747. return nil
  748. }
  749. func lnRect(_ value: Any?) -> CGRect {
  750. if let arr = value as? NSArray {
  751. return arr.ln_rectValue()
  752. }
  753. if let arr = value as? [Any] {
  754. return (arr as NSArray).ln_rectValue()
  755. }
  756. return .zero
  757. }
  758. }
  759. private extension UIColor {
  760. static func lnColor(hex: String) -> UIColor? {
  761. let cleaned = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
  762. var int: UInt64 = 0
  763. guard Scanner(string: cleaned).scanHexInt64(&int) else { return nil }
  764. let a, r, g, b: UInt64
  765. switch cleaned.count {
  766. case 3:
  767. (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
  768. case 6:
  769. (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
  770. case 8:
  771. (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
  772. default:
  773. return nil
  774. }
  775. return UIColor(
  776. red: CGFloat(r) / 255,
  777. green: CGFloat(g) / 255,
  778. blue: CGFloat(b) / 255,
  779. alpha: CGFloat(a) / 255
  780. )
  781. }
  782. }