import 'dart:async'; import 'dart:io'; import 'dart:math'; import 'package:flutter/services.dart'; import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; import 'package:flutter_voice_processor/flutter_voice_processor.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; import 'package:star_lock/talk/call/callTalk.dart'; import 'package:star_lock/talk/startChart/constant/talk_status.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pbenum.dart'; import 'package:star_lock/talk/startChart/start_chart_manage.dart'; import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart'; import '../../../../talk/call/g711.dart'; import '../../../../talk/udp/udp_manage.dart'; import '../../../../talk/udp/udp_senderManage.dart'; import '../../../../tools/baseGetXController.dart'; import '../../../../tools/eventBusEventManage.dart'; class TalkViewLogic extends BaseGetXController { final TalkViewState state = TalkViewState(); Timer? _syncTimer; int _startTime = 0; final int bufferSize = 22; // 缓冲区大小(以帧为单位) final List frameTimestamps = []; int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS) int maxFrameIntervalMs = 100; // 最大帧间隔(约10 FPS) /// 收到Talk发送的状态 StreamSubscription? _getTalkStatusRefreshUIEvent; void _initFlutterPcmSound() { const int sampleRate = 44100; FlutterPcmSound.setLogLevel(LogLevel.verbose); FlutterPcmSound.setup(sampleRate: sampleRate, channelCount: 2); // 设置 feed 阈值 if (Platform.isAndroid) { FlutterPcmSound.setFeedThreshold(-1); // Android 平台的特殊处理 } else { FlutterPcmSound.setFeedThreshold(sampleRate ~/ 32); // 非 Android 平台的处理 } } /// 挂断 void udpHangUpAction() async { if (state.talkStatus.value == TalkStatus.duringCall) { // 如果是通话中就挂断 StartChartManage().sendTalkHangupMessage(); } else { // 拒绝 StartChartManage().sendTalkRejectMessage(); } Get.back(); } // 发起接听命令 void initiateAnswerCommand() { StartChartManage().sendTalkAcceptMessage(); } void _updateFps(List frameTimestamps) { final int now = DateTime.now().millisecondsSinceEpoch; // 移除超过1秒的时间戳 frameTimestamps.removeWhere((timestamp) => now - timestamp > 1000); // 计算 FPS final double fps = frameTimestamps.length.toDouble(); // 更新 FPS state.fps.value = fps; } // 监听音视频数据流 void _startListenTalkData() { state.talkDataRepository.talkDataStream.listen((talkData) { final contentType = talkData.contentType; // 判断数据类型,进行分发处理 switch (contentType) { case TalkData_ContentTypeE.G711: // state.audioBuffer.add(talkData); if (state.audioBuffer.length < 60) { // 假设缓冲区大小为60帧 state.audioBuffer.add(talkData); } break; case TalkData_ContentTypeE.Image: // state.videoBuffer.add(talkData); // 增加视频缓冲区大小 if (state.videoBuffer.length < 60) { // 假设缓冲区大小为60帧 state.videoBuffer.add(talkData); } break; } }); } /// 监听对讲状态 void _startListenTalkStatus() { state.startChartTalkStatus.statusStream.listen((talkStatus) { state.talkStatus.value = talkStatus; switch (talkStatus) { case TalkStatus.rejected: case TalkStatus.hangingUpDuring: case TalkStatus.notTalkData: case TalkStatus.notTalkPing: case TalkStatus.end: _handleInvalidTalkStatus(); break; default: // 其他状态的处理 break; } }); } void _playAudioData(TalkData talkData) { // 将 PCM 数据转换为 PcmArrayInt16 final PcmArrayInt16 fromList = PcmArrayInt16.fromList(talkData.content); FlutterPcmSound.feed(fromList); if (!state.isPlaying.value) { FlutterPcmSound.play(); state.isPlaying.value = true; } } void _playVideoData(TalkData talkData) { state.listData.value = Uint8List.fromList(talkData.content); } void _startPlayback() { int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) Future.delayed(Duration(milliseconds: 800), () { _startTime = DateTime.now().millisecondsSinceEpoch; _syncTimer ??= Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { final currentTime = DateTime.now().millisecondsSinceEpoch; final elapsedTime = currentTime - _startTime; // 根据 elapsedTime 同步音频和视频 // AppLog.log('Elapsed Time: $elapsedTime ms'); // 动态调整帧间隔 _adjustFrameInterval(); // 播放合适的音频帧 if (state.audioBuffer.isNotEmpty && state.audioBuffer.first.durationMs <= elapsedTime) { _playAudioData(state.audioBuffer.removeAt(0)); } // 播放合适的视频帧 // 跳帧策略:如果缓冲区中有多个帧,且它们的时间戳都在当前时间之前,则播放最新的帧 while (state.videoBuffer.isNotEmpty && state.videoBuffer.first.durationMs <= elapsedTime) { // 如果有多个帧,移除旧的帧,保持最新的帧 if (state.videoBuffer.length > 1) { state.videoBuffer.removeAt(0); } else { // 记录当前时间戳 frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); // 计算并更新 FPS _updateFps(frameTimestamps); _playVideoData(state.videoBuffer.removeAt(0)); } } }); }); } /// 动态调整帧间隔 void _adjustFrameInterval() { if (state.videoBuffer.length < 10 && frameIntervalMs < maxFrameIntervalMs) { // 如果缓冲区较小且帧间隔小于最大值,则增加帧间隔 frameIntervalMs += 5; } else if (state.videoBuffer.length > 20 && frameIntervalMs > minFrameIntervalMs) { // 如果缓冲区较大且帧间隔大于最小值,则减少帧间隔 frameIntervalMs -= 5; } _syncTimer?.cancel(); _syncTimer = Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { final currentTime = DateTime.now().millisecondsSinceEpoch; final elapsedTime = currentTime - _startTime; // 播放合适的音频帧 if (state.audioBuffer.isNotEmpty && state.audioBuffer.first.durationMs <= elapsedTime) { _playAudioData(state.audioBuffer.removeAt(0)); } // 播放合适的视频帧 // 跳帧策略:如果缓冲区中有多个帧,且它们的时间戳都在当前时间之前,则播放最新的帧 while (state.videoBuffer.isNotEmpty && state.videoBuffer.first.durationMs <= elapsedTime) { // 如果有多个帧,移除旧的帧,保持最新的帧 if (state.videoBuffer.length > 1) { state.videoBuffer.removeAt(0); } else { // 记录当前时间戳 frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); // 计算并更新 FPS _updateFps(frameTimestamps); _playVideoData(state.videoBuffer.removeAt(0)); } } }); } /// 停止播放音频 void _stopPlayG711Data() async { print('停止播放'); await FlutterPcmSound.pause(); await FlutterPcmSound.stop(); await FlutterPcmSound.clear(); } /// 开门 udpOpenDoorAction(List list) async {} Future getPermissionStatus() async { final Permission permission = Permission.microphone; //granted 通过,denied 被拒绝,permanentlyDenied 拒绝且不在提示 final PermissionStatus status = await permission.status; if (status.isGranted) { return true; } else if (status.isDenied) { requestPermission(permission); } else if (status.isPermanentlyDenied) { openAppSettings(); } else if (status.isRestricted) { requestPermission(permission); } else {} return false; } ///申请权限 void requestPermission(Permission permission) async { final PermissionStatus status = await permission.request(); if (status.isPermanentlyDenied) { openAppSettings(); } } @override void onReady() { super.onReady(); } @override void onInit() { super.onInit(); // 监听音视频数据流 _startListenTalkData(); // 监听对讲状态 _startListenTalkStatus(); // 在没有监听成功之前赋值一遍状态 // *** 由于页面会在状态变化之后才会初始化,导致识别不到最新的状态,在这里手动赋值 *** state.talkStatus.value = state.startChartTalkStatus.status; _initFlutterPcmSound(); _startPlayback(); } @override void onClose() { _stopPlayG711Data(); state.listData.value = Uint8List(0); _syncTimer?.cancel(); } /// 处理无效通话状态 void _handleInvalidTalkStatus() { state.listData.value = Uint8List(0); // 停止播放音频 _stopPlayG711Data(); // 状态错误,返回页面 Get.back(); } }