fix:增加页面播放逻辑、调整proto文件
This commit is contained in:
parent
133f863448
commit
c865db7a9f
@ -1011,6 +1011,7 @@
|
||||
"请在锁设置中开启远程开锁": "Please enable remote unlocking in the lock settings",
|
||||
"接听": "Answer",
|
||||
"截图已保存到相册": "Screenshot saved to album",
|
||||
"录屏已保存到相册": "Screen recording file saved to album",
|
||||
"添加遥控": "Add remote control",
|
||||
"已连接到锁,请按遥控": "Connected to the lock, please press the remote control",
|
||||
"遥控号": "Remote control number",
|
||||
|
||||
@ -1014,6 +1014,7 @@
|
||||
"请在锁设置中开启远程开锁": "请在锁设置中开启远程开锁",
|
||||
"接听": "接听",
|
||||
"截图已保存到相册": "截图已保存到相册",
|
||||
"录屏已保存到相册": "录屏已保存到相册",
|
||||
"添加遥控": "添加遥控",
|
||||
"已连接到锁,请按遥控": "已连接到锁,请按遥控",
|
||||
"遥控号": "遥控号",
|
||||
|
||||
@ -1013,6 +1013,7 @@
|
||||
"请在锁设置中开启远程开锁": "请在锁设置中开启远程开锁",
|
||||
"接听": "接听",
|
||||
"截图已保存到相册": "截图已保存到相册",
|
||||
"录屏已保存到相册": "录屏已保存到相册",
|
||||
"添加遥控": "添加遥控",
|
||||
"已连接到锁,请按遥控": "已连接到锁,请按遥控",
|
||||
"遥控号": "遥控号",
|
||||
|
||||
@ -39,6 +39,9 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle
|
||||
stopRingtone();
|
||||
// 设置状态为接听成功
|
||||
talkStatus.setAnsweredSuccessfully();
|
||||
// 同意接听之后,停止对讲请求超时监听定时器
|
||||
talkeRequestOverTimeTimerManager.receiveMessage();
|
||||
talkeRequestOverTimeTimerManager.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -39,6 +39,10 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle
|
||||
startChartManage.stopTalkExpectMessageTimer();
|
||||
talkStatus.setHangingUpDuring();
|
||||
stopRingtone();
|
||||
|
||||
// 拒绝接听之后,停止对讲请求超时监听定时器
|
||||
talkeRequestOverTimeTimerManager.receiveMessage();
|
||||
talkeRequestOverTimeTimerManager.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -18,16 +18,13 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle
|
||||
@override
|
||||
void handleReq(ScpMessage scpMessage) {
|
||||
// 收到接听拒绝请求
|
||||
startChartManage.sendGenericRespSuccessMessage(
|
||||
ToPeerId: scpMessage.FromPeerId!,
|
||||
FromPeerId: scpMessage.ToPeerId!,
|
||||
PayloadType: scpMessage.PayloadType!,
|
||||
);
|
||||
startChartManage.stopTalkPingMessageTimer();
|
||||
startChartManage.stopTalkExpectMessageTimer();
|
||||
// 回复成功消息
|
||||
replySuccessMessage(scpMessage);
|
||||
// 停止铃声
|
||||
stopRingtone();
|
||||
// 收到接听拒绝回复
|
||||
talkStatus.setRejected();
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
@ -35,6 +32,11 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle
|
||||
startChartManage.stopTalkPingMessageTimer();
|
||||
startChartManage.stopTalkExpectMessageTimer();
|
||||
stopRingtone();
|
||||
|
||||
|
||||
// 拒绝接听之后,停止对讲请求超时监听定时器
|
||||
talkeRequestOverTimeTimerManager.receiveMessage();
|
||||
talkeRequestOverTimeTimerManager.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -34,6 +34,16 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
|
||||
startChartManage.ToPeerId = scpMessage.FromPeerId!;
|
||||
// 处理收到接听请求后的事件
|
||||
_talkRequestEvent(talkObjectName: talkReq.callerName);
|
||||
|
||||
// 启动对讲请求超时定时器
|
||||
talkeRequestOverTimeTimerManager.startTimer();
|
||||
talkeRequestOverTimeTimerManager.setOnTimeout(() {
|
||||
if (talkStatus.status == TalkStatus.waitingAnswer) {
|
||||
// 超时未接听,发送挂断请求
|
||||
startChartManage.sendTalkRejectMessage();
|
||||
Get.back();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -36,6 +36,11 @@ class ScpMessageBaseHandle {
|
||||
|
||||
final audioManager = AudioPlayerManager();
|
||||
|
||||
// 通话请求超时未处理监听定时器管理
|
||||
final talkeRequestOverTimeTimerManager = OverTimeTimerManager(
|
||||
timeoutInSeconds: 30,
|
||||
);
|
||||
|
||||
// 通话保持超时监听定时器管理
|
||||
final talkePingOverTimeTimerManager = OverTimeTimerManager(
|
||||
timeoutInSeconds: 260,
|
||||
|
||||
@ -86,7 +86,7 @@ class StartChartManage {
|
||||
final int _maxPayloadSize = 8 * 1024; // 分包大小
|
||||
|
||||
// 默认通话的期望数据格式
|
||||
TalkExpectReq defaultTalkExpect = TalkExpectReq(
|
||||
TalkExpectReq _defaultTalkExpect = TalkExpectReq(
|
||||
videoType: [VideoTypeE.IMAGE],
|
||||
audioType: [AudioTypeE.G711],
|
||||
);
|
||||
@ -857,7 +857,7 @@ class StartChartManage {
|
||||
(Timer timer) {
|
||||
// 发送期望接受消息
|
||||
sendTalkExpectMessage(
|
||||
talkExpect: defaultTalkExpect,
|
||||
talkExpect: _defaultTalkExpect,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -971,9 +971,16 @@ class StartChartManage {
|
||||
talkExpectTimer = null; // 清除定时器引用
|
||||
}
|
||||
|
||||
// 重新发送预期数据
|
||||
void reStartTalkExpectMessageTimer() {
|
||||
stopTalkExpectMessageTimer();
|
||||
startTalkExpectTimer();
|
||||
}
|
||||
|
||||
/// 修改预期接收到的数据
|
||||
void changeTalkExpectDataType({required TalkExpectReq talkExpect}) {
|
||||
defaultTalkExpect = talkExpect;
|
||||
_defaultTalkExpect = talkExpect;
|
||||
reStartTalkExpectMessageTimer();
|
||||
}
|
||||
|
||||
/// 销毁资源
|
||||
|
||||
@ -1,34 +1,36 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_pcm_sound/flutter_pcm_sound.dart';
|
||||
import 'package:flutter_voice_processor/flutter_voice_processor.dart';
|
||||
import 'package:flutter_screen_recording/flutter_screen_recording.dart';
|
||||
import 'package:gallery_saver/gallery_saver.dart';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:path_provider/path_provider.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/proto/talk_expect.pb.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 int bufferSize = 20; // 缓冲区大小(以帧为单位)
|
||||
final List<int> frameTimestamps = [];
|
||||
int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS)
|
||||
int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS)
|
||||
@ -82,22 +84,27 @@ class TalkViewLogic extends BaseGetXController {
|
||||
void _startListenTalkData() {
|
||||
state.talkDataRepository.talkDataStream.listen((talkData) {
|
||||
final contentType = talkData.contentType;
|
||||
final currentTimestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
/// 如果不是通话中的状态不处理对讲数据
|
||||
if (state.startChartTalkStatus.status != TalkStatus.duringCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断数据类型,进行分发处理
|
||||
switch (contentType) {
|
||||
case TalkData_ContentTypeE.G711:
|
||||
// state.audioBuffer.add(talkData);
|
||||
if (state.audioBuffer.length < 60) {
|
||||
// 假设缓冲区大小为60帧
|
||||
if (state.audioBuffer.length < bufferSize) {
|
||||
state.audioBuffer.add(talkData);
|
||||
}
|
||||
break;
|
||||
case TalkData_ContentTypeE.Image:
|
||||
// state.videoBuffer.add(talkData);
|
||||
// 增加视频缓冲区大小
|
||||
if (state.videoBuffer.length < 60) {
|
||||
// 假设缓冲区大小为60帧
|
||||
if (state.videoBuffer.length < bufferSize) {
|
||||
state.videoBuffer.add(talkData);
|
||||
}
|
||||
|
||||
/// 更新网络状态
|
||||
updateNetworkStatus(currentTimestamp);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@ -136,6 +143,7 @@ class TalkViewLogic extends BaseGetXController {
|
||||
state.listData.value = Uint8List.fromList(talkData.content);
|
||||
}
|
||||
|
||||
/// 启动播放
|
||||
void _startPlayback() {
|
||||
int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS)
|
||||
|
||||
@ -154,7 +162,15 @@ class TalkViewLogic extends BaseGetXController {
|
||||
// 播放合适的音频帧
|
||||
if (state.audioBuffer.isNotEmpty &&
|
||||
state.audioBuffer.first.durationMs <= elapsedTime) {
|
||||
_playAudioData(state.audioBuffer.removeAt(0));
|
||||
// 判断音频开关是否打开
|
||||
if (state.isOpenVoice.value) {
|
||||
_playAudioData(state.audioBuffer.removeAt(0));
|
||||
} else {
|
||||
// 如果不播放音频,只从缓冲区中读取数据,但不移除
|
||||
// 你可以根据需要调整此处逻辑,例如保留缓冲区的最大长度,防止无限增长
|
||||
// 仅移除缓冲区数据但不播放音频,确保音频也是实时更新的
|
||||
state.audioBuffer.removeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 播放合适的视频帧
|
||||
@ -165,10 +181,10 @@ class TalkViewLogic extends BaseGetXController {
|
||||
if (state.videoBuffer.length > 1) {
|
||||
state.videoBuffer.removeAt(0);
|
||||
} else {
|
||||
// 记录当前时间戳
|
||||
frameTimestamps.add(DateTime.now().millisecondsSinceEpoch);
|
||||
// 计算并更新 FPS
|
||||
_updateFps(frameTimestamps);
|
||||
// // 记录当前时间戳
|
||||
// frameTimestamps.add(DateTime.now().millisecondsSinceEpoch);
|
||||
// // 计算并更新 FPS
|
||||
// _updateFps(frameTimestamps);
|
||||
_playVideoData(state.videoBuffer.removeAt(0));
|
||||
}
|
||||
}
|
||||
@ -195,7 +211,15 @@ class TalkViewLogic extends BaseGetXController {
|
||||
// 播放合适的音频帧
|
||||
if (state.audioBuffer.isNotEmpty &&
|
||||
state.audioBuffer.first.durationMs <= elapsedTime) {
|
||||
_playAudioData(state.audioBuffer.removeAt(0));
|
||||
// 判断音频开关是否打开
|
||||
if (state.isOpenVoice.value) {
|
||||
_playAudioData(state.audioBuffer.removeAt(0));
|
||||
} else {
|
||||
// 如果不播放音频,只从缓冲区中读取数据,但不移除
|
||||
// 你可以根据需要调整此处逻辑,例如保留缓冲区的最大长度,防止无限增长
|
||||
// 仅移除缓冲区数据但不播放音频,确保音频也是实时更新的
|
||||
state.audioBuffer.removeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 播放合适的视频帧
|
||||
@ -206,19 +230,47 @@ class TalkViewLogic extends BaseGetXController {
|
||||
if (state.videoBuffer.length > 1) {
|
||||
state.videoBuffer.removeAt(0);
|
||||
} else {
|
||||
// 记录当前时间戳
|
||||
frameTimestamps.add(DateTime.now().millisecondsSinceEpoch);
|
||||
// 计算并更新 FPS
|
||||
_updateFps(frameTimestamps);
|
||||
// // 记录当前时间戳
|
||||
// frameTimestamps.add(DateTime.now().millisecondsSinceEpoch);
|
||||
// // 计算并更新 FPS
|
||||
// _updateFps(frameTimestamps);
|
||||
_playVideoData(state.videoBuffer.removeAt(0));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 修改网络状态
|
||||
void updateNetworkStatus(int currentTimestamp) {
|
||||
if (state.lastFrameTimestamp.value != 0) {
|
||||
final frameInterval = currentTimestamp - state.lastFrameTimestamp.value;
|
||||
if (frameInterval > 500 && frameInterval <= 1000) {
|
||||
// 判断帧间隔是否在500毫秒到1秒之间
|
||||
state.networkStatus.value = NetworkStatus.lagging;
|
||||
showNetworkStatus("Network is lagging");
|
||||
} else if (frameInterval > 1000) {
|
||||
// 判断帧间隔是否超过1秒
|
||||
state.networkStatus.value = NetworkStatus.delayed;
|
||||
showNetworkStatus("Network is delayed");
|
||||
} else {
|
||||
state.networkStatus.value = NetworkStatus.normal;
|
||||
state.alertCount.value = 0; // 重置计数器
|
||||
EasyLoading.dismiss(); // 网络恢复正常时关闭提示
|
||||
}
|
||||
}
|
||||
state.lastFrameTimestamp.value = currentTimestamp;
|
||||
}
|
||||
|
||||
/// 提示网络状态
|
||||
void showNetworkStatus(String message) {
|
||||
if (state.alertCount.value < 3 && !EasyLoading.isShow) {
|
||||
showToast(message);
|
||||
state.alertCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止播放音频
|
||||
void _stopPlayG711Data() async {
|
||||
print('停止播放');
|
||||
await FlutterPcmSound.pause();
|
||||
await FlutterPcmSound.stop();
|
||||
await FlutterPcmSound.clear();
|
||||
@ -288,4 +340,85 @@ class TalkViewLogic extends BaseGetXController {
|
||||
// 状态错误,返回页面
|
||||
Get.back();
|
||||
}
|
||||
|
||||
/// 更新发送预期数据
|
||||
void updateTalkExpect() {
|
||||
TalkExpectReq talkExpectReq = TalkExpectReq();
|
||||
if (state.isOpenVoice.value) {
|
||||
talkExpectReq = TalkExpectReq(
|
||||
videoType: [VideoTypeE.IMAGE],
|
||||
audioType: [AudioTypeE.G711],
|
||||
);
|
||||
} else {
|
||||
talkExpectReq = TalkExpectReq(
|
||||
videoType: [VideoTypeE.IMAGE],
|
||||
audioType: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// 修改发送预期数据
|
||||
StartChartManage().changeTalkExpectDataType(talkExpect: talkExpectReq);
|
||||
state.isOpenVoice.value = !state.isOpenVoice.value;
|
||||
}
|
||||
|
||||
/// 截图并保存到相册
|
||||
Future<void> captureAndSavePng() async {
|
||||
try {
|
||||
if (state.globalKey.currentContext == null) {
|
||||
AppLog.log('截图失败: 未找到当前上下文');
|
||||
return;
|
||||
}
|
||||
final RenderRepaintBoundary boundary = state.globalKey.currentContext!
|
||||
.findRenderObject()! as RenderRepaintBoundary;
|
||||
final ui.Image image = await boundary.toImage();
|
||||
final ByteData? byteData =
|
||||
await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
|
||||
if (byteData == null) {
|
||||
AppLog.log('截图失败: 图像数据为空');
|
||||
return;
|
||||
}
|
||||
final Uint8List pngBytes = byteData.buffer.asUint8List();
|
||||
|
||||
// 获取应用程序的文档目录
|
||||
final Directory directory = await getApplicationDocumentsDirectory();
|
||||
final String imagePath = '${directory.path}/screenshot.png';
|
||||
|
||||
// 将截图保存为文件
|
||||
final File imgFile = File(imagePath);
|
||||
await imgFile.writeAsBytes(pngBytes);
|
||||
|
||||
// 将截图保存到相册
|
||||
await ImageGallerySaver.saveFile(imagePath);
|
||||
|
||||
AppLog.log('截图保存路径: $imagePath');
|
||||
showToast('截图已保存到相册'.tr);
|
||||
} catch (e) {
|
||||
AppLog.log('截图失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 开始录屏
|
||||
Future<void> startRecording() async {
|
||||
getPermissionStatus();
|
||||
bool started =
|
||||
await FlutterScreenRecording.startRecordScreenAndAudio("Recording");
|
||||
if (started) {
|
||||
state.isRecording.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止录屏
|
||||
Future<void> stopRecording() async {
|
||||
String path = await FlutterScreenRecording.stopRecordScreen;
|
||||
if (path != null) {
|
||||
state.isRecording.value = false;
|
||||
// 保存录制的视频到相册
|
||||
await GallerySaver.saveVideo(path).then((bool? success) {});
|
||||
showToast('录屏已保存到相册'.tr);
|
||||
} else {
|
||||
state.isRecording.value = false;
|
||||
print("Recording failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screen_recording/flutter_screen_recording.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gallery_saver/gallery_saver.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:star_lock/app_settings/app_settings.dart';
|
||||
import 'package:star_lock/main/lockDetail/realTimePicture/realTimePicture_state.dart';
|
||||
import 'package:star_lock/talk/call/callTalk.dart';
|
||||
@ -43,7 +49,6 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
state.animationController.repeat();
|
||||
//动画开始、结束、向前移动或向后移动时会调用StatusListener
|
||||
state.animationController.addStatusListener((AnimationStatus status) {
|
||||
// AppLog.log("AnimationStatus:$status");
|
||||
if (status == AnimationStatus.completed) {
|
||||
state.animationController.reset();
|
||||
state.animationController.forward();
|
||||
@ -70,23 +75,29 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
height: ScreenUtil().screenHeight,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Image.memory(
|
||||
state.listData.value,
|
||||
gaplessPlayback: true,
|
||||
width: 1.sw,
|
||||
height: 1.sh,
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.high,
|
||||
errorBuilder: (
|
||||
BuildContext context,
|
||||
Object error,
|
||||
StackTrace? stackTrace,
|
||||
) {
|
||||
return Container(color: Colors.transparent);
|
||||
},
|
||||
: PopScope(
|
||||
canPop: false,
|
||||
child: RepaintBoundary(
|
||||
key: state.globalKey,
|
||||
child: Image.memory(
|
||||
state.listData.value,
|
||||
gaplessPlayback: true,
|
||||
width: 1.sw,
|
||||
height: 1.sh,
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.high,
|
||||
errorBuilder: (
|
||||
BuildContext context,
|
||||
Object error,
|
||||
StackTrace? stackTrace,
|
||||
) {
|
||||
return Container(color: Colors.transparent);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(() => state.listData.value.isEmpty
|
||||
Obx(() => state.talkStatus.value == TalkStatus.answeredSuccessfully
|
||||
? Positioned(
|
||||
bottom: 300.h,
|
||||
child: Text(
|
||||
@ -114,17 +125,20 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 100.h,
|
||||
left: 10.w,
|
||||
child: Obx(
|
||||
() => Text(
|
||||
'FPS:${state.fps.value}',
|
||||
style: TextStyle(fontSize: 30.sp, color: Colors.orange,fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(() => state.listData.value.isEmpty
|
||||
// Positioned(
|
||||
// top: 100.h,
|
||||
// left: 10.w,
|
||||
// child: Obx(
|
||||
// () => Text(
|
||||
// 'FPS:${state.fps.value}',
|
||||
// style: TextStyle(
|
||||
// fontSize: 30.sp,
|
||||
// color: Colors.orange,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
Obx(() => state.talkStatus.value == TalkStatus.answeredSuccessfully
|
||||
? buildRotationTransition()
|
||||
: Container())
|
||||
],
|
||||
@ -137,7 +151,7 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
// 打开关闭声音
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
state.isOpenVoice.value = !state.isOpenVoice.value;
|
||||
logic.updateTalkExpect();
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
@ -148,16 +162,16 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
height: 40.w,
|
||||
image: state.isOpenVoice.value
|
||||
? const AssetImage(
|
||||
'images/main/icon_lockDetail_monitoringCloseVoice.png')
|
||||
'images/main/icon_lockDetail_monitoringOpenVoice.png')
|
||||
: const AssetImage(
|
||||
'images/main/icon_lockDetail_monitoringOpenVoice.png'))),
|
||||
'images/main/icon_lockDetail_monitoringCloseVoice.png'))),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 50.w),
|
||||
// 截图
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Get.toNamed(Routers.monitoringRealTimeScreenPage);
|
||||
onTap: () async {
|
||||
await logic.captureAndSavePng();
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
@ -173,32 +187,38 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
SizedBox(width: 50.w),
|
||||
// 录制
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Get.toNamed(Routers.monitoringRealTimeScreenPage);
|
||||
onTap: () async {
|
||||
if (state.isRecording.value) {
|
||||
await logic.stopRecording();
|
||||
} else {
|
||||
await logic.startRecording();
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(5.w),
|
||||
child: Image(
|
||||
width: 40.w,
|
||||
height: 40.w,
|
||||
fit: BoxFit.fill,
|
||||
image: const AssetImage(
|
||||
'images/main/icon_lockDetail_monitoringScreenRecording.png')),
|
||||
width: 40.w,
|
||||
height: 40.w,
|
||||
fit: BoxFit.fill,
|
||||
image: const AssetImage(
|
||||
'images/main/icon_lockDetail_monitoringScreenRecording.png'),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 50.w),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
logic.showToast('功能暂未开放'.tr);
|
||||
},
|
||||
child: Image(
|
||||
width: 28.w,
|
||||
height: 28.w,
|
||||
fit: BoxFit.fill,
|
||||
image: const AssetImage(
|
||||
'images/main/icon_lockDetail_rectangle.png')))
|
||||
onTap: () {
|
||||
logic.showToast('功能暂未开放'.tr);
|
||||
},
|
||||
child: Image(
|
||||
width: 28.w,
|
||||
height: 28.w,
|
||||
fit: BoxFit.fill,
|
||||
image: const AssetImage('images/main/icon_lockDetail_rectangle.png'),
|
||||
),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -212,7 +232,10 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
getAnswerBtnImg(),
|
||||
getAnswerBtnName(),
|
||||
Colors.white,
|
||||
longPress: () async {},
|
||||
longPress: () async {
|
||||
if (state.talkStatus.value == TalkStatus.answeredSuccessfully ||
|
||||
state.talkStatus.value == TalkStatus.duringCall) {}
|
||||
},
|
||||
longPressUp: () async {},
|
||||
onClick: () async {
|
||||
if (state.talkStatus.value == TalkStatus.waitingAnswer) {
|
||||
|
||||
@ -13,8 +13,15 @@ import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
|
||||
import '../../../../tools/storage.dart';
|
||||
|
||||
enum NetworkStatus {
|
||||
normal, // 0
|
||||
lagging, // 1
|
||||
delayed, // 2
|
||||
packetLoss // 3
|
||||
}
|
||||
|
||||
class TalkViewState {
|
||||
RxBool isOpenVoice = false.obs;
|
||||
|
||||
int udpSendDataFrameNumber = 0; // 帧序号
|
||||
// var isSenderAudioData = false.obs;// 是否要发送音频数据
|
||||
|
||||
@ -27,7 +34,7 @@ class TalkViewState {
|
||||
|
||||
Rx<Uint8List> listData = Uint8List(0).obs; //得到的视频流字节数据
|
||||
RxList<int> listAudioData = <int>[].obs; //得到的音频流字节数据
|
||||
|
||||
GlobalKey globalKey = GlobalKey();
|
||||
late final VoiceProcessor? voiceProcessor;
|
||||
|
||||
late Timer oneMinuteTimeTimer =
|
||||
@ -40,8 +47,6 @@ class TalkViewState {
|
||||
late Timer openDoorTimer;
|
||||
late AnimationController animationController;
|
||||
|
||||
RxDouble fps = 0.0.obs; // 添加 FPS 计数
|
||||
|
||||
|
||||
late Timer autoBackTimer =
|
||||
Timer(const Duration(seconds: 1), () {}); //发送30秒监视后自动返回
|
||||
@ -50,19 +55,25 @@ class TalkViewState {
|
||||
RxInt elapsedSeconds = 0.obs;
|
||||
|
||||
|
||||
|
||||
|
||||
// 星图对讲相关状态
|
||||
List<TalkData> audioBuffer = <TalkData>[].obs;
|
||||
List<TalkData> videoBuffer = <TalkData>[].obs;
|
||||
|
||||
|
||||
RxBool isPlaying = false.obs; // 是否开始播放
|
||||
|
||||
|
||||
Rx<TalkStatus> talkStatus = TalkStatus.none.obs; //星图对讲状态
|
||||
|
||||
// 获取 startChartTalkStatus 的唯一实例
|
||||
final StartChartTalkStatus startChartTalkStatus =
|
||||
StartChartTalkStatus.instance;
|
||||
|
||||
// 通话数据流的单例流数据处理类
|
||||
final TalkDataRepository talkDataRepository = TalkDataRepository.instance;
|
||||
RxInt lastFrameTimestamp = 0.obs; // 上一帧的时间戳,用来判断网络环境
|
||||
Rx<NetworkStatus> networkStatus =
|
||||
NetworkStatus.normal.obs; // 网络状态:0-正常 1-网络卡顿 2-网络延迟 3-网络丢包
|
||||
RxInt alertCount = 0.obs; // 网络状态提示计数器
|
||||
RxInt maxAlertNumber = 3.obs; // 网络状态提示最大提示次数
|
||||
RxBool isOpenVoice = true.obs; // 是否打开声音
|
||||
RxBool isRecording = true.obs; // 是否录屏中
|
||||
RxDouble fps = 0.0.obs; // 添加 FPS 计数
|
||||
|
||||
}
|
||||
|
||||
@ -256,6 +256,10 @@ dependencies:
|
||||
asn1lib: ^1.0.0
|
||||
fast_rsa: ^3.6.6
|
||||
protobuf: ^3.1.0
|
||||
#录屏
|
||||
flutter_screen_recording: 2.0.16
|
||||
#图库保存
|
||||
gallery_saver: ^2.3.2
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user