303 lines
11 KiB
Swift
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")
|
||
|
|
}
|
||
|
|
}
|