MediaSettingViewModel.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //
  2. // MediaSettingViewModel.swift
  3. // TUIRoomKit
  4. //
  5. // Created by janejntang on 2023/1/16.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. import Foundation
  9. import RTCRoomEngine
  10. #if canImport(TXLiteAVSDK_TRTC)
  11. import TXLiteAVSDK_TRTC
  12. #elseif canImport(TXLiteAVSDK_Professional)
  13. import TXLiteAVSDK_Professional
  14. #endif
  15. protocol MediaSettingViewEventResponder: AnyObject {
  16. func showResolutionAlert()
  17. func showFrameRateAlert()
  18. func showQualityView()
  19. func updateStackView(item: ListCellItemData)
  20. func makeToast(text: String)
  21. }
  22. class MediaSettingViewModel {
  23. private(set) var videoItems: [ListCellItemData] = []
  24. private(set) var audioItems: [ListCellItemData] = []
  25. private(set) var otherItems: [ListCellItemData] = []
  26. weak var viewResponder: MediaSettingViewEventResponder? = nil
  27. var engineManager: EngineManager {
  28. EngineManager.shared
  29. }
  30. var store: RoomStore {
  31. engineManager.store
  32. }
  33. var videoSetting: VideoModel {
  34. engineManager.store.videoSetting
  35. }
  36. var audioSetting: AudioModel {
  37. engineManager.store.audioSetting
  38. }
  39. let resolutionNameItems: [String] = [.smoothResolutionText, .standardResolutionText, .highResolutionText, .superResolutionText]
  40. private let resolutionItems: [TUIVideoQuality] = [.quality360P, .quality540P, .quality720P, .quality1080P]
  41. private let bitrateArray = [550, 850, 1_200, 2_000]
  42. let topItems: [String] = [.videoText, .audioText, .otherText]
  43. let frameRateArray = ["15", "20"]
  44. init() {
  45. createVideoItem()
  46. createAudioItem()
  47. createOtherItem()
  48. }
  49. private func createVideoItem() {
  50. let resolutionItem = ListCellItemData()
  51. resolutionItem.titleText = .resolutionText
  52. resolutionItem.hasOverAllAction = true
  53. resolutionItem.type = .resolutionType
  54. resolutionItem.hasDownLineView = true
  55. resolutionItem.hasRightButton = true
  56. let buttonData = ButtonItemData()
  57. if let resolutionName = getResolutionName(videoQuality: videoSetting.videoQuality) {
  58. buttonData.normalTitle = resolutionName
  59. }
  60. buttonData.orientation = .right
  61. buttonData.normalIcon = "room_down_arrow1"
  62. buttonData.resourceBundle = tuiRoomKitBundle()
  63. buttonData.titleFont = UIFont.systemFont(ofSize: 16, weight: .regular)
  64. buttonData.titleColor = UIColor(0xD1D9EC)
  65. buttonData.size = CGSize(width: 80, height: 30)
  66. buttonData.isEnabled = false
  67. resolutionItem.buttonData = buttonData
  68. resolutionItem.action = { [weak self] sender in
  69. guard let self = self else { return }
  70. self.resolutionAction()
  71. }
  72. videoItems.append(resolutionItem)
  73. let frameRateItem = ListCellItemData()
  74. frameRateItem.titleText = .frameRateText
  75. frameRateItem.hasOverAllAction = true
  76. frameRateItem.type = .frameRateType
  77. frameRateItem.hasRightButton = true
  78. let frameRateButtonData = ButtonItemData()
  79. frameRateButtonData.orientation = .right
  80. frameRateButtonData.normalIcon = "room_down_arrow1"
  81. frameRateButtonData.normalTitle = String(videoSetting.videoFps)
  82. frameRateButtonData.resourceBundle = tuiRoomKitBundle()
  83. frameRateButtonData.titleFont = UIFont.systemFont(ofSize: 16, weight: .regular)
  84. frameRateButtonData.titleColor = UIColor(0xD1D9EC)
  85. frameRateButtonData.size = CGSize(width: 80, height: 30)
  86. frameRateButtonData.isEnabled = false
  87. frameRateItem.buttonData = frameRateButtonData
  88. frameRateItem.action = { [weak self] sender in
  89. guard let self = self else { return }
  90. self.frameRateAction()
  91. }
  92. videoItems.append(frameRateItem)
  93. }
  94. private func createAudioItem() {
  95. let captureVolumeItem = ListCellItemData()
  96. captureVolumeItem.titleText = .captureVolumeText
  97. captureVolumeItem.hasSlider = true
  98. captureVolumeItem.hasSliderLabel = true
  99. captureVolumeItem.minimumValue = 0
  100. captureVolumeItem.maximumValue = 100
  101. captureVolumeItem.sliderStep = 1
  102. captureVolumeItem.sliderDefault = Float(audioSetting.captureVolume)
  103. captureVolumeItem.hasDownLineView = true
  104. captureVolumeItem.action = { [weak self] sender in
  105. guard let self = self, let view = sender as? UISlider else { return }
  106. self.captureVolumeAction(sender: view)
  107. }
  108. audioItems.append(captureVolumeItem)
  109. let playingVolumeItem = ListCellItemData()
  110. playingVolumeItem.titleText = .playVolumeText
  111. playingVolumeItem.hasSlider = true
  112. playingVolumeItem.hasSliderLabel = true
  113. playingVolumeItem.minimumValue = 0
  114. playingVolumeItem.maximumValue = 100
  115. playingVolumeItem.sliderStep = 1
  116. playingVolumeItem.sliderDefault = Float(audioSetting.playVolume)
  117. playingVolumeItem.hasDownLineView = true
  118. playingVolumeItem.action = { [weak self] sender in
  119. guard let self = self, let view = sender as? UISlider else { return }
  120. self.playingVolumeAction(sender: view)
  121. }
  122. audioItems.append(playingVolumeItem)
  123. let volumePromptItem = ListCellItemData()
  124. volumePromptItem.titleText = .volumePromptText
  125. volumePromptItem.hasSwitch = true
  126. volumePromptItem.isSwitchOn = audioSetting.volumePrompt
  127. volumePromptItem.action = { [weak self] sender in
  128. guard let self = self, let view = sender as? UISwitch else { return }
  129. self.volumePromptAction(sender: view)
  130. }
  131. audioItems.append(volumePromptItem)
  132. }
  133. private func resolutionAction() {
  134. viewResponder?.showResolutionAlert()
  135. }
  136. private func createOtherItem() {
  137. let qualityItem = ListCellItemData()
  138. qualityItem.titleText = .qualityInspectionText
  139. qualityItem.hasOverAllAction = true
  140. qualityItem.hasRightButton = true
  141. let buttonData = ButtonItemData()
  142. buttonData.orientation = .right
  143. buttonData.normalIcon = "room_right_arrow1"
  144. buttonData.resourceBundle = tuiRoomKitBundle()
  145. buttonData.titleFont = UIFont.systemFont(ofSize: 16, weight: .regular)
  146. buttonData.titleColor = UIColor(0xD1D9EC)
  147. buttonData.size = CGSize(width: 80, height: 30)
  148. buttonData.isEnabled = false
  149. qualityItem.buttonData = buttonData
  150. qualityItem.action = { [weak self] sender in
  151. guard let self = self else { return }
  152. self.showQualityAction()
  153. }
  154. otherItems.append(qualityItem)
  155. let floatChatItem = ListCellItemData()
  156. floatChatItem.titleText = .floatChatText
  157. floatChatItem.hasSwitch = true
  158. floatChatItem.isSwitchOn = store.shouldShowFloatChatView
  159. floatChatItem.action = { [weak self] sender in
  160. guard let self = self, let view = sender as? UISwitch else { return }
  161. self.floatChatShowAction(shouldShow: view.isOn)
  162. }
  163. otherItems.append(floatChatItem)
  164. }
  165. private func showQualityAction() {
  166. viewResponder?.showQualityView()
  167. }
  168. private func floatChatShowAction(shouldShow: Bool) {
  169. store.updateFloatChatShowState(shouldShow: shouldShow)
  170. }
  171. private func frameRateAction() {
  172. viewResponder?.showFrameRateAlert()
  173. }
  174. private func captureVolumeAction(sender: UISlider) {
  175. engineManager.setAudioCaptureVolume(Int(sender.value))
  176. }
  177. private func playingVolumeAction(sender: UISlider) {
  178. engineManager.setAudioPlayoutVolume(Int(sender.value))
  179. }
  180. private func volumePromptAction(sender: UISwitch) {
  181. engineManager.enableAudioVolumeEvaluation(isVolumePrompt: sender.isOn)
  182. }
  183. func changeResolutionAction(index: Int) {
  184. guard let videoItem = videoItems.first(where: { $0.type == .resolutionType }) else { return }
  185. guard let quality = resolutionItems[safe: index] else { return }
  186. guard let resolutionName = getResolutionName(videoQuality: quality) else { return }
  187. videoItem.buttonData?.normalTitle = resolutionName
  188. viewResponder?.updateStackView(item: videoItem)
  189. engineManager.setVideoEncoder(videoQuality: quality, bitrate: getBitrate(videoQuality: quality))
  190. }
  191. func changeFrameRateAction(index: Int) {
  192. guard let videoItem = videoItems.first(where: { $0.type == .frameRateType }) else { return }
  193. guard let frameRate = frameRateArray[safe: index] else { return }
  194. videoItem.buttonData?.normalTitle = frameRate
  195. viewResponder?.updateStackView(item: videoItem)
  196. engineManager.setVideoEncoder(fps: Int(frameRate))
  197. }
  198. func getCurrentResolutionIndex() -> Int {
  199. guard let index = resolutionItems.firstIndex(where: { $0 == videoSetting.videoQuality }) else { return 0 }
  200. return index
  201. }
  202. func getCurrentFrameRateIndex() -> Int {
  203. let frameRateString = String(videoSetting.videoFps)
  204. guard let index = frameRateArray.firstIndex(where: { $0 == frameRateString }) else { return 0 }
  205. return index
  206. }
  207. private func getResolutionName(videoQuality: TUIVideoQuality) -> String? {
  208. guard let index = resolutionItems.firstIndex(of: videoQuality) else { return nil }
  209. guard let resolutionName = resolutionNameItems[safe: index] else { return nil }
  210. return resolutionName
  211. }
  212. private func getBitrate(videoQuality: TUIVideoQuality) -> Int? {
  213. guard let index = resolutionItems.firstIndex(of: videoQuality) else { return nil }
  214. guard let bitrate = bitrateArray[safe: index] else { return nil }
  215. return bitrate
  216. }
  217. func reportSettingsPanelShow() {
  218. RoomKitReport.reportData(.metricsSettingsPanelShow)
  219. }
  220. deinit {
  221. debugPrint("deinit \(self)")
  222. }
  223. }
  224. private extension String {
  225. static var videoText: String {
  226. localized("Video Settings")
  227. }
  228. static var audioText: String {
  229. localized("Audio Settings")
  230. }
  231. static var otherText: String {
  232. localized("Other Settings")
  233. }
  234. static var versionLowToastText: String {
  235. localized("Your system version is below 12.0. Please update.")
  236. }
  237. static var resolutionText: String {
  238. localized("Resolution")
  239. }
  240. static var frameRateText: String {
  241. localized("Frame Rate")
  242. }
  243. static var bitrateText: String {
  244. localized("Bitrate")
  245. }
  246. static var localMirrorText: String {
  247. localized("Local Mirror")
  248. }
  249. static var captureVolumeText: String {
  250. localized("Capture Volume")
  251. }
  252. static var playVolumeText: String {
  253. localized("Playback Volume")
  254. }
  255. static var volumePromptText: String {
  256. localized("Volume Reminder")
  257. }
  258. static var audioRecordingText: String {
  259. localized("Audio Recording")
  260. }
  261. static var smoothResolutionText: String {
  262. localized("Smooth")
  263. }
  264. static var standardResolutionText: String {
  265. localized("Standard Definition")
  266. }
  267. static var highResolutionText: String {
  268. localized("High Definition")
  269. }
  270. static var superResolutionText: String {
  271. localized("Full High Definition")
  272. }
  273. static var qualityInspectionText: String {
  274. localized("Network Quality Monitoring")
  275. }
  276. static var floatChatText: String {
  277. localized("Floating Chat")
  278. }
  279. }