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

975 lines
37 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:adaptive_dialog/adaptive_dialog.dart';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_easyloading/flutter_easyloading.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/agora/AgoraUtil.dart';
import '../../utils/permission/PermissionService.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.floating.value?.close();
state.memberNameSearchController.dispose();
state.sendMsgController.dispose();
state.pageController.dispose();
stopTime();
leaveMeetingToRtc();
leaveMeetingToSocket();
}
/// 获取会议室Token
Future<void> doHttpGetMeetingToken(bool isInit) async {
BaseStructureResult<String> res =
await getClient().getMeetingToken(state.roomNumber.value);
state.meetingToken.value = res.data!;
if (isInit == true) {
signalRSocket();
}else{
initRtc();
}
}
/// 合并请求
/// 1.获取会议室信息
/// 2.获取会议室所有用户
Future<void> mergeFetch(bool isAgain) async {
try {
if (isAgain == false) {
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>);
} finally {
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;
initRtc();
startTime();
}
/// 获取会议室所有用户
void getMeetingRoomAllUser(List<MeetingRoomUser> meetingRoomUsers) async {
state.users.value = meetingRoomUsers;
state.cacheUsers.value = meetingRoomUsers;
update();
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;
}
update();
}
/// ------------------------------------------------------------------------------发言权限
/// 申请发言权限
Future<void> doHttpApplySpeak() async {
await getClient().applySpeak(state.roomNumber.value);
}
/// 关闭发言权限
Future<void> doHttpCancelSpeak() async {
await getClient().cancelSpeak(
state.meetingRoomInfo.value!.id,
state.meetingRoomInfo.value!.roomNum,
UserStore.to.userInfoEntity.value!.uid);
}
/// 关闭发言权限合并请求
Future<void> cancelSpeak() async {
try {
ToastUtils.showLoading();
await Future.wait(
[doHttpCancelSpeak(), doHttpSetMicr(false), doHttpSetCamer(false)]);
} finally {
ToastUtils.dismiss();
}
}
/// ------------------------------------------------------------------------------麦克风相关(操作)
/// 用户开闭麦
Future<void> doHttpSetMicr(bool isOpenMicrophone) async {
await getClient().setMicr(state.roomNumber.value, isOpenMicrophone,
UserStore.to.userInfoEntity.value!.uid);
}
/// ------------------------------------------------------------------------------视频-屏幕相关(操作)
/// 用户开闭摄像头
Future<void> doHttpSetCamer(bool isOpenCamera) async {
await getClient().setCamera(state.roomNumber.value, isOpenCamera,
UserStore.to.userInfoEntity.value!.uid);
}
/// 设置视频是否开启
void setCameraOpen(bool isOpen) {
state.isOpenCamera.value = isOpen;
}
/// 获取当前全员观看主播
Future<void> doHttpGetTvAnchor() async {
BaseStructureResult res =
await getClient().getTvAnchor(state.roomNumber.value);
state.remoteUid.value = res.data!.toString();
debugPrint("wgs输出===:获取当前全员观看主播${res.data}");
if (res.data!.toString().length != 9) {
// 摄像头
if (state.remoteUid.value == UserStore.to.userInfoEntity.value!.uid) {
debugPrint("wgs输出===:当前全员观看是自己");
// 全员观看主播是自己
// 设置主播ID为0自己
state.remoteUid.value = "0";
// 关闭右上角悬浮窗
state.floating.value?.close();
if (state.isSpeak.value == true && state.isOpenCamera.value == true) {
// 有发言权限且开启了摄像头时,切换页面到视频状态
changePageState(1);
} else {
// 没有发言权限或没开启摄像头时,切换页面到语音状态
changePageState(0);
}
} else {
debugPrint("wgs输出===:当前全员观看不是自己");
// 全员观看主播不是自己
// 判断当前会议室是否存在全员观看主播
var isCurrentUserIsCamera = false;
for (var i = 0; i < state.cacheUsers.value.length; i++) {
if (state.remoteUid.value == state.cacheUsers.value[i].uid &&
state.cacheUsers.value[i].enableCamera == true) {
isCurrentUserIsCamera = true;
}
}
if (isCurrentUserIsCamera == true) {
// 当前会议室存在全员观看主播时,切换页面到视频状态
debugPrint("wgs输出===:当前会议室存在全员观看主播时,切换页面到视频状态");
changePageState(0);
Future.delayed(const Duration(milliseconds: 200), () {
changePageState(1);
});
} else {
// 当前会议室不存在全员观看主播时设置主播ID为空并切换页面到视频状态
debugPrint("wgs输出===当前会议室不存在全员观看主播时设置主播ID为空并切换页面到视频状态");
state.remoteUid.value = "";
changePageState(1);
}
}
} else {
if (state.remoteUid.value ==
UserStore.to.userInfoEntity.value!.screenShareId) {
// 如果是自己在共享
} else {
// 共享不是自己
for (var i = 0; i < state.cacheUsers.value.length; i++) {
if (state.remoteUid.value ==
state.cacheUsers.value[i].screenShareId) {
state.cacheUsers.value[i].enableShare = true;
}
}
}
}
update();
}
/// 设置当前全员观看主播
Future<void> doHttpSetTvAnchor(String id) async {
await getClient().setTvAnchor(state.roomNumber.value, id,
UserStore.to.userInfoEntity.value!.userName);
}
/// 启动共享屏幕(此版本不做)
/*Future<void> startScreenCapture() async {
debugPrint("wgs输出===RTC-启动屏幕共享");
state.isOpenShare.value = true;
final shareShareUid = int.tryParse(UserStore.to.userInfoEntity.value!.screenShareId);
await state.rctEngine.value?.setAudioScenario(AudioScenarioType.audioScenarioGameStreaming);
await state.rctEngine.value?.startScreenCapture(const ScreenCaptureParameters2(captureAudio: true, captureVideo: true));
await state.rctEngine.value?.joinChannelEx(
token: state.meetingToken.value,
connection: RtcConnection(channelId: state.roomNumber.value, localUid: shareShareUid),
options: const ChannelMediaOptions(
autoSubscribeVideo: false,
autoSubscribeAudio: false,
publishScreenTrack: true,
publishSecondaryScreenTrack: true,
publishCameraTrack: false,
publishMicrophoneTrack: false,
publishScreenCaptureAudio: true,
publishScreenCaptureVideo: true,
clientRoleType: ClientRoleType.clientRoleBroadcaster,
));
doHttpSetTvAnchor(UserStore.to.userInfoEntity.value!.screenShareId);
}*/
/// 停止共享屏幕(此版本不做)
/*Future<void> stopScreenCapture() async {
debugPrint("wgs输出===RTC-停止屏幕共享");
state.isOpenShare.value = false;
final shareShareUid = int.tryParse(UserStore.to.userInfoEntity.value!.screenShareId);
await state.rctEngine.value?.stopScreenCapture();
await state.rctEngine.value?.leaveChannelEx(
connection: RtcConnection(
channelId: state.roomNumber.value,
localUid: shareShareUid
),
options: const LeaveChannelOptions(
stopMicrophoneRecording: false,
stopAllEffect: false
));
doHttpSetTvAnchor(UserStore.to.userInfoEntity.value!.uid);
}*/
/// ------------------------------------------------------------------------------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"),
))
// 配置断线重连策略默认重连5次分别间隔2秒 4秒 4秒 5秒 10秒
.withAutomaticReconnect([2000, 4000, 4000, 5000, 10000]).build();
await state.hubConnection.value?.start();
// 开始重新连接时回调
state.hubConnection.value?.onreconnecting((error) {
debugPrint("wgs输出===SignalR Socket-重连$error");
if (EasyLoading.isShow == false) {
ToastUtils.showLoadingToMask("网络故障,正在重连...", EasyLoadingMaskType.black);
}
});
// 重新连接成功时回调5次重连任意一次成功就回调
state.hubConnection.value?.onreconnected((connectionId) {
debugPrint("wgs输出===SignalR Socket-重连成功$connectionId");
joinMeetingToSocket(true);
ToastUtils.dismiss();
if (EasyLoading.isShow == false) {
ToastUtils.showSuccessToMask("重连成功!", EasyLoadingMaskType.black);
}
});
// 重新连接失败时回调5次重连都失败才回调
state.hubConnection.value?.onclose((error) {
debugPrint("wgs输出===SignalR Socket-重连失败$error");
ToastUtils.dismiss();
if (state.isNormaExit.value == false &&
state.isShowOkAlertDialog.value == false) {
showOkAlertDialog(
context: Get.context!,
title: "提示",
message: "网络错误,请重新加入会议室",
okLabel: "确定",
barrierDismissible: false,
).then((OkCancelResult value) {
Get.back();
Get.back();
});
}
});
joinMeetingToSocket(false);
/// ------------------------------------------------------------------------------发言权限相关回调
/// 开启关闭发言权限
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;
debugPrint("wgs输出===Socket-状态:${state.defaulOpenState.value}");
if (state.defaulOpenState.value == 1) {
doHttpSetMicr(true);
} else if (state.defaulOpenState.value == 2) {
doHttpSetCamer(true);
}
// 设置声网SDK角色为主播
setClientRole("主播");
debugPrint("wgs输出===Socket-开启发言权限:主播");
}
} 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.enableCamera = false;
mru.isRoomManager = meetingRoomUser.isRoomManager;
}
}
if (UserStore.to.userInfoEntity.value!.uid == meetingRoomUser.uid) {
state.isSpeak.value = false;
state.defaulOpenState.value = 0;
state.isOpenMicrophone.value = false;
state.isOpenCamera.value = false;
state.isOpenShare.value = false;
// 关闭麦克风视频
doHttpSetMicr(false);
doHttpSetCamer(false);
// 设置声网SDK角色为观众
setClientRole("观众");
// 取消发布视频流
muteLocalVideoStream(true);
// 停止本地预览
stopPreview();
// 关闭本地预览悬浮窗
state.floating.value?.close();
// 当前全员观看主播是自己时
if (state.remoteUid.value == "0") {
state.remoteUid.value = "";
changePageState(0);
}
// 停止共享屏幕(此版本不做)
// stopScreenCapture();
debugPrint("wgs输出===Socket-关闭发言权限:观众");
}
}
update();
});
/// ------------------------------------------------------------------------------麦克风相关回调
/// 所有用户开闭麦回调
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];
update();
});
/// 用户单独开闭麦回调
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-用户单独开麦${meetingRoomUser.uid}-${meetingRoomUser.userName}");
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;
// 恢复发布音频流
muteLocalAudioStream(false);
}
} else {
debugPrint(
"wgs输出===Socket-用户单独闭麦${meetingRoomUser.uid}-${meetingRoomUser.userName}");
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;
// 取消发布音频流
muteLocalAudioStream(true);
}
}
update();
});
/// ------------------------------------------------------------------------------会议室进出相关回调
/// 远端用户或主播加入会议室回调
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;
update();
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;
update();
doHttpGetTvAnchor();
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");
state.isNormaExit.value = true;
ToastUtils.showSuccess("你已被移除会议");
Future.delayed(const Duration(seconds: 1), () {
Get.back();
});
});
/// 全员结束会议回调
state.hubConnection.value?.on("AllLeave", (e) {
debugPrint("wgs输出===Socket-全员结束会议");
state.isNormaExit.value = true;
ToastUtils.showSuccess("会议已结束");
Future.delayed(const Duration(seconds: 1), () {
Get.back();
});
});
/// ------------------------------------------------------------------------------会议室聊天相关回调
/// 会议室接收消息回调
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);
DateTime dateTime = DateTime.now();
MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg(list[0], list[1], list[2],
0, formatDate(dateTime, [HH, ':', nn, ':', ss]));
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("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;
// 恢复发布视频流
muteLocalVideoStream(false);
// 开始本地预览
startPreview();
// 切换页面到视频状态
changePageState(1);
// 只有在视频大屏并且全员观看主播不是自己的时候,才打开右上角悬浮窗
if (state.pageIndex.value == 0 && state.remoteUid.value != "0") {
state.floating.value?.open(state.context.value!);
}
}
} 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;
// 取消发布视频流
muteLocalVideoStream(true);
// 停止本地预览
stopPreview();
// 当前全员观看主播是自己时
if (state.remoteUid.value == "0") {
state.remoteUid.value = "";
}
// 关闭本地预览悬浮窗
if (state.floating.value?.isShowing == true) {
state.floating.value?.close();
}
}
}
update();
});
/// 设置新的全员观看视频主播回调
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);
doHttpGetTvAnchor();
debugPrint("wgs输出===Socket-设置新的全员观看视频主播:${list[0]}");
});
}
/// 加入会议室
/// isAgain是否重新加入
Future<void> joinMeetingToSocket(bool isAgain) async {
await state.hubConnection.value?.invoke("joinChannel",
args: [state.roomNumber.value, false, false, false]);
mergeFetch(isAgain);
/*if(isAgain == false){
mergeFetch(isAgain);
initRtc(false);
}else{
mergeFetch(true);
}*/
}
/// 离开会议室
Future<void> leaveMeetingToSocket() async {
await state.hubConnection.value
?.invoke("levelChannel", args: [state.roomNumber.value]);
state.hubConnection.value?.stop();
}
/// 会议室发送消息
Future<void> sendMsg(String msg) async {
DateTime dateTime = DateTime.now();
MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg(
UserStore.to.userInfoEntity.value!.uid,
UserStore.to.userInfoEntity.value!.userName,
msg,
1,
formatDate(dateTime, [HH, ':', nn, ':', ss]));
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 {
// 请求麦克风、摄像头权限
PermissionService.requestPermissions();
// 创建 RtcEngine 对象
state.rctEngine.value = createAgoraRtcEngineEx();
// 初始化 RtcEngine设置频道场景为 channelProfileLiveBroadcasting直播场景
await state.rctEngine.value?.initialize(RtcEngineContext(
appId: state.appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
// logConfig:const LogConfig()
));
// 音频模块默认启动,所以这里不再调用启动方法
// 启用视频模块
enableVideo();
// 设置默认音频路由为听筒
await state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false);
// 打开用户音量回调
await state.rctEngine.value?.enableAudioVolumeIndication(
interval: 200, smooth: 3, reportVad: true);
// 在发送端设置双流模式并设置视频小流
await state.rctEngine.value
?.setDualStreamMode(mode: SimulcastStreamMode.enableSimulcastStream);
joinMeetingToRtc();
// 回调
state.rctEngine.value?.registerEventHandler(
RtcEngineEventHandler(
// 成功加入会议室回调
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
state.isJoinSuccess.value = true;
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 {
// 判断是否用户取消了共享
if (remoteUid.toString().length == 9) {
for (var i = 0; i < state.cacheUsers.value.length; i++) {
if (remoteUid.toString() ==
state.cacheUsers.value[i].screenShareId) {
state.cacheUsers.value[i].enableShare = false;
}
}
}
update();
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 remoteVideoState,
RemoteVideoStateReason remoteVideoStateReason,
int elapsed) {
debugPrint(
"wgs输出===RTC-远端视频状态发生改变ID-$remoteUid-状态-$remoteVideoStateReason");
if (remoteVideoStateReason ==
RemoteVideoStateReason.remoteVideoStateReasonRemoteMuted) {
// 远端用户停止发送视频流或远端用户禁用视频模块
if (remoteUid.toString().length != 9) {
// 摄像头
if (remoteUid.toString() == state.remoteUid.value) {
// 如果停止发送视频流或禁用视频模块的远端用户是当前全员观看主播
doHttpGetTvAnchor();
}
} else {
// 共享屏幕(此版本不做)
}
}
},
// 用户音量提示回调
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!);
if (avi.volume != 0) {
state.spokesman.value = mru.userName;
state.spokesmanVolume.value =
CountMicrophoneVolume.getVolume(avi.volume!);
} else {
state.spokesman.value = "";
state.spokesmanVolume.value = 0;
}
}
}
}
}
}
},
// 切换用户角色回调
onClientRoleChanged: (RtcConnection connection,
ClientRoleType oldRole,
ClientRoleType newRole,
ClientRoleOptions newRoleOptions) {
debugPrint(
"wgs输出===RTC-切换用户角色为:${newRole == ClientRoleType.clientRoleBroadcaster ? "主播" : "观众"}");
},
// token即将在30秒内过期回调
onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
doHttpGetMeetingToken(false);
},
// 本地视频状态发生改变回调
onLocalVideoStateChanged: (VideoSourceType source,
LocalVideoStreamState state, LocalVideoStreamReason reason) {
debugPrint("wgs输出===RTC-本地视频状态发生改变:$source--$state--$reason");
},
// 网络连接状态回调
onConnectionStateChanged: (RtcConnection connection,
ConnectionStateType stateType,
ConnectionChangedReasonType reason) {
debugPrint("wgs输出===RTC-网络连接状态发生改变:"
"会议室编号(${connection.channelId}"
"网络状态($stateType-${AgoraUtil.getConnectionStateChangedType(stateType)}"
"网络改变原因($reason-${AgoraUtil.getConnectionChangedReasonType(reason)}");
if (stateType == ConnectionStateType.connectionStateReconnecting) {
if (EasyLoading.isShow == false) {
ToastUtils.showLoadingToMask(
"网络故障,正在重连...", EasyLoadingMaskType.black);
}
} else if (stateType == ConnectionStateType.connectionStateConnected &&
reason ==
ConnectionChangedReasonType.connectionChangedRejoinSuccess) {
ToastUtils.dismiss();
if (EasyLoading.isShow == false) {
ToastUtils.showSuccessToMask("重连成功!", EasyLoadingMaskType.black);
}
} else if (reason ==
ConnectionChangedReasonType.connectionChangedLost) {
// 和服务器失去连接后再延迟15秒和signalR Socket一致让SDK继续重连如果重连不上则告知用户需要重新加入
Future.delayed(const Duration(milliseconds: 15000), () {
ToastUtils.dismiss();
if (state.isShowOkAlertDialog.value == false) {
showOkAlertDialog(
context: Get.context!,
title: "提示",
message: "网络错误,请重新加入会议室",
okLabel: "确定",
barrierDismissible: false,
).then((OkCancelResult value) {
Get.back();
Get.back();
});
}
});
}
}
// 获取设备权限出错回调
/*onPermissionError: (PermissionType permissionType){
debugPrint("wgs输出===RTC-获取设备权限出错:$permissionType");
if(permissionType == PermissionType.screenCapture){
// 获取共享屏幕出错(此版本不做)
state.isOpenShare.value = false;
stopScreenCapture();
}
}*/
),
);
}
/// 加入会议室
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> enableVideo() async {
await state.rctEngine.value?.enableVideo();
}
/// 取消或恢复发布本地音频流
Future<void> muteLocalAudioStream(bool mute) async {
await state.rctEngine.value?.muteLocalAudioStream(mute);
}
/// 取消或恢复发布本地视频流
Future<void> muteLocalVideoStream(bool mute) async {
await state.rctEngine.value?.muteLocalVideoStream(mute);
}
/// 开始本地预览
Future<void> startPreview() async {
await state.rctEngine.value?.startPreview();
}
/// 停止本地预览
Future<void> stopPreview() async {
await state.rctEngine.value?.stopPreview();
}
/// 切换前置/后置摄像头
Future<void> switchCamera() async {
await state.rctEngine.value?.switchCamera();
}
}