Files
midi_ios/TUIKit/TUIRoomKit/Source/View/ViewModel/MediaSettingViewModel.swift
2025-08-14 10:07:49 +08:00

303 lines
11 KiB
Swift

//
// MediaSettingViewModel.swift
// TUIRoomKit
//
// Created by janejntang on 2023/1/16.
// Copyright © 2023 Tencent. All rights reserved.
//
import Foundation
import RTCRoomEngine
#if canImport(TXLiteAVSDK_TRTC)
import TXLiteAVSDK_TRTC
#elseif canImport(TXLiteAVSDK_Professional)
import TXLiteAVSDK_Professional
#endif
protocol MediaSettingViewEventResponder: AnyObject {
func showResolutionAlert()
func showFrameRateAlert()
func showQualityView()
func updateStackView(item: ListCellItemData)
func makeToast(text: String)
}
class MediaSettingViewModel {
private(set) var videoItems: [ListCellItemData] = []
private(set) var audioItems: [ListCellItemData] = []
private(set) var otherItems: [ListCellItemData] = []
weak var viewResponder: MediaSettingViewEventResponder? = nil
var engineManager: EngineManager {
EngineManager.shared
}
var store: RoomStore {
engineManager.store
}
var videoSetting: VideoModel {
engineManager.store.videoSetting
}
var audioSetting: AudioModel {
engineManager.store.audioSetting
}
let resolutionNameItems: [String] = [.smoothResolutionText, .standardResolutionText, .highResolutionText, .superResolutionText]
private let resolutionItems: [TUIVideoQuality] = [.quality360P, .quality540P, .quality720P, .quality1080P]
private let bitrateArray = [550, 850, 1_200, 2_000]
let topItems: [String] = [.videoText, .audioText, .otherText]
let frameRateArray = ["15", "20"]
init() {
createVideoItem()
createAudioItem()
createOtherItem()
}
private func createVideoItem() {
let resolutionItem = ListCellItemData()
resolutionItem.titleText = .resolutionText
resolutionItem.hasOverAllAction = true
resolutionItem.type = .resolutionType
resolutionItem.hasDownLineView = true
resolutionItem.hasRightButton = true
let buttonData = ButtonItemData()
if let resolutionName = getResolutionName(videoQuality: videoSetting.videoQuality) {
buttonData.normalTitle = resolutionName
}
buttonData.orientation = .right
buttonData.normalIcon = "room_down_arrow1"
buttonData.resourceBundle = tuiRoomKitBundle()
buttonData.titleFont = UIFont.systemFont(ofSize: 16, weight: .regular)
buttonData.titleColor = UIColor(0xD1D9EC)
buttonData.size = CGSize(width: 80, height: 30)
buttonData.isEnabled = false
resolutionItem.buttonData = buttonData
resolutionItem.action = { [weak self] sender in
guard let self = self else { return }
self.resolutionAction()
}
videoItems.append(resolutionItem)
let frameRateItem = ListCellItemData()
frameRateItem.titleText = .frameRateText
frameRateItem.hasOverAllAction = true
frameRateItem.type = .frameRateType
frameRateItem.hasRightButton = true
let frameRateButtonData = ButtonItemData()
frameRateButtonData.orientation = .right
frameRateButtonData.normalIcon = "room_down_arrow1"
frameRateButtonData.normalTitle = String(videoSetting.videoFps)
frameRateButtonData.resourceBundle = tuiRoomKitBundle()
frameRateButtonData.titleFont = UIFont.systemFont(ofSize: 16, weight: .regular)
frameRateButtonData.titleColor = UIColor(0xD1D9EC)
frameRateButtonData.size = CGSize(width: 80, height: 30)
frameRateButtonData.isEnabled = false
frameRateItem.buttonData = frameRateButtonData
frameRateItem.action = { [weak self] sender in
guard let self = self else { return }
self.frameRateAction()
}
videoItems.append(frameRateItem)
}
private func createAudioItem() {
let captureVolumeItem = ListCellItemData()
captureVolumeItem.titleText = .captureVolumeText
captureVolumeItem.hasSlider = true
captureVolumeItem.hasSliderLabel = true
captureVolumeItem.minimumValue = 0
captureVolumeItem.maximumValue = 100
captureVolumeItem.sliderStep = 1
captureVolumeItem.sliderDefault = Float(audioSetting.captureVolume)
captureVolumeItem.hasDownLineView = true
captureVolumeItem.action = { [weak self] sender in
guard let self = self, let view = sender as? UISlider else { return }
self.captureVolumeAction(sender: view)
}
audioItems.append(captureVolumeItem)
let playingVolumeItem = ListCellItemData()
playingVolumeItem.titleText = .playVolumeText
playingVolumeItem.hasSlider = true
playingVolumeItem.hasSliderLabel = true
playingVolumeItem.minimumValue = 0
playingVolumeItem.maximumValue = 100
playingVolumeItem.sliderStep = 1
playingVolumeItem.sliderDefault = Float(audioSetting.playVolume)
playingVolumeItem.hasDownLineView = true
playingVolumeItem.action = { [weak self] sender in
guard let self = self, let view = sender as? UISlider else { return }
self.playingVolumeAction(sender: view)
}
audioItems.append(playingVolumeItem)
let volumePromptItem = ListCellItemData()
volumePromptItem.titleText = .volumePromptText
volumePromptItem.hasSwitch = true
volumePromptItem.isSwitchOn = audioSetting.volumePrompt
volumePromptItem.action = { [weak self] sender in
guard let self = self, let view = sender as? UISwitch else { return }
self.volumePromptAction(sender: view)
}
audioItems.append(volumePromptItem)
}
private func resolutionAction() {
viewResponder?.showResolutionAlert()
}
private func createOtherItem() {
let qualityItem = ListCellItemData()
qualityItem.titleText = .qualityInspectionText
qualityItem.hasOverAllAction = true
qualityItem.hasRightButton = true
let buttonData = ButtonItemData()
buttonData.orientation = .right
buttonData.normalIcon = "room_right_arrow1"
buttonData.resourceBundle = tuiRoomKitBundle()
buttonData.titleFont = UIFont.systemFont(ofSize: 16, weight: .regular)
buttonData.titleColor = UIColor(0xD1D9EC)
buttonData.size = CGSize(width: 80, height: 30)
buttonData.isEnabled = false
qualityItem.buttonData = buttonData
qualityItem.action = { [weak self] sender in
guard let self = self else { return }
self.showQualityAction()
}
otherItems.append(qualityItem)
let floatChatItem = ListCellItemData()
floatChatItem.titleText = .floatChatText
floatChatItem.hasSwitch = true
floatChatItem.isSwitchOn = store.shouldShowFloatChatView
floatChatItem.action = { [weak self] sender in
guard let self = self, let view = sender as? UISwitch else { return }
self.floatChatShowAction(shouldShow: view.isOn)
}
otherItems.append(floatChatItem)
}
private func showQualityAction() {
viewResponder?.showQualityView()
}
private func floatChatShowAction(shouldShow: Bool) {
store.updateFloatChatShowState(shouldShow: shouldShow)
}
private func frameRateAction() {
viewResponder?.showFrameRateAlert()
}
private func captureVolumeAction(sender: UISlider) {
engineManager.setAudioCaptureVolume(Int(sender.value))
}
private func playingVolumeAction(sender: UISlider) {
engineManager.setAudioPlayoutVolume(Int(sender.value))
}
private func volumePromptAction(sender: UISwitch) {
engineManager.enableAudioVolumeEvaluation(isVolumePrompt: sender.isOn)
}
func changeResolutionAction(index: Int) {
guard let videoItem = videoItems.first(where: { $0.type == .resolutionType }) else { return }
guard let quality = resolutionItems[safe: index] else { return }
guard let resolutionName = getResolutionName(videoQuality: quality) else { return }
videoItem.buttonData?.normalTitle = resolutionName
viewResponder?.updateStackView(item: videoItem)
engineManager.setVideoEncoder(videoQuality: quality, bitrate: getBitrate(videoQuality: quality))
}
func changeFrameRateAction(index: Int) {
guard let videoItem = videoItems.first(where: { $0.type == .frameRateType }) else { return }
guard let frameRate = frameRateArray[safe: index] else { return }
videoItem.buttonData?.normalTitle = frameRate
viewResponder?.updateStackView(item: videoItem)
engineManager.setVideoEncoder(fps: Int(frameRate))
}
func getCurrentResolutionIndex() -> Int {
guard let index = resolutionItems.firstIndex(where: { $0 == videoSetting.videoQuality }) else { return 0 }
return index
}
func getCurrentFrameRateIndex() -> Int {
let frameRateString = String(videoSetting.videoFps)
guard let index = frameRateArray.firstIndex(where: { $0 == frameRateString }) else { return 0 }
return index
}
private func getResolutionName(videoQuality: TUIVideoQuality) -> String? {
guard let index = resolutionItems.firstIndex(of: videoQuality) else { return nil }
guard let resolutionName = resolutionNameItems[safe: index] else { return nil }
return resolutionName
}
private func getBitrate(videoQuality: TUIVideoQuality) -> Int? {
guard let index = resolutionItems.firstIndex(of: videoQuality) else { return nil }
guard let bitrate = bitrateArray[safe: index] else { return nil }
return bitrate
}
deinit {
debugPrint("deinit \(self)")
}
}
private extension String {
static var videoText: String {
localized("Video Settings")
}
static var audioText: String {
localized("Audio Settings")
}
static var otherText: String {
localized("Other Settings")
}
static var versionLowToastText: String {
localized("Your system version is below 12.0. Please update.")
}
static var resolutionText: String {
localized("Resolution")
}
static var frameRateText: String {
localized("Frame Rate")
}
static var bitrateText: String {
localized("Bitrate")
}
static var localMirrorText: String {
localized("Local Mirror")
}
static var captureVolumeText: String {
localized("Capture Volume")
}
static var playVolumeText: String {
localized("Playback Volume")
}
static var volumePromptText: String {
localized("Volume Reminder")
}
static var audioRecordingText: String {
localized("Audio Recording")
}
static var smoothResolutionText: String {
localized("Smooth")
}
static var standardResolutionText: String {
localized("Standard Definition")
}
static var highResolutionText: String {
localized("High Definition")
}
static var superResolutionText: String {
localized("Full High Definition")
}
static var qualityInspectionText: String {
localized("Network Quality Monitoring")
}
static var floatChatText: String {
localized("Floating Chat")
}
}