WGShare.Mobile.Flutter/wgshare/lib/pages/metting/meeting_main_logic.dart

657 lines
23 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 'package:wgshare/utils/count_microphone_volume.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(true);
}
@override
void onClose() {
super.onClose();
state.memberNameSearchController.dispose();
state.sendMsgController.dispose();
stopTime();
leaveMeetingToRtc();
leaveMeetingToSocket();
state.hubConnection.value?.stop();
}
/// 获取会议室Token
Future<void> doHttpGetMeetingToken(bool isInit) async {
BaseStructureResult<String> res = await getClient().getMeetingToken(state.roomNumber.value);
state.meetingToken.value = res.data!;
if(isInit == true){
initRtc();
signalRSocket();
}
}
/// 合并请求
/// 1.获取会议室信息
/// 2.获取会议室所有用户
Future<void> 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<MeetingRoomUser>);
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<MeetingRoomUser> meetingRoomUsers) async {
state.users.value = meetingRoomUsers;
state.cacheUsers.value = meetingRoomUsers;
doHttpGetTvAnchor();
}
/// 启动计时
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<MeetingRoomUser> 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<void> doHttpApplySpeak() async {
BaseStructureResult res = await getClient().applySpeak(state.roomNumber.value);
}
/// 用户开闭麦
Future<void> doHttpSetMicr() async {
BaseStructureResult res = await getClient().setMicr(state.roomNumber.value, state.isOpenMicrophone.value, UserStore.to.userInfoEntity.value!.uid);
}
/// 用户开闭摄像头
Future<void> doHttpSetCamer() async {
BaseStructureResult res = await getClient().setCamera(state.roomNumber.value, state.isOpenCamera.value, UserStore.to.userInfoEntity.value!.uid);
}
/// 结束发言
Future<void> doHttpCancelSpeak() async {
BaseStructureResult res = await getClient().cancelSpeak(state.meetingRoomInfo.value!.id, state.meetingRoomInfo.value!.roomNum, UserStore.to.userInfoEntity.value!.uid);
setClientRole("观众");
setMicrophoneOpen(false);
setCameraOpen(false);
changePageState(0);
}
/// 设置麦克风是否开启
Future<void> setMicrophoneOpen(bool isOpen) async{
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);
doHttpSetMicr();
}
/// 设置视频是否打开
void setCameraOpen(bool isOpen){
if(isOpen == true){
state.isOpenCamera.value = true;
setEnableVideo();
setStartPreview();
setClientRole("主播");
changePageState(1);
}else{
state.isOpenCamera.value = false;
setDisablePreview();
}
if(state.isOpenMicrophone.value == false){
setMicrophoneOpen(isOpen);
}
doHttpSetCamer();
}
/// 挂断视频
void hangUpVideo(){
setDisableVideo();
setDisablePreview();
state.isOpenCamera.value = false;
changePageState(0);
doHttpSetCamer();
}
/// 获取当前全员观看视频主播
Future<void> doHttpGetTvAnchor() async {
BaseStructureResult res = await getClient().getTvAnchor(state.roomNumber.value);
/*var s = 2;
// if(res.data!.toString().length != 9){
if(s == 2){
state.remoteUid.value = "18";
setEnableVideo();
changePageState(1);
state.isOpenCamera.value = true;
setEnableVideo();
setClientRole("主播");
changePageState(1);
*//*var isSaveLive = false;
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == state.remoteUid.value){
isSaveLive = true;
}
}
if(isSaveLive == true){
changePageState(1);
}*//*
}*/
Future.delayed(const Duration(milliseconds: 500), () {
state.remoteUid.value = "18";
setEnableVideo();
changePageState(1);
});
}
/// --------------------------signalR Socket相关
/// Socket长连接
Future<void> signalRSocket() async {
state.hubConnection.value = HubConnectionBuilder()
.withUrl('${RequestConfig().baseUrl}/session-manage', HttpConnectionOptions(
transport: HttpTransportType.webSockets,
accessTokenFactory: () async => await Future.value(UserStore.to.token),
logging: (level, message) => debugPrint("wgs输出===SignalR Socket-$message"),
))
// 配置断线重连策略默认重连4次分别间隔10秒 4秒 10秒 10秒
.withAutomaticReconnect([10000, 4000, 10000, 10000]).build();
await state.hubConnection.value?.start();
joinMeetingToSocket();
/// 远端用户或主播加入会议室回调
state.hubConnection.value?.on("UserJoined", (e){
// var jsonStr = const Utf8Decoder().convert(json.encode(user).runes.toList());
var jsonStr = json.encode(e);
var listDynamic = jsonDecode(jsonStr);
List<MeetingRoomUser> meetingRoomUsers = (listDynamic as List<dynamic>).map((e) => MeetingRoomUser.fromJson((e as Map<String,dynamic>))).toList();
state.cacheUsers.value.addAll(meetingRoomUsers);
state.users.value = state.cacheUsers.value;
debugPrint("wgs输出===Socket-远端用户或主播加入会议室:$jsonStr");
});
/// 远端用户或主播离开会议室回调
state.hubConnection.value?.on("UserLeave", (e){
// var jsonStr = const Utf8Decoder().convert(json.encode(uid).runes.toList());
var jsonStr = json.encode(e);
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 jsonStr = json.encode(e?[0]);
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;
setClientRole("主播");
setEnableLocalAudio(true);
doHttpSetMicr();
}
}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;
setClientRole("观众");
setEnableLocalAudio(false);
doHttpSetMicr();
}
}
update();
});
/// 会议室接收消息回调
state.hubConnection.value?.on("ReceiveMessage", (e){
// var jsonStr = const Utf8Decoder().convert(json.encode(e).runes.toList());
var jsonStr = json.encode(e);
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 jsonStr = json.encode(e);
debugPrint("wgs输出===Socket-被移除会议:$jsonStr");
Get.back();
ToastUtils.showSuccess("你已被移除会议");
});
/// 全员结束会议回调
state.hubConnection.value?.on("AllLeave", (e){
debugPrint("wgs输出===Socket-全员结束会议");
Get.back();
ToastUtils.showSuccess("会议已结束");
});
/// 所有用户开闭麦回调
state.hubConnection.value?.on("OperAllMicr", (e){
if(e?[0] == true){
debugPrint("wgs输出===Socket-全员开麦");
}else{
debugPrint("wgs输出===Socket-全员闭麦");
}
for(var i = 0; i < state.cacheUsers.value.length; i++){
state.cacheUsers.value[i].enableMicr = e?[0];
}
state.users.value = state.cacheUsers.value;
state.isOpenMicrophone.value = e?[0];
});
/// 单独用户开闭麦回调
state.hubConnection.value?.on("OperMicr", (e){
// var jsonStr = const Utf8Decoder().convert(json.encode(e?[0]).runes.toList());
var jsonStr = json.encode(e?[0]);
var listDynamic = jsonDecode(jsonStr);
MeetingRoomUser meetingRoomUser = MeetingRoomUser.fromJson(listDynamic);
if(meetingRoomUser.enableMicr == true){
debugPrint("wgs输出===Socket-单独用户开麦");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.enableMicr = true;
}
}
if(meetingRoomUser.uid == UserStore.to.userInfoEntity.value!.uid) {
state.isOpenMicrophone.value = true;
}
}else{
debugPrint("wgs输出===Socket-单独用户闭麦");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.enableMicr = false;
}
}
if(meetingRoomUser.uid == UserStore.to.userInfoEntity.value!.uid) {
state.isOpenMicrophone.value = false;
}
}
});
/// 单独用户开闭摄像头回调
state.hubConnection.value?.on("OperCamera", (e){
// var jsonStr = const Utf8Decoder().convert(json.encode(e?[0]).runes.toList());
var jsonStr = json.encode(e?[0]);
var listDynamic = jsonDecode(jsonStr);
MeetingRoomUser meetingRoomUser = MeetingRoomUser.fromJson(listDynamic);
if(meetingRoomUser.enableCamera == true){
debugPrint("wgs输出===Socket-单独用户开摄像头");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.enableCamera = true;
}
}
if(meetingRoomUser.uid == UserStore.to.userInfoEntity.value!.uid){
state.isOpenCamera.value = true;
}
}else{
debugPrint("wgs输出===Socket-单独用户闭摄像头");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.enableCamera = false;
}
}
if(meetingRoomUser.uid == UserStore.to.userInfoEntity.value!.uid){
state.isOpenCamera.value = false;
}
}
});
/// 设置新的全员观看视频主播回调
state.hubConnection.value?.on("ShowUser", (e){
// var jsonStr = const Utf8Decoder().convert(json.encode(e).runes.toList());
var jsonStr = json.encode(e);
List list = json.decode(jsonStr);
/*if(list[0].toString().length != 9){
state.remoteUid.value = list[0].toString();
}*/
doHttpGetTvAnchor();
debugPrint("wgs输出===Socket-设置新的全员观看视频主播:${list[0]}");
});
}
/// 加入会议室
Future<void> joinMeetingToSocket() async {
await state.hubConnection.value?.invoke("joinChannel", args: [state.roomNumber.value, false, false, false]);
mergeFetch();
}
/// 离开会议室
Future<void> leaveMeetingToSocket() async {
await state.hubConnection.value?.invoke("levelChannel", args: [state.roomNumber.value]);
}
/// 会议室发送消息
Future<void> 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<void> 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,
));
// 设置默认音频路由为听筒
await state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false);
// 打开用户音量回调
await state.rctEngine.value?.enableAudioVolumeIndication(interval: 200, smooth: 3, reportVad: true);
// 启用音频模块
setEnableLocalAudio(true);
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");
},
// 视频采集开关回调
onRemoteVideoStateChanged: (RtcConnection connection,
int remoteUid,
RemoteVideoState state,
RemoteVideoStateReason reason,
int elapsed){
debugPrint("wgs输出===RTC-视频采集开关:$state");
},
// 用户音量提示回调
onAudioVolumeIndication: (
RtcConnection connection,
List<AudioVolumeInfo> speakers,
int speakerNumber,
int totalVolume){
if(speakers.isNotEmpty){
for(AudioVolumeInfo avi in speakers){
for(MeetingRoomUser mru in state.cacheUsers.value){
if(avi.uid == 0){
//debugPrint("wgs输出===RTC-用户音量提示(自己):${CountMicrophoneVolume.getVolume(avi.volume!)}");
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
state.microphoneVolume.value = CountMicrophoneVolume.getVolume(avi.volume!);
}else{
if(avi.uid.toString() == mru.uid){
//debugPrint("wgs输出===RTC-用户音量提示(远端用户):${speakers[0].uid}--${speakers[0].volume}");
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
}
}
}
}
}
},
// 切换用户角色回调
onClientRoleChanged: (
RtcConnection connection,
ClientRoleType oldRole,
ClientRoleType newRole,
ClientRoleOptions newRoleOptions){
debugPrint("wgs输出===RTC-切换用户角色为:${newRole == ClientRoleType.clientRoleBroadcaster ? "主播" : "观众"}");
},
// token即将在30秒内过期
onTokenPrivilegeWillExpire: (RtcConnection connection, String token){
doHttpGetMeetingToken(false);
}
),
);
}
/// 加入会议室
Future<void> joinMeetingToRtc() async {
await state.rctEngine.value?.joinChannel(
token: state.meetingToken.value,
channelId: state.roomNumber.value,
uid: int.parse(UserStore.to.userInfoEntity.value!.uid),
options: const ChannelMediaOptions(
// 自动订阅所有视频流
autoSubscribeVideo: true,
// 自动订阅所有音频流
autoSubscribeAudio: true,
// 发布摄像头采集的视频
publishCameraTrack: true,
// 发布麦克风采集的音频
publishMicrophoneTrack: true,
// 设置用户角色为 clientRoleBroadcaster主播或 clientRoleAudience观众
// 这里设置角色为clientRoleBroadcaster主播
// 主播:可以在频道内发布音视频,同时也可以订阅其他主播发布的音视频
// 观众:可以在频道内订阅音视频,不具备发布音视频权限
clientRoleType: ClientRoleType.clientRoleAudience),
);
}
/// 离开会议室
Future<void> leaveMeetingToRtc() async {
// 离开
await state.rctEngine.value?.leaveChannel();
// 释放资源
await state.rctEngine.value?.release();
}
/// 设置用户角色
Future<void> setClientRole(String roleStr) async {
if(roleStr == "主播"){
await state.rctEngine.value?.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
}else{
await state.rctEngine.value?.setClientRole(role: ClientRoleType.clientRoleAudience);
}
}
/// 设置音频输出路由(没有外接设备时生效)
Future<void> setEnableSpeakerphone(int mode) async {
state.communicationMode.value = mode;
await state.rctEngine.value?.setEnableSpeakerphone(mode == 1 ? false : true);
}
/// 设置是否启用音频模块
Future<void> setEnableLocalAudio(bool enabled) async {
if(enabled == true){
// 启用音频模块
await state.rctEngine.value?.enableAudio();
}else{
// 关闭音频模块
await state.rctEngine.value?.disableAudio();
}
}
/// 设置启用视频模块
Future<void> setEnableVideo() async {
await state.rctEngine.value?.enableVideo();
}
/// 设置关闭视频模块
Future<void> setDisableVideo() async {
await state.rctEngine.value?.disableVideo();
}
/// 设置启用本地预览
Future<void> setStartPreview() async {
await state.rctEngine.value?.startPreview();
}
/// 设置关闭本地预览
Future<void> setDisablePreview() async {
await state.rctEngine.value?.stopPreview();
}
/// 切换前置/后置摄像头
Future<void> switchCamera() async {
await state.rctEngine.value?.switchCamera();
}
}