diff --git a/lib/main.dart b/lib/main.dart index b2d0f4d..265b61e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'package:dating_touchme_app/network/network_service.dart'; import 'package:dating_touchme_app/pages/main/main_page.dart'; import 'package:dating_touchme_app/pages/mine/login_page.dart'; import 'package:dating_touchme_app/pages/mine/user_info_page.dart'; +import 'package:dating_touchme_app/rtc/rtc_manager.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -21,6 +22,7 @@ void main() async { // 设置环境配置 - 根据是否为release模式 EnvConfig.setEnvironment(Environment.dev); + RTCManager.instance.initialize(appId: '4c2ea9dcb4c5440593a418df0fdd512d'); IMManager.instance.initialize('1165251016193374#demo'); // 初始化全局依赖 final networkService = NetworkService(); diff --git a/lib/rtc/rtc_manager.dart b/lib/rtc/rtc_manager.dart new file mode 100644 index 0000000..c89abe9 --- /dev/null +++ b/lib/rtc/rtc_manager.dart @@ -0,0 +1,425 @@ +import 'package:agora_rtc_engine/agora_rtc_engine.dart'; + +/// RTC 管理器,负责管理声网音视频通话功能 +class RTCManager { + // 单例模式 + static final RTCManager _instance = RTCManager._internal(); + factory RTCManager() => _instance; + // 静态getter用于instance访问 + static RTCManager get instance => _instance; + + RtcEngine? _engine; + bool _isInitialized = false; + bool _isInChannel = false; + String? _currentChannelId; + int? _currentUid; + + // 事件回调 + Function(RtcConnection connection, int elapsed)? onJoinChannelSuccess; + Function(RtcConnection connection, int remoteUid, int elapsed)? onUserJoined; + Function( + RtcConnection connection, + int remoteUid, + UserOfflineReasonType reason, + )? + onUserOffline; + Function(RtcConnection connection, RtcStats stats)? onLeaveChannel; + Function( + RtcConnection connection, + ConnectionStateType state, + ConnectionChangedReasonType reason, + )? + onConnectionStateChanged; + Function(int uid, UserInfo userInfo)? onUserInfoUpdated; + Function(RtcConnection connection, int uid, int elapsed)? + onFirstRemoteVideoDecoded; + Function( + RtcConnection connection, + VideoSourceType sourceType, + int uid, + int width, + int height, + int rotation, + )? + onVideoSizeChanged; + Function(RtcConnection connection, int uid, bool muted)? onUserMuteAudio; + Function(RtcConnection connection, int uid, bool muted)? onUserMuteVideo; + Function(RtcConnection connection)? onConnectionLost; + Function(RtcConnection connection, int code, String msg)? onError; + + RTCManager._internal() { + print('RTCManager instance created'); + } + + /// 初始化 RTC Engine + /// [appId] 声网 App ID + /// [channelProfile] 频道场景类型,默认为通信模式 + Future initialize({ + required String appId, + ChannelProfileType channelProfile = + ChannelProfileType.channelProfileCommunication, + }) async { + try { + if (_isInitialized && _engine != null) { + print('RTC Engine already initialized'); + return true; + } + + // 创建 RTC Engine + _engine = createAgoraRtcEngine(); + + // 初始化 RTC Engine + await _engine!.initialize( + RtcEngineContext(appId: appId, channelProfile: channelProfile), + ); + + // 注册事件处理器 + _registerEventHandlers(); + + _isInitialized = true; + print('RTC Engine initialized successfully'); + return true; + } catch (e) { + print('Failed to initialize RTC Engine: $e'); + return false; + } + } + + /// 注册事件处理器 + void _registerEventHandlers() { + if (_engine == null) return; + + _engine!.registerEventHandler( + RtcEngineEventHandler( + onJoinChannelSuccess: (RtcConnection connection, int elapsed) { + _isInChannel = true; + _currentChannelId = connection.channelId; + print('加入频道成功,频道名:${connection.channelId},耗时:${elapsed}ms'); + if (onJoinChannelSuccess != null) { + onJoinChannelSuccess!(connection, elapsed); + } + }, + onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) { + print('用户加入,UID:$remoteUid'); + if (onUserJoined != null) { + onUserJoined!(connection, remoteUid, elapsed); + } + }, + onUserOffline: + ( + RtcConnection connection, + int remoteUid, + UserOfflineReasonType reason, + ) { + print('用户离开,UID:$remoteUid,原因:$reason'); + if (onUserOffline != null) { + onUserOffline!(connection, remoteUid, reason); + } + }, + onLeaveChannel: (RtcConnection connection, RtcStats stats) { + _isInChannel = false; + _currentChannelId = null; + print('离开频道,统计信息:${stats.duration}秒'); + if (onLeaveChannel != null) { + onLeaveChannel!(connection, stats); + } + }, + onConnectionStateChanged: + ( + RtcConnection connection, + ConnectionStateType state, + ConnectionChangedReasonType reason, + ) { + print('连接状态改变:$state,原因:$reason'); + if (onConnectionStateChanged != null) { + onConnectionStateChanged!(connection, state, reason); + } + }, + onUserInfoUpdated: (int uid, UserInfo userInfo) { + print('用户信息更新,UID:$uid'); + if (onUserInfoUpdated != null) { + onUserInfoUpdated!(uid, userInfo); + } + }, + onFirstRemoteVideoDecoded: + ( + RtcConnection connection, + int uid, + int width, + int height, + int elapsed, + ) { + print('首次远程视频解码,UID:$uid,分辨率:${width}x${height}'); + if (onFirstRemoteVideoDecoded != null) { + onFirstRemoteVideoDecoded!(connection, uid, elapsed); + } + }, + onVideoSizeChanged: + ( + RtcConnection connection, + VideoSourceType sourceType, + int uid, + int width, + int height, + int rotation, + ) { + print('视频尺寸改变,UID:$uid,分辨率:${width}x${height}'); + if (onVideoSizeChanged != null) { + onVideoSizeChanged!( + connection, + sourceType, + uid, + width, + height, + rotation, + ); + } + }, + onUserMuteAudio: (RtcConnection connection, int uid, bool muted) { + print('用户静音状态改变,UID:$uid,静音:$muted'); + if (onUserMuteAudio != null) { + onUserMuteAudio!(connection, uid, muted); + } + }, + onUserMuteVideo: (RtcConnection connection, int uid, bool muted) { + print('用户视频状态改变,UID:$uid,关闭:$muted'); + if (onUserMuteVideo != null) { + onUserMuteVideo!(connection, uid, muted); + } + }, + onConnectionLost: (RtcConnection connection) { + print('连接丢失'); + if (onConnectionLost != null) { + onConnectionLost!(connection); + } + }, + onError: (ErrorCodeType err, String msg) { + print('RTC Engine 错误:$err,消息:$msg'); + if (onError != null) { + onError!( + RtcConnection( + channelId: _currentChannelId ?? '', + localUid: _currentUid ?? 0, + ), + err.value(), + msg, + ); + } + }, + ), + ); + } + + /// 启用视频 + Future enableVideo() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.enableVideo(); + print('视频已启用'); + } + + /// 禁用视频 + Future disableVideo() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.disableVideo(); + print('视频已禁用'); + } + + /// 启用音频 + Future enableAudio() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.enableAudio(); + print('音频已启用'); + } + + /// 禁用音频 + Future disableAudio() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.disableAudio(); + print('音频已禁用'); + } + + /// 开启本地视频预览 + Future startPreview() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.startPreview(); + print('本地视频预览已开启'); + } + + /// 停止本地视频预览 + Future stopPreview() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.stopPreview(); + print('本地视频预览已停止'); + } + + /// 设置本地视频视图 + /// [viewId] 视图ID + /// [mirrorMode] 镜像模式 + Future setupLocalVideo({ + required int viewId, + VideoSourceType sourceType = VideoSourceType.videoSourceCameraPrimary, + VideoMirrorModeType mirrorMode = VideoMirrorModeType.videoMirrorModeAuto, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.setupLocalVideo( + VideoCanvas(view: viewId, sourceType: sourceType, mirrorMode: mirrorMode), + ); + print('本地视频视图已设置,viewId:$viewId'); + } + + /// 设置远程视频视图 + /// [uid] 远程用户ID + /// [viewId] 视图ID + Future setupRemoteVideo({ + required int uid, + required int viewId, + VideoSourceType sourceType = VideoSourceType.videoSourceCameraPrimary, + VideoMirrorModeType mirrorMode = + VideoMirrorModeType.videoMirrorModeDisabled, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.setupRemoteVideo( + VideoCanvas( + uid: uid, + view: viewId, + sourceType: sourceType, + mirrorMode: mirrorMode, + ), + ); + print('远程视频视图已设置,UID:$uid,viewId:$viewId'); + } + + /// 加入频道 + /// [token] 频道令牌(可选,如果频道未开启鉴权则可以为空字符串) + /// [channelId] 频道ID + /// [uid] 用户ID(0表示自动分配) + /// [options] 频道媒体选项 + Future joinChannel({ + String? token, + required String channelId, + int uid = 0, + ChannelMediaOptions? options, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + if (_isInChannel) { + print('已经在频道中,先离开当前频道'); + await leaveChannel(); + } + + _currentUid = uid; + await _engine!.joinChannel( + token: token ?? '', + channelId: channelId, + uid: uid, + options: options ?? const ChannelMediaOptions(), + ); + print('正在加入频道:$channelId,UID:$uid'); + } + + /// 离开频道 + Future leaveChannel() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + if (!_isInChannel) { + print('当前不在频道中'); + return; + } + + await _engine!.leaveChannel(); + _currentUid = null; + print('已离开频道'); + } + + /// 切换摄像头 + Future switchCamera() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.switchCamera(); + print('摄像头已切换'); + } + + /// 静音/取消静音本地音频 + /// [muted] true表示静音,false表示取消静音 + Future muteLocalAudio(bool muted) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.muteLocalAudioStream(muted); + print('本地音频${muted ? "已静音" : "已取消静音"}'); + } + + /// 开启/关闭本地视频 + /// [enabled] true表示开启,false表示关闭 + Future muteLocalVideo(bool enabled) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.muteLocalVideoStream(!enabled); + print('本地视频${enabled ? "已开启" : "已关闭"}'); + } + + /// 设置客户端角色(仅用于直播场景) + /// [role] 客户端角色:主播或观众 + Future setClientRole({ + required ClientRoleType role, + ClientRoleOptions? options, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.setClientRole(role: role, options: options); + print('客户端角色已设置为:$role'); + } + + /// 获取当前是否在频道中 + bool get isInChannel => _isInChannel; + + /// 获取当前频道ID + String? get currentChannelId => _currentChannelId; + + /// 获取当前用户ID + int? get currentUid => _currentUid; + + /// 获取 RTC Engine 实例(用于高级操作) + RtcEngine? get engine => _engine; + + /// 释放资源 + Future dispose() async { + try { + if (_isInChannel) { + await leaveChannel(); + } + if (_engine != null) { + await _engine!.release(); + _engine = null; + } + _isInitialized = false; + _isInChannel = false; + _currentChannelId = null; + _currentUid = null; + print('RTC Engine disposed'); + } catch (e) { + print('Failed to dispose RTC Engine: $e'); + } + } +}