提交
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user