import 'dart:typed_data'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.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/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pbserver.dart'; import '../../start_chart_manage.dart'; class UdpTalkDataHandler extends ScpMessageBaseHandle implements ScpMessageHandler { @override void handleReq(ScpMessage scpMessage) {} @override void handleResp(ScpMessage scpMessage) {} @override void handleInvalidReq(ScpMessage scpMessage) {} @override void handleRealTimeData(ScpMessage scpMessage) { print('收到音视频数据:$scpMessage'); if (scpMessage.Payload != null) { final TalkData talkData = scpMessage.Payload; // 处理音视频数据 _handleTalkData(talkData: talkData); // 收到数据后调用更新,防止定时器超时 talkDataOverTimeTimerManager.receiveMessage(); } } void _handleTalkData({required TalkData talkData}) { if (talkData == null) return; final contentType = talkData.contentType; switch (contentType) { case TalkData_ContentTypeE.H264: _handleVideoH264(talkData); break; case TalkData_ContentTypeE.Image: _handleVideoImage(talkData); break; case TalkData_ContentTypeE.G711: _handleVideoG711(talkData); break; default: print('❌未知的TalkData--->contentType类型'); break; } } void _handleVideoH264(TalkData talkData) { final List content = talkData.content; // 解析 H.264 NALU _parseH264Nalus(content); } void _handleVideoImage(TalkData talkData) {} void _handleVideoG711(TalkData talkData) {} // 解析 H.264 NALU void _parseH264Nalus(List h264Stream) { print('开始解析 H.264 NALU...'); int index = 0; while (index < h264Stream.length) { // 查找下一个 NALU 的起始码 int nextStartCodeIndex = findNextStartCode(h264Stream, index); if (nextStartCodeIndex == -1) { // 如果没有找到更多的起始码,结束解析 break; } // 提取当前 NALU Uint8List naluData = Uint8List.fromList(h264Stream.sublist(index, nextStartCodeIndex)); // 解析 NALU 头部 int naluType = naluData[0] & 0x1F; // NALU 类型位于第一个字节的低 5 位 print('找到 NALU,类型: $naluType,长度: ${naluData.length}'); // 根据 NALU 类型进行处理 handleNalu(naluType, naluData); // 更新索引到下一个 NALU 的起始位置 index = nextStartCodeIndex; } } // 查找下一个 NALU 的起始码 int findNextStartCode(List data, int startIndex) { for (int i = startIndex; i < data.length - 3; i++) { // 检查 3 字节起始码 if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x01) { return i; } // 检查 4 字节起始码 if (i < data.length - 4 && data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x00 && data[i + 3] == 0x01) { return i; } } // 如果没有找到更多的起始码,返回 -1 return -1; } // 处理 NALU void handleNalu(int naluType, Uint8List naluData) { switch (naluType) { case 5: print('IDR 帧 (关键帧)'); break; case 1: print('非 IDR 帧 (P 帧)'); break; case 7: print('SPS (序列参数集)'); break; case 8: print('PPS (图像参数集)'); break; default: print('其他 NALU 类型: $naluType'); } // 你可以在这里根据 NALU 类型进行进一步处理 // 例如,将 SPS 和 PPS 传递给解码器,或将视频帧渲染到屏幕上 } }