import 'dart:async'; import 'dart:convert'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:signalr_core/signalr_core.dart'; import 'package:wgshare/common/store/user_store.dart'; import '../../common/config/request_config.dart'; import '../../common/mixins/request_tool_mixin.dart'; import '../../common/models/common/base_structure_result.dart'; import '../../common/models/meeting_room_info.dart'; import '../../common/models/meeting_room_msg.dart'; import '../../common/models/meeting_room_user.dart'; import '../../utils/permission_handler.dart'; import '../../utils/toast_utils.dart'; import 'meeting_main_state.dart'; class MeetingMainLogic extends GetxController with RequestToolMixin{ final MeetingMainState state = MeetingMainState(); @override void onInit() { super.onInit(); // 接收参数 var data = Get.arguments; state.roomNumber.value = data["roomNumber"]; doHttpGetMeetingToken(); } @override void onClose() { super.onClose(); state.memberNameSearchController.dispose(); state.sendMsgController.dispose(); stopTime(); leaveMeetingToRtc(); leaveMeetingToSocket(); state.hubConnection.value?.stop(); } /// 获取会议室Token Future doHttpGetMeetingToken() async { BaseStructureResult res = await getClient().getMeetingToken(state.roomNumber.value); state.meetingToken.value = res.data!; initRtc(); signalRSocket(); } /// 合并请求 /// 1.获取会议室信息 /// 2.获取会议室所有用户 Future mergeFetch() async { ToastUtils.showLoading(); var results = await Future.wait([ getClient().getMeetingRoomInfo(state.roomNumber.value), getClient().getMeetingRoomAllUser(state.roomNumber.value)]); getMeetingRoomInfo(results[0].data as MeetingRoomInfo); getMeetingRoomAllUser(results[1].data as List); ToastUtils.dismiss(); } /// 改变会议信息浮层显示状态 void changeMeetingInfoState(bool isShow){ state.isShowMeetingInfoFloatingLayer.value = isShow; } /// 改变音频选择浮层显示状态 void changeMeetingAudioState(bool isShow){ state.isShowMeetingAudioFloatingLayer.value = isShow; } /// 改变当前页面状态 void changePageState(int pageState){ state.pageState.value = pageState; } /// 获取会议室信息 void getMeetingRoomInfo(MeetingRoomInfo meetingRoomInfo) async { state.meetingRoomInfo.value = meetingRoomInfo; startTime(); } /// 获取会议室所有用户 void getMeetingRoomAllUser(List meetingRoomUsers) async { state.users.value = meetingRoomUsers; state.cacheUsers.value = meetingRoomUsers; } /// 启动计时 void startTime(){ state.stopwatch.value.start(); state.timer.value = Timer.periodic(const Duration(milliseconds: 100), upTime); } /// 更新计时 void upTime(Timer t) { if(state.stopwatch.value.isRunning){ state.duration.value = "${state.stopwatch.value.elapsed.inHours.toString().padLeft(2, "0")}" ":${(state.stopwatch.value.elapsed.inMinutes % 60).toString().padLeft(2, "0")}" ":${(state.stopwatch.value.elapsed.inSeconds % 60).toString().padLeft(2, "0")}"; } } /// 停止计时 void stopTime() { state.timer.value?.cancel(); state.stopwatch.value.stop(); } /// 搜索成员 void searchMember(String value){ if(value.isNotEmpty){ List memberSearchList = []; for(var i = 0; i < state.cacheUsers.value.length; i++){ if(state.cacheUsers.value[i].userName.contains(value)){ memberSearchList.add(state.cacheUsers.value[i]); } } state.users.value = memberSearchList; }else{ state.users.value = state.cacheUsers.value; } } /// 申请发言 Future doHttpApplySpeak() async { BaseStructureResult res = await getClient().applySpeak(state.roomNumber.value); } /// 结束发言 Future doHttpCancelSpeak() async { BaseStructureResult res = await getClient().cancelSpeak(state.meetingRoomInfo.value!.id, state.meetingRoomInfo.value!.roomNum, UserStore.to.userInfoEntity.value!.uid); } /// 设置麦克风是否静音 void setMicrophoneOpen(bool isOpen){ state.isOpenMicrophone.value = isOpen; for(var i = 0; i < state.cacheUsers.value.length; i++){ if(state.cacheUsers.value[i].uid == UserStore.to.userInfoEntity.value!.uid){ state.cacheUsers.value[i].enableMicr = isOpen; } } state.users.value = state.cacheUsers.value; setEnableLocalAudio(isOpen); } /// --------------------------signalR Socket相关 /// Socket长连接 Future signalRSocket() async { state.hubConnection.value = HubConnectionBuilder().withUrl('${RequestConfig().baseUrl}/session-manage', HttpConnectionOptions( transport: HttpTransportType.longPolling, accessTokenFactory: () async => await Future.value(UserStore.to.token), logging: (level, message) => debugPrint("wgs输出===:SignalR Socket-$message"), )).build(); await state.hubConnection.value?.start(); joinMeetingToSocket(); /// 远端用户或主播加入会议室回调 state.hubConnection.value?.on("UserJoined", (user){ var jsonStr = const Utf8Decoder().convert(json.encode(user).runes.toList()); var listDynamic = jsonDecode(jsonStr); List meetingRoomUsers = (listDynamic as List).map((e) => MeetingRoomUser.fromJson((e as Map))).toList(); state.cacheUsers.value.addAll(meetingRoomUsers); state.users.value = state.cacheUsers.value; debugPrint("wgs输出===:Socket-远端用户或主播加入会议室:$jsonStr"); }); /// 远端用户或主播离开会议室回调 state.hubConnection.value?.on("UserLeave", (uid){ var jsonStr = const Utf8Decoder().convert(json.encode(uid).runes.toList()); List listDynamic = jsonDecode(jsonStr); for(String uidStr in listDynamic){ for(var j = 0; j < state.cacheUsers.value.length; j++){ if(state.cacheUsers.value[j].uid == uidStr){ state.cacheUsers.value.removeAt(j); } } } state.users.value = state.cacheUsers.value; debugPrint("wgs输出===:Socket-远端用户或主播离开会议室:$jsonStr"); }); /// 申请/结束发言-管理员同意或拒绝回调 state.hubConnection.value?.on("ManagerRefresh", (e){ var jsonStr = const Utf8Decoder().convert(json.encode(e?[0]).runes.toList()); var listDynamic = jsonDecode(jsonStr); MeetingRoomUser meetingRoomUser = MeetingRoomUser.fromJson(listDynamic); if(meetingRoomUser.isRoomManager == true){ debugPrint("wgs输出===:Socket-申请发言:${e?[0]}--${e?[1]}"); for(MeetingRoomUser mru in state.cacheUsers.value){ if(mru.uid == meetingRoomUser.uid){ mru.roleId = meetingRoomUser.roleId; mru.enableMicr = true; mru.isRoomManager = meetingRoomUser.isRoomManager; } } if(UserStore.to.userInfoEntity.value!.uid == meetingRoomUser.uid){ state.isSpeak.value = true; state.isOpenMicrophone.value = true; setEnableLocalAudio(true); } }else{ debugPrint("wgs输出===:Socket-停止发言:${e?[0]}--${e?[1]}"); for(MeetingRoomUser mru in state.cacheUsers.value){ if(mru.uid == meetingRoomUser.uid){ mru.roleId = meetingRoomUser.roleId; mru.enableMicr = false; mru.isRoomManager = meetingRoomUser.isRoomManager; } } if(UserStore.to.userInfoEntity.value!.uid == meetingRoomUser.uid){ state.isSpeak.value = false; state.isOpenMicrophone.value = false; setEnableLocalAudio(false); } } update(); }); /// 会议室接收消息回调 state.hubConnection.value?.on("ReceiveMessage", (e){ var jsonStr = const Utf8Decoder().convert(json.encode(e).runes.toList()); List list = json.decode(jsonStr); MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg(list[0],list[1],list[2],0); state.meetingRoomMsgs.value.add(meetingRoomMsg); update(); Future.delayed(const Duration(milliseconds: 100), () { state.chatController.jumpTo(state.chatController.position.maxScrollExtent); }); debugPrint("wgs输出===:Socket-会议室接收消息:$jsonStr"); }); /// 被移除会议回调 state.hubConnection.value?.on("ForceExitRoom", (e){ var jsonStr = const Utf8Decoder().convert(json.encode(e).runes.toList()); var listDynamic = jsonDecode(jsonStr); debugPrint("wgs输出===:Socket-被移除会议:$jsonStr"); Get.back(); ToastUtils.showSuccess("你已被移除会议"); }); /// 全员结束会议回调 state.hubConnection.value?.on("AllLeave", (e){ debugPrint("wgs输出===:Socket-全员结束会议"); Get.back(); ToastUtils.showSuccess("会议已结束"); }); } /// 加入会议室 Future joinMeetingToSocket() async { await state.hubConnection.value?.invoke("joinChannel", args: [state.roomNumber.value, false, false, false]); mergeFetch(); } /// 离开会议室 Future leaveMeetingToSocket() async { await state.hubConnection.value?.invoke("levelChannel", args: [state.roomNumber.value]); } /// 会议室发送消息 Future sendMsg(String msg) async { MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg( UserStore.to.userInfoEntity.value!.uid, UserStore.to.userInfoEntity.value!.userName, msg,1); state.meetingRoomMsgs.value.add(meetingRoomMsg); update(); Future.delayed(const Duration(milliseconds: 100), () { state.chatController.jumpTo(state.chatController.position.maxScrollExtent); }); await state.hubConnection.value?.invoke("sendChannelMsg", args: [state.roomNumber.value, msg]); } /// --------------------------声网SDK相关 /// 初始化声网SDK Future initRtc() async { // 请求麦克风、摄像头权限 PermissionHandler.requestCameraPermission(); PermissionHandler.requestMicrophonePermission(); // 创建 RtcEngine 对象 state.rctEngine.value = createAgoraRtcEngine(); // 初始化 RtcEngine,设置频道场景为 channelProfileLiveBroadcasting(直播场景) await state.rctEngine.value?.initialize(RtcEngineContext( appId: state.appId, channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, )); // 设置默认音频路由为听筒 state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false); joinMeetingToRtc(); // 回调 state.rctEngine.value?.registerEventHandler( RtcEngineEventHandler( // 成功加入会议室回调 onJoinChannelSuccess: (RtcConnection connection, int elapsed) { debugPrint("wgs输出===:RTC-自己加入会议室,ID:${connection.localUid}"); }, // 成功离开会议室回调 onLeaveChannel: (RtcConnection connection, RtcStats stats){ debugPrint("wgs输出===:RTC-自己离开会议室,ID:${connection.localUid}"); }, // 远端用户或主播加入当前会议室回调-主播角色才能接收该回调 onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) { debugPrint("wgs输出===:RTC-远端用户或主播加入会议室,用户或主机的ID:$remoteUid"); }, // 远端用户或主播离开当前会议室回调-主播角色才能接收该回调 onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) async { debugPrint("wgs输出===:RTC-远端用户或主播离开会议室,用户或主机的ID:$remoteUid"); }, // 音频路由发生变化回调 onAudioRoutingChanged: (int routing){ debugPrint("wgs输出===:RTC-音频路由切换:$routing"); state.communicationMode.value = routing; if(routing == 1){ debugPrint("wgs输出===:RTC-音频路由切换为听筒"); }else if(routing == 3){ debugPrint("wgs输出===:RTC-音频路由切换为扬声器"); }else{ debugPrint("wgs输出===:RTC-音频路由切换为外接设备"); } }, // 音频采集开关回调 onLocalAudioStateChanged: (RtcConnection connection, LocalAudioStreamState state, LocalAudioStreamReason reason){ debugPrint("wgs输出===:RTC-音频采集开关:$state"); } ), ); } /// 加入会议室 Future joinMeetingToRtc() async { await state.rctEngine.value?.joinChannel( token: state.meetingToken.value, channelId: state.roomNumber.value, uid: 0, options: const ChannelMediaOptions( // 自动订阅所有视频流 autoSubscribeVideo: true, // 自动订阅所有音频流 autoSubscribeAudio: true, // 发布摄像头采集的视频 publishCameraTrack: false, // 发布麦克风采集的音频 publishMicrophoneTrack: false, // 设置用户角色为 clientRoleBroadcaster(主播)或 clientRoleAudience(观众) // 这里设置角色为clientRoleBroadcaster(主播) // 主播:可以在频道内发布音视频,同时也可以订阅其他主播发布的音视频 // 观众:可以在频道内订阅音视频,不具备发布音视频权限 clientRoleType: ClientRoleType.clientRoleBroadcaster), ); } /// 离开会议室 Future leaveMeetingToRtc() async { // 离开 await state.rctEngine.value?.leaveChannel(); // 释放资源 await state.rctEngine.value?.release(); } /// 设置音频输出路由(没有外接设备时生效) Future setEnableSpeakerphone(int mode) async { state.communicationMode.value = mode; await state.rctEngine.value?.setEnableSpeakerphone(mode == 1 ? false : true); } /// 设置是否打开本地音频采集 Future setEnableLocalAudio(bool enabled) async { await state.rctEngine.value?.enableLocalAudio(enabled); } }