814 lines
30 KiB
Dart
814 lines
30 KiB
Dart
import 'dart:async';
|
||
import 'dart:convert';
|
||
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
||
import 'package:flutter/cupertino.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.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){
|
||
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 {
|
||
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> 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();
|
||
|
||
if(res.data!.toString().length != 9){
|
||
// 摄像头
|
||
// 判断自己是不是全员观看主播
|
||
if(state.remoteUid.value == UserStore.to.userInfoEntity.value!.uid) {
|
||
state.isSelf.value = true;
|
||
}else{
|
||
state.isSelf.value = false;
|
||
}
|
||
|
||
if(state.isSelf.value == true){
|
||
// 如果自己是全员观看主播
|
||
if(state.isOpenCamera.value == true){
|
||
// 有发言权限且开了摄像头的时候才切换页面到视频状态
|
||
changePageState(1);
|
||
state.floating.value?.close();
|
||
}else{
|
||
// 否则切换页面到语音状态
|
||
changePageState(0);
|
||
}
|
||
}else{
|
||
// 如果自己不是全员观看主播
|
||
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){
|
||
changePageState(1);
|
||
}else{
|
||
changePageState(0);
|
||
}
|
||
}
|
||
}else{
|
||
// 共享屏幕
|
||
// 判断是不是自己在共享
|
||
if(state.remoteUid.value != UserStore.to.userInfoEntity.value!.screenShareId){
|
||
state.isSelf.value = false;
|
||
}else{
|
||
state.isSelf.value = true;
|
||
}
|
||
|
||
// 不是自己在共享才切换页面状态
|
||
if(state.isSelf.value == false){
|
||
changePageState(1);
|
||
}
|
||
}
|
||
/*if(res.data!.toString().length != 9){
|
||
if(state.remoteUid.value != UserStore.to.userInfoEntity.value!.uid) {
|
||
state.isSelf.value = false;
|
||
}else{
|
||
state.isSelf.value = true;
|
||
}
|
||
if(state.isSelf.value == true){
|
||
if(state.isOpenCamera.value == true){
|
||
changePageState(1);
|
||
}else{
|
||
changePageState(0);
|
||
}
|
||
state.floating.value?.close();
|
||
}else{
|
||
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){
|
||
changePageState(1);
|
||
}else{
|
||
changePageState(0);
|
||
}
|
||
}
|
||
}else{
|
||
if(state.remoteUid.value != UserStore.to.userInfoEntity.value!.screenShareId){
|
||
state.isSelf.value = false;
|
||
}else{
|
||
state.isSelf.value = true;
|
||
}
|
||
|
||
if(state.isSelf.value == false){
|
||
changePageState(1);
|
||
}
|
||
}*/
|
||
}
|
||
|
||
/// 设置当前全员观看主播
|
||
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"),
|
||
))
|
||
// 配置断线重连策略:默认重连4次,分别间隔10秒 4秒 10秒 10秒
|
||
.withAutomaticReconnect([10000, 4000, 10000, 10000]).build();
|
||
|
||
await state.hubConnection.value?.start();
|
||
|
||
joinMeetingToSocket();
|
||
|
||
/// ------------------------------------------------------------------------------发言权限相关回调
|
||
/// 开启关闭发言权限
|
||
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;
|
||
// 设置声网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.isRoomManager = meetingRoomUser.isRoomManager;
|
||
}
|
||
}
|
||
if(UserStore.to.userInfoEntity.value!.uid == meetingRoomUser.uid){
|
||
state.isSpeak.value = false;
|
||
state.isOpenMicrophone.value = false;
|
||
state.isOpenCamera.value = false;
|
||
state.isOpenShare.value = false;
|
||
state.isSelf.value = false;
|
||
state.remoteUid.value = "";
|
||
|
||
// 设置声网SDK角色为观众
|
||
setClientRole("观众");
|
||
// 取消发布视频流
|
||
muteLocalVideoStream(true);
|
||
// 停止本地预览
|
||
stopPreview();
|
||
// 关闭本地预览悬浮窗
|
||
state.floating.value?.close();
|
||
// 切换页面状态
|
||
changePageState(0);
|
||
// 停止共享屏幕
|
||
stopScreenCapture();
|
||
|
||
|
||
debugPrint("wgs输出===:Socket-关闭发言权限:观众");
|
||
}
|
||
}
|
||
});
|
||
|
||
/// ------------------------------------------------------------------------------麦克风相关回调
|
||
/// 所有用户开闭麦回调
|
||
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-用户单独开麦${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);
|
||
}
|
||
}
|
||
});
|
||
|
||
/// ------------------------------------------------------------------------------会议室进出相关回调
|
||
/// 远端用户或主播加入会议室回调
|
||
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);
|
||
doHttpGetTvAnchor();
|
||
}
|
||
}
|
||
}
|
||
state.users.value = state.cacheUsers.value;
|
||
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("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("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();
|
||
// 只有在视频大屏,并且全员观看不是自己的时候
|
||
if(state.pageIndex.value == 0 && state.isSelf.value == false){
|
||
// 打开本地预览悬浮窗
|
||
state.floating.value?.open(state.context.value!);
|
||
}
|
||
changePageState(1);
|
||
}
|
||
}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;
|
||
|
||
// 当前全员观看是自己时
|
||
if(state.isSelf.value == true){
|
||
changePageState(0);
|
||
state.isSelf.value = false;
|
||
}
|
||
|
||
// 取消发布视频流
|
||
muteLocalVideoStream(true);
|
||
// 停止本地预览
|
||
stopPreview();
|
||
// 关闭本地预览悬浮窗
|
||
state.floating.value?.close();
|
||
}else{
|
||
if(meetingRoomUser.uid == state.remoteUid.value || meetingRoomUser.screenShareId == state.remoteUid.value){
|
||
if(state.isOpenCamera.value == true){
|
||
state.remoteUid.value = "";
|
||
changePageState(0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
/// 设置新的全员观看视频主播回调
|
||
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]);
|
||
state.hubConnection.value?.stop();
|
||
}
|
||
|
||
/// 会议室发送消息
|
||
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 = 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);
|
||
|
||
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!);
|
||
|
||
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");
|
||
},
|
||
|
||
// 获取设备权限出错回调
|
||
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();
|
||
}
|
||
}
|