1.全员观看http接口以及socket回调

2.用户开闭麦接口
3.用户开闭摄像头接口
4.视频-右上角观看自己
5.视频-大屏远端观看别人
This commit is contained in:
fuenmao 2024-12-02 14:59:14 +08:00
parent e1e6550e31
commit 3a3f9cb665
5 changed files with 185 additions and 89 deletions

View File

@ -67,4 +67,26 @@ abstract class RetrofitClient {
@Field("roomNum") String roomNum, @Field("roomNum") String roomNum,
@Field("userId") String userId, @Field("userId") String userId,
); );
///
@GET("/room/show-user")
Future<BaseStructureResult<String>> getTvAnchor(
@Query("roomNum") String roomNum,
);
///
@GET("/room/oper-micr")
Future<BaseStructureResult<String>> setMicr(
@Query("roomNum") String roomNum,
@Query("enableMicr") bool enableMicr,
@Query("uid") String uid,
);
///
@GET("/room/oper-camera")
Future<BaseStructureResult<String>> setCamera(
@Query("roomNum") String roomNum,
@Query("enableCamera") bool enableCamera,
@Query("uid") String uid,
);
} }

View File

@ -6,6 +6,7 @@ import 'package:get/get.dart';
import 'package:signalr_core/signalr_core.dart'; import 'package:signalr_core/signalr_core.dart';
import 'package:wgshare/common/store/user_store.dart'; import 'package:wgshare/common/store/user_store.dart';
import 'package:wgshare/utils/count_microphone_volume.dart'; import 'package:wgshare/utils/count_microphone_volume.dart';
import 'package:wgshare/utils/storage.dart';
import '../../common/config/request_config.dart'; import '../../common/config/request_config.dart';
import '../../common/mixins/request_tool_mixin.dart'; import '../../common/mixins/request_tool_mixin.dart';
import '../../common/models/common/base_structure_result.dart'; import '../../common/models/common/base_structure_result.dart';
@ -135,14 +136,27 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
BaseStructureResult res = await getClient().applySpeak(state.roomNumber.value); 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 { Future<void> doHttpCancelSpeak() async {
BaseStructureResult res = await getClient().cancelSpeak(state.meetingRoomInfo.value!.id, state.meetingRoomInfo.value!.roomNum, UserStore.to.userInfoEntity.value!.uid); BaseStructureResult res = await getClient().cancelSpeak(state.meetingRoomInfo.value!.id, state.meetingRoomInfo.value!.roomNum, UserStore.to.userInfoEntity.value!.uid);
setClientRole("观众"); setClientRole("观众");
setMicrophoneOpen(false);
setCameraOpen(false);
changePageState(0);
} }
/// ///
void setMicrophoneOpen(bool isOpen){ Future<void> setMicrophoneOpen(bool isOpen) async{
state.isOpenMicrophone.value = isOpen; state.isOpenMicrophone.value = isOpen;
for(var i = 0; i < state.cacheUsers.value.length; i++){ for(var i = 0; i < state.cacheUsers.value.length; i++){
if(state.cacheUsers.value[i].uid == UserStore.to.userInfoEntity.value!.uid){ if(state.cacheUsers.value[i].uid == UserStore.to.userInfoEntity.value!.uid){
@ -150,21 +164,42 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
} }
} }
state.users.value = state.cacheUsers.value; state.users.value = state.cacheUsers.value;
if(isOpen == true){ setEnableLocalAudio(isOpen);
setClientRole("主播"); doHttpSetMicr();
}else{
setClientRole("观众");
}
} }
/// ///
void setCameraOpen(bool isOpen){ void setCameraOpen(bool isOpen){
state.isOpenCamera.value = isOpen; state.isOpenCamera.value = isOpen;
setEnableLocalVideo(isOpen);
if(state.isOpenMicrophone.value == false){
setMicrophoneOpen(isOpen);
}
if(isOpen == true){ if(isOpen == true){
setEnableLocalVideo(isOpen);
setClientRole("主播"); setClientRole("主播");
changePageState(1);
doHttpGetTvAnchor();
}else{ }else{
setClientRole("观众"); changePageState(0);
}
doHttpSetCamer();
}
///
Future<void> doHttpGetTvAnchor() async {
BaseStructureResult res = await getClient().getTvAnchor(state.roomNumber.value);
if(res.data!.toString().length != 9){
state.remoteUid.value = res.data!;
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);
}*/
} }
} }
@ -230,6 +265,8 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
state.isSpeak.value = true; state.isSpeak.value = true;
state.isOpenMicrophone.value = true; state.isOpenMicrophone.value = true;
setClientRole("主播"); setClientRole("主播");
setEnableLocalAudio(true);
doHttpSetMicr();
} }
}else{ }else{
debugPrint("wgs输出===Socket-停止发言:${e?[0]}--${e?[1]}"); debugPrint("wgs输出===Socket-停止发言:${e?[0]}--${e?[1]}");
@ -244,6 +281,8 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
state.isSpeak.value = false; state.isSpeak.value = false;
state.isOpenMicrophone.value = false; state.isOpenMicrophone.value = false;
setClientRole("观众"); setClientRole("观众");
setEnableLocalAudio(false);
doHttpSetMicr();
} }
} }
update(); update();
@ -317,6 +356,18 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
state.isOpenMicrophone.value = false; state.isOpenMicrophone.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]}");
});
} }
/// ///
@ -365,7 +416,7 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
// //
await state.rctEngine.value?.enableAudioVolumeIndication(interval: 200, smooth: 3, reportVad: true); await state.rctEngine.value?.enableAudioVolumeIndication(interval: 200, smooth: 3, reportVad: true);
// //
await state.rctEngine.value?.enableAudio(); setEnableLocalAudio(true);
joinMeetingToRtc(); joinMeetingToRtc();
@ -503,9 +554,19 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
await state.rctEngine.value?.setEnableSpeakerphone(mode == 1 ? false : true); 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> setEnableLocalVideo(bool enabled) async { Future<void> setEnableLocalVideo(bool enabled) async {
state.isOpenCamera.value = enabled;
if(enabled == true){ if(enabled == true){
// //
await state.rctEngine.value?.enableVideo(); await state.rctEngine.value?.enableVideo();

View File

@ -59,6 +59,8 @@ class MeetingMainState {
late RxDouble microphoneVolume = 0.0.obs; late RxDouble microphoneVolume = 0.0.obs;
/// ///
late RxBool isOpenCamera = false.obs; late RxBool isOpenCamera = false.obs;
/// ID
late RxString remoteUid = "".obs;
/// ///
late RxList<MeetingRoomMsg> meetingRoomMsgs = RxList([]); late RxList<MeetingRoomMsg> meetingRoomMsgs = RxList([]);

View File

@ -177,7 +177,12 @@ class MeetingMainPage extends StatelessWidget {
Visibility( Visibility(
visible: state.pageState.value == 1, visible: state.pageState.value == 1,
child: null != state.rctEngine.value child: null != state.rctEngine.value
? MeetingMainVideoComponent(rtcEngine: state.rctEngine.value!, channelId: state.roomNumber.value,isOpenCamera: state.isOpenCamera.value) ? MeetingMainVideoComponent(
rtcEngine: state.rctEngine.value!,
channelId: state.roomNumber.value,
isOpenCamera: state.isOpenCamera.value,
remoteUid: state.remoteUid.value,
)
: Container() : Container()
), ),

View File

@ -9,10 +9,16 @@ import 'meeting_main_video_logic.dart';
import 'meeting_main_video_state.dart'; import 'meeting_main_video_state.dart';
class MeetingMainVideoComponent extends StatelessWidget { class MeetingMainVideoComponent extends StatelessWidget {
MeetingMainVideoComponent({super.key, required this.rtcEngine, required this.channelId, required this.isOpenCamera}); MeetingMainVideoComponent(
{super.key,
required this.rtcEngine,
required this.channelId,
required this.isOpenCamera,
required this.remoteUid});
final RtcEngine rtcEngine; final RtcEngine rtcEngine;
final String channelId; final String channelId;
final String remoteUid;
final bool isOpenCamera; final bool isOpenCamera;
final MeetingMainVideoLogic logic = Get.put(MeetingMainVideoLogic()); final MeetingMainVideoLogic logic = Get.put(MeetingMainVideoLogic());
@ -38,82 +44,81 @@ class MeetingMainVideoComponent extends StatelessWidget {
debugPrint('wgs输出===$index'); debugPrint('wgs输出===$index');
}, },
children: <Widget>[ children: <Widget>[
Container( Stack(
child: Stack( alignment: Alignment.center,
alignment: Alignment.center, children: [
children: [ remoteUid != ""
Container( ? AgoraVideoView(
decoration: BoxDecoration( controller: VideoViewController.remote(
image: DecorationImage( rtcEngine: rtcEngine,
fit: BoxFit.fill, canvas: VideoCanvas(uid: int.tryParse(remoteUid)),
image: NetworkImage( connection: RtcConnection(channelId: channelId),
"https://tse4-mm.cn.bing.net/th/id/OIP-C.acWMNnQ04Ks6Bh2b9Zq8XwHaKF?rs=1&pid=ImgDetMain",
),
), ),
)), )
Positioned( : const CircularProgressIndicator(),
bottom: 110, Positioned(
child: Image.asset( bottom: 110,
'assets/images/meeting_main_hang_up.png', child: Image.asset(
width: 50.w, 'assets/images/meeting_main_hang_up.png',
height: 50.h, width: 50.w,
height: 50.h,
),
),
Positioned(
top: 16,
right: 16,
child: Container(
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: ColorUtil.Color_0_0_0_96),
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(
children: [
Text(
'正在讲话:',
style: TextStyle(
fontSize: 10.sp,
color: ColorUtil.Color_185_184_184),
),
Image.asset(
'assets/images/meeting_main_speak2.png',
width: 20.w,
height: 20.h,
),
Text(
'晓晓',
style: TextStyle(
fontSize: 10.sp,
color: ColorUtil.Color_185_184_184),
)
],
), ),
), ),
Positioned( ),
top: 16,
right: 16, ///
child: Container( Visibility(
height: 30, visible: isOpenCamera,
decoration: BoxDecoration( child: Positioned(
borderRadius: BorderRadius.circular(8),
color: ColorUtil.Color_0_0_0_96),
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(
children: [
Text(
'正在讲话:',
style: TextStyle(
fontSize: 10.sp,
color: ColorUtil.Color_185_184_184),
),
Image.asset(
'assets/images/meeting_main_speak2.png',
width: 20.w,
height: 20.h,
),
Text(
'晓晓',
style: TextStyle(
fontSize: 10.sp,
color: ColorUtil.Color_185_184_184),
)
],
),
),
),
///
Positioned(
top: 58, top: 58,
right: 12, right: 13,
child: Stack( child: Stack(
children: [ children: [
Visibility( SizedBox(
visible: isOpenCamera, width: 120,
child: SizedBox( height: 150,
width: 120.w,
height: 150.h,
child: Center( child: Center(
child: isOpenCamera child: isOpenCamera
? AgoraVideoView( ? AgoraVideoView(
controller: VideoViewController( controller: VideoViewController(
rtcEngine: rtcEngine, rtcEngine: rtcEngine,
canvas: VideoCanvas(uid: int.parse(UserStore.to.userInfoEntity.value!.uid)), canvas: const VideoCanvas(uid: 0),
), ),
) )
: const CircularProgressIndicator(), : const CircularProgressIndicator(),
), ),
), ),
),
Positioned( Positioned(
left: 4, left: 4,
bottom: 4, bottom: 4,
@ -127,20 +132,22 @@ class MeetingMainVideoComponent extends StatelessWidget {
Container( Container(
height: 15, height: 15,
margin: const EdgeInsets.only(left: 4), margin: const EdgeInsets.only(left: 4),
padding: const EdgeInsets.only(left: 4, right: 4), padding:
const EdgeInsets.only(left: 4, right: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
color: ColorUtil.Color_0_0_0_96), color: ColorUtil.Color_0_0_0_96),
child: Row( child: Row(
children: [ children: [
Image.asset( /*Image.asset(
'assets/images/meeting_main_microphone_open.png', 'assets/images/meeting_main_microphone_open.png',
width: 13.w, width: 13.w,
height: 14.h, height: 14.h,
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),*/
Text( Text(
'晓晓', UserStore
.to.userInfoEntity.value!.userName,
style: TextStyle( style: TextStyle(
fontSize: 10.sp, fontSize: 10.sp,
color: ColorUtil.Color_185_184_184), color: ColorUtil.Color_185_184_184),
@ -153,15 +160,14 @@ class MeetingMainVideoComponent extends StatelessWidget {
) )
], ],
), ),
) ),
], )
), ],
), ),
Container( Container(
color: ColorUtil.Color_57_57_57, color: ColorUtil.Color_57_57_57,
child: GridView.builder( child: GridView.builder(
gridDelegate: gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
childAspectRatio: 0.8, childAspectRatio: 0.8,
crossAxisSpacing: 0), crossAxisSpacing: 0),
@ -178,8 +184,7 @@ class MeetingMainVideoComponent extends StatelessWidget {
"https://tse1-mm.cn.bing.net/th/id/OIP-C.hdhK40Dw3yN_2mjNQNqFCgAAAA?w=186&h=186&c=7&r=0&o=5&pid=1.7", "https://tse1-mm.cn.bing.net/th/id/OIP-C.hdhK40Dw3yN_2mjNQNqFCgAAAA?w=186&h=186&c=7&r=0&o=5&pid=1.7",
), ),
), ),
) )),
),
Positioned( Positioned(
left: 4, left: 4,
bottom: 4, bottom: 4,
@ -193,7 +198,8 @@ class MeetingMainVideoComponent extends StatelessWidget {
Container( Container(
height: 15, height: 15,
margin: const EdgeInsets.only(left: 4), margin: const EdgeInsets.only(left: 4),
padding: const EdgeInsets.only(left: 4, right: 4), padding:
const EdgeInsets.only(left: 4, right: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
color: ColorUtil.Color_0_0_0_96), color: ColorUtil.Color_0_0_0_96),