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

292 lines
9.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}