TRTCCallingImpl.dart 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. import 'dart:convert';
  2. import 'dart:math';
  3. import 'package:tencent_im_sdk_plugin/enum/V2TimSDKListener.dart';
  4. import 'package:tencent_im_sdk_plugin/enum/V2TimSignalingListener.dart';
  5. import 'package:tencent_im_sdk_plugin/models/v2_tim_callback.dart';
  6. import 'package:tencent_trtc_cloud/tx_beauty_manager.dart';
  7. import '../TRTCCalling.dart';
  8. import '../TRTCCallingDef.dart';
  9. import '../TRTCCallingDelegate.dart';
  10. //trtc sdk
  11. import 'package:tencent_trtc_cloud/trtc_cloud.dart';
  12. import 'package:tencent_trtc_cloud/trtc_cloud_def.dart';
  13. import 'package:tencent_trtc_cloud/tx_audio_effect_manager.dart';
  14. import 'package:tencent_trtc_cloud/tx_device_manager.dart';
  15. //im sdk
  16. import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
  17. import 'package:tencent_im_sdk_plugin/models/v2_tim_value_callback.dart';
  18. import 'package:tencent_im_sdk_plugin/enum/log_level.dart';
  19. import 'package:tencent_im_sdk_plugin/manager/v2_tim_manager.dart';
  20. class TRTCCallingImpl extends TRTCCalling {
  21. String logTag = "TRTCCallingImpl";
  22. static TRTCCallingImpl? sInstance;
  23. int timeOutCount = 30; //超时时间,默认30s
  24. int codeErr = -1;
  25. // 是否首次邀请
  26. bool isOnCalling = false;
  27. bool mIsInRoom = false;
  28. int mEnterRoomTime = 0;
  29. String mCurCallID = "";
  30. int mCurRoomID = 0;
  31. String mCurGroupId = ""; //当前群组通话的群组ID
  32. String? mNickName;
  33. String? mFaceUrl;
  34. /*
  35. * 当前邀请列表
  36. * C2C通话时会记录自己邀请的用户
  37. * IM群组通话时会同步群组内邀请的用户
  38. * 当用户接听、拒绝、忙线、超时会从列表中移除该用户
  39. */
  40. List<String> mCurInvitedList = [];
  41. List<dynamic> mCurCallList = [];
  42. //当前语音通话中的远端用户
  43. Set mCurRoomRemoteUserSet = new Set();
  44. /*
  45. * C2C通话的邀请人
  46. * 例如A邀请B,B存储的mCurSponsorForMe为A
  47. */
  48. String mCurSponsorForMe = "";
  49. //当前通话的类型
  50. int? mCurCallType;
  51. late int mSdkAppId;
  52. late String mCurUserId;
  53. late String mCurUserSig;
  54. String? mRoomId;
  55. String? mOwnerUserId;
  56. bool mIsInitIMSDK = false;
  57. bool mIsLogin = false;
  58. String mRole = "audience"; //默认为观众,archor为主播
  59. late V2TIMManager timManager;
  60. late TRTCCloud mTRTCCloud;
  61. late TXAudioEffectManager txAudioManager;
  62. late TXDeviceManager txDeviceManager;
  63. Set<VoiceListenerFunc> listeners = Set();
  64. TRTCCallingImpl() {
  65. //获取腾讯即时通信IM manager
  66. timManager = TencentImSDKPlugin.v2TIMManager;
  67. initTRTC();
  68. }
  69. initTRTC() async {
  70. mTRTCCloud = (await TRTCCloud.sharedInstance())!;
  71. txDeviceManager = mTRTCCloud.getDeviceManager();
  72. txAudioManager = mTRTCCloud.getAudioEffectManager();
  73. }
  74. static sharedInstance() {
  75. if (sInstance == null) {
  76. sInstance = new TRTCCallingImpl();
  77. }
  78. return sInstance;
  79. }
  80. static void destroySharedInstance() {
  81. if (sInstance != null) {
  82. sInstance = null;
  83. }
  84. TRTCCloud.destroySharedInstance();
  85. }
  86. @override
  87. void destroy() {
  88. mTRTCCloud.stopLocalPreview();
  89. mTRTCCloud.stopLocalAudio();
  90. mTRTCCloud.exitRoom();
  91. }
  92. @override
  93. void registerListener(VoiceListenerFunc func) {
  94. if (listeners.isEmpty) {
  95. //监听im事件
  96. timManager
  97. .getSignalingManager()
  98. .addSignalingListener(listener: signalingListener());
  99. //监听trtc事件
  100. mTRTCCloud.registerListener(rtcListener);
  101. }
  102. listeners.add(func);
  103. }
  104. @override
  105. void unRegisterListener(VoiceListenerFunc func) {
  106. listeners.remove(func);
  107. if (listeners.isEmpty) {
  108. mTRTCCloud.unRegisterListener(rtcListener);
  109. timManager
  110. .getSignalingManager()
  111. .removeSignalingListener(listener: signalingListener);
  112. }
  113. }
  114. emitEvent(type, params) {
  115. for (var item in listeners) {
  116. item(type, params);
  117. }
  118. }
  119. signalingListener() {
  120. TRTCCallingDelegate type;
  121. return new V2TimSignalingListener(
  122. onInvitationCancelled: (inviteID, inviter, data) {
  123. if (!_isCallingData(data)) {
  124. return;
  125. }
  126. if (inviteID == mCurCallID) {
  127. _stopCall();
  128. emitEvent(TRTCCallingDelegate.onCallingCancel, {});
  129. }
  130. },
  131. onInvitationTimeout: (inviteID, inviteeList) {
  132. //邀请者
  133. String curGroupCallId = _getGroupCallId(mCurUserId);
  134. if (!_isEmpty(mCurCallID) && inviteID != mCurCallID) {
  135. return;
  136. } else if (_isEmpty(mCurCallID) && inviteID != curGroupCallId) {
  137. return;
  138. }
  139. if (mCurSponsorForMe.isEmpty) {
  140. for (var i = 0; i < inviteeList.length; i++) {
  141. emitEvent(TRTCCallingDelegate.onNoResp, inviteeList[i]);
  142. mCurInvitedList.remove(inviteeList[i]);
  143. }
  144. } else {
  145. //被邀请者
  146. if (inviteeList.contains(mCurUserId)) {
  147. _stopCall();
  148. emitEvent(TRTCCallingDelegate.onCallingTimeout, {});
  149. }
  150. mCurInvitedList.remove(inviteeList);
  151. }
  152. // 每次超时都需要判断当前是否需要结束通话
  153. _preExitRoom(null);
  154. },
  155. onInviteeAccepted: (inviteID, invitee, data) {
  156. if (!_isCallingData(data)) {
  157. return;
  158. }
  159. mCurInvitedList.remove(invitee);
  160. },
  161. onInviteeRejected: (inviteID, invitee, data) {
  162. if (!_isCallingData(data)) {
  163. return;
  164. }
  165. String curGroupCallId = _getGroupCallId(invitee);
  166. if (mCurCallID == inviteID || curGroupCallId == inviteID) {
  167. try {
  168. Map<String, dynamic>? customMap = jsonDecode(data);
  169. mCurInvitedList.remove(invitee);
  170. if (customMap != null && customMap.containsKey('line_busy')) {
  171. emitEvent(TRTCCallingDelegate.onLineBusy, invitee);
  172. } else {
  173. emitEvent(TRTCCallingDelegate.onReject, invitee);
  174. }
  175. _preExitRoom(null);
  176. } catch (e) {
  177. print(logTag +
  178. "=onInviteeRejected JsonSyntaxException:" +
  179. e.toString());
  180. }
  181. }
  182. },
  183. onReceiveNewInvitation:
  184. (inviteID, inviter, groupID, inviteeList, data) async {
  185. if (!_isCallingData(data)) {
  186. return;
  187. }
  188. try {
  189. Map<String, dynamic>? customMap = jsonDecode(data);
  190. if (customMap == null) {
  191. print(logTag + "onReceiveNewInvitation extraMap is null, ignore");
  192. return;
  193. }
  194. if (customMap.containsKey('call_type')) {
  195. mCurCallType = customMap['call_type'];
  196. }
  197. if (customMap.containsKey('call_end')) {
  198. _preExitRoom(null);
  199. return;
  200. }
  201. if (customMap.containsKey('room_id')) {
  202. mCurRoomID = customMap['room_id'];
  203. }
  204. } catch (e) {
  205. print(logTag +
  206. "=onReceiveNewInvitation JsonSyntaxException:" +
  207. e.toString());
  208. }
  209. if (isOnCalling && inviteeList.contains(mCurUserId)) {
  210. // 正在通话时,收到了一个邀请我的通话请求,需要告诉对方忙线
  211. Map<String, dynamic> busyMap = _getCustomMap();
  212. busyMap['line_busy'] = 'line_busy';
  213. await timManager
  214. .getSignalingManager()
  215. .reject(inviteID: inviteID, data: jsonEncode(busyMap));
  216. return;
  217. }
  218. // 与对方处在同一个群中,此时收到了邀请群中其他人通话的请求,界面上展示连接动画
  219. if (!_isEmpty(groupID) && !_isEmpty(mCurGroupId)) {
  220. mCurInvitedList.addAll(inviteeList);
  221. TRTCCallingDelegate type =
  222. TRTCCallingDelegate.onGroupCallInviteeListUpdate;
  223. emitEvent(type, mCurInvitedList);
  224. }
  225. if (!inviteeList.contains(mCurUserId)) {
  226. return;
  227. }
  228. mCurSponsorForMe = inviter;
  229. mCurCallID = inviteID;
  230. mCurGroupId = groupID;
  231. type = TRTCCallingDelegate.onInvited;
  232. emitEvent(type, {
  233. 'sponsor': inviter,
  234. 'userIds': inviteeList.remove(mCurUserId),
  235. 'isFromGroup': !_isEmpty(groupID),
  236. 'type': mCurCallType
  237. });
  238. },
  239. );
  240. }
  241. rtcListener(rtcType, param) {
  242. String typeStr = rtcType.toString();
  243. TRTCCallingDelegate type;
  244. typeStr = typeStr.replaceFirst("TRTCCloudListener.", "");
  245. if (typeStr == "onEnterRoom") {
  246. if (param < 0) {
  247. _stopCall();
  248. } else {
  249. mIsInRoom = true;
  250. }
  251. } else if (typeStr == "onError") {
  252. type = TRTCCallingDelegate.onError;
  253. emitEvent(type, param);
  254. } else if (typeStr == "onUserVoiceVolume") {
  255. type = TRTCCallingDelegate.onUserVoiceVolume;
  256. emitEvent(type, param);
  257. } else if (typeStr == "onUserVideoAvailable") {
  258. type = TRTCCallingDelegate.onUserVideoAvailable;
  259. emitEvent(type, param);
  260. } else if (typeStr == "onUserAudioAvailable") {
  261. type = TRTCCallingDelegate.onUserAudioAvailable;
  262. emitEvent(type, param);
  263. } else if (typeStr == "onRemoteUserEnterRoom") {
  264. mCurRoomRemoteUserSet.add(param);
  265. // 只有单聊这个时间才是正确的,因为单聊只会有一个用户进群,群聊这个时间会被后面的人重置
  266. mEnterRoomTime = DateTime.now().millisecondsSinceEpoch;
  267. type = TRTCCallingDelegate.onUserEnter;
  268. emitEvent(type, param);
  269. } else if (typeStr == "onRemoteUserLeaveRoom") {
  270. mCurRoomRemoteUserSet.remove(param['userId']);
  271. mCurInvitedList.remove(param['userId']);
  272. type = TRTCCallingDelegate.onUserLeave;
  273. emitEvent(type, param['userId']);
  274. _preExitRoom(param['userId']);
  275. }
  276. }
  277. // 多人通话时,根据userId找到对应的通话id
  278. _getGroupCallId(String userId) {
  279. for (int i = 0; i < mCurCallList.length; i++) {
  280. if (mCurCallList[i]['userId'] == userId) {
  281. return mCurCallList[i]['callId'];
  282. }
  283. }
  284. return '';
  285. }
  286. /*
  287. * 重要:用于判断是否需要结束本次通话
  288. * 在用户超时、拒绝、忙线、有人退出房间时需要进行判断
  289. */
  290. _preExitRoom(String? leaveUser) {
  291. if (mCurRoomRemoteUserSet.isEmpty && mCurInvitedList.isEmpty && mIsInRoom) {
  292. // 当没有其他用户在房间里了,则结束通话。
  293. if (!_isEmpty(leaveUser)) {
  294. Map<String, dynamic> customMap = _getCustomMap();
  295. //customMap['call_end'] = 'call_end';
  296. customMap['call_end'] = 10;
  297. if (_isEmpty(mCurGroupId)) {
  298. timManager
  299. .getSignalingManager()
  300. .invite(invitee: leaveUser!, data: jsonEncode(customMap));
  301. } else {
  302. timManager.getSignalingManager().inviteInGroup(
  303. groupID: mCurGroupId,
  304. inviteeList: mCurInvitedList,
  305. data: jsonEncode(customMap));
  306. }
  307. }
  308. _exitRoom();
  309. _stopCall();
  310. emitEvent(TRTCCallingDelegate.onCallEnd, {});
  311. }
  312. }
  313. @override
  314. Future<ActionCallback> login(
  315. int sdkAppId, String userId, String userSig) async {
  316. mSdkAppId = sdkAppId;
  317. mCurUserId = userId;
  318. mCurUserSig = userSig;
  319. if (!mIsInitIMSDK) {
  320. //初始化SDK
  321. V2TimValueCallback<bool> initRes = await timManager.initSDK(
  322. sdkAppID: sdkAppId, //填入在控制台上申请的sdkappid
  323. loglevel: LogLevel.V2TIM_LOG_ERROR,
  324. listener: new V2TimSDKListener(onKickedOffline: () {
  325. TRTCCallingDelegate type = TRTCCallingDelegate.onKickedOffline;
  326. emitEvent(type, {});
  327. }));
  328. if (initRes.code != 0) {
  329. //初始化sdk错误
  330. return ActionCallback(code: 0, desc: 'init im sdk error');
  331. }
  332. }
  333. mIsInitIMSDK = true;
  334. // 登陆到 IM
  335. String? loginedUserId = (await timManager.getLoginUser()).data;
  336. if (loginedUserId != null && loginedUserId == userId) {
  337. mIsLogin = true;
  338. return ActionCallback(code: 0, desc: 'login im success');
  339. }
  340. V2TimCallback loginRes =
  341. await timManager.login(userID: userId, userSig: userSig);
  342. if (loginRes.code == 0) {
  343. mIsLogin = true;
  344. return ActionCallback(code: 0, desc: 'login im success');
  345. } else {
  346. return ActionCallback(code: codeErr, desc: loginRes.desc);
  347. }
  348. }
  349. @override
  350. Future<ActionCallback> logout() async {
  351. V2TimCallback loginRes = await timManager.logout();
  352. _stopCall();
  353. _exitRoom();
  354. mNickName = "";
  355. mFaceUrl = "";
  356. return ActionCallback(code: loginRes.code, desc: loginRes.desc);
  357. }
  358. @override
  359. Future<ActionCallback> call(String userId, int type) async {
  360. if (!isOnCalling) {
  361. // 首次拨打电话,生成id,并进入trtc房间
  362. mCurRoomID = _generateRoomID();
  363. mCurCallType = type;
  364. _enterTRTCRoom();
  365. }
  366. mCurInvitedList.add(userId);
  367. V2TimValueCallback res = await timManager.getSignalingManager().invite(
  368. invitee: userId,
  369. data: jsonEncode(_getCustomMap()),
  370. timeout: timeOutCount,
  371. onlineUserOnly: false);
  372. mCurCallID = res.data;
  373. mCurCallList.add({'userId': userId, 'callId': mCurCallID});
  374. return ActionCallback(code: res.code, desc: res.desc);
  375. }
  376. _isCallingData(String data) {
  377. try {
  378. Map<String, dynamic> customMap = jsonDecode(data);
  379. if (customMap.containsKey('call_type')) {
  380. return true;
  381. }
  382. } catch (e) {
  383. print("isCallingData json parse error");
  384. return false;
  385. }
  386. return false;
  387. }
  388. _getCustomMap() {
  389. Map<String, dynamic> customMap = new Map<String, dynamic>();
  390. customMap['version'] = 1;
  391. customMap['call_type'] = mCurCallType;
  392. customMap['room_id'] = mCurRoomID;
  393. return customMap;
  394. }
  395. /*
  396. * trtc 进房
  397. */
  398. _enterTRTCRoom() {
  399. isOnCalling = true;
  400. if (mCurCallType == TRTCCalling.typeVideoCall) {
  401. // 开启基础美颜
  402. TXBeautyManager txBeautyManager = mTRTCCloud.getBeautyManager();
  403. // 自然美颜
  404. txBeautyManager.setBeautyStyle(1);
  405. txBeautyManager.setBeautyLevel(6);
  406. // 进房前需要设置一下关键参数
  407. TRTCVideoEncParam encParam = new TRTCVideoEncParam();
  408. encParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_960_540;
  409. encParam.videoFps = 15;
  410. encParam.videoBitrate = 1000;
  411. encParam.videoResolutionMode =
  412. TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT;
  413. encParam.enableAdjustRes = true;
  414. mTRTCCloud.setVideoEncoderParam(encParam);
  415. }
  416. mTRTCCloud.enableAudioVolumeEvaluation(500);
  417. txDeviceManager.setAudioRoute(TRTCCloudDef.TRTC_AUDIO_ROUTE_SPEAKER);
  418. mTRTCCloud.muteLocalAudio(false);
  419. mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_DEFAULT);
  420. mTRTCCloud.enterRoom(
  421. TRTCParams(
  422. sdkAppId: mSdkAppId,
  423. userId: mCurUserId,
  424. userSig: mCurUserSig,
  425. roomId: mCurRoomID,
  426. role: TRTCCloudDef.TRTCRoleAnchor),
  427. mCurCallType == TRTCCalling.typeVideoCall
  428. ? TRTCCloudDef.TRTC_APP_SCENE_VIDEOCALL
  429. : TRTCCloudDef.TRTC_APP_SCENE_AUDIOCALL);
  430. }
  431. @override
  432. Future<ActionCallback> groupCall(
  433. List<String> userIdList, int type, String? groupId) async {
  434. if (_isListEmpty(userIdList)) {
  435. return ActionCallback(code: codeErr, desc: 'userIdList is empty');
  436. }
  437. if (!isOnCalling) {
  438. // 首次拨打电话,生成id,并进入trtc房间
  439. mCurRoomID = _generateRoomID();
  440. mCurCallType = type;
  441. _enterTRTCRoom();
  442. }
  443. // 过滤已经邀请的用户id
  444. List<String> filterInvitedList = [];
  445. for (var i = 0; i < userIdList.length; i++) {
  446. if (!mCurInvitedList.contains(userIdList[i])) {
  447. filterInvitedList.add(userIdList[i]);
  448. }
  449. }
  450. if (_isListEmpty(filterInvitedList)) {
  451. return ActionCallback(
  452. code: codeErr, desc: 'the userIdList has been invited');
  453. }
  454. mCurInvitedList = filterInvitedList;
  455. if (_isEmpty(groupId)) {
  456. for (int i = 0; i < mCurInvitedList.length; i++) {
  457. V2TimValueCallback res = await timManager.getSignalingManager().invite(
  458. invitee: mCurInvitedList[i],
  459. data: jsonEncode(_getCustomMap()),
  460. timeout: timeOutCount,
  461. onlineUserOnly: false);
  462. mCurCallList.add({'userId': mCurInvitedList[i], 'callId': res.data});
  463. }
  464. return ActionCallback(code: 0, desc: '');
  465. } else {
  466. V2TimValueCallback res = await timManager
  467. .getSignalingManager()
  468. .inviteInGroup(
  469. groupID: groupId!,
  470. inviteeList: mCurInvitedList,
  471. data: jsonEncode(_getCustomMap()),
  472. timeout: timeOutCount,
  473. onlineUserOnly: false);
  474. mCurCallID = res.data;
  475. return ActionCallback(code: res.code, desc: res.desc);
  476. }
  477. }
  478. _isListEmpty(List? list) {
  479. return list == null || list.length == 0;
  480. }
  481. _isEmpty(String? data) {
  482. return data == null || data == "";
  483. }
  484. /*
  485. * 停止此次通话,把所有的变量都会重置
  486. */
  487. _stopCall() {
  488. isOnCalling = false;
  489. mIsInRoom = false;
  490. mEnterRoomTime = 0;
  491. mCurCallID = "";
  492. mCurRoomID = 0;
  493. mCurInvitedList = [];
  494. mCurCallList = [];
  495. mCurRoomRemoteUserSet.clear();
  496. mCurSponsorForMe = "";
  497. mCurGroupId = "";
  498. mCurCallType = TRTCCalling.typeUnknow;
  499. }
  500. @override
  501. Future<ActionCallback> accept() async {
  502. _enterTRTCRoom();
  503. V2TimCallback res = await timManager
  504. .getSignalingManager()
  505. .accept(inviteID: mCurCallID, data: jsonEncode(_getCustomMap()));
  506. return ActionCallback(code: res.code, desc: res.desc);
  507. }
  508. @override
  509. Future<void> closeCamera() async {
  510. return mTRTCCloud.stopLocalPreview();
  511. }
  512. @override
  513. Future<void> hangup() async {
  514. if (!isOnCalling) {
  515. await reject();
  516. return;
  517. }
  518. _exitRoom();
  519. if (_isEmpty(mCurGroupId)) {
  520. for (int i = 0; i < mCurInvitedList.length; i++) {
  521. await timManager.getSignalingManager().cancel(
  522. inviteID: _getGroupCallId(mCurInvitedList[i]),
  523. data: jsonEncode(_getCustomMap()));
  524. }
  525. } else {
  526. if (mCurRoomRemoteUserSet.isEmpty) {
  527. await timManager.getSignalingManager().cancel(
  528. inviteID: _getGroupCallId(mCurCallID),
  529. data: jsonEncode(_getCustomMap()));
  530. }
  531. }
  532. _stopCall();
  533. }
  534. /*
  535. * trtc 退房
  536. */
  537. _exitRoom() {
  538. mTRTCCloud.stopLocalPreview();
  539. mTRTCCloud.stopLocalAudio();
  540. mTRTCCloud.exitRoom();
  541. }
  542. @override
  543. Future<void> openCamera(bool isFrontCamera, int viewId) {
  544. return mTRTCCloud.startLocalPreview(isFrontCamera, viewId);
  545. }
  546. @override
  547. Future<void> updateLocalView(int viewId) {
  548. return mTRTCCloud.updateLocalView(viewId);
  549. }
  550. @override
  551. Future<ActionCallback> reject() async {
  552. V2TimCallback res = await timManager
  553. .getSignalingManager()
  554. .reject(inviteID: mCurCallID, data: jsonEncode(_getCustomMap()));
  555. _stopCall();
  556. return ActionCallback(code: res.code, desc: res.desc);
  557. }
  558. @override
  559. Future<void> setHandsFree(bool isHandsFree) {
  560. if (isHandsFree) {
  561. return txDeviceManager
  562. .setAudioRoute(TRTCCloudDef.TRTC_AUDIO_ROUTE_SPEAKER);
  563. } else {
  564. return txDeviceManager
  565. .setAudioRoute(TRTCCloudDef.TRTC_AUDIO_ROUTE_EARPIECE);
  566. }
  567. }
  568. @override
  569. Future<void> setMicMute(bool isMute) {
  570. return mTRTCCloud.muteLocalAudio(isMute);
  571. }
  572. @override
  573. Future<void> startRemoteView(String userId, int streamType, int viewId) {
  574. return mTRTCCloud.startRemoteView(userId, streamType, viewId);
  575. }
  576. @override
  577. Future<void> stopRemoteView(String userId, int streamType) {
  578. return mTRTCCloud.stopRemoteView(userId, streamType);
  579. }
  580. @override
  581. Future<void> updateRemoteView(String userId, int streamType, int viewId) {
  582. return mTRTCCloud.updateRemoteView(viewId, streamType, userId);
  583. }
  584. @override
  585. Future<void> switchCamera(bool isFrontCamera) {
  586. return txDeviceManager.switchCamera(isFrontCamera);
  587. }
  588. _generateRoomID() {
  589. Random rng = new Random();
  590. //2147483647
  591. String numStr = '';
  592. for (var i = 0; i < 9; i++) {
  593. numStr += rng.nextInt(9).toString();
  594. }
  595. return int.tryParse(numStr);
  596. }
  597. }
  598. /// @nodoc
  599. typedef VoiceListenerFunc<P> = void Function(
  600. TRTCCallingDelegate type, P params);