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

213 lines
9.1 KiB
Swift

//
// RoomVideoFloatViewModel.swift
// TUIRoomKit
//
// Created by janejntang on 2023/7/11.
//
import Foundation
import RTCRoomEngine
protocol RoomVideoFloatViewResponder: NSObject {
func updateUserStatus(user: UserEntity)
func updateUserAudioVolume(hasAudio: Bool, volume: Int)
func makeToast(text: String)
func showAvatarImageView(isShow: Bool)
}
class RoomVideoFloatViewModel: NSObject {
var userId: String = ""
var streamType: TUIVideoStreamType = .cameraStream
weak var renderView: UIView?
weak var viewResponder: RoomVideoFloatViewResponder?
var engineManager: EngineManager {
EngineManager.shared
}
var roomInfo: TUIRoomInfo {
engineManager.store.roomInfo
}
var currentUser: UserEntity {
engineManager.store.currentUser
}
override init() {
super.init()
subscribeEngine()
subLogoutNotification()
}
private func subscribeEngine() {
EngineEventCenter.shared.subscribeEngine(event: .onUserVideoStateChanged, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onRoomDismissed, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onKickedOutOfRoom, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onUserAudioStateChanged, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onUserVoiceVolumeChanged, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onKickedOffLine, observer: self)
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, responder: self)
}
private func unsubscribeEngine() {
EngineEventCenter.shared.unsubscribeEngine(event: .onUserVideoStateChanged, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onRoomDismissed, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onKickedOutOfRoom, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onUserAudioStateChanged, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onUserVoiceVolumeChanged, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onKickedOffLine, observer: self)
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, responder: self)
}
private func subLogoutNotification() {
NotificationCenter.default.addObserver(self,
selector: #selector(dismissFloatViewForLogout),
name: NSNotification.Name.TUILogoutSuccess, object: nil)
}
private func unsubLogoutNotification() {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.TUILogoutSuccess, object: nil)
}
func showRoomMainView() {
if engineManager.store.isEnteredRoom {
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ShowRoomMainView, param: [:])
}
}
func showFloatWindowViewVideo(renderView: UIView?) {
self.renderView = renderView
if let userModel = getScreenUserModel() { //If someone is screen sharing, show the screen share first
showScreenStream(userModel: userModel)
} else { //Show host without screen sharing
showCameraStream()
}
}
func getUserEntity(userId: String) -> UserEntity? {
return engineManager.store.attendeeList.first(where: { $0.userId == userId })
}
@objc private func dismissFloatViewForLogout() {
RoomVideoFloatView.dismiss()
}
deinit {
unsubscribeEngine()
unsubLogoutNotification()
debugPrint("deinit \(self)")
}
}
extension RoomVideoFloatViewModel {
private func getScreenUserModel() -> UserEntity? {
return engineManager.store.attendeeList.first(where: { $0.hasScreenStream == true })
}
private func showScreenStream(userModel: UserEntity) {
let streamType: TUIVideoStreamType = userModel.userId == currentUser.userId ? .cameraStream : .screenStream
startPlayVideo(userId: userModel.userId, streamType: streamType)
changePlayingState(userId: userModel.userId, streamType: streamType)
viewResponder?.updateUserStatus(user: userModel)
viewResponder?.showAvatarImageView(isShow: false)
}
private func showCameraStream() {
guard let userModel = getUserEntity(userId: roomInfo.ownerId) ?? getUserEntity(userId: currentUser.userId) else { return }
changePlayingState(userId: userModel.userId, streamType: .cameraStream)
viewResponder?.updateUserStatus(user: userModel)
if userModel.hasVideoStream {
startPlayVideo(userId: userModel.userId, streamType: .cameraStream)
} else {
viewResponder?.showAvatarImageView(isShow: true)
}
}
private func startPlayVideo(userId: String, streamType: TUIVideoStreamType) {
if userId == currentUser.userId {
engineManager.setLocalVideoView(streamType: streamType, view: renderView)
} else {
engineManager.setRemoteVideoView(userId: userId, streamType: streamType, view: renderView)
engineManager.startPlayRemoteVideo(userId: userId, streamType: streamType)
}
viewResponder?.showAvatarImageView(isShow: false)
}
private func stopPlayVideo(userId: String, streamType: TUIVideoStreamType) {
if userId == currentUser.userId {
engineManager.setLocalVideoView(streamType: streamType, view: nil)
return
}
engineManager.setRemoteVideoView(userId: userId, streamType: streamType, view: nil)
guard let userItem = getUserEntity(userId: userId) else { return }
if streamType == .screenStream, userItem.hasScreenStream {
engineManager.stopPlayRemoteVideo(userId: userId, streamType: .screenStream)
} else if streamType == .cameraStream, userItem.hasVideoStream {
engineManager.stopPlayRemoteVideo(userId: userId, streamType: .cameraStream)
}
}
private func changePlayingState(userId: String, streamType: TUIVideoStreamType) {
self.userId = userId
self.streamType = streamType
}
}
extension RoomVideoFloatViewModel: RoomEngineEventResponder {
func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
switch name {
case .onKickedOutOfRoom, .onRoomDismissed:
engineManager.destroyEngineManager()
RoomVideoFloatView.dismiss()
case .onUserVideoStateChanged:
guard let userId = param?["userId"] as? String else { return }
guard let streamType = param?["streamType"] as? TUIVideoStreamType else { return }
guard let hasVideo = param?["hasVideo"] as? Bool else { return }
if streamType == .screenStream {
if hasVideo {
stopPlayVideo(userId: roomInfo.ownerId, streamType: .cameraStream)
guard let userModel = getUserEntity(userId: userId) else { return }
showScreenStream(userModel: userModel)
} else {
stopPlayVideo(userId: self.userId, streamType: .screenStream)
showCameraStream()
}
return
}
guard getScreenUserModel() == nil else { return } //If someone is screen sharing, don't show the host screen
guard userId == roomInfo.ownerId else { return }
if hasVideo {
startPlayVideo(userId: userId, streamType: streamType)
} else {
viewResponder?.showAvatarImageView(isShow: true)
}
case .onUserAudioStateChanged:
guard let userId = param?["userId"] as? String else { return }
guard let hasAudio = param?["hasAudio"] as? Bool else { return }
guard userId == self.userId else { return }
var volume = 0
if let userModel = getUserEntity(userId: self.userId) {
volume = userModel.userVoiceVolume
}
viewResponder?.updateUserAudioVolume(hasAudio: hasAudio, volume: volume)
case .onUserVoiceVolumeChanged:
guard let volumeNumber = param?[self.userId] as? NSNumber else { return }
guard let userModel = getUserEntity(userId: self.userId) else { return }
viewResponder?.updateUserAudioVolume(hasAudio: userModel.hasAudioStream, volume: volumeNumber.intValue)
case .onKickedOffLine:
RoomVideoFloatView.dismiss()
default: break
}
}
}
extension RoomVideoFloatViewModel: RoomKitUIEventResponder {
func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) {
switch key {
case .TUIRoomKitService_RoomOwnerChanged:
guard getScreenUserModel() == nil else { return } //If someone is screen sharing, don't show the host screen
stopPlayVideo(userId: self.userId, streamType: .cameraStream)
showCameraStream()
default: break
}
}
}