From bf4c2b47508bd9b908382bfe95e7f84f127bcf4f Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 18 Jun 2025 14:59:34 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0=E9=9F=B3=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=95=B0=E6=8D=AE=E7=9A=84=E4=B8=A2=E5=8C=85=E7=8E=87?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E3=80=81=E8=B0=83=E6=95=B4udp=E5=8F=91?= =?UTF-8?q?=E9=80=81=E5=92=8C=E6=8E=A5=E6=94=B6=E7=BC=93=E5=86=B2=E5=8C=BA?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/talk/starChart/star_chart_manage.dart | 101 +++++++++++++++++----- 1 file changed, 77 insertions(+), 24 deletions(-) diff --git a/lib/talk/starChart/star_chart_manage.dart b/lib/talk/starChart/star_chart_manage.dart index 7a185c47..13228e01 100644 --- a/lib/talk/starChart/star_chart_manage.dart +++ b/lib/talk/starChart/star_chart_manage.dart @@ -50,6 +50,10 @@ import 'package:star_lock/tools/deviceInfo_utils.dart'; import 'package:star_lock/tools/storage.dart'; import 'package:uuid/uuid.dart'; +// Socket选项常量 +const int SO_RCVBUF = 8; // 接收缓冲区 +const int SO_SNDBUF = 7; // 发送缓冲区 + class StartChartManage { // 私有构造函数,防止外部直接new对象 StartChartManage._internal(); @@ -125,6 +129,17 @@ class StartChartManage { // 获取 StartChartTalkStatus 的唯一实例 StartChartTalkStatus talkStatus = StartChartTalkStatus.instance; + // 音视频帧级丢包统计变量 + final Map> _avFrameParts = {}; + int _avFrameTotal = 0; + int _avFrameLost = 0; + + // 查询音视频帧丢包率 + double getAvFrameLossRate() { + if (_avFrameTotal == 0) return 0.0; + return _avFrameLost / _avFrameTotal; + } + // 星图服务初始化 Future init() async { if (F.isXHJ) { @@ -225,6 +240,25 @@ class StartChartManage { var addressIListenFrom = InternetAddress.anyIPv4; RawDatagramSocket.bind(addressIListenFrom, localPort) .then((RawDatagramSocket socket) { + + // 设置接收缓冲区大小 (SO_RCVBUF = 8) + socket.setRawOption( + RawSocketOption.fromInt( + RawSocketOption.levelSocket, + 8, // SO_RCVBUF for Android/iOS + 2 * 1024 * 1024, // 2MB receive buffer + ), + ); + + // 设置发送缓冲区大小 (SO_SNDBUF = 7) + socket.setRawOption( + RawSocketOption.fromInt( + RawSocketOption.levelSocket, + 7, // SO_SNDBUF for Android/iOS + 2 * 1024 * 1024, // 2MB send buffer + ), + ); + _udpSocket = socket; /// 广播功能 @@ -1017,35 +1051,54 @@ class StartChartManage { void _onReceiveData(RawDatagramSocket socket, BuildContext context) { socket.listen((RawSocketEvent event) { if (event == RawSocketEvent.read) { - Datagram? dg = socket.receive(); - try { - if (dg?.data != null) { - final deserialize = ScpMessage.deserialize(dg!.data); + Datagram? dg; + while ((dg = socket.receive()) != null) { + try { + if (dg?.data != null) { + final deserialize = ScpMessage.deserialize(dg!.data); - // //ToDo: 增加对讲调试、正式可删除 - // UdpTalkDataHandler().updateRecvDataRate(dg.data.length); - - // // 更新调试信息 - // Provider.of(context, listen: false).updateDebugInfo( - // UdpTalkDataHandler().getLastRecvDataRate() ~/ 1024, // 转换为KB - // UdpTalkDataHandler().getLastRecvPacketCount(), - // UdpTalkDataHandler().getLastSendDataRate() ~/ 1024, // 转换为KB - // UdpTalkDataHandler().getLastSendPacketCount(), - // ); - - if (deserialize != null) { - // 处理返回数据 - _handleUdpResultData(deserialize); - } - if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) { - if (deserialize.Payload != null) { - // _log(text: 'Udp收到结构体数据---》$deserialize'); + // 音视频帧丢包统计:只统计PayloadType==talkData的数据包,结合分包信息 + if (deserialize != null && + deserialize.PayloadType == PayloadTypeConstant.talkData) { + int? msgId = deserialize.MessageId; + int spTotal = deserialize.SpTotal ?? 1; + int spIndex = deserialize.SpIndex ?? 1; + if (msgId != null) { + // 记录收到的分包 + _avFrameParts.putIfAbsent(msgId, () => {}); + _avFrameParts[msgId]!.add(spIndex); + // 如果收到最后一个分包,判断该帧是否完整 + if (spIndex == spTotal) { + _avFrameTotal++; + if (_avFrameParts[msgId]!.length < spTotal) { + _avFrameLost++; + // _log(text: '音视频丢包,丢失的messageId: $msgId'); + } + _avFrameParts.remove(msgId); + // 可选:每100帧打印一次丢包率 + if (_avFrameTotal % 100 == 0) { + _log( + text: + '音视频帧丢包率: ${(getAvFrameLossRate() * 100).toStringAsFixed(2)}%'); + } + } + } } + + if (deserialize != null) { + // 处理返回数据 + _handleUdpResultData(deserialize); + } + // if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) { + // if (deserialize.Payload != null) { + // _log(text: 'Udp收到结构体数据---》$deserialize'); + // } // _log(text: 'text---》${utf8.decode(deserialize.Payload)}'); + // } } + } catch (e, stackTrace) { + throw StartChartMessageException('$e\n,$stackTrace'); } - } catch (e, stackTrace) { - throw StartChartMessageException('$e\n,$stackTrace'); } } });