fix:增加解析模拟对讲服务的数据并显示到屏幕上、增加异常类、增加发送消息前的状态判断
This commit is contained in:
parent
cdbd0c3d03
commit
f98d1ab05c
@ -24,7 +24,6 @@ class StarChartLogic extends BaseGetXController {
|
||||
|
||||
int startTime = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
@ -51,6 +50,8 @@ class StarChartLogic extends BaseGetXController {
|
||||
_cancelTimers();
|
||||
stopProcessing();
|
||||
state.listPhotoData.value = Uint8List(0);
|
||||
// 停止播放音频
|
||||
_stopPlayG711Data();
|
||||
// 状态错误,返回页面
|
||||
Get.back();
|
||||
|
||||
@ -72,6 +73,10 @@ class StarChartLogic extends BaseGetXController {
|
||||
case TalkData_ContentTypeE.G711:
|
||||
_playG711Data(talkData.content);
|
||||
break;
|
||||
case TalkData_ContentTypeE.Image:
|
||||
// 收到视频数据
|
||||
state.listPhotoData.value = Uint8List.fromList(talkData.content);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -107,16 +112,18 @@ class StarChartLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
void _startCallTimer() {
|
||||
if (state.oneMinuteTimeTimer.isActive) return;
|
||||
|
||||
state.oneMinuteTimeTimer.cancel();
|
||||
state.oneMinuteTimeTimer =
|
||||
Timer.periodic(const Duration(seconds: 1), (Timer t) {
|
||||
state.oneMinuteTime.value++;
|
||||
if (state.oneMinuteTime.value >= 60) {
|
||||
t.cancel();
|
||||
initiateHangUpCommand();
|
||||
AppLog.log('通话时间超过60秒,自动挂断');
|
||||
state.oneMinuteTime.value = 0;
|
||||
}
|
||||
// if (state.oneMinuteTime.value >= 60) {
|
||||
// t.cancel();
|
||||
// initiateHangUpCommand();
|
||||
// AppLog.log('通话时间超过60秒,自动挂断');
|
||||
// state.oneMinuteTime.value = 0;
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -38,7 +38,6 @@ class _StarChartPageState extends State<StarChartPage> {
|
||||
super.initState();
|
||||
|
||||
initAsync();
|
||||
// _getTVDataRefreshUIAction();
|
||||
}
|
||||
|
||||
Future<void> initAsync() async {
|
||||
@ -265,52 +264,12 @@ class _StarChartPageState extends State<StarChartPage> {
|
||||
}
|
||||
}
|
||||
|
||||
// void _getTVDataRefreshUIAction() {
|
||||
// // state.getTVDataRefreshUIEvent = eventBus
|
||||
// // .on<GetTVDataRefreshUI>()
|
||||
// // .listen((GetTVDataRefreshUI event) async {
|
||||
// // if (event.tvList.isNotEmpty && event.tvList.length > 100) {
|
||||
// // final Uint8List imageData = Uint8List.fromList(event.tvList);
|
||||
// // if (!listEquals(state.listPhotoData.value, imageData)) {
|
||||
// // state.listPhotoData.value = imageData;
|
||||
// // state.shouldUpdateUI.value = true;
|
||||
// // if (state.shouldUpdateUI.value) {
|
||||
// // setState(() {});
|
||||
// // state.shouldUpdateUI.value = false;
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // });
|
||||
// state.talkDataRepository.talkDataStream.listen((talkData) {
|
||||
// final contentType = talkData.contentType;
|
||||
// // 判断数据类型,进行分发处理
|
||||
// switch (contentType) {
|
||||
// case TalkData_ContentTypeE.Image:
|
||||
// state.listPhotoData.value = Uint8List.fromList(talkData.content);
|
||||
// if (talkData.content.isNotEmpty && talkData.content.length > 100) {
|
||||
// // 比较新旧数据是否相同
|
||||
// final Uint8List imageData = Uint8List.fromList(talkData.content);
|
||||
// if (!listEquals(state.listPhotoData.value, imageData)) {
|
||||
// // 更新状态
|
||||
// state.listPhotoData.value = imageData;
|
||||
// // 设置标志为true,表示需要更新UI
|
||||
// state.shouldUpdateUI.value = true;
|
||||
// // WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// // 调用setState方法之前检查标志,只有当标志为true时才更新UI
|
||||
// if (state.shouldUpdateUI.value) {
|
||||
// setState(() {
|
||||
// // 更新UI
|
||||
// });
|
||||
// // 更新完UI后将标志重新设置为false
|
||||
// state.shouldUpdateUI.value = false;
|
||||
// }
|
||||
// // });
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
String listToHexString(List<int> intList) {
|
||||
// 将整数列表转换为十六进制字符串列表
|
||||
List<String> hexList = intList.map((num) => num.toRadixString(16)).toList();
|
||||
// 将十六进制字符串列表连接成一个字符串,没有空格
|
||||
return hexList.join('');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -324,58 +283,67 @@ class _StarChartPageState extends State<StarChartPage> {
|
||||
}
|
||||
|
||||
Widget _buildMpeg4TalkView() {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
Image.memory(
|
||||
state.listPhotoData.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);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
top: ScreenUtil().statusBarHeight + 30.h,
|
||||
width: 1.sw,
|
||||
child: Obx(() {
|
||||
final String sec =
|
||||
(state.oneMinuteTime.value % 60).toString().padLeft(2, '0');
|
||||
final String min =
|
||||
(state.oneMinuteTime.value ~/ 60).toString().padLeft(2, '0');
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text('$min:$sec',
|
||||
style: TextStyle(fontSize: 26.sp, color: Colors.white)),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 10.w,
|
||||
child: Container(
|
||||
width: 1.sw - 30.w * 2,
|
||||
margin: EdgeInsets.all(30.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xC83C3F41),
|
||||
borderRadius: BorderRadius.circular(20.h),
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SizedBox(height: 20.h),
|
||||
buildTopButtons(),
|
||||
SizedBox(height: 20.h),
|
||||
buildBottomButtons(),
|
||||
SizedBox(height: 20.h),
|
||||
],
|
||||
return Obx(
|
||||
() => Stack(
|
||||
children: <Widget>[
|
||||
state.listPhotoData.value.isNotEmpty
|
||||
? Image.memory(
|
||||
state.listPhotoData.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);
|
||||
},
|
||||
)
|
||||
: Image.asset(
|
||||
'images/main/monitorBg.png',
|
||||
width: 1.sw,
|
||||
height: 1.sh,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Positioned(
|
||||
top: ScreenUtil().statusBarHeight + 30.h,
|
||||
width: 1.sw,
|
||||
child: Obx(() {
|
||||
final String sec =
|
||||
(state.oneMinuteTime.value % 60).toString().padLeft(2, '0');
|
||||
final String min =
|
||||
(state.oneMinuteTime.value ~/ 60).toString().padLeft(2, '0');
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text('$min:$sec',
|
||||
style: TextStyle(fontSize: 26.sp, color: Colors.white)),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 10.w,
|
||||
child: Container(
|
||||
width: 1.sw - 30.w * 2,
|
||||
margin: EdgeInsets.all(30.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xC83C3F41),
|
||||
borderRadius: BorderRadius.circular(20.h),
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SizedBox(height: 20.h),
|
||||
buildTopButtons(),
|
||||
SizedBox(height: 20.h),
|
||||
buildBottomButtons(),
|
||||
SizedBox(height: 20.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -56,6 +56,9 @@ class StarChartState {
|
||||
|
||||
RxInt talkStatus = 0.obs; //星图对讲状态
|
||||
|
||||
// 获取 StartChartTalkStatus 的唯一实例
|
||||
StartChartTalkStatus talkStatusInstance = StartChartTalkStatus.instance;
|
||||
|
||||
// 通话数据流的单例流数据处理类
|
||||
final TalkDataRepository talkDataRepository = TalkDataRepository.instance;
|
||||
|
||||
|
||||
3
lib/talk/startChart/constant/exception_constant.dart
Normal file
3
lib/talk/startChart/constant/exception_constant.dart
Normal file
@ -0,0 +1,3 @@
|
||||
class ExceptionConstant{
|
||||
static const String relay = 'relay';
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:star_lock/app_settings/app_settings.dart';
|
||||
import 'package:star_lock/talk/startChart/exception/start_chart_message_exception.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart';
|
||||
|
||||
|
||||
class ScpMessage {
|
||||
ScpMessage({
|
||||
this.ProtocolFlag,
|
||||
@ -149,12 +149,13 @@ class ScpMessage {
|
||||
static ScpMessage deserialize(Uint8List bytes) {
|
||||
final message = ScpMessage();
|
||||
int offset = 0;
|
||||
// Convert byte array to hex string with zero padding and without spaces
|
||||
|
||||
// String hexString =
|
||||
// bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
||||
//
|
||||
// // Log the hex string
|
||||
// _log(text: '原始字节数组: $hexString');
|
||||
// // _log(text: 'result bytes hex: ${hexString}');
|
||||
// _log(
|
||||
// text:
|
||||
// '\n result bytes hex: ${hexString} \n payload hex: ${hexString.substring(194)}');
|
||||
|
||||
// ProtocolFlag (4 bytes)
|
||||
if (bytes.length - offset >= 4) {
|
||||
@ -244,8 +245,7 @@ class ScpMessage {
|
||||
// 处理其他类型的Payload
|
||||
if (message.PayloadLength != null &&
|
||||
bytes.length - offset >= message.PayloadLength!) {
|
||||
final Uint8List sublist =
|
||||
bytes.sublist(offset, offset + message.PayloadLength!);
|
||||
final sublist = bytes.sublist(offset, offset + message.PayloadLength!);
|
||||
offset += message.PayloadLength!;
|
||||
message.Payload = _handlePayLoad(
|
||||
payloadType: message.PayloadType ?? 0,
|
||||
@ -268,7 +268,7 @@ class ScpMessage {
|
||||
static dynamic _handlePayLoad({
|
||||
required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
@ -280,7 +280,7 @@ class ScpMessage {
|
||||
final ScpMessageHandler handler =
|
||||
ScpMessageHandlerFactory.createHandler(payloadType);
|
||||
// 处理荷载信息并返回
|
||||
return handler.deserializePayload(
|
||||
final payload = handler.deserializePayload(
|
||||
payloadType: payloadType,
|
||||
messageType: messageType,
|
||||
byte: byte,
|
||||
@ -290,12 +290,9 @@ class ScpMessage {
|
||||
spIndex: spIndex,
|
||||
messageId: messageId,
|
||||
);
|
||||
return payload;
|
||||
} catch (e, stackTrace) {
|
||||
// 打印异常信息
|
||||
_log(text: '❌反序列化udp数据时遇到错误----》$e');
|
||||
// 打印堆栈跟踪信息
|
||||
_log(text: '堆栈跟踪:\n$stackTrace');
|
||||
return '';
|
||||
throw StartChartMessageException('❌反序列化udp数据时遇到错误----》$e \n$stackTrace');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
class StartChartMessageException implements Exception {
|
||||
final String message;
|
||||
|
||||
StartChartMessageException(this.message);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'StartChartMessageException{message: $message}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
class StartChartTalkStatusException implements Exception {
|
||||
final String message;
|
||||
|
||||
StartChartTalkStatusException(this.message);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'StartChartTalkStatusException{message: $message}';
|
||||
}
|
||||
}
|
||||
@ -35,7 +35,7 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -36,7 +36,7 @@ class UdpEchoTestHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -36,7 +36,7 @@ class UdpGateWayResetHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -39,7 +39,7 @@ class UdpGateWayTransferHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -42,7 +42,7 @@ class UdpGoOnlineHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -43,7 +43,7 @@ class UdpHeartBeatHandler extends ScpMessageBaseHandle
|
||||
void handleRealTimeData(ScpMessage scpMessage) {}
|
||||
|
||||
@override
|
||||
deserializePayload({required int payloadType, required int messageType, required Uint8List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) {
|
||||
deserializePayload({required int payloadType, required int messageType, required List<int> byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) {
|
||||
// 心跳
|
||||
HeartbeatResponse heartbeatResponse = HeartbeatResponse.fromBytes(byte);
|
||||
return heartbeatResponse;
|
||||
|
||||
@ -38,7 +38,7 @@ class UdpRemoteUnLockHandler extends ScpMessageBaseHandle implements ScpMessageH
|
||||
}
|
||||
|
||||
@override
|
||||
deserializePayload({required int payloadType, required int messageType, required Uint8List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) {
|
||||
deserializePayload({required int payloadType, required int messageType, required List<int> byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) {
|
||||
if (messageType == MessageTypeConstant.Resp) {
|
||||
final GenericResp genericResp = GenericResp();
|
||||
genericResp.mergeFromBuffer(byte);
|
||||
|
||||
@ -44,7 +44,7 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle
|
||||
stopRingtone();
|
||||
// 设置状态为接听中
|
||||
talkStatus.setAnsweredSuccessfully();
|
||||
talkStatus.setDuringCall();
|
||||
talkStatus.setWaitingData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -30,10 +30,45 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
|
||||
talkDataOverTimeTimerManager.receiveMessage();
|
||||
if (scpMessage.Payload != null) {
|
||||
final TalkData talkData = scpMessage.Payload;
|
||||
print('talkData: ${listToHexString(talkData.content)}');
|
||||
|
||||
// 处理音视频数据
|
||||
_handleTalkData(talkData: talkData);
|
||||
// 设置状态为接听中
|
||||
talkStatus.setDuringCall();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
int? spIndex,
|
||||
int? messageId}) {
|
||||
if (messageType == MessageTypeConstant.RealTimeData) {
|
||||
print(
|
||||
'收到音视频数据:${byte.length} messageId:$messageId spTotal:$spTotal spIndex:$spIndex PayloadLength:$PayloadLength');
|
||||
// 回声测试
|
||||
if (spTotal != null &&
|
||||
spTotal > 1 &&
|
||||
messageId != null &&
|
||||
spIndex != null) {
|
||||
// 分包处理
|
||||
return handleFragmentedPayload(
|
||||
messageId: messageId,
|
||||
spTotal: spTotal,
|
||||
spIndex: spIndex,
|
||||
byte: byte,
|
||||
payloadType: payloadType,
|
||||
);
|
||||
} else {
|
||||
// 没有分包直接解析
|
||||
final TalkData talkData = TalkData();
|
||||
talkData.mergeFromBuffer(byte);
|
||||
return talkData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +104,15 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
|
||||
}
|
||||
|
||||
/// 处理图片数据
|
||||
void _handleVideoImage(TalkData talkData) {}
|
||||
void _handleVideoImage(TalkData talkData) {
|
||||
final List<Uint8List> processCompletePayload =
|
||||
_processCompletePayload(Uint8List.fromList(talkData.content));
|
||||
// 循环发送每一帧的数据
|
||||
processCompletePayload.forEach((element) {
|
||||
talkData.content = element;
|
||||
talkDataRepository.addTalkData(talkData);
|
||||
});
|
||||
}
|
||||
|
||||
/// 处理g711音频数据
|
||||
void _handleVideoG711(TalkData talkData) {
|
||||
@ -84,36 +127,36 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
int? spIndex,
|
||||
int? messageId}) {
|
||||
if (messageType == MessageTypeConstant.RealTimeData) {
|
||||
// 回声测试
|
||||
if (spTotal != null &&
|
||||
spTotal > 1 &&
|
||||
messageId != null &&
|
||||
spIndex != null) {
|
||||
// 分包处理
|
||||
return handleFragmentedPayload(
|
||||
messageId: messageId,
|
||||
spTotal: spTotal,
|
||||
spIndex: spIndex,
|
||||
byte: byte,
|
||||
payloadType: payloadType,
|
||||
);
|
||||
/// 查找完整的帧数据
|
||||
List<Uint8List> _processCompletePayload(Uint8List payload) {
|
||||
// 存储找到的所有完整帧
|
||||
List<Uint8List> frames = [];
|
||||
|
||||
// 寻找完整帧 (0xFFD8 开始, 0xFFD9 结束)
|
||||
int startIdx = payload.indexOf(0xFF);
|
||||
while (startIdx != -1 && startIdx + 1 < payload.length) {
|
||||
if (payload[startIdx + 1] == 0xD8) {
|
||||
// 找到帧的起始标志 0xFFD8
|
||||
int endIdx = startIdx + 2;
|
||||
while (endIdx < payload.length - 1) {
|
||||
endIdx = payload.indexOf(0xFF, endIdx);
|
||||
if (endIdx == -1) break;
|
||||
if (endIdx + 1 < payload.length && payload[endIdx + 1] == 0xD9) {
|
||||
// 找到帧的结束标志 0xFFD9
|
||||
Uint8List frame = payload.sublist(startIdx, endIdx + 2);
|
||||
frames.add(frame);
|
||||
startIdx = endIdx + 2; // 继续寻找下一个帧
|
||||
break;
|
||||
} else {
|
||||
endIdx += 1; // 继续寻找结束标志
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有分包直接解析
|
||||
final TalkData talkData = TalkData();
|
||||
talkData.mergeFromBuffer(byte);
|
||||
return talkData;
|
||||
startIdx = payload.indexOf(0xFF, startIdx + 1); // 寻找下一个起始标志
|
||||
}
|
||||
}
|
||||
|
||||
// 返回找到的所有完整帧
|
||||
return frames;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -53,7 +53,7 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -36,7 +36,7 @@ class UdpTalkPingHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -37,7 +37,7 @@ class UdpTalkPushHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -35,7 +35,7 @@ class UdpTalkReceiverTransferHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -50,7 +50,7 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -95,7 +95,7 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -34,7 +34,7 @@ class UnKnowPayloadTypeHandler extends ScpMessageBaseHandle
|
||||
deserializePayload(
|
||||
{required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
@ -19,6 +20,7 @@ import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
|
||||
class ScpMessageBaseHandle {
|
||||
final startChartManage = StartChartManage();
|
||||
final List<int> _buffer = [];
|
||||
|
||||
/// 分包缓冲区
|
||||
// 存储每个 messageId 对应的分包数据
|
||||
@ -33,12 +35,12 @@ class ScpMessageBaseHandle {
|
||||
|
||||
// 通话保持超时监听定时器管理
|
||||
final talkePingOverTimeTimerManager = OverTimeTimerManager(
|
||||
timeoutInSeconds: 15,
|
||||
timeoutInSeconds: 55,
|
||||
);
|
||||
|
||||
// 通话数据超时定时器
|
||||
final talkDataOverTimeTimerManager = OverTimeTimerManager(
|
||||
timeoutInSeconds: 13,
|
||||
timeoutInSeconds: 53,
|
||||
);
|
||||
|
||||
// 回复成功消息
|
||||
@ -86,7 +88,7 @@ class ScpMessageBaseHandle {
|
||||
}) {
|
||||
// 初始化分包列表
|
||||
String key = '$messageId-$payloadType';
|
||||
if (!_packetBuffer.containsKey(messageId)) {
|
||||
if (!_packetBuffer.containsKey(key)) {
|
||||
_packetBuffer[key] = List.filled(spTotal, []);
|
||||
_startTimer(key);
|
||||
}
|
||||
@ -104,24 +106,17 @@ class ScpMessageBaseHandle {
|
||||
// 检查是否接收到所有分包
|
||||
if (_packetBuffer[key]!.every((packet) => packet.isNotEmpty)) {
|
||||
// 重组所有分包
|
||||
List<int> completePayload =
|
||||
_packetBuffer[key]!.expand((packet) => packet).toList();
|
||||
|
||||
Uint8List completePayload = Uint8List.fromList(
|
||||
_packetBuffer[key]!.expand((packet) => packet).toList());
|
||||
// 清除已重组和超时的分包数据
|
||||
_clearPacketData(key);
|
||||
|
||||
// 解析完整的 payload
|
||||
// 使用重组的包构造成TalkData
|
||||
if (payloadType == PayloadTypeConstant.talkData) {
|
||||
final TalkData talkData = TalkData();
|
||||
final talkData = TalkData();
|
||||
talkData.mergeFromBuffer(completePayload);
|
||||
return talkData;
|
||||
}
|
||||
// if (payloadType == PayloadTypeConstant.echoTest) {
|
||||
// return completePayload;
|
||||
// } else {
|
||||
// String payload = utf8.decode(completePayload);
|
||||
// return payload;
|
||||
// }
|
||||
} else {
|
||||
// 如果分包尚未接收完全,返回 null 或其他指示符
|
||||
return null;
|
||||
|
||||
@ -19,7 +19,7 @@ abstract class ScpMessageHandler {
|
||||
dynamic deserializePayload({
|
||||
required int payloadType,
|
||||
required int messageType,
|
||||
required Uint8List byte,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
int? spTotal,
|
||||
|
||||
@ -17,7 +17,7 @@ import 'talk_data.pbenum.dart';
|
||||
|
||||
export 'talk_data.pbenum.dart';
|
||||
|
||||
/// 注意这个包不应该使用Req,而应该只用单向发送类型,不等待响应。
|
||||
/// 注意这个包不应该使用请求响应(Req/Resp),应该用单向发送类型(RealTimeData),不等待响应。
|
||||
/// 在未收到对方的Ping,或者其他情况,即停止发送。
|
||||
class TalkData extends $pb.GeneratedMessage {
|
||||
factory TalkData({
|
||||
@ -78,6 +78,7 @@ class TalkData extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(1)
|
||||
void clearContentType() => clearField(1);
|
||||
|
||||
/// 音视频数据,例如PCM的字节,或者H264的字节,或者图片的字节
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get content => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
|
||||
@ -3,7 +3,7 @@ syntax = "proto3";
|
||||
package main;
|
||||
option go_package = "./spb/talk";
|
||||
|
||||
// 注意这个包不应该使用Req,而应该只用单向发送类型,不等待响应。
|
||||
// 注意这个包不应该使用请求响应(Req/Resp),应该用单向发送类型(RealTimeData),不等待响应。
|
||||
// 在未收到对方的Ping,或者其他情况,即停止发送。
|
||||
message TalkData {
|
||||
// 内容类型枚举: 一张图传;一帧H264;一段G711
|
||||
@ -13,10 +13,10 @@ message TalkData {
|
||||
G711 = 2;
|
||||
};
|
||||
ContentTypeE ContentType = 1;
|
||||
// 音视频数据,例如PCM的字节,或者H264的字节,或者图片的字节
|
||||
bytes Content = 2;
|
||||
// 时间 毫秒,例如第一帧视频,就是0ms,第2秒的第一帧,就是1000ms
|
||||
// 该字段仅用于协调音视频同步,而不是用于影响音视频播放时机
|
||||
// 对于对讲场景,应该根据网络延迟和设备性能自行决定缓冲时长,在满足播放条件时立即进行渲染。
|
||||
uint32 DurationMs = 3;
|
||||
|
||||
// 该字段仅用于协调音视频同步,而不是用于影响音视频播放时机
|
||||
// 对于对讲场景,应该根据网络延迟和设备性能自行决定缓冲时长,在满足播放条件时立即进行渲染。
|
||||
uint32 DurationMs = 3;
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import 'package:star_lock/talk/startChart/entity/relay_info_entity.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/report_information_data.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/star_chart_register_node_entity.dart';
|
||||
import 'package:star_lock/talk/startChart/exception/start_chart_message_exception.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||
@ -363,10 +364,6 @@ class StartChartManage {
|
||||
|
||||
// 发送期望接受消息
|
||||
void sendTalkExpectMessage({required TalkExpect talkExpect}) async {
|
||||
if (talkStatus.status != TalkStatus.duringCall) {
|
||||
_log(text: '当前未处于接听状态, 无法发送期望接受数据消息');
|
||||
return;
|
||||
}
|
||||
final message = MessageCommand.talkExpectMessage(
|
||||
ToPeerId: ToPeerId,
|
||||
FromPeerId: FromPeerId,
|
||||
@ -407,10 +404,6 @@ class StartChartManage {
|
||||
// 发送通话保持消息
|
||||
Future<void> sendTalkPingMessage(
|
||||
{required String ToPeerId, required String FromPeerId}) async {
|
||||
if (talkStatus.status != TalkStatus.duringCall) {
|
||||
_log(text: '当前未处于接听状态, 无法发送通话保持消息');
|
||||
return;
|
||||
}
|
||||
final message = MessageCommand.talkPingMessage(
|
||||
ToPeerId: ToPeerId,
|
||||
FromPeerId: FromPeerId,
|
||||
@ -474,7 +467,8 @@ class StartChartManage {
|
||||
var result = await _udpSocket?.send(
|
||||
message, InternetAddress(remoteHost), remotePort);
|
||||
if (result != message.length) {
|
||||
AppLog.log('❌Udp send data error----> $result ${message.length}');
|
||||
throw StartChartMessageException(
|
||||
'❌Udp send data error----> $result ${message.length}');
|
||||
// _udpSocket = null;
|
||||
}
|
||||
}
|
||||
@ -663,7 +657,7 @@ class StartChartManage {
|
||||
.signPKCS1v15Bytes(signData, fastRsa.Hash.SHA256, pemPrivateKey);
|
||||
resultSign = hex.encode(result);
|
||||
} catch (e) {
|
||||
_log(text: '❌--->上报信息生成签名时出现错误: $e');
|
||||
throw StartChartMessageException('❌--->上报信息生成签名时出现错误: $e');
|
||||
e.printError();
|
||||
}
|
||||
return resultSign ?? '';
|
||||
@ -785,14 +779,14 @@ class StartChartManage {
|
||||
}
|
||||
if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) {
|
||||
if (deserialize.Payload != null) {
|
||||
_log(text: 'Udp收到结构体数据---》$deserialize');
|
||||
// _log(text: 'Udp收到结构体数据---》$deserialize');
|
||||
}
|
||||
// _log(text: 'text---》${utf8.decode(deserialize.Payload)}');
|
||||
}
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
_log(text: '❌ Udp result data error ----> $e');
|
||||
_log(text: '堆栈跟踪:\n$stackTrace');
|
||||
throw StartChartMessageException(
|
||||
'❌ Udp result data error ----> $e\n,$stackTrace');
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -815,7 +809,7 @@ class StartChartManage {
|
||||
handler.handleInvalidReq(scpMessage);
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
_log(text: '❌ 处理udp返回数据时遇到错误---> $e\n,$stackTrace');
|
||||
throw StartChartMessageException('❌ 处理udp返回数据时遇到错误---> $e\n,$stackTrace');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,6 +64,13 @@ class StartChartTalkStatus {
|
||||
// 可以在这里添加特定于 "waitingAnswer" 状态的逻辑
|
||||
}
|
||||
|
||||
/// 设置状态为等待数据
|
||||
void setWaitingData() {
|
||||
_setStatus(TalkStatus.waitingData);
|
||||
// 可以在这里添加特定于 "waitingAnswer" 状态的逻辑
|
||||
}
|
||||
|
||||
|
||||
/// 设置状态为通话中
|
||||
void setDuringCall() {
|
||||
_setStatus(TalkStatus.duringCall);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user