controller.dart 14 KB

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