fix:更新组包缓冲区的key
This commit is contained in:
parent
1108d4c665
commit
a029c4b4e0
@ -25,12 +25,6 @@ class StarChartLogic extends BaseGetXController {
|
|||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
super.onReady();
|
super.onReady();
|
||||||
// 初始化音频播放设备,采样率为 44100 Hz,单声道
|
|
||||||
FlutterPcmSound.setup(sampleRate: 44100, channelCount: 1, );
|
|
||||||
|
|
||||||
// 设置音频数据的供给阈值,假设我们使用一秒钟的供给阈值
|
|
||||||
int feedThreshold = 44100 * 1; // 44100 个样本,相当于一秒钟的数据量
|
|
||||||
FlutterPcmSound.setFeedThreshold(feedThreshold);
|
|
||||||
_getTalkStatusRefreshUIAction();
|
_getTalkStatusRefreshUIAction();
|
||||||
_startListenTalkData();
|
_startListenTalkData();
|
||||||
}
|
}
|
||||||
@ -75,8 +69,16 @@ class StarChartLogic extends BaseGetXController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 播放音频数据
|
/// 播放音频数据
|
||||||
Future<void> _playG711Data(List<int> audioData) async {
|
Future<void> _playG711Data(List<int> pcmData) async {
|
||||||
final PcmArrayInt16 fromList = PcmArrayInt16.fromList(audioData);
|
// 将 PCM 数据转换为 PcmArrayInt16
|
||||||
|
final PcmArrayInt16 fromList = PcmArrayInt16.fromList(pcmData);
|
||||||
|
|
||||||
|
// 初始化音频播放设备,确保采样率和声道数与 PCM 数据匹配
|
||||||
|
await FlutterPcmSound.setup(sampleRate: 8000, channelCount: 1);
|
||||||
|
|
||||||
|
// 设置音频数据的供给阈值
|
||||||
|
FlutterPcmSound.setFeedThreshold(8000 ~/ 2); // 根据需要调整
|
||||||
|
// final PcmArrayInt16 fromList = PcmArrayInt16.fromList(audioData);
|
||||||
|
|
||||||
await FlutterPcmSound.feed(fromList);
|
await FlutterPcmSound.feed(fromList);
|
||||||
FlutterPcmSound.play();
|
FlutterPcmSound.play();
|
||||||
|
|||||||
@ -316,7 +316,7 @@ class _StarChartPageState extends State<StarChartPage> {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
logic.stopProcessing();
|
logic.stopProcessing();
|
||||||
state.getTVDataRefreshUIEvent!.cancel();
|
// state.getTVDataRefreshUIEvent!.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTalkView({required bool isMpeg4}) {
|
Widget _buildTalkView({required bool isMpeg4}) {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class StarChartState {
|
|||||||
RxBool isOpenVoice = false.obs;
|
RxBool isOpenVoice = false.obs;
|
||||||
int udpSendDataFrameNumber = 0; // 帧序号
|
int udpSendDataFrameNumber = 0; // 帧序号
|
||||||
// var isSenderAudioData = false.obs;// 是否要发送音频数据
|
// var isSenderAudioData = false.obs;// 是否要发送音频数据
|
||||||
StreamSubscription? getTVDataRefreshUIEvent; //收到视频流数据
|
// StreamSubscription? getTVDataRefreshUIEvent; //收到视频流数据
|
||||||
RxBool shouldUpdateUI = false.obs; //是否需要更新UI
|
RxBool shouldUpdateUI = false.obs; //是否需要更新UI
|
||||||
|
|
||||||
Future<String?> userMobileIP = NetworkInfo().getWifiIP();
|
Future<String?> userMobileIP = NetworkInfo().getWifiIP();
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
|
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
|
||||||
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
||||||
import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
|
import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
|
||||||
|
import 'package:star_lock/talk/startChart/constant/udp_constant.dart';
|
||||||
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
||||||
import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart';
|
import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart';
|
||||||
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
|
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
|
||||||
@ -17,6 +18,7 @@ import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart';
|
|||||||
|
|
||||||
class MessageCommand {
|
class MessageCommand {
|
||||||
// 全局字典,用于存储每个 ToPeerId 对应的当前 messageId
|
// 全局字典,用于存储每个 ToPeerId 对应的当前 messageId
|
||||||
|
// 单个会话的messageId需要递增
|
||||||
static Map<String, int> _messageIdMap = {};
|
static Map<String, int> _messageIdMap = {};
|
||||||
static int _maxIntValue = 9223372036854775807; // Dart 中 int 的最大值
|
static int _maxIntValue = 9223372036854775807; // Dart 中 int 的最大值
|
||||||
|
|
||||||
@ -322,8 +324,8 @@ class MessageCommand {
|
|||||||
int? MessageId,
|
int? MessageId,
|
||||||
}) {
|
}) {
|
||||||
final genericResp = GenericResp();
|
final genericResp = GenericResp();
|
||||||
genericResp.message = 'ok';
|
genericResp.message = UdpConstant.genericRespSuccessMsg;
|
||||||
genericResp.code = 0;
|
genericResp.code = UdpConstant.genericRespSuccessCode;
|
||||||
final payload = genericResp.writeToBuffer();
|
final payload = genericResp.writeToBuffer();
|
||||||
ScpMessage message = ScpMessage(
|
ScpMessage message = ScpMessage(
|
||||||
ProtocolFlag: ProtocolFlagConstant.scp01,
|
ProtocolFlag: ProtocolFlagConstant.scp01,
|
||||||
@ -348,10 +350,11 @@ class MessageCommand {
|
|||||||
required String ToPeerId,
|
required String ToPeerId,
|
||||||
required int PayloadType,
|
required int PayloadType,
|
||||||
int? MessageId,
|
int? MessageId,
|
||||||
|
String? errorMessageText,
|
||||||
}) {
|
}) {
|
||||||
final genericResp = GenericResp();
|
final genericResp = GenericResp();
|
||||||
genericResp.message = 'error';
|
genericResp.message = errorMessageText ?? UdpConstant.genericRespErrorMsg;
|
||||||
genericResp.code = -1;
|
genericResp.code = UdpConstant.genericRespErrorCode;
|
||||||
final payload = genericResp.writeToBuffer();
|
final payload = genericResp.writeToBuffer();
|
||||||
ScpMessage message = ScpMessage(
|
ScpMessage message = ScpMessage(
|
||||||
ProtocolFlag: ProtocolFlagConstant.scp01,
|
ProtocolFlag: ProtocolFlagConstant.scp01,
|
||||||
|
|||||||
@ -2,4 +2,7 @@ class UdpConstant{
|
|||||||
// generic成功响应
|
// generic成功响应
|
||||||
static const int genericRespSuccessCode = 0;
|
static const int genericRespSuccessCode = 0;
|
||||||
static const String genericRespSuccessMsg = 'ok';
|
static const String genericRespSuccessMsg = 'ok';
|
||||||
|
// generic失败响应
|
||||||
|
static const int genericRespErrorCode = -1;
|
||||||
|
static const String genericRespErrorMsg = 'error';
|
||||||
}
|
}
|
||||||
@ -31,12 +31,12 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle
|
|||||||
// 收到同意接听回复
|
// 收到同意接听回复
|
||||||
final GenericResp genericResp = scpMessage.Payload;
|
final GenericResp genericResp = scpMessage.Payload;
|
||||||
if (checkGenericRespSuccess(genericResp)) {
|
if (checkGenericRespSuccess(genericResp)) {
|
||||||
// 延迟一秒启动定时器判断
|
// 启动发送预期数据请求
|
||||||
|
_handleStartSendTalkExpectDataRequest();
|
||||||
Future.delayed(Duration(seconds: 1), () {
|
Future.delayed(Duration(seconds: 1), () {
|
||||||
|
print('启动定时器判断');
|
||||||
// 启动通话保持定时器
|
// 启动通话保持定时器
|
||||||
_handleStartTalkPing();
|
_handleStartTalkPing();
|
||||||
// 启动发送预期数据请求
|
|
||||||
_handleStartSendTalkExpectDataRequest();
|
|
||||||
// 启动通话数据检查的定时器
|
// 启动通话数据检查的定时器
|
||||||
_handleCheckTalkDataTimer();
|
_handleCheckTalkDataTimer();
|
||||||
});
|
});
|
||||||
@ -78,7 +78,7 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动通话保持
|
// 启动通话保持,判断x秒内是否收到通话保持
|
||||||
void _handleStartTalkPing() {
|
void _handleStartTalkPing() {
|
||||||
// 启动通话保持
|
// 启动通话保持
|
||||||
startChartManage.startTalkPingMessageTimer();
|
startChartManage.startTalkPingMessageTimer();
|
||||||
|
|||||||
@ -85,11 +85,7 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
|
|||||||
int? spTotal,
|
int? spTotal,
|
||||||
int? spIndex,
|
int? spIndex,
|
||||||
int? messageId}) {
|
int? messageId}) {
|
||||||
if (messageType == MessageTypeConstant.Resp) {
|
if (messageType == MessageTypeConstant.RealTimeData) {
|
||||||
final GenericResp genericResp = GenericResp();
|
|
||||||
genericResp.mergeFromBuffer(byte);
|
|
||||||
return genericResp;
|
|
||||||
} else if (messageType == MessageTypeConstant.RealTimeData) {
|
|
||||||
// 回声测试
|
// 回声测试
|
||||||
if (spTotal != null &&
|
if (spTotal != null &&
|
||||||
spTotal > 1 &&
|
spTotal > 1 &&
|
||||||
@ -109,9 +105,6 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
|
|||||||
talkData.mergeFromBuffer(byte);
|
talkData.mergeFromBuffer(byte);
|
||||||
return talkData;
|
return talkData;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
String payload = utf8.decode(byte);
|
|
||||||
return payload;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -65,6 +66,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
|
|||||||
Routers.starChartPage,
|
Routers.starChartPage,
|
||||||
arguments: <String, String>{'lockId': '111'},
|
arguments: <String, String>{'lockId': '111'},
|
||||||
);
|
);
|
||||||
|
// 触发轻微震动反馈
|
||||||
|
HapticFeedback.lightImpact();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收到来电请求时进行本地通知
|
// 收到来电请求时进行本地通知
|
||||||
|
|||||||
@ -12,9 +12,6 @@ class OverTimeTimerManager {
|
|||||||
// 超时时间(以秒为单位),是 final 的,因此必须在构造函数中初始化
|
// 超时时间(以秒为单位),是 final 的,因此必须在构造函数中初始化
|
||||||
final int timeoutInSeconds;
|
final int timeoutInSeconds;
|
||||||
|
|
||||||
// 默认超时时间(以秒为单位)
|
|
||||||
static const int defaultTimeoutInSeconds = 5;
|
|
||||||
|
|
||||||
// 超时回调函数
|
// 超时回调函数
|
||||||
TimeoutCallback? _onTimeout;
|
TimeoutCallback? _onTimeout;
|
||||||
|
|
||||||
@ -24,7 +21,7 @@ class OverTimeTimerManager {
|
|||||||
_timer = Timer(Duration(seconds: timeoutInSeconds), () {
|
_timer = Timer(Duration(seconds: timeoutInSeconds), () {
|
||||||
// 超时回调方法
|
// 超时回调方法
|
||||||
_onTimeout?.call();
|
_onTimeout?.call();
|
||||||
// 清楚定时器
|
// 清除定时器
|
||||||
_cancelTimer();
|
_cancelTimer();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,8 @@ class ScpMessageBaseHandle {
|
|||||||
|
|
||||||
/// 分包缓冲区
|
/// 分包缓冲区
|
||||||
// 存储每个 messageId 对应的分包数据
|
// 存储每个 messageId 对应的分包数据
|
||||||
static Map<int, List<List<int>>> _packetBuffer = {};
|
static Map<String, List<List<int>>> _packetBuffer = {};
|
||||||
final Map<int, Timer> _packetTimers = {};
|
final Map<String, Timer> _packetTimers = {};
|
||||||
final Duration _timeoutDuration = Duration(seconds: 10); // 分包组包最大超时时间
|
final Duration _timeoutDuration = Duration(seconds: 10); // 分包组包最大超时时间
|
||||||
|
|
||||||
// 通话数据流的单例流数据处理类
|
// 通话数据流的单例流数据处理类
|
||||||
@ -85,28 +85,30 @@ class ScpMessageBaseHandle {
|
|||||||
required int payloadType,
|
required int payloadType,
|
||||||
}) {
|
}) {
|
||||||
// 初始化分包列表
|
// 初始化分包列表
|
||||||
|
String key = '$messageId-$payloadType';
|
||||||
if (!_packetBuffer.containsKey(messageId)) {
|
if (!_packetBuffer.containsKey(messageId)) {
|
||||||
_packetBuffer[messageId] = List.filled(spTotal, []);
|
_packetBuffer[key] = List.filled(spTotal, []);
|
||||||
_startTimer(messageId);
|
_startTimer(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查分包索引是否在合法范围内
|
// 检查分包索引是否在合法范围内
|
||||||
if (spIndex < 1 || spIndex > spTotal) {
|
if (spIndex < 1 || spIndex > spTotal) {
|
||||||
print('Invalid spIndex: $spIndex for messageId: $messageId');
|
print(
|
||||||
|
'Invalid spTotal: $spTotal spIndex: $spIndex for messageId: $messageId');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储当前分包
|
// 存储当前分包
|
||||||
_packetBuffer[messageId]![spIndex - 1] = byte;
|
_packetBuffer[key]![spIndex - 1] = byte;
|
||||||
|
|
||||||
// 检查是否接收到所有分包
|
// 检查是否接收到所有分包
|
||||||
if (_packetBuffer[messageId]!.every((packet) => packet.isNotEmpty)) {
|
if (_packetBuffer[key]!.every((packet) => packet.isNotEmpty)) {
|
||||||
// 重组所有分包
|
// 重组所有分包
|
||||||
List<int> completePayload =
|
List<int> completePayload =
|
||||||
_packetBuffer[messageId]!.expand((packet) => packet).toList();
|
_packetBuffer[key]!.expand((packet) => packet).toList();
|
||||||
|
|
||||||
// 清除已重组的分包数据
|
// 清除已重组和超时的分包数据
|
||||||
_clearPacketData(messageId);
|
_clearPacketData(key);
|
||||||
|
|
||||||
// 解析完整的 payload
|
// 解析完整的 payload
|
||||||
if (payloadType == PayloadTypeConstant.talkData) {
|
if (payloadType == PayloadTypeConstant.talkData) {
|
||||||
@ -126,16 +128,16 @@ class ScpMessageBaseHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 启动定时器
|
// 启动定时器
|
||||||
void _startTimer(int messageId) {
|
void _startTimer(String key) {
|
||||||
_packetTimers[messageId]?.cancel();
|
_packetTimers[key]?.cancel();
|
||||||
_packetTimers[messageId] = Timer(_timeoutDuration, () {
|
_packetTimers[key] = Timer(_timeoutDuration, () {
|
||||||
_clearPacketData(messageId);
|
_clearPacketData(key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除分包数据和定时器
|
// 清除分包数据和定时器
|
||||||
void _clearPacketData(int messageId) {
|
void _clearPacketData(String key) {
|
||||||
_packetBuffer.remove(messageId);
|
_packetBuffer.remove(key);
|
||||||
_packetTimers.remove(messageId)?.cancel();
|
_packetTimers.remove(key)?.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -296,6 +296,7 @@ class StartChartManage {
|
|||||||
// 取到分包数据
|
// 取到分包数据
|
||||||
List<int> packet = payload.sublist(start, end);
|
List<int> packet = payload.sublist(start, end);
|
||||||
|
|
||||||
|
// 分包数据不递增messageID
|
||||||
final messageId =
|
final messageId =
|
||||||
MessageCommand.getNextMessageId(toPeerId, increment: false);
|
MessageCommand.getNextMessageId(toPeerId, increment: false);
|
||||||
// 组装分包数据
|
// 组装分包数据
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user