controller.dart 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. import 'dart:async';
  2. import 'package:camera/camera.dart';
  3. import 'package:flutter_sound/flutter_sound.dart';
  4. import 'package:get/get.dart';
  5. import 'package:permission_handler/permission_handler.dart';
  6. import 'package:trtc_demo/page/hardware_detection/state.dart';
  7. import 'package:audio_session/audio_session.dart';
  8. import 'package:flutter/foundation.dart' show kDebugMode, kIsWeb;
  9. import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart';
  10. import 'package:tencent_trtc_cloud/trtc_cloud.dart';
  11. import 'package:tencent_trtc_cloud/tx_device_manager.dart';
  12. class HardwareDetectionController extends GetxController {
  13. HardwareDetectionController();
  14. /// 本地硬件设置持久化缓存服务
  15. // HardwareSettingService hardwareSettingService =
  16. // Get.find<HardwareSettingService>();
  17. // HardwareSetting hardwareSetting = HardwareSetting.ins;
  18. final state = HardwareDetectionState();
  19. /// 相机控制器需要的参数
  20. late List<CameraDescription> _cameras;
  21. late CameraController cameraController;
  22. /// 扬声器控制器需要的参数
  23. final FlutterSoundPlayer flutterSound = FlutterSoundPlayer();
  24. /// 麦克风控制器需要的参数
  25. Codec _codec = Codec.aacMP4;
  26. final FlutterSoundRecorder flutterRecorder = FlutterSoundRecorder();
  27. /// dispose 释放内存
  28. @override
  29. void dispose() {
  30. super.dispose();
  31. cameraController.dispose();
  32. flutterSound.closePlayer();
  33. flutterRecorder.closeRecorder();
  34. }
  35. /// 在 [onDelete] 方法之前调用。
  36. @override
  37. void onClose() {
  38. super.onClose();
  39. }
  40. /// 在 widget 内存中分配后立即调用。
  41. @override
  42. void onInit() async {
  43. // hardwareSettingService.init();
  44. await _intialize();
  45. super.onInit();
  46. }
  47. /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
  48. @override
  49. void onReady() {
  50. super.onReady();
  51. }
  52. // 刷新设备列表
  53. Future<void> refreshAllDevices() async {
  54. await refreshCameraList();
  55. await refreshMicrophoneList();
  56. await refreshSpeakerList();
  57. }
  58. /// 刷新摄像头列表
  59. Future<void> refreshCameraList() async {
  60. state.searchingCamera = true;
  61. if (state.detectingCamera) stopDetectingCamera();
  62. try {
  63. _cameras = await availableCameras();
  64. state.cameraList = [];
  65. state.cameraAvailable = null;
  66. await Future.delayed(Duration(milliseconds: 100));
  67. state.cameraList = await _trtcGetCameras();
  68. state.searchingCamera = false;
  69. } on Exception catch (e) {
  70. state.cameraList = [];
  71. state.cameraAvailable = null;
  72. state.searchingCamera = false;
  73. print('Search camera failed: $e');
  74. }
  75. // 如果当前没有选择摄像头,则选择第一个
  76. if (state.currentCameraId.isEmpty && state.cameraList.isNotEmpty) {
  77. selectCameraById(state.cameraList.first.id);
  78. }
  79. }
  80. /// 刷新麦克风列表
  81. Future<void> refreshMicrophoneList() async {
  82. state.searchingMicrophone = true;
  83. if (state.detectingMicrophone) stopDetectingMicrophone();
  84. state.microphoneList = [];
  85. state.microphoneAvailable = null;
  86. await Future.delayed(Duration(milliseconds: 100));
  87. try {
  88. state.microphoneList = await _trtcGetMicrophones();
  89. state.searchingMicrophone = false;
  90. } on Exception catch (e) {
  91. state.microphoneList = [];
  92. state.searchingMicrophone = false;
  93. print('Search microphone failed: $e');
  94. }
  95. // 如果当前没有选择麦克风,则选择第一个
  96. if (state.currentMicrophoneId.isEmpty && state.microphoneList.isNotEmpty) {
  97. selectMicrophoneById(state.microphoneList.first.id);
  98. }
  99. }
  100. /// 刷新扬声器列表
  101. Future<void> refreshSpeakerList() async {
  102. state.searchingSpeaker = true;
  103. if (state.detectingSpeaker) stopDetectingSpeaker();
  104. state.speakerList = [];
  105. state.speakerAvailable = null;
  106. await Future.delayed(Duration(milliseconds: 100));
  107. try {
  108. state.speakerList = await _trtcGetSpeakers();
  109. state.searchingSpeaker = false;
  110. } on Exception catch (e) {
  111. state.speakerList = [];
  112. state.searchingSpeaker = false;
  113. print('Search speaker failed: $e');
  114. }
  115. // 如果当前没有选择扬声器,则选择第一个
  116. if (state.currentSpeaker == null && state.speakerList.isNotEmpty) {
  117. selectSpeakerById(state.speakerList.first.id);
  118. }
  119. }
  120. /// 选择摄像头
  121. void selectCameraById(String deviceId) {
  122. if (state.currentCamera != null && deviceId == state.currentCameraId)
  123. return;
  124. state.cameraList.forEach((element) {
  125. if (element.id == deviceId) {
  126. print('select camera: ${element.name}');
  127. print('select camera: ${element.id}');
  128. state.currentCamera = element;
  129. state.cameraAvailable = null;
  130. state.currentCameraId = element.id;
  131. // hardwareSetting.setIdealWebVideoInputDeviceName(element.name);
  132. // hardwareSettingService.save();
  133. }
  134. });
  135. if (state.currentCamera == null) return;
  136. if (state.detectingCamera) {
  137. _stopCamera();
  138. _openSelectedCamera();
  139. }
  140. }
  141. /// 选择麦克风
  142. Future<void> selectMicrophoneById(String deviceId) async {
  143. state.microphoneList.forEach((element) {
  144. if (element.id == deviceId) {
  145. state.currentMicrophone = element;
  146. state.microphoneAvailable = null;
  147. state.currentMicrophoneId = element.id;
  148. }
  149. });
  150. if (state.currentMicrophone == null) return;
  151. if (state.detectingMicrophone) {
  152. await _stopRecordMicrophone();
  153. _startRecordMicrophone();
  154. }
  155. }
  156. /// 选择扬声器
  157. void selectSpeakerById(String deviceId) {
  158. state.speakerList.forEach((element) {
  159. if (element.id == deviceId) {
  160. state.currentSpeaker = element;
  161. state.speakerAvailable = null;
  162. }
  163. });
  164. if (state.currentSpeaker == null) return;
  165. }
  166. /// 设置当前选中的摄像头可用性
  167. void setCameraAvailable(bool available) {
  168. state.cameraAvailable = available;
  169. stopDetectingCamera();
  170. }
  171. /// 设置当前选中的麦克风可用性
  172. void setMicrophoneAvailable(bool available) {
  173. state.microphoneAvailable = available;
  174. stopDetectingMicrophone();
  175. }
  176. /// 设置当前选中的扬声器可用性
  177. void setSpeakerAvailable(bool available) {
  178. state.speakerAvailable = available;
  179. stopDetectingSpeaker();
  180. }
  181. /// 开始检测所有设备
  182. Future<void> startDetectingAll() async {
  183. await refreshAllDevices();
  184. if (state.cameraList.isNotEmpty) startDetectingCamera();
  185. if (state.microphoneList.isNotEmpty) startDetectingMicrophone();
  186. if (state.speakerList.isNotEmpty) startDetectingSpeaker();
  187. }
  188. /// 开始检测摄像头
  189. void startDetectingCamera() {
  190. state.cameraAvailable = null;
  191. state.detectingCamera = true;
  192. _openSelectedCamera();
  193. }
  194. /// 开始检测麦克风
  195. void startDetectingMicrophone() {
  196. state.microphoneAvailable = null;
  197. state.detectingMicrophone = true;
  198. _startRecordMicrophone();
  199. }
  200. /// 开始检测扬声器
  201. void startDetectingSpeaker() {
  202. state.speakerAvailable = null;
  203. state.detectingSpeaker = true;
  204. _startPlaySound();
  205. }
  206. /// 结束检测摄像头
  207. void stopDetectingCamera() {
  208. state.detectingCamera = false;
  209. _stopCamera();
  210. }
  211. /// 结束检测麦克风
  212. void stopDetectingMicrophone() {
  213. state.detectingMicrophone = false;
  214. _stopRecordMicrophone();
  215. }
  216. /// 结束检测扬声器
  217. void stopDetectingSpeaker() {
  218. state.detectingSpeaker = false;
  219. _stopPlaySound();
  220. }
  221. /// 初始化摄像头列表
  222. Future<void> _initCameras() async {
  223. state.searchingCamera = true;
  224. List<HardwareDevice> _cameraList = await _trtcGetCameras();
  225. try {
  226. _cameras = await availableCameras();
  227. } on Exception catch (e) {
  228. print('Search camera failed: $e');
  229. _cameras = [];
  230. }
  231. for (var i = 0; i < _cameras.length; i++) {
  232. for (var j = 0; j < _cameraList.length; j++) {
  233. if (_cameras[i].name == _cameraList[j].name) {
  234. state.cameraList.add(_cameraList[j]);
  235. break;
  236. }
  237. }
  238. }
  239. if (state.cameraList.isNotEmpty) {
  240. state.currentCamera = state.cameraList[0];
  241. state.currentCameraId = state.cameraList[0].id;
  242. }
  243. // final idealCameraName = hardwareSetting.idealWebVideoInputDeviceName;
  244. // bool isFind = false;
  245. // if (idealCameraName != '') {
  246. // for (var i = 0; i < state.cameraList.length; i++) {
  247. // if (state.cameraList[i].name == idealCameraName) {
  248. // state.currentCamera = state.cameraList[i];
  249. // isFind = true;
  250. // break;
  251. // }
  252. // }
  253. // }
  254. // if (isFind == false && state.cameraList.isNotEmpty) {
  255. // hardwareSetting.setIdealWebVideoInputDeviceName(state.cameraList[0].name);
  256. // hardwareSettingService.save();
  257. // }
  258. state.searchingCamera = false;
  259. }
  260. /// 初始化麦克风列表
  261. Future<void> _initMicrophones() async {
  262. state.searchingMicrophone = true;
  263. List<HardwareDevice> _microphoneList = await _trtcGetMicrophones();
  264. state.microphoneList.addAll(_microphoneList);
  265. await _initTheRecorder();
  266. if (state.microphoneList.isNotEmpty) {
  267. state.currentMicrophone = state.microphoneList[0];
  268. state.currentMicrophoneId = state.microphoneList[0].id;
  269. }
  270. // final idealMicrophoneName = '';
  271. // bool isFind = false;
  272. // if (idealMicrophoneName != '') {
  273. // for (var i = 0; i < state.microphoneList.length; i++) {
  274. // if (state.microphoneList[i].name == idealMicrophoneName) {
  275. // state.currentMicrophone = state.microphoneList[i];
  276. // // isFind = true;
  277. // break;
  278. // }
  279. // }
  280. // }
  281. // if (isFind == false && state.microphoneList.isNotEmpty) {
  282. // hardwareSetting
  283. // .setIdealWebAudioInputDeviceName(state.microphoneList[0].name);
  284. // hardwareSettingService.save();
  285. // }
  286. state.searchingMicrophone = false;
  287. }
  288. /// 初始化扬声器列表
  289. Future<void> _initSpeakers() async {
  290. state.searchingSpeaker = true;
  291. List<HardwareDevice> _speakerList = await _trtcGetSpeakers();
  292. state.speakerList.addAll(_speakerList);
  293. await _initThePlayer();
  294. if (state.speakerList.isNotEmpty) {
  295. state.currentSpeaker = state.speakerList[0];
  296. state.currentSpeakerId = state.speakerList[0].id;
  297. }
  298. state.searchingSpeaker = false;
  299. }
  300. /// 初始化播放器
  301. Future<void> _initThePlayer() async {
  302. await flutterSound.openPlayer();
  303. await flutterSound
  304. .setSubscriptionDuration(const Duration(milliseconds: 100));
  305. state.isDisplaySpeakerWave = true;
  306. }
  307. /// 初始化麦克风录音器
  308. Future<void> _initTheRecorder() async {
  309. await flutterRecorder.openRecorder();
  310. if (!await flutterRecorder.isEncoderSupported(_codec) && kIsWeb) {
  311. _codec = Codec.opusWebM;
  312. if (!await flutterRecorder.isEncoderSupported(_codec) && kIsWeb) {
  313. state.isDisplayMicrophoneWave = true;
  314. return;
  315. }
  316. }
  317. // 设置采样率
  318. await flutterRecorder
  319. .setSubscriptionDuration(const Duration(milliseconds: 50));
  320. final session = await AudioSession.instance;
  321. await session.configure(AudioSessionConfiguration(
  322. avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
  323. avAudioSessionCategoryOptions:
  324. AVAudioSessionCategoryOptions.allowBluetooth |
  325. AVAudioSessionCategoryOptions.defaultToSpeaker,
  326. avAudioSessionMode: AVAudioSessionMode.spokenAudio,
  327. avAudioSessionRouteSharingPolicy:
  328. AVAudioSessionRouteSharingPolicy.defaultPolicy,
  329. avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
  330. androidAudioAttributes: const AndroidAudioAttributes(
  331. contentType: AndroidAudioContentType.speech,
  332. flags: AndroidAudioFlags.none,
  333. usage: AndroidAudioUsage.voiceCommunication,
  334. ),
  335. androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
  336. androidWillPauseWhenDucked: true,
  337. ));
  338. state.isDisplayMicrophoneWave = true;
  339. }
  340. /// 初始化设备列表
  341. Future<void> _intialize() async {
  342. await _initCameras();
  343. await _initMicrophones();
  344. await _initSpeakers();
  345. }
  346. /// 打开当前选中的摄像头
  347. _openSelectedCamera() {
  348. bool found = false;
  349. int cameraIndex = 0;
  350. for (var i = 0; i < _cameras.length; i++) {
  351. if (_cameras[i].name == state.currentCamera?.name) {
  352. cameraIndex = i;
  353. found = true;
  354. break;
  355. }
  356. }
  357. if (!found) {
  358. return;
  359. }
  360. cameraController =
  361. CameraController(_cameras[cameraIndex], ResolutionPreset.max);
  362. cameraController.initialize().then((_) {
  363. state.isDisplayCameraWindow = true;
  364. }).catchError((Object e) {
  365. if (e is CameraException) {
  366. switch (e.code) {
  367. case 'CameraAccessDenied':
  368. // Handle access errors here.
  369. break;
  370. default:
  371. // Handle other errors here.
  372. break;
  373. }
  374. }
  375. });
  376. }
  377. /// 使用当前的扬声器播放测试音频
  378. Future<void> _startPlaySound() async {
  379. await flutterSound.startPlayer(
  380. fromURI: kDebugMode
  381. ? "assets/audio/testspeak.mp3"
  382. : "assets/assets/audio/testspeak.mp3",
  383. codec: Codec.aacADTS,
  384. whenFinished: () {});
  385. }
  386. // 开始采集麦克风音频
  387. void _startRecordMicrophone() async {
  388. /// 获取到当前选中的麦克风的id
  389. if (state.currentMicrophone == null) {
  390. return;
  391. }
  392. final String fileNameWithMicrophoneId =
  393. "temp@${state.currentMicrophone!.id}.webm";
  394. /// 将麦克风的id作为文件名,web端的record会做处理,会将文件名作为选中的麦克风id进行设置
  395. await flutterRecorder.startRecorder(
  396. toFile: fileNameWithMicrophoneId,
  397. codec: _codec,
  398. audioSource: AudioSource.microphone);
  399. }
  400. /// 关闭摄像头并释放
  401. _stopCamera() {
  402. state.isDisplayCameraWindow = false;
  403. if (cameraController.value.isStreamingImages) {
  404. cameraController.stopImageStream();
  405. }
  406. cameraController.dispose();
  407. }
  408. /// 停止播放音频
  409. _stopPlaySound() {
  410. flutterSound.stopPlayer();
  411. }
  412. /// 停止采集麦克风音频
  413. Future<void> _stopRecordMicrophone() async {
  414. await flutterRecorder.stopRecorder();
  415. }
  416. /// 调用 TRTC 获取摄像头列表以及ids
  417. Future<List<HardwareDevice>> _trtcGetCameras() async {
  418. List<HardwareDevice> deviceList = [];
  419. TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
  420. TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
  421. final List<dynamic>? cameras = await deviceManager.getDevicesList(2);
  422. if (cameras != null && cameras.isNotEmpty) {
  423. cameras.forEach((camera) {
  424. deviceList.add(HardwareDevice(
  425. id: camera["deviceId"],
  426. name: camera["label"],
  427. type: HardwareDeviceType.camera));
  428. });
  429. }
  430. return deviceList;
  431. }
  432. /// 调用 TRTC 获取麦克风列表
  433. Future<List<HardwareDevice>> _trtcGetMicrophones() async {
  434. List<HardwareDevice> deviceList = [];
  435. TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
  436. TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
  437. final List<dynamic>? microphones = await deviceManager.getDevicesList(0);
  438. if (microphones != null && microphones.isNotEmpty) {
  439. microphones.forEach((microphone) {
  440. deviceList.add(HardwareDevice(
  441. id: microphone["deviceId"],
  442. name: microphone["label"],
  443. type: HardwareDeviceType.microphone));
  444. });
  445. }
  446. return deviceList;
  447. }
  448. /// 调用 TRTC 获取扬声器列表
  449. Future<List<HardwareDevice>> _trtcGetSpeakers() async {
  450. List<HardwareDevice> deviceList = [];
  451. TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
  452. TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
  453. final List<dynamic>? speakers = await deviceManager.getDevicesList(1);
  454. if (speakers != null && speakers.isNotEmpty) {
  455. speakers.forEach((speaker) {
  456. deviceList.add(HardwareDevice(
  457. id: speaker["deviceId"],
  458. name: speaker["label"],
  459. type: HardwareDeviceType.speaker));
  460. });
  461. }
  462. return deviceList;
  463. }
  464. }