app-starlock/lib/talk/startChart/views/talkView/talk_view_logic.dart

292 lines
9.5 KiB
Dart
Raw Normal View History

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<int> 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<int> 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<int> list) async {}
Future<bool> 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();
}
}