| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- //
- // QualityInfoViewModel.swift
- // TUIRoomKit
- //
- // Created by CY zhao on 2024/4/19.
- // Copyright © 2024 Tencent. All rights reserved.
- //
- import Foundation
- #if canImport(TXLiteAVSDK_TRTC)
- import TXLiteAVSDK_TRTC
- #elseif canImport(TXLiteAVSDK_Professional)
- import TXLiteAVSDK_Professional
- #endif
- protocol QualityViewResponder: AnyObject {
- func reloadData()
- }
- class QualityCellModel: NSObject {
- enum CellType {
- case upDown
- case normal
- }
- var titleText: String = ""
- var type: CellType = .upDown
- var uplinkString: String = ""
- var downlinkString: String = ""
- var normalString: String = ""
- }
- class QualitySectionModel: NSObject {
- var titleText: String = ""
- var items: [QualityCellModel] = []
- }
- class QualityInfoViewModel: NSObject {
- static let timeSuffix = "ms"
- static let lossSuffix = "%"
- static let bitrateSuffix = "kbps"
- static let framerateSuffix = "FPS"
-
- var sections: [QualitySectionModel] = []
- weak var viewResponder: QualityViewResponder? = nil
-
- private var rttCellModel: QualityCellModel = {
- var rttCellModel = QualityCellModel()
- rttCellModel.titleText = .rttString
- rttCellModel.normalString = "0" + timeSuffix
- rttCellModel.type = .normal
- return rttCellModel
- }()
-
- private var lossCellModel: QualityCellModel = {
- var lossCellModel = QualityCellModel()
- lossCellModel.titleText = .lossString
- lossCellModel.uplinkString = "0" + lossSuffix
- lossCellModel.downlinkString = "0" + lossSuffix
- return lossCellModel
- }()
-
- private var audioBitrateCellModel: QualityCellModel = {
- var bitrateCellModel = QualityCellModel()
- bitrateCellModel.titleText = .bitrateString
- bitrateCellModel.uplinkString = "0" + bitrateSuffix
- bitrateCellModel.downlinkString = "0" + bitrateSuffix
- return bitrateCellModel
- }()
-
- private var videoResCellModel: QualityCellModel = {
- var resCellModel = QualityCellModel()
- resCellModel.titleText = .resolutionString
- resCellModel.uplinkString = "0x0"
- resCellModel.downlinkString = "0x0"
- return resCellModel
- }()
-
- private var videoFrameRateCellModel: QualityCellModel = {
- var frameCellModel = QualityCellModel()
- frameCellModel.titleText = .frameRateString
- frameCellModel.uplinkString = "0" + framerateSuffix
- frameCellModel.downlinkString = "0" + framerateSuffix
- return frameCellModel
- }()
-
- private var videoBitrateCellModel: QualityCellModel = {
- var bitrateCellModel = QualityCellModel()
- bitrateCellModel.titleText = .bitrateString
- bitrateCellModel.uplinkString = "0" + bitrateSuffix
- bitrateCellModel.downlinkString = "0" + bitrateSuffix
- return bitrateCellModel
- }()
-
- override init() {
- super.init()
- self.addSection(with: [self.rttCellModel, self.lossCellModel], title: .networkString)
- self.addSection(with: [self.audioBitrateCellModel], title: .audioString)
- self.addSection(with: [self.videoResCellModel, self.videoFrameRateCellModel, self.videoBitrateCellModel], title: .videoString)
- subscribeEngine()
- }
-
- private func addSection(with items: [QualityCellModel], title: String) {
- let section = QualitySectionModel()
- section.titleText = title
- section.items = items
- self.sections.append(section)
- }
-
- private func subscribeEngine() {
- EngineEventCenter.shared.subscribeEngine(event: .onStatistics, observer: self)
- }
-
- private func unsubscribeEngine() {
- EngineEventCenter.shared.unsubscribeEngine(event: .onStatistics, observer: self)
- }
-
- deinit {
- unsubscribeEngine()
- }
- }
- extension QualityInfoViewModel: RoomEngineEventResponder {
- func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
- switch name {
- case .onStatistics:
- guard let data = param?["statistics"] as? TRTCStatistics else { return }
- handleStatistics(data: data)
- default:
- break
- }
- }
-
- private func handleStatistics(data: TRTCStatistics) {
- let localStatistics = data.localStatistics.first(where: { $0.streamType == .big })
- let remoteStatistics = data.remoteStatistics ?? []
-
- self.rttCellModel.normalString = String(data.rtt) + QualityInfoViewModel.timeSuffix
- // assemble uplink data
- self.lossCellModel.uplinkString = String(data.upLoss) + QualityInfoViewModel.lossSuffix
- self.audioBitrateCellModel.uplinkString = String(localStatistics?.audioBitrate ?? 0) + QualityInfoViewModel.bitrateSuffix
- self.videoResCellModel.uplinkString = String(localStatistics?.width ?? 0) + "x" + String(localStatistics?.height ?? 0)
- self.videoFrameRateCellModel.uplinkString = String(localStatistics?.frameRate ?? 0) + QualityInfoViewModel.framerateSuffix
- self.videoBitrateCellModel.uplinkString = String(localStatistics?.videoBitrate ?? 0) + QualityInfoViewModel.bitrateSuffix
-
- // assemble downlink data
- let remoteSumAudioBitrate = remoteStatistics.reduce(0) { sum, stream in
- return sum + stream.audioBitrate
- }
- let remoteMaxFramerate = remoteStatistics.max(by: {$0.frameRate < $1.frameRate})
- let remoteMaxVideoRes = remoteStatistics.max(by: { $0.width * $0.height < $1.width * $1.height })
- let remoteSumVideoBitrate = remoteStatistics.reduce(0) { sum, stream in
- return sum + stream.videoBitrate
- }
- self.lossCellModel.downlinkString = String(data.downLoss) + QualityInfoViewModel.lossSuffix
- self.audioBitrateCellModel.downlinkString = String(remoteSumAudioBitrate) + QualityInfoViewModel.bitrateSuffix
- self.videoResCellModel.downlinkString = String(remoteMaxVideoRes?.width ?? 0) + "x" + String(remoteMaxVideoRes?.height ?? 0)
- self.videoFrameRateCellModel.downlinkString = String(remoteMaxFramerate?.frameRate ?? 0) + QualityInfoViewModel.framerateSuffix
- self.videoBitrateCellModel.downlinkString = String(remoteSumVideoBitrate) + QualityInfoViewModel.bitrateSuffix
-
- self.viewResponder?.reloadData()
- }
- }
- private extension String {
- static var networkString: String {
- localized("Network")
- }
- static var audioString: String {
- localized("Audio")
- }
- static var videoString: String {
- localized("Video")
- }
- static var rttString: String {
- localized("Latency")
- }
- static var lossString: String {
- localized("Packet Loss Rate")
- }
- static var bitrateString: String {
- localized("Bitrate")
- }
- static var resolutionString: String {
- localized("Resolution")
- }
- static var frameRateString: String {
- localized("Frame Rate")
- }
- }
|