123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- import 'dart:async';
- import 'package:camera/camera.dart';
- import 'package:get/get.dart';
- import 'package:tencent_trtc_cloud/trtc_cloud.dart';
- import 'package:tencent_trtc_cloud/tx_device_manager.dart';
- import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart';
- import 'package:flutter_sound/flutter_sound.dart';
- import 'package:audio_session/audio_session.dart';
- import 'package:flutter/foundation.dart' show kIsWeb;
- import 'index.dart';
- class HardwareDetectionController extends GetxController {
- HardwareDetectionController();
- final state = HardwareDetectionState();
- /// 相机控制器需要的参数
- late List<CameraDescription> _cameras;
- late CameraController cameraController;
- /// 扬声器控制器需要的参数
- final FlutterSoundPlayer flutterSound = FlutterSoundPlayer();
- /// 麦克风控制器需要的参数
- Codec _codec = Codec.aacMP4;
- String _mPath = 'tau_file.mp4';
- final FlutterSoundRecorder flutterRecorder = FlutterSoundRecorder();
- /// 初始化设备列表
- Future<void> _intialize() async {
- await _initCameras();
- await _initMicrophones();
- await _initSpeakers();
- print("总设备数:${state.deviceList.length}");
- state.cameraList = state.deviceList
- .where((element) => element.type == HardwareDeviceType.camera)
- .toList();
- state.microphoneList = state.deviceList
- .where((element) => element.type == HardwareDeviceType.microphone)
- .toList();
- state.speakerList = state.deviceList
- .where((element) => element.type == HardwareDeviceType.speaker)
- .toList();
- if (state.cameraList.isNotEmpty) state.currentCamera = state.cameraList[0];
- if (state.microphoneList.isNotEmpty)
- state.currentMicrophone = state.microphoneList[0];
- if (state.speakerList.isNotEmpty)
- state.currentSpeaker = state.speakerList[0];
- }
- /// 初始化摄像头列表
- Future<void> _initCameras() async {
- List<HardwareDevice> _cameraList = await _trtcGetCameras();
- _cameras = await availableCameras();
- print("初始摄像头数量:${_cameras.length}");
- for (var i = 0; i < _cameras.length; i++) {
- for (var j = 0; j < _cameraList.length; j++) {
- if (_cameras[i].name == _cameraList[j].name) {
- state.deviceList.add(_cameraList[j]);
- break;
- }
- }
- }
- }
- /// 初始化麦克风列表
- Future<void> _initMicrophones() async {
- List<HardwareDevice> _microphoneList = await _trtcGetMicrophones();
- state.deviceList.addAll(_microphoneList);
- await _initTheRecorder();
- }
- /// 初始化扬声器列表
- Future<void> _initSpeakers() async {
- List<HardwareDevice> _speakerList = await _trtcGetSpeakers();
- state.deviceList.addAll(_speakerList);
- await _initThePlayer();
- }
- // 刷新设备列表
- Future<void> refreshAllDevices() async {
- await refreshCameraList();
- await refreshMicrophoneList();
- await refreshSpeakerList();
- }
- /// 设置当前选中的摄像头可用性
- void setCameraAvailable(bool available) {
- state.cameraAvailable = available;
- stopDetectingCamera();
- }
- /// 设置当前选中的麦克风可用性
- void setMicrophoneAvailable(bool available) {
- state.microphoneAvailable = available;
- stopDetectingMicrophone();
- }
- /// 设置当前选中的扬声器可用性
- void setSpeakerAvailable(bool available) {
- state.speakerAvailable = available;
- stopDetectingSpeaker();
- }
- /// 开始检测摄像头
- void startDetectingCamera() {
- state.cameraAvailable = null;
- state.detectingCamera = true;
- _openSelectedCamera();
- }
- /// 结束检测摄像头
- void stopDetectingCamera() {
- state.detectingCamera = false;
- _stopCamera();
- }
- /// 开始检测麦克风
- void startDetectingMicrophone() {
- state.microphoneAvailable = null;
- state.detectingMicrophone = true;
- _startRecordMicrophone();
- }
- /// 结束检测麦克风
- void stopDetectingMicrophone() {
- state.detectingMicrophone = false;
- _stopRecordMicrophone();
- }
- /// 开始检测扬声器
- void startDetectingSpeaker() {
- state.speakerAvailable = null;
- state.detectingSpeaker = true;
- _startPlaySound();
- }
- /// 结束检测扬声器
- void stopDetectingSpeaker() {
- state.detectingSpeaker = false;
- _stopPlaySound();
- }
- /// 开始检测所有设备
- Future<void> startDetectingAll() async {
- await refreshAllDevices();
- if (state.cameraList.isNotEmpty) startDetectingCamera();
- if (state.microphoneList.isNotEmpty) startDetectingMicrophone();
- if (state.speakerList.isNotEmpty) startDetectingSpeaker();
- }
- /// 选择摄像头
- void selectCameraById(String deviceId) {
- if (state.currentCamera != null && deviceId == state.currentCamera!.id)
- return;
- state.cameraList.forEach((element) {
- if (element.id == deviceId) {
- state.currentCamera = element;
- state.cameraAvailable = null;
- print("选择摄像头: ${element.name} id: ${element.id}");
- }
- });
- if (state.currentCamera == null) return;
- if (state.detectingCamera) {
- _stopCamera();
- _openSelectedCamera();
- }
- }
- /// 选择麦克风
- Future<void> selectMicrophoneById(String deviceId) async {
- state.microphoneList.forEach((element) {
- if (element.id == deviceId) {
- state.currentMicrophone = element;
- state.microphoneAvailable = null;
- print("选择麦克风: ${element.name} id: ${element.id}");
- }
- });
- if (state.currentMicrophone == null) return;
- if (state.detectingMicrophone) {
- await _stopRecordMicrophone();
- _startRecordMicrophone();
- }
- }
- /// 选择扬声器
- void selectSpeakerById(String deviceId) {
- state.speakerList.forEach((element) {
- if (element.id == deviceId) {
- state.currentSpeaker = element;
- state.speakerAvailable = null;
- print("选择扬声器: ${element.name} id: ${element.id}");
- }
- });
- if (state.currentSpeaker == null) return;
- }
- /// 刷新扬声器列表
- Future<void> refreshSpeakerList() async {
- if (state.detectingSpeaker) stopDetectingSpeaker();
- state.speakerList = [];
- state.speakerAvailable = null;
- await Future.delayed(Duration(milliseconds: 100));
- state.speakerList = await _trtcGetSpeakers();
- for (int i = 0; i < state.deviceList.length; i++) {
- if (state.deviceList[i].type == HardwareDeviceType.speaker) {
- state.deviceList.removeAt(i);
- i--;
- }
- }
- state.deviceList.addAll(state.speakerList);
- }
- /// 刷新摄像头列表
- Future<void> refreshCameraList() async {
- if (state.detectingCamera) stopDetectingCamera();
- state.cameraList = [];
- state.cameraAvailable = null;
- await Future.delayed(Duration(milliseconds: 100));
- state.cameraList = await _trtcGetCameras();
- for (int i = 0; i < state.deviceList.length; i++) {
- if (state.deviceList[i].type == HardwareDeviceType.camera) {
- state.deviceList.removeAt(i);
- i--;
- }
- }
- state.deviceList.addAll(state.cameraList);
- }
- /// 刷新麦克风列表
- Future<void> refreshMicrophoneList() async {
- if (state.detectingMicrophone) stopDetectingMicrophone();
- state.microphoneList = [];
- state.microphoneAvailable = null;
- await Future.delayed(Duration(milliseconds: 100));
- state.microphoneList = await _trtcGetMicrophones();
- for (int i = 0; i < state.deviceList.length; i++) {
- if (state.deviceList[i].type == HardwareDeviceType.microphone) {
- state.deviceList.removeAt(i);
- i--;
- }
- }
- state.deviceList.addAll(state.microphoneList);
- }
- /// 调用 TRTC 获取摄像头列表
- Future<List<HardwareDevice>> _trtcGetCameras() async {
- List<HardwareDevice> deviceList = [];
- TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
- TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
- final List<dynamic>? cameras = await deviceManager.getDevicesList(2);
- if (cameras != null && cameras.isNotEmpty) {
- cameras.forEach((camera) {
- deviceList.add(HardwareDevice(
- id: camera["deviceId"],
- name: camera["label"],
- type: HardwareDeviceType.camera));
- });
- }
- return deviceList;
- }
- /// 调用 TRTC 获取麦克风列表
- Future<List<HardwareDevice>> _trtcGetMicrophones() async {
- List<HardwareDevice> deviceList = [];
- TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
- TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
- final List<dynamic>? microphones = await deviceManager.getDevicesList(0);
- if (microphones != null && microphones.isNotEmpty) {
- microphones.forEach((microphone) {
- deviceList.add(HardwareDevice(
- id: microphone["deviceId"],
- name: microphone["label"],
- type: HardwareDeviceType.microphone));
- });
- }
- return deviceList;
- }
- /// 调用 TRTC 获取扬声器列表
- Future<List<HardwareDevice>> _trtcGetSpeakers() async {
- List<HardwareDevice> deviceList = [];
- TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;
- TXDeviceManager deviceManager = trtcCloud.getDeviceManager();
- final List<dynamic>? speakers = await deviceManager.getDevicesList(1);
- if (speakers != null && speakers.isNotEmpty) {
- speakers.forEach((speaker) {
- deviceList.add(HardwareDevice(
- id: speaker["deviceId"],
- name: speaker["label"],
- type: HardwareDeviceType.speaker));
- });
- }
- return deviceList;
- }
- /// TODO 调用 js 获取所有设备列表
- /// const devices = await navigator.mediaDevices.enumerateDevices();
- /// 打开当前选中的摄像头
- _openSelectedCamera() {
- bool found = false;
- int cameraIndex = 0;
- for (var i = 0; i < _cameras.length; i++) {
- if (_cameras[i].name == state.currentCamera?.name) {
- cameraIndex = i;
- found = true;
- break;
- }
- }
- if (!found) {
- print("未找到当前选中的摄像头");
- return;
- }
- cameraController =
- CameraController(_cameras[cameraIndex], ResolutionPreset.max);
- cameraController.initialize().then((_) {
- state.isDisplayCameraWindow = true;
- }).catchError((Object e) {
- if (e is CameraException) {
- switch (e.code) {
- case 'CameraAccessDenied':
- // Handle access errors here.
- break;
- default:
- // Handle other errors here.
- break;
- }
- }
- });
- }
- /// 关闭摄像头并释放
- _stopCamera() {
- state.isDisplayCameraWindow = false;
- if (cameraController.value.isStreamingImages) {
- cameraController.stopImageStream();
- }
- cameraController.dispose();
- }
- /// 使用当前的扬声器播放测试音频
- Future<void> _startPlaySound() async {
- await flutterSound.startPlayer(
- fromURI: "assets/testspeak.mp3",
- codec: Codec.aacADTS,
- whenFinished: () {
- print("播放完成");
- });
- }
- /// 停止播放音频
- _stopPlaySound() {
- print("停止播放音频");
- flutterSound.stopPlayer();
- }
- /// 初始化播放器
- Future<void> _initThePlayer() async {
- await flutterSound.openPlayer();
- await flutterSound
- .setSubscriptionDuration(const Duration(milliseconds: 100));
- state.isDisplaySpeakerWave = true;
- }
- // 开始采集麦克风音频
- void _startRecordMicrophone() async {
- /// 获取到当前选中的麦克风的id
- if (state.currentMicrophone == null) {
- return;
- }
- final String fileNameWithMicrophoneId =
- "${state.currentMicrophone!.id}.webm";
- /// 将麦克风的id作为文件名,web端的record会做处理,会将文件名作为选中的麦克风id进行设置
- await flutterRecorder.startRecorder(
- toFile: fileNameWithMicrophoneId,
- codec: _codec,
- audioSource: AudioSource.microphone);
- }
- /// 停止采集麦克风音频
- Future<void> _stopRecordMicrophone() async {
- await flutterRecorder.stopRecorder();
- }
- /// 初始化麦克风录音器
- Future<void> _initTheRecorder() async {
- await flutterRecorder.openRecorder();
- if (!await flutterRecorder.isEncoderSupported(_codec) && kIsWeb) {
- _codec = Codec.opusWebM;
- _mPath = 'tau_file.webm';
- if (!await flutterRecorder.isEncoderSupported(_codec) && kIsWeb) {
- state.isDisplayMicrophoneWave = true;
- return;
- }
- }
- // 设置采样率
- await flutterRecorder
- .setSubscriptionDuration(const Duration(milliseconds: 50));
- final session = await AudioSession.instance;
- await session.configure(AudioSessionConfiguration(
- avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
- avAudioSessionCategoryOptions:
- AVAudioSessionCategoryOptions.allowBluetooth |
- AVAudioSessionCategoryOptions.defaultToSpeaker,
- avAudioSessionMode: AVAudioSessionMode.spokenAudio,
- avAudioSessionRouteSharingPolicy:
- AVAudioSessionRouteSharingPolicy.defaultPolicy,
- avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
- androidAudioAttributes: const AndroidAudioAttributes(
- contentType: AndroidAudioContentType.speech,
- flags: AndroidAudioFlags.none,
- usage: AndroidAudioUsage.voiceCommunication,
- ),
- androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
- androidWillPauseWhenDucked: true,
- ));
- state.isDisplayMicrophoneWave = true;
- }
- /// 在 widget 内存中分配后立即调用。
- @override
- void onInit() async {
- await _intialize();
- super.onInit();
- }
- /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
- @override
- void onReady() {
- super.onReady();
- }
- /// 在 [onDelete] 方法之前调用。
- @override
- void onClose() {
- super.onClose();
- }
- /// dispose 释放内存
- @override
- void dispose() {
- super.dispose();
- cameraController.dispose();
- flutterSound.closePlayer();
- flutterRecorder.closeRecorder();
- }
- }
|