ShowAgoraKitManager.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. //
  2. // ShowAgoraKitManager.swift
  3. // AgoraEntScenarios
  4. //
  5. // Created by FanPengpeng on 2022/11/22.
  6. //
  7. import Foundation
  8. import AgoraRtcKit
  9. import UIKit
  10. import YYCategories
  11. import VideoLoaderAPI
  12. class ShowAgoraKitManager: NSObject {
  13. static let shared = ShowAgoraKitManager()
  14. private var videoLoader: IVideoLoaderApi?
  15. // 是否开启绿幕功能
  16. static var isOpenGreen: Bool = false
  17. static var isBlur: Bool = false
  18. public let rtcParam = ShowRTCParams()
  19. public var deviceLevel: DeviceLevel = .medium
  20. public var deviceScore: Int = 100
  21. public var netCondition: NetCondition = .good
  22. public var performanceMode: PerformanceMode = .smooth
  23. private var broadcasterConnection: AgoraRtcConnection?
  24. var exposureRangeX: Int?
  25. var exposureRangeY: Int?
  26. var matrixCoefficientsExt: Int?
  27. var videoFullrangeExt: Int?
  28. let encoderConfig = AgoraVideoEncoderConfiguration()
  29. public lazy var captureConfig: AgoraCameraCapturerConfiguration = {
  30. let config = AgoraCameraCapturerConfiguration()
  31. config.followEncodeDimensionRatio = true
  32. config.cameraDirection = .front
  33. config.frameRate = 15
  34. return config
  35. }()
  36. public var engine: AgoraRtcEngineKit?
  37. private var player: AgoraRtcMediaPlayerProtocol?
  38. func mediaPlayer() -> AgoraRtcMediaPlayerProtocol? {
  39. if let p = player {
  40. return p
  41. } else {
  42. player = engine?.createMediaPlayer(with: self)
  43. player?.setLoopCount(-1)
  44. return player
  45. }
  46. }
  47. func prepareEngine() {
  48. let engine = AgoraRtcEngineKit.sharedEngine(with: engineConfig(), delegate: nil)
  49. self.engine = engine
  50. let loader = VideoLoaderApiImpl()
  51. loader.addListener(listener: self)
  52. let config = VideoLoaderConfig()
  53. config.rtcEngine = engine
  54. config.userId = UInt(VLUserCenter.user.id)!
  55. loader.setup(config: config)
  56. videoLoader = loader
  57. showLogger.info("load AgoraRtcEngineKit, sdk version: \(AgoraRtcEngineKit.getSdkVersion())", context: kShowLogBaseContext)
  58. }
  59. func destoryEngine() {
  60. AgoraRtcEngineKit.destroy()
  61. showLogger.info("deinit-- ShowAgoraKitManager")
  62. }
  63. // 退出已加入的频道和子频道
  64. func leaveAllRoom() {
  65. cleanTimestampMap()
  66. videoLoader?.cleanCache()
  67. if let p = player {
  68. engine?.destroyMediaPlayer(p)
  69. player = nil
  70. }
  71. }
  72. //MARK: private
  73. private func engineConfig() -> AgoraRtcEngineConfig {
  74. let config = AgoraRtcEngineConfig()
  75. config.appId = KeyCenter.AppId
  76. config.channelProfile = .liveBroadcasting
  77. config.areaCode = .global
  78. return config
  79. }
  80. private func setupContentInspectConfig(_ enable: Bool, connection: AgoraRtcConnection) {
  81. let config = AgoraContentInspectConfig()
  82. let dic: [String: String] = [
  83. "id": VLUserCenter.user.id,
  84. "sceneName": "show",
  85. "userNo": VLUserCenter.user.userNo
  86. ]
  87. guard let jsonData = try? JSONSerialization.data(withJSONObject: dic, options: .prettyPrinted) else {
  88. showLogger.error("setupContentInspectConfig fail")
  89. return
  90. }
  91. let jsonStr = String(data: jsonData, encoding: .utf8)
  92. config.extraInfo = jsonStr
  93. let module = AgoraContentInspectModule()
  94. module.interval = 30
  95. module.type = .imageModeration
  96. config.modules = [module]
  97. let ret = engine?.enableContentInspectEx(enable, config: config, connection: connection)
  98. showLogger.info("setupContentInspectConfig: \(ret ?? -1)")
  99. }
  100. /// 语音审核
  101. private func moderationAudio(channelName: String, role: AgoraClientRole) {
  102. guard role == .broadcaster else { return }
  103. let userInfo = ["id": VLUserCenter.user.id,
  104. "sceneName": "show",
  105. "userNo": VLUserCenter.user.userNo,
  106. "userName": VLUserCenter.user.name]
  107. let parasm: [String: Any] = ["appId": KeyCenter.AppId,
  108. "channelName": channelName,
  109. "channelType": engineConfig().channelProfile.rawValue,
  110. "traceId": NSString.withUUID().md5(),
  111. "src": "iOS",
  112. "payload": JSONObject.toJsonString(dict: userInfo) ?? ""]
  113. NetworkManager.shared.postRequest(urlString: "https://service.agora.io/toolbox/v1/moderation/audio",
  114. params: parasm) { response in
  115. showLogger.info("response === \(response)")
  116. } failure: { errr in
  117. showLogger.error(errr)
  118. }
  119. }
  120. private func _joinChannelEx(currentChannelId: String,
  121. targetChannelId: String,
  122. ownerId: UInt,
  123. token: String,
  124. options:AgoraRtcChannelMediaOptions,
  125. role: AgoraClientRole) {
  126. if role == .audience {
  127. let roomInfo = _getRoomInfo(channelId: targetChannelId, uid: ownerId)
  128. let newState: RoomStatus = broadcasterConnection == nil ? .prejoined : .joined
  129. videoLoader?.switchRoomState(newState: newState, roomInfo: roomInfo, tagId: currentChannelId)
  130. return
  131. }
  132. guard let engine = engine else {
  133. assert(true, "rtc engine not initlized")
  134. return
  135. }
  136. if let _ = broadcasterConnection {
  137. return
  138. }
  139. let mediaOptions = AgoraRtcChannelMediaOptions()
  140. mediaOptions.publishCameraTrack = true
  141. mediaOptions.publishMicrophoneTrack = true
  142. mediaOptions.autoSubscribeAudio = true
  143. mediaOptions.autoSubscribeVideo = true
  144. mediaOptions.clientRoleType = .broadcaster
  145. updateVideoEncoderConfigurationForConnenction(currentChannelId: currentChannelId)
  146. let connection = AgoraRtcConnection()
  147. connection.channelId = targetChannelId
  148. connection.localUid = UInt(VLUserCenter.user.id) ?? 0
  149. let proxy = videoLoader?.getRTCListener(roomId: currentChannelId)
  150. let date = Date()
  151. showLogger.info("try to join room[\(connection.channelId)] ex uid: \(connection.localUid)", context: kShowLogBaseContext)
  152. let ret =
  153. engine.joinChannelEx(byToken: token,
  154. connection: connection,
  155. delegate: proxy,
  156. mediaOptions: mediaOptions) {[weak self] channelName, uid, elapsed in
  157. let cost = Int(-date.timeIntervalSinceNow * 1000)
  158. showLogger.info("join room[\(channelName)] ex success uid: \(uid) cost \(cost) ms", context: kShowLogBaseContext)
  159. self?.setupContentInspectConfig(true, connection: connection)
  160. // self?.moderationAudio(channelName: targetChannelId, role: role)
  161. self?.applySimulcastStream(connection: connection)
  162. }
  163. engine.updateChannelEx(with: mediaOptions, connection: connection)
  164. broadcasterConnection = connection
  165. if ret == 0 {
  166. showLogger.info("join room ex: channelId: \(targetChannelId) ownerId: \(ownerId)",
  167. context: "AgoraKitManager")
  168. }else{
  169. showLogger.error("join room ex fail: channelId: \(targetChannelId) ownerId: \(ownerId) token = \(token), \(ret)",
  170. context: kShowLogBaseContext)
  171. }
  172. }
  173. func updateVideoEncoderConfigurationForConnenction(currentChannelId: String) {
  174. guard let engine = engine else {
  175. assert(true, "rtc engine not initlized")
  176. return
  177. }
  178. let connection = AgoraRtcConnection()
  179. connection.channelId = currentChannelId
  180. connection.localUid = UInt(VLUserCenter.user.id) ?? 0
  181. let encoderRet = engine.setVideoEncoderConfigurationEx(encoderConfig, connection: connection)
  182. showLogger.info("setVideoEncoderConfigurationEx dimensions = \(encoderConfig.dimensions), bitrate = \(encoderConfig.bitrate), fps = \(encoderConfig.frameRate), encoderRet = \(encoderRet)", context: kShowLogBaseContext)
  183. }
  184. //MARK: public method
  185. func addRtcDelegate(delegate: AgoraRtcEngineDelegate, roomId: String) {
  186. videoLoader?.addRTCListener(roomId: roomId, listener: delegate)
  187. }
  188. func removeRtcDelegate(delegate: AgoraRtcEngineDelegate, roomId: String) {
  189. videoLoader?.removeRTCListener(roomId: roomId, listener: delegate)
  190. }
  191. func renewToken(channelId: String) {
  192. showLogger.info("renewToken with channelId: \(channelId)",
  193. context: kShowLogBaseContext)
  194. NetworkManager.shared.generateToken(channelName: channelId,
  195. uid: UserInfo.userId,
  196. tokenType: .token007,
  197. type: .rtc) {[weak self] token in
  198. guard let token = token else {
  199. showLogger.error("renewToken fail: token is empty")
  200. return
  201. }
  202. let option = AgoraRtcChannelMediaOptions()
  203. option.token = token
  204. AppContext.shared.rtcToken = token
  205. self?.updateChannelEx(channelId: channelId, options: option)
  206. }
  207. }
  208. // 耗时计算
  209. private var savedTimestampMap: [String: Date] = [String: Date]()
  210. func callTimestampStart(clean: Bool, roomId: String?) {
  211. guard let roomId = roomId else {return}
  212. showLogger.info("callTimeStampsSaved : start")
  213. if (clean) {
  214. savedTimestampMap[roomId] = nil
  215. }
  216. if savedTimestampMap[roomId] == nil {
  217. showLogger.info("callTimeStampsSaved : saved")
  218. savedTimestampMap[roomId] = Date()
  219. }
  220. }
  221. func callTimestampEnd(_ roomId: String?) -> TimeInterval? {
  222. guard let roomId = roomId else {return nil}
  223. showLogger.info("callTimeStampsSaved : end called")
  224. guard let saved = savedTimestampMap[roomId] else {
  225. return nil
  226. }
  227. showLogger.info("callTimeStampsSaved : end value")
  228. savedTimestampMap[roomId] = nil
  229. return -saved.timeIntervalSinceNow * 1000
  230. }
  231. private func cleanTimestampMap(){
  232. savedTimestampMap.removeAll()
  233. }
  234. //MARK: public sdk method
  235. /// 初始化并预览
  236. /// - Parameter canvasView: 画布
  237. func startPreview(canvasView: UIView) {
  238. guard let engine = engine else {
  239. assert(true, "rtc engine not initlized")
  240. return
  241. }
  242. engine.setClientRole(.broadcaster)
  243. engine.setVideoEncoderConfiguration(encoderConfig)
  244. engine.setCameraCapturerConfiguration(captureConfig)
  245. BeautyManager.shareManager.beautyAPI.setupLocalVideo(canvasView, renderMode: .hidden)
  246. engine.enableVideo()
  247. engine.startPreview()
  248. }
  249. /// 切换摄像头
  250. func switchCamera(_ channelId: String? = nil) {
  251. BeautyManager.shareManager.beautyAPI.switchCamera()
  252. }
  253. /// 开启虚化背景
  254. func enableVirtualBackground(isOn: Bool, greenCapacity: Float = 0) {
  255. guard let engine = engine else {
  256. assert(true, "rtc engine not initlized")
  257. return
  258. }
  259. let source = AgoraVirtualBackgroundSource()
  260. source.backgroundSourceType = .blur
  261. source.blurDegree = .high
  262. var seg: AgoraSegmentationProperty?
  263. if ShowAgoraKitManager.isOpenGreen {
  264. seg = AgoraSegmentationProperty()
  265. seg?.modelType = .agoraGreen
  266. seg?.greenCapacity = greenCapacity
  267. }
  268. let ret = engine.enableVirtualBackground(isOn, backData: source, segData: seg)
  269. showLogger.info("isOn = \(isOn), enableVirtualBackground ret = \(ret)")
  270. }
  271. /// 设置虚拟背景
  272. func seVirtualtBackgoundImage(imagePath: String?, isOn: Bool, greenCapacity: Float = 0) {
  273. guard let bundlePath = Bundle.main.path(forResource: "showResource", ofType: "bundle"),
  274. let bundle = Bundle(path: bundlePath) else { return }
  275. let imgPath = bundle.path(forResource: imagePath, ofType: "jpg")
  276. let source = AgoraVirtualBackgroundSource()
  277. source.backgroundSourceType = .img
  278. source.source = imgPath
  279. var seg: AgoraSegmentationProperty?
  280. if ShowAgoraKitManager.isOpenGreen {
  281. seg = AgoraSegmentationProperty()
  282. seg?.modelType = .agoraGreen
  283. seg?.greenCapacity = greenCapacity
  284. }
  285. guard let engine = engine else {
  286. assert(true, "rtc engine not initlized")
  287. return
  288. }
  289. engine.enableVirtualBackground(isOn, backData: source, segData: seg)
  290. }
  291. /// 预加载
  292. /// - Parameter preloadRoomList: <#preloadRoomList description#>
  293. public func preloadRoom(preloadRoomList: [RoomInfo]) {
  294. videoLoader?.preloadRoom(preloadRoomList: preloadRoomList)
  295. }
  296. func updateChannelEx(channelId: String, options: AgoraRtcChannelMediaOptions) {
  297. guard let engine = engine,
  298. let connection = (broadcasterConnection?.channelId == channelId ? broadcasterConnection : nil) ?? videoLoader?.getConnectionMap()[channelId] else {
  299. showLogger.error("updateChannelEx fail: connection is empty")
  300. return
  301. }
  302. showLogger.info("updateChannelEx[\(channelId)]: \(options.publishMicrophoneTrack) \(options.publishCameraTrack)")
  303. engine.updateChannelEx(with: options, connection: connection)
  304. }
  305. /// 切换连麦角色
  306. func switchRole(role: AgoraClientRole,
  307. channelId: String,
  308. options:AgoraRtcChannelMediaOptions,
  309. uid: String?,
  310. canvasView: UIView?) {
  311. guard let uid = UInt(uid ?? ""), let canvasView = canvasView else {
  312. showLogger.error("switchRole fatel")
  313. return
  314. }
  315. options.clientRoleType = role
  316. options.audienceLatencyLevel = role == .audience ? .lowLatency : .ultraLowLatency
  317. updateChannelEx(channelId:channelId, options: options)
  318. if "\(uid)" == VLUserCenter.user.id {
  319. videoLoader?.leaveChannelWithout(roomId: channelId)
  320. setupLocalVideo(uid: uid, canvasView: canvasView)
  321. } else {
  322. setupRemoteVideo(channelId: channelId, uid: uid, canvasView: canvasView)
  323. }
  324. }
  325. /// 设置编码分辨率
  326. /// - Parameter size: 分辨率
  327. func setVideoDimensions(_ size: CGSize){
  328. guard let engine = engine else {
  329. assert(true, "rtc engine not initlized")
  330. return
  331. }
  332. encoderConfig.dimensions = CGSize(width: size.width, height: size.height)
  333. engine.setVideoEncoderConfiguration(encoderConfig)
  334. }
  335. func cleanCapture() {
  336. guard let engine = engine else {
  337. assert(true, "rtc engine not initlized")
  338. return
  339. }
  340. // ByteBeautyManager.shareManager.destroy()
  341. // setupContentInspectConfig(false)
  342. engine.stopPreview()
  343. engine.setVideoFrameDelegate(nil)
  344. }
  345. func leaveChannelEx(roomId: String, channelId: String) {
  346. if let connection = broadcasterConnection, connection.channelId == channelId {
  347. engine?.leaveChannelEx(connection)
  348. broadcasterConnection = nil
  349. return
  350. }
  351. let roomInfo = _getRoomInfo(channelId: channelId)
  352. videoLoader?.switchRoomState(newState: .idle, roomInfo: roomInfo, tagId: roomId)
  353. }
  354. func joinChannelEx(currentChannelId: String,
  355. targetChannelId: String,
  356. ownerId: UInt,
  357. options:AgoraRtcChannelMediaOptions,
  358. role: AgoraClientRole,
  359. completion: (()->())?) {
  360. if let rtcToken = AppContext.shared.rtcToken {
  361. _joinChannelEx(currentChannelId: currentChannelId,
  362. targetChannelId: targetChannelId,
  363. ownerId: ownerId,
  364. token: rtcToken,
  365. options: options,
  366. role: role)
  367. completion?()
  368. return
  369. }
  370. NetworkManager.shared.generateToken(channelName: targetChannelId,
  371. uid: VLUserCenter.user.id,
  372. tokenType: .token007,
  373. type: .rtc) {[weak self] token in
  374. defer {
  375. completion?()
  376. }
  377. guard let token = token else {
  378. showLogger.error("joinChannelEx fail: token is empty")
  379. return
  380. }
  381. AppContext.shared.rtcToken = token
  382. self?._joinChannelEx(currentChannelId: currentChannelId,
  383. targetChannelId: targetChannelId,
  384. ownerId: ownerId,
  385. token: token,
  386. options: options,
  387. role: role)
  388. }
  389. }
  390. func setupLocalVideo(uid: UInt, canvasView: UIView) {
  391. guard let engine = engine else {
  392. assert(true, "rtc engine not initlized")
  393. return
  394. }
  395. let canvas = AgoraRtcVideoCanvas()
  396. canvas.view = canvasView
  397. canvas.uid = uid
  398. canvas.mirrorMode = .disabled
  399. engine.setupLocalVideo(canvas)
  400. engine.startPreview()
  401. engine.setDefaultAudioRouteToSpeakerphone(true)
  402. engine.enableAudio()
  403. engine.enableVideo()
  404. showLogger.info("setupLocalVideo target uid:\(uid), user uid\(UserInfo.userId)", context: kShowLogBaseContext)
  405. }
  406. func setupRemoteVideo(channelId: String, uid: UInt, canvasView: UIView) {
  407. if let connection = broadcasterConnection, broadcasterConnection?.channelId == channelId {
  408. let videoCanvas = AgoraRtcVideoCanvas()
  409. videoCanvas.uid = uid
  410. videoCanvas.view = canvasView
  411. videoCanvas.renderMode = .hidden
  412. let ret = engine?.setupRemoteVideoEx(videoCanvas, connection: connection)
  413. showLogger.info("setupRemoteVideoEx ret = \(ret ?? -1), uid:\(uid) localuid: \(UserInfo.userId) channelId: \(channelId)", context: kShowLogBaseContext)
  414. return
  415. }
  416. let roomInfo = _getRoomInfo(channelId: channelId, uid: uid)
  417. let container = VideoCanvasContainer()
  418. container.uid = uid
  419. container.container = canvasView
  420. videoLoader?.renderVideo(roomInfo: roomInfo, container: container)
  421. }
  422. func updateLoadingType(roomId: String, channelId: String, playState: RoomStatus) {
  423. if broadcasterConnection?.channelId == channelId {return}
  424. let roomInfo = _getRoomInfo(channelId: channelId)
  425. videoLoader?.switchRoomState(newState: playState, roomInfo: roomInfo, tagId: roomId)
  426. }
  427. func cleanChannel(without roomIds: [String]) {
  428. guard let videoLoader = videoLoader else {return}
  429. for (key, _) in videoLoader.getConnectionMap() {
  430. if roomIds.contains(key) {continue}
  431. let roomInfo = RoomInfo()
  432. roomInfo.channelName = key
  433. videoLoader.switchRoomState(newState: .idle, roomInfo: roomInfo, tagId: key)
  434. }
  435. }
  436. }
  437. //MARK: private param
  438. extension ShowAgoraKitManager {
  439. func initBroadcasterConfig() {
  440. engine?.setParameters("{\"rtc.enable_crypto_access\":false}")
  441. engine?.setParameters("{\"rtc.use_global_location_priority_domain\":true}")
  442. engine?.setParameters("{\"che.video.has_intra_request\":false}")
  443. engine?.setParameters("{\"che.hardware_encoding\":1}")
  444. engine?.setParameters("{\"engine.video.enable_hw_encoder\":true}")
  445. engine?.setParameters("{\"che.video.keyFrameInterval\":2}")
  446. engine?.setParameters("{\"che.video.hw265_enc_enable\":1}")
  447. engine?.setParameters("{\"che.video.enable_first_frame_sw_decode\":true}")
  448. engine?.setParameters("{\"rtc.asyncCreateMediaEngine\":true}")
  449. }
  450. func initAudienceConfig() {
  451. engine?.setParameters("{\"rtc.enable_crypto_access\":false}")
  452. engine?.setParameters("{\"rtc.use_global_location_priority_domain\":true}")
  453. engine?.setParameters("{\"che.hardware_decoding\":0}")
  454. engine?.setParameters("{\"rtc.enable_nasa2\": false}")
  455. engine?.setParameters("{\"rtc.asyncCreateMediaEngine\":true}")
  456. engine?.setParameters("{\"che.video.enable_first_frame_sw_decode\":true}")
  457. }
  458. func initH265Config() {
  459. engine?.setParameters("{\"che.video.videoCodecIndex\":2}") // 265
  460. }
  461. func initH264Config() {
  462. engine?.setParameters("{\"che.video.videoCodecIndex\":1}") //264
  463. engine?.setParameters("{\"che.video.minQP\":10}")
  464. engine?.setParameters("{\"che.video.maxQP\":35}")
  465. }
  466. }
  467. extension ShowAgoraKitManager {
  468. private func _getRoomInfo(channelId: String, uid: UInt? = nil)->RoomInfo {
  469. let roomInfo = RoomInfo()
  470. roomInfo.channelName = channelId
  471. roomInfo.uid = uid ?? (UInt(VLUserCenter.user.id) ?? 0)
  472. roomInfo.token = AppContext.shared.rtcToken ?? ""
  473. return roomInfo
  474. }
  475. func setOffMediaOptionsVideo(roomid: String) {
  476. guard let connection = videoLoader?.getConnectionMap()[roomid] else {
  477. showLogger.info("setOffMediaOptionsVideo connection 不存在 \(roomid)")
  478. return
  479. }
  480. showLogger.info("setOffMediaOptionsVideo with roomid = \(roomid)")
  481. let mediaOptions = AgoraRtcChannelMediaOptions()
  482. mediaOptions.autoSubscribeVideo = false
  483. engine?.updateChannelEx(with: mediaOptions, connection: connection)
  484. }
  485. func setOffMediaOptionsAudio() {
  486. videoLoader?.getConnectionMap().forEach { _, connention in
  487. let mediaOptions = AgoraRtcChannelMediaOptions()
  488. mediaOptions.autoSubscribeAudio = false
  489. engine?.updateChannelEx(with: mediaOptions, connection: connention)
  490. }
  491. }
  492. }
  493. // MARK: - IVideoLoaderApiListener
  494. extension ShowAgoraKitManager: IVideoLoaderApiListener {
  495. public func debugInfo(_ message: String) {
  496. showLogger.info(message, context: "VideoLoaderApi")
  497. }
  498. public func debugWarning(_ message: String) {
  499. showLogger.warning(message, context: "VideoLoaderApi")
  500. }
  501. public func debugError(_ message: String) {
  502. showLogger.error(message, context: "VideoLoaderApi")
  503. }
  504. }
  505. // MARK: - AgoraRtcMediaPlayerDelegate
  506. extension ShowAgoraKitManager: AgoraRtcMediaPlayerDelegate {
  507. func AgoraRtcMediaPlayer(_ playerKit: AgoraRtcMediaPlayerProtocol, didChangedTo state: AgoraMediaPlayerState, error: AgoraMediaPlayerError) {
  508. if state == .openCompleted {
  509. playerKit.play()
  510. }
  511. }
  512. }