LNVAPPlayerView.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. import UIKit
  2. import CoreVideo
  3. @objcMembers
  4. public final class LNVAPPlayerView: UIView {
  5. private let playbackView = UIView()
  6. private let delegateBridge = LNPlayerDelegateBridge()
  7. private var didStart = false
  8. public weak var delegate: LNVAPPlaybackDelegate?
  9. public var enterBackgroundOperation: LNEnterBackgroundOperation = .stop {
  10. didSet { LNPlaybackRuntime.set(playbackView, key: "hwd_enterBackgroundOP", value: enterBackgroundOperation.legacyRawValue) }
  11. }
  12. public var renderByOpenGL: Bool {
  13. get { LNPlaybackRuntime.boolValue(playbackView, key: "hwd_renderByOpenGL") }
  14. set { LNPlaybackRuntime.set(playbackView, key: "hwd_renderByOpenGL", value: newValue) }
  15. }
  16. public var fps: Int {
  17. get { LNPlaybackRuntime.intValue(playbackView, key: "hwd_fps") }
  18. set { LNPlaybackRuntime.set(playbackView, key: "hwd_fps", value: newValue) }
  19. }
  20. public override init(frame: CGRect) {
  21. super.init(frame: frame)
  22. delegateBridge.owner = self
  23. setupPlaybackView()
  24. }
  25. public required init?(coder: NSCoder) {
  26. super.init(coder: coder)
  27. delegateBridge.owner = self
  28. setupPlaybackView()
  29. }
  30. public override func layoutSubviews() {
  31. super.layoutSubviews()
  32. playbackView.frame = bounds
  33. }
  34. @objc(lnPlayWithFilePath:repeatCount:)
  35. public func lnPlay(filePath: String, repeatCount: Int) {
  36. didStart = false
  37. let played = LNPlaybackRuntime.playHWDMP4(playbackView, filePath: filePath, repeatCount: repeatCount, delegate: delegateBridge)
  38. if !played {
  39. let error = NSError(domain: "LNVAPPlayerView", code: -1, userInfo: [NSLocalizedDescriptionKey: "Missing runtime selector playHWDMP4:repeatCount:delegate:"])
  40. delegate?.lnPlayerDidFail?(self, error: error)
  41. }
  42. }
  43. @objc(lnPlayWithFilePath:)
  44. public func lnPlay(filePath: String) {
  45. lnPlay(filePath: filePath, repeatCount: 0)
  46. }
  47. @objc(lnStop)
  48. public func lnStop() {
  49. LNPlaybackRuntime.callNoArg(playbackView, selectorName: "stopHWDMP4")
  50. notifyStopIfNeeded()
  51. }
  52. @objc(lnPause)
  53. public func lnPause() {
  54. LNPlaybackRuntime.callNoArg(playbackView, selectorName: "pauseHWDMP4")
  55. }
  56. @objc(lnResume)
  57. public func lnResume() {
  58. LNPlaybackRuntime.callNoArg(playbackView, selectorName: "resumeHWDMP4")
  59. }
  60. @objc(lnSetMute:)
  61. public func lnSetMute(_ mute: Bool) {
  62. LNPlaybackRuntime.callBool(playbackView, selectorName: "setMute:", value: mute)
  63. }
  64. @objc(lnEnableOldVersion:)
  65. public func lnEnableOldVersion(_ enable: Bool) {
  66. LNPlaybackRuntime.callBool(playbackView, selectorName: "enableOldVersion:", value: enable)
  67. }
  68. @objc(lnAddTapGestureWithTarget:action:)
  69. public func lnAddTapGesture(target: Any, action: Selector) {
  70. let tap = UITapGestureRecognizer(target: target, action: action)
  71. playbackView.addGestureRecognizer(tap)
  72. playbackView.isUserInteractionEnabled = true
  73. }
  74. @objc(lnAddVapTapGesture:)
  75. public func lnAddVapTapGesture(_ handler: @escaping LNVAPGestureEventBlock) {
  76. let added = LNPlaybackRuntime.addVapTapGesture(playbackView) { gesture, insideSource, source in
  77. handler(gesture, insideSource, source)
  78. }
  79. if !added {
  80. let tap = UITapGestureRecognizer()
  81. tap.ln_addActionBlock { sender in
  82. guard let gesture = sender as? UIGestureRecognizer else { return }
  83. handler(gesture, false, nil)
  84. }
  85. playbackView.addGestureRecognizer(tap)
  86. playbackView.isUserInteractionEnabled = true
  87. }
  88. }
  89. @objc(lnAddVapGesture:callback:)
  90. public func lnAddVapGesture(_ gestureRecognizer: UIGestureRecognizer, callback: @escaping LNVAPGestureEventBlock) {
  91. let added = LNPlaybackRuntime.addVapGesture(playbackView, gestureRecognizer: gestureRecognizer) { gesture, insideSource, source in
  92. callback(gesture, insideSource, source)
  93. }
  94. if !added {
  95. gestureRecognizer.ln_addActionBlock { sender in
  96. guard let gesture = sender as? UIGestureRecognizer else { return }
  97. callback(gesture, false, nil)
  98. }
  99. playbackView.addGestureRecognizer(gestureRecognizer)
  100. playbackView.isUserInteractionEnabled = true
  101. }
  102. }
  103. fileprivate func notifyShouldStart(_ config: LNVAPConfigModel) -> Bool {
  104. delegate?.lnPlayerShouldStart?(self, config: config) ?? true
  105. }
  106. fileprivate func notifyStart() {
  107. didStart = true
  108. delegate?.lnPlayerDidStart?(self)
  109. }
  110. fileprivate func notifyPlay(_ frame: LNMP4AnimatedImageFrame) {
  111. delegate?.lnPlayerDidPlay?(self, frame: frame)
  112. }
  113. fileprivate func notifyFinish(_ totalFrameCount: Int) {
  114. delegate?.lnPlayerDidFinish?(self, totalFrameCount: totalFrameCount)
  115. }
  116. fileprivate func notifyFail(_ error: NSError) {
  117. delegate?.lnPlayerDidFail?(self, error: error)
  118. }
  119. fileprivate func contentForTag(_ tag: String, resource: LNVAPSourceInfo) -> String? {
  120. delegate?.lnPlayerContent?(forTag: tag, resource: resource)
  121. }
  122. fileprivate func loadImage(withURL url: String, context: NSDictionary, completion: @escaping LNVAPImageCompletion) {
  123. if let handler = delegate?.lnPlayerLoadImage {
  124. handler(url, context, completion)
  125. return
  126. }
  127. completion(nil, nil, url)
  128. }
  129. fileprivate func notifyStopIfNeeded() {
  130. if didStart {
  131. delegate?.lnPlayerDidStop?(self)
  132. }
  133. didStart = false
  134. }
  135. private func setupPlaybackView() {
  136. playbackView.frame = bounds
  137. playbackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  138. addSubview(playbackView)
  139. LNPlaybackRuntime.set(playbackView, key: "hwd_enterBackgroundOP", value: enterBackgroundOperation.legacyRawValue)
  140. }
  141. }
  142. private final class LNPlayerDelegateBridge: NSObject {
  143. weak var owner: LNVAPPlayerView?
  144. @objc(shouldStartPlayMP4:config:)
  145. func shouldStartPlayMP4(_ container: UIView, config: NSObject) -> Bool {
  146. guard let owner else { return true }
  147. return owner.notifyShouldStart(LNPlaybackLegacyMapper.toConfig(config))
  148. }
  149. @objc(viewDidStartPlayMP4:)
  150. func viewDidStartPlayMP4(_ container: UIView) {
  151. owner?.notifyStart()
  152. }
  153. @objc(viewDidPlayMP4AtFrame:view:)
  154. func viewDidPlayMP4AtFrame(_ frame: NSObject, view container: UIView) {
  155. owner?.notifyPlay(LNPlaybackLegacyMapper.toFrame(frame))
  156. }
  157. @objc(viewDidStopPlayMP4:view:)
  158. func viewDidStopPlayMP4(_ lastFrameIndex: Int, view container: UIView) {
  159. owner?.notifyStopIfNeeded()
  160. }
  161. @objc(viewDidFinishPlayMP4:view:)
  162. func viewDidFinishPlayMP4(_ totalFrameCount: Int, view container: UIView) {
  163. owner?.notifyFinish(totalFrameCount)
  164. }
  165. @objc(viewDidFailPlayMP4:)
  166. func viewDidFailPlayMP4(_ error: NSError) {
  167. owner?.notifyFail(error)
  168. }
  169. @objc(contentForVapTag:resource:)
  170. func contentForVapTag(_ tag: String, resource: NSObject) -> String? {
  171. guard let owner else { return nil }
  172. return owner.contentForTag(tag, resource: LNPlaybackLegacyMapper.toSource(resource))
  173. }
  174. @objc(loadVapImageWithURL:context:completion:)
  175. func loadVapImageWithURL(_ url: String, context: NSDictionary, completion: @escaping (UIImage?, NSError?, String) -> Void) {
  176. owner?.loadImage(withURL: url, context: context, completion: completion)
  177. }
  178. }
  179. private enum LNPlaybackLegacyMapper {
  180. static func toConfig(_ object: NSObject) -> LNVAPConfigModel {
  181. if let config = object as? LNVAPConfigModel {
  182. return config
  183. }
  184. let config = LNVAPConfigModel()
  185. if let infoObj = object.value(forKey: "info") as? NSObject {
  186. config.info = toCommonInfo(infoObj)
  187. }
  188. if let resources = object.value(forKey: "resources") as? [NSObject] {
  189. config.resources = resources.map { toSource($0) }
  190. }
  191. return config
  192. }
  193. static func toCommonInfo(_ object: NSObject) -> LNVAPCommonInfo {
  194. let info = LNVAPCommonInfo()
  195. info.version = intValue(object, key: "version")
  196. info.framesCount = intValue(object, key: "framesCount")
  197. info.size = cgSizeValue(object, key: "size")
  198. info.videoSize = cgSizeValue(object, key: "videoSize")
  199. info.targetOrientaion = LNVAPOrientation(rawValue: intValue(object, key: "targetOrientaion")) ?? .none
  200. info.fps = intValue(object, key: "fps")
  201. info.isMerged = boolValue(object, key: "isMerged")
  202. info.alphaAreaRect = cgRectValue(object, key: "alphaAreaRect")
  203. info.rgbAreaRect = cgRectValue(object, key: "rgbAreaRect")
  204. return info
  205. }
  206. static func toSource(_ object: NSObject) -> LNVAPSourceInfo {
  207. if let source = object as? LNVAPSourceInfo {
  208. return source
  209. }
  210. let source = LNVAPSourceInfo()
  211. source.type = stringValue(object, key: "type")
  212. source.loadType = stringValue(object, key: "loadType")
  213. source.contentTag = stringValue(object, key: "contentTag")
  214. source.contentTagValue = stringValue(object, key: "contentTagValue")
  215. source.color = object.value(forKey: "color") as? UIColor
  216. source.style = stringValue(object, key: "style")
  217. source.size = cgSizeValue(object, key: "size")
  218. source.fitType = stringValue(object, key: "fitType")
  219. source.sourceImage = object.value(forKey: "sourceImage") as? UIImage
  220. return source
  221. }
  222. static func toFrame(_ object: NSObject) -> LNMP4AnimatedImageFrame {
  223. if let frame = object as? LNMP4AnimatedImageFrame {
  224. return frame
  225. }
  226. let frame = LNMP4AnimatedImageFrame()
  227. frame.frameIndex = intValue(object, key: "frameIndex")
  228. frame.duration = timeIntervalValue(object, key: "duration")
  229. frame.pts = uint64Value(object, key: "pts")
  230. frame.defaultFps = Int32(intValue(object, key: "defaultFps"))
  231. return frame
  232. }
  233. static func toSourceDisplayItem(_ object: AnyObject?) -> LNVAPSourceDisplayItem? {
  234. guard let object else { return nil }
  235. if let item = object as? LNVAPSourceDisplayItem {
  236. return item
  237. }
  238. guard let sourceObj = object as? NSObject else { return nil }
  239. let item = LNVAPSourceDisplayItem()
  240. item.frame = cgRectValue(sourceObj, key: "frame")
  241. if let sourceInfoObj = sourceObj.value(forKey: "sourceInfo") as? NSObject {
  242. item.sourceInfo = toSource(sourceInfoObj)
  243. }
  244. return item
  245. }
  246. private static func intValue(_ object: NSObject, key: String) -> Int {
  247. (object.value(forKey: key) as? NSNumber)?.intValue ?? 0
  248. }
  249. private static func uint64Value(_ object: NSObject, key: String) -> UInt64 {
  250. (object.value(forKey: key) as? NSNumber)?.uint64Value ?? 0
  251. }
  252. private static func timeIntervalValue(_ object: NSObject, key: String) -> TimeInterval {
  253. (object.value(forKey: key) as? NSNumber)?.doubleValue ?? 0
  254. }
  255. private static func boolValue(_ object: NSObject, key: String) -> Bool {
  256. (object.value(forKey: key) as? NSNumber)?.boolValue ?? false
  257. }
  258. private static func stringValue(_ object: NSObject, key: String) -> String? {
  259. object.value(forKey: key) as? String
  260. }
  261. private static func cgSizeValue(_ object: NSObject, key: String) -> CGSize {
  262. (object.value(forKey: key) as? NSValue)?.cgSizeValue ?? .zero
  263. }
  264. private static func cgRectValue(_ object: NSObject, key: String) -> CGRect {
  265. (object.value(forKey: key) as? NSValue)?.cgRectValue ?? .zero
  266. }
  267. }
  268. private enum LNPlaybackRuntime {
  269. private typealias LNObjCGestureBlock = @convention(block) (UIGestureRecognizer, Bool, AnyObject?) -> Void
  270. static func callNoArg(_ target: NSObject, selectorName: String) {
  271. let selector = NSSelectorFromString(selectorName)
  272. guard target.responds(to: selector) else { return }
  273. typealias Function = @convention(c) (AnyObject, Selector) -> Void
  274. let imp = target.method(for: selector)
  275. unsafeBitCast(imp, to: Function.self)(target, selector)
  276. }
  277. static func callBool(_ target: NSObject, selectorName: String, value: Bool) {
  278. let selector = NSSelectorFromString(selectorName)
  279. guard target.responds(to: selector) else { return }
  280. typealias Function = @convention(c) (AnyObject, Selector, Bool) -> Void
  281. let imp = target.method(for: selector)
  282. unsafeBitCast(imp, to: Function.self)(target, selector, value)
  283. }
  284. static func playHWDMP4(_ target: NSObject, filePath: String, repeatCount: Int, delegate: AnyObject?) -> Bool {
  285. let selector = NSSelectorFromString("playHWDMP4:repeatCount:delegate:")
  286. guard target.responds(to: selector) else { return false }
  287. typealias Function = @convention(c) (AnyObject, Selector, NSString, Int, AnyObject?) -> Void
  288. let imp = target.method(for: selector)
  289. unsafeBitCast(imp, to: Function.self)(target, selector, filePath as NSString, repeatCount, delegate)
  290. return true
  291. }
  292. static func addVapTapGesture(_ target: NSObject, callback: @escaping LNVAPGestureEventBlock) -> Bool {
  293. let selector = NSSelectorFromString("addVapTapGesture:")
  294. guard target.responds(to: selector) else { return false }
  295. let block: LNObjCGestureBlock = { gesture, insideSource, sourceObj in
  296. callback(gesture, insideSource, LNPlaybackLegacyMapper.toSourceDisplayItem(sourceObj))
  297. }
  298. typealias Function = @convention(c) (AnyObject, Selector, AnyObject) -> Void
  299. let imp = target.method(for: selector)
  300. unsafeBitCast(imp, to: Function.self)(target, selector, unsafeBitCast(block, to: AnyObject.self))
  301. return true
  302. }
  303. static func addVapGesture(_ target: NSObject, gestureRecognizer: UIGestureRecognizer, callback: @escaping LNVAPGestureEventBlock) -> Bool {
  304. let selector = NSSelectorFromString("addVapGesture:callback:")
  305. guard target.responds(to: selector) else { return false }
  306. let block: LNObjCGestureBlock = { gesture, insideSource, sourceObj in
  307. callback(gesture, insideSource, LNPlaybackLegacyMapper.toSourceDisplayItem(sourceObj))
  308. }
  309. typealias Function = @convention(c) (AnyObject, Selector, UIGestureRecognizer, AnyObject) -> Void
  310. let imp = target.method(for: selector)
  311. unsafeBitCast(imp, to: Function.self)(target, selector, gestureRecognizer, unsafeBitCast(block, to: AnyObject.self))
  312. return true
  313. }
  314. static func set(_ target: NSObject, key: String, value: Any) {
  315. let setter = setterSelectorName(for: key)
  316. let setterSelector = NSSelectorFromString(setter)
  317. guard target.responds(to: setterSelector) else { return }
  318. target.setValue(value, forKey: key)
  319. }
  320. static func intValue(_ target: NSObject, key: String) -> Int {
  321. let selector = NSSelectorFromString(key)
  322. guard target.responds(to: selector) else { return 0 }
  323. if let value = target.value(forKey: key) as? NSNumber { return value.intValue }
  324. return 0
  325. }
  326. static func boolValue(_ target: NSObject, key: String) -> Bool {
  327. let selector = NSSelectorFromString(key)
  328. guard target.responds(to: selector) else { return false }
  329. if let value = target.value(forKey: key) as? NSNumber { return value.boolValue }
  330. return false
  331. }
  332. private static func setterSelectorName(for key: String) -> String {
  333. guard let first = key.first else { return "" }
  334. return "set\(String(first).uppercased())\(key.dropFirst()):"
  335. }
  336. }