controller.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. void refreshDevices() {
  72. ///TODO 刷新设备列表
  73. }
  74. /// 设置当前选中的摄像头可用性
  75. void setCameraAvailable(bool available) {
  76. state.cameraAvailable = available;
  77. stopDetectingCamera();
  78. }
  79. /// 设置当前选中的麦克风可用性
  80. void setMicrophoneAvailable(bool available) {
  81. state.microphoneAvailable = available;
  82. stopDetectingMicrophone();
  83. }
  84. /// 设置当前选中的扬声器可用性
  85. void setSpeakerAvailable(bool available) {
  86. state.speakerAvailable = available;
  87. stopDetectingSpeaker();
  88. }
  89. /// 开始检测摄像头
  90. void startDetectingCamera() {
  91. state.cameraAvailable = null;
  92. state.detectingCamera = true;
  93. _openSelectedCamera();
  94. }
  95. /// 结束检测摄像头
  96. void stopDetectingCamera() {
  97. state.detectingCamera = false;
  98. _stopCamera();
  99. }
  100. /// 开始检测麦克风
  101. void startDetectingMicrophone() {
  102. state.microphoneAvailable = null;
  103. state.detectingMicrophone = true;
  104. _startRecordMicrophone();
  105. }
  106. /// 结束检测麦克风
  107. void stopDetectingMicrophone() {
  108. state.detectingMicrophone = false;
  109. _stopRecordMicrophone();
  110. }
  111. /// 开始检测扬声器
  112. void startDetectingSpeaker() {
  113. state.speakerAvailable = null;
  114. state.detectingSpeaker = true;
  115. _startPlaySound();
  116. }
  117. /// 结束检测扬声器
  118. void stopDetectingSpeaker() {
  119. state.detectingSpeaker = false;
  120. _stopPlaySound();
  121. }
  122. /// 选择摄像头
  123. void selectCameraById(String deviceId) {
  124. if (state.currentCamera != null && deviceId == state.currentCamera!.id)
  125. return;
  126. state.cameraList.forEach((element) {
  127. if (element.id == deviceId) {
  128. state.currentCamera = element;
  129. state.cameraAvailable = null;
  130. print("选择摄像头: ${element.name} id: ${element.id}");
  131. }
  132. });
  133. if (state.currentCamera == null) return;
  134. /// 如果当前正在检测摄像头,则刷新
  135. if (state.detectingCamera) {
  136. _stopCamera();
  137. _openSelectedCamera();
  138. }
  139. }
  140. /// 选择麦克风 [web 无法修改麦克风]
  141. void selectMicrophoneById(String deviceId) {
  142. state.microphoneList.forEach((element) {
  143. if (element.id == deviceId) {
  144. state.currentMicrophone = element;
  145. state.microphoneAvailable = null;
  146. print("选择麦克风: ${element.name} id: ${element.id}");
  147. }
  148. });
  149. if (state.currentMicrophone == null) return;
  150. }
  151. /// 选择扬声器
  152. void selectSpeakerById(String deviceId) {
  153. state.speakerList.forEach((element) {
  154. if (element.id == deviceId) {
  155. state.currentSpeaker = element;
  156. state.speakerAvailable = null;
  157. print("选择扬声器: ${element.name} id: ${element.id}");
  158. }
  159. });
  160. if (state.currentSpeaker == null) return;
  161. }
  162. /// 刷新扬声器列表
  163. Future<void> refreshSpeakerList() async {
  164. if (state.detectingSpeaker) stopDetectingSpeaker();
  165. state.speakerList = [];
  166. state.speakerAvailable = null;
  167. await Future.delayed(Duration(milliseconds: 100));
  168. state.speakerList = await _trtcGetSpeakers();
  169. for (int i = 0; i < state.deviceList.length; i++) {
  170. if (state.deviceList[i].type == HardwareDeviceType.speaker) {
  171. state.deviceList.removeAt(i);
  172. i--;
  173. }
  174. }
  175. state.deviceList.addAll(state.speakerList);
  176. print("当前总设备数量: ${state.deviceList.length}");
  177. }
  178. /// 刷新摄像头列表
  179. Future<void> refreshCameraList() async {
  180. if (state.detectingCamera) stopDetectingCamera();
  181. state.cameraList = [];
  182. state.cameraAvailable = null;
  183. await Future.delayed(Duration(milliseconds: 100));
  184. state.cameraList = await _trtcGetCameras();
  185. for (int i = 0; i < state.deviceList.length; i++) {
  186. if (state.deviceList[i].type == HardwareDeviceType.camera) {
  187. state.deviceList.removeAt(i);
  188. i--;
  189. }
  190. }
  191. state.deviceList.addAll(state.cameraList);
  192. print("当前总设备数量: ${state.deviceList.length}");
  193. }
  194. /// 刷新麦克风列表
  195. Future<void> refreshMicrophoneList() async {
  196. if (state.detectingMicrophone) stopDetectingMicrophone();
  197. state.microphoneList = [];
  198. state.microphoneAvailable = null;
  199. await Future.delayed(Duration(milliseconds: 100));
  200. state.microphoneList = await _trtcGetMicrophones();
  201. for (int i = 0; i < state.deviceList.length; i++) {
  202. if (state.deviceList[i].type == HardwareDeviceType.microphone) {
  203. state.deviceList.removeAt(i);
  204. i--;
  205. }
  206. }
  207. state.deviceList.addAll(state.microphoneList);
  208. print("当前总设备数量: ${state.deviceList.length}");
  209. }
  210. /// 调用 TRTC 获取摄像头列表
  211. Future<List<HardwareDevice>> _trtcGetCameras() async {
  212. List<HardwareDevice> deviceList = [];
  213. TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
  214. TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
  215. final List<dynamic>? cameras = await deviceManager.getDevicesList(2);
  216. if (cameras != null && cameras.isNotEmpty) {
  217. cameras.forEach((camera) {
  218. deviceList.add(HardwareDevice(
  219. id: camera["deviceId"],
  220. name: camera["label"],
  221. type: HardwareDeviceType.camera));
  222. });
  223. }
  224. return deviceList;
  225. }
  226. /// 调用 TRTC 获取麦克风列表
  227. Future<List<HardwareDevice>> _trtcGetMicrophones() async {
  228. List<HardwareDevice> deviceList = [];
  229. TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
  230. TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
  231. final List<dynamic>? microphones = await deviceManager.getDevicesList(0);
  232. if (microphones != null && microphones.isNotEmpty) {
  233. microphones.forEach((microphone) {
  234. deviceList.add(HardwareDevice(
  235. id: microphone["deviceId"],
  236. name: microphone["label"],
  237. type: HardwareDeviceType.microphone));
  238. });
  239. }
  240. return deviceList;
  241. }
  242. /// 调用 TRTC 获取扬声器列表
  243. Future<List<HardwareDevice>> _trtcGetSpeakers() async {
  244. List<HardwareDevice> deviceList = [];
  245. TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
  246. TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
  247. final List<dynamic>? speakers = await deviceManager.getDevicesList(1);
  248. if (speakers != null && speakers.isNotEmpty) {
  249. speakers.forEach((speaker) {
  250. deviceList.add(HardwareDevice(
  251. id: speaker["deviceId"],
  252. name: speaker["label"],
  253. type: HardwareDeviceType.speaker));
  254. });
  255. }
  256. return deviceList;
  257. }
  258. /// 打开当前选中的摄像头
  259. _openSelectedCamera() {
  260. bool found = false;
  261. int cameraIndex = 0;
  262. for (var i = 0; i < _cameras.length; i++) {
  263. if (_cameras[i].name == state.currentCamera?.name) {
  264. cameraIndex = i;
  265. found = true;
  266. break;
  267. }
  268. }
  269. if (!found) {
  270. print("未找到当前选中的摄像头");
  271. return;
  272. }
  273. cameraController =
  274. CameraController(_cameras[cameraIndex], ResolutionPreset.max);
  275. cameraController.initialize().then((_) {
  276. state.isDisplayCameraWindow = true;
  277. }).catchError((Object e) {
  278. if (e is CameraException) {
  279. switch (e.code) {
  280. case 'CameraAccessDenied':
  281. // Handle access errors here.
  282. break;
  283. default:
  284. // Handle other errors here.
  285. break;
  286. }
  287. }
  288. });
  289. }
  290. /// 关闭摄像头并释放
  291. _stopCamera() {
  292. state.isDisplayCameraWindow = false;
  293. if (cameraController.value.isStreamingImages) {
  294. cameraController.stopImageStream();
  295. }
  296. cameraController.dispose();
  297. }
  298. /// 使用当前的扬声器播放测试音频
  299. Future<void> _startPlaySound() async {
  300. await flutterSound.startPlayer(
  301. fromURI: "assets/testspeak.mp3",
  302. codec: Codec.aacADTS,
  303. whenFinished: () {
  304. print("播放完成");
  305. });
  306. }
  307. /// 停止播放音频
  308. _stopPlaySound() {
  309. print("停止播放音频");
  310. flutterSound.stopPlayer();
  311. }
  312. /// 初始化播放器
  313. Future<void> _initThePlayer() async {
  314. await flutterSound.openPlayer();
  315. await flutterSound
  316. .setSubscriptionDuration(const Duration(milliseconds: 100));
  317. state.isDisplaySpeakerWave = true;
  318. }
  319. // 开始采集麦克风音频
  320. void _startRecordMicrophone() async {
  321. await flutterRecorder.startRecorder(
  322. toFile: _mPath, codec: _codec, audioSource: AudioSource.microphone);
  323. }
  324. /// 停止采集麦克风音频
  325. void _stopRecordMicrophone() async {
  326. await flutterRecorder.stopRecorder();
  327. }
  328. /// 初始化麦克风录音器
  329. Future<void> _initTheRecorder() async {
  330. await flutterRecorder.openRecorder();
  331. if (!await flutterRecorder.isEncoderSupported(_codec) && kIsWeb) {
  332. _codec = Codec.opusWebM;
  333. _mPath = 'tau_file.webm';
  334. if (!await flutterRecorder.isEncoderSupported(_codec) && kIsWeb) {
  335. state.isDisplayMicrophoneWave = true;
  336. return;
  337. }
  338. }
  339. // 设置采样率
  340. await flutterRecorder
  341. .setSubscriptionDuration(const Duration(milliseconds: 50));
  342. final session = await AudioSession.instance;
  343. await session.configure(AudioSessionConfiguration(
  344. avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
  345. avAudioSessionCategoryOptions:
  346. AVAudioSessionCategoryOptions.allowBluetooth |
  347. AVAudioSessionCategoryOptions.defaultToSpeaker,
  348. avAudioSessionMode: AVAudioSessionMode.spokenAudio,
  349. avAudioSessionRouteSharingPolicy:
  350. AVAudioSessionRouteSharingPolicy.defaultPolicy,
  351. avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
  352. androidAudioAttributes: const AndroidAudioAttributes(
  353. contentType: AndroidAudioContentType.speech,
  354. flags: AndroidAudioFlags.none,
  355. usage: AndroidAudioUsage.voiceCommunication,
  356. ),
  357. androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
  358. androidWillPauseWhenDucked: true,
  359. ));
  360. state.isDisplayMicrophoneWave = true;
  361. }
  362. /// 在 widget 内存中分配后立即调用。
  363. @override
  364. void onInit() async {
  365. await _intialize();
  366. super.onInit();
  367. }
  368. /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
  369. @override
  370. void onReady() {
  371. super.onReady();
  372. }
  373. /// 在 [onDelete] 方法之前调用。
  374. @override
  375. void onClose() {
  376. super.onClose();
  377. }
  378. /// dispose 释放内存
  379. @override
  380. void dispose() {
  381. super.dispose();
  382. cameraController.dispose();
  383. flutterSound.closePlayer();
  384. flutterRecorder.closeRecorder();
  385. }
  386. }