fix:增加对讲数据发送逻辑
This commit is contained in:
parent
aad509e7b9
commit
3200df7793
@ -236,9 +236,9 @@ class _StarLockLoginPageState extends State<StarLockLoginPage> {
|
|||||||
}
|
}
|
||||||
: null)),
|
: null)),
|
||||||
SubmitBtn(
|
SubmitBtn(
|
||||||
btnName: '跳转至html',
|
btnName: '跳转至通话',
|
||||||
onClick: () {
|
onClick: () {
|
||||||
Get.toNamed(Routers.LocalHtmlPage);
|
Get.toNamed(Routers.lockMonitoringPage);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 50.w),
|
SizedBox(height: 50.w),
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import 'package:star_lock/app_settings/app_settings.dart';
|
|||||||
import 'package:star_lock/main/lockDetail/monitoring/monitoring/star_chart_logic.dart';
|
import 'package:star_lock/main/lockDetail/monitoring/monitoring/star_chart_logic.dart';
|
||||||
import 'package:star_lock/talk/call/callTalk.dart';
|
import 'package:star_lock/talk/call/callTalk.dart';
|
||||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||||
|
import 'package:star_lock/talk/startChart/webView/h264_web_view.dart';
|
||||||
import 'package:star_lock/talk/udp/udp_manage.dart';
|
import 'package:star_lock/talk/udp/udp_manage.dart';
|
||||||
import 'package:star_lock/tools/eventBusEventManage.dart';
|
import 'package:star_lock/tools/eventBusEventManage.dart';
|
||||||
import 'package:star_lock/tools/showTFView.dart';
|
import 'package:star_lock/tools/showTFView.dart';
|
||||||
@ -56,62 +57,7 @@ class _LockMonitoringPageState extends State<LockMonitoringPage> {
|
|||||||
width: 1.sw,
|
width: 1.sw,
|
||||||
height: 1.sh,
|
height: 1.sh,
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: Stack(
|
child: _buildTalkView(isMpeg4: false),
|
||||||
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),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -356,4 +302,109 @@ class _LockMonitoringPageState extends State<LockMonitoringPage> {
|
|||||||
logic.stopProcessing();
|
logic.stopProcessing();
|
||||||
state.getTVDataRefreshUIEvent!.cancel();
|
state.getTVDataRefreshUIEvent!.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildTalkView({required bool isMpeg4}) {
|
||||||
|
return isMpeg4 ? _buildMpeg4TalkView() : _buildH264TalkView();
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildH264TalkView() {
|
||||||
|
return Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
H264WebView(),
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,6 @@ 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.pb.dart';
|
||||||
import 'package:star_lock/talk/startChart/proto/talk_data.pbserver.dart';
|
import 'package:star_lock/talk/startChart/proto/talk_data.pbserver.dart';
|
||||||
|
|
||||||
|
|
||||||
class UdpTalkDataHandler extends ScpMessageBaseHandle
|
class UdpTalkDataHandler extends ScpMessageBaseHandle
|
||||||
implements ScpMessageHandler {
|
implements ScpMessageHandler {
|
||||||
@override
|
@override
|
||||||
@ -24,11 +23,10 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void handleRealTimeData(ScpMessage scpMessage) {
|
void handleRealTimeData(ScpMessage scpMessage) {
|
||||||
// print('收到音视频数据:$scpMessage');
|
|
||||||
if (scpMessage.Payload != null) {
|
if (scpMessage.Payload != null) {
|
||||||
final TalkData talkData = scpMessage.Payload;
|
final TalkData talkData = scpMessage.Payload;
|
||||||
// 处理音视频数据
|
// 处理音视频数据
|
||||||
// _handleTalkData(talkData: talkData);
|
_handleTalkData(talkData: talkData);
|
||||||
// 收到数据后调用更新,防止定时器超时
|
// 收到数据后调用更新,防止定时器超时
|
||||||
talkDataOverTimeTimerManager.receiveMessage();
|
talkDataOverTimeTimerManager.receiveMessage();
|
||||||
}
|
}
|
||||||
@ -55,86 +53,10 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
|
|||||||
|
|
||||||
void _handleVideoH264(TalkData talkData) {
|
void _handleVideoH264(TalkData talkData) {
|
||||||
final List<int> content = talkData.content;
|
final List<int> content = talkData.content;
|
||||||
// 解析 H.264 NALU
|
talkDataRepository.addTalkData(content);
|
||||||
_parseH264Nalus(content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleVideoImage(TalkData talkData) {}
|
void _handleVideoImage(TalkData talkData) {}
|
||||||
|
|
||||||
void _handleVideoG711(TalkData talkData) {}
|
void _handleVideoG711(TalkData talkData) {}
|
||||||
|
|
||||||
// 解析 H.264 NALU
|
|
||||||
void _parseH264Nalus(List<int> 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<int> 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 传递给解码器,或将视频帧渲染到屏幕上
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
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:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
||||||
@ -5,6 +8,7 @@ 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/handle/scp_message_handle.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';
|
||||||
|
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||||
import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart';
|
import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart';
|
||||||
|
|
||||||
import '../../start_chart_manage.dart';
|
import '../../start_chart_manage.dart';
|
||||||
@ -16,6 +20,12 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle
|
|||||||
// 收到预期音视频数据请求
|
// 收到预期音视频数据请求
|
||||||
final TalkExpect talkExpect = scpMessage.Payload;
|
final TalkExpect talkExpect = scpMessage.Payload;
|
||||||
print('收到预期音视频数据请求:$talkExpect');
|
print('收到预期音视频数据请求:$talkExpect');
|
||||||
|
|
||||||
|
// 回复请求
|
||||||
|
replySuccessMessage(scpMessage);
|
||||||
|
|
||||||
|
// 启动发送通话数据
|
||||||
|
// startChartManage.startTalkDataTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -28,12 +38,8 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleInvalidReq(ScpMessage scpMessage) {
|
void handleInvalidReq(ScpMessage scpMessage) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleRealTimeData(ScpMessage scpMessage) {
|
void handleRealTimeData(ScpMessage scpMessage) {}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
30
lib/talk/startChart/handle/other/talk_data_repository.dart
Normal file
30
lib/talk/startChart/handle/other/talk_data_repository.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||||
|
|
||||||
|
class TalkDataRepository {
|
||||||
|
// 创建一个私有的构造函数,防止外部创建实例
|
||||||
|
TalkDataRepository._();
|
||||||
|
|
||||||
|
// 使用 _instance 来保存单例对象
|
||||||
|
static final TalkDataRepository _instance = TalkDataRepository._();
|
||||||
|
|
||||||
|
// 提供一个静态方法来获取单例实例
|
||||||
|
static TalkDataRepository get instance => _instance;
|
||||||
|
|
||||||
|
// 创建一个 StreamController
|
||||||
|
final StreamController<List<int>> _talkDataStreamController = StreamController<List<int>>.broadcast();
|
||||||
|
|
||||||
|
// 提供一个方法来获取 Stream
|
||||||
|
Stream<List<int>> get talkDataStream => _talkDataStreamController.stream;
|
||||||
|
|
||||||
|
// 提供一个方法来添加 TalkData 到 Stream
|
||||||
|
void addTalkData(List<int> talkData) {
|
||||||
|
_talkDataStreamController.add(talkData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提供一个方法来关闭 StreamController
|
||||||
|
void dispose() {
|
||||||
|
_talkDataStreamController.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,9 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|||||||
import 'package:star_lock/app_settings/app_settings.dart';
|
import 'package:star_lock/app_settings/app_settings.dart';
|
||||||
import 'package:star_lock/talk/other/audio_player_manager.dart';
|
import 'package:star_lock/talk/other/audio_player_manager.dart';
|
||||||
import 'package:star_lock/talk/startChart/constant/udp_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/handle/other/overtime_timer_manger.dart';
|
import 'package:star_lock/talk/startChart/handle/other/overtime_timer_manger.dart';
|
||||||
|
import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart';
|
||||||
|
|
||||||
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
|
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
|
||||||
import 'package:star_lock/talk/startChart/start_chart_manage.dart';
|
import 'package:star_lock/talk/startChart/start_chart_manage.dart';
|
||||||
@ -13,6 +15,9 @@ import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
|||||||
class ScpMessageBaseHandle {
|
class ScpMessageBaseHandle {
|
||||||
final startChartManage = StartChartManage();
|
final startChartManage = StartChartManage();
|
||||||
|
|
||||||
|
// 通话数据流的单例流数据处理类
|
||||||
|
final TalkDataRepository talkDataRepository = TalkDataRepository.instance;
|
||||||
|
|
||||||
final audioManager = AudioPlayerManager();
|
final audioManager = AudioPlayerManager();
|
||||||
|
|
||||||
// 通话保持超时监听定时器管理
|
// 通话保持超时监听定时器管理
|
||||||
@ -25,12 +30,18 @@ class ScpMessageBaseHandle {
|
|||||||
timeoutInSeconds: 3,
|
timeoutInSeconds: 3,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 回复成功消息
|
||||||
|
void replySuccessMessage(ScpMessage scpMessage){
|
||||||
|
startChartManage.sendGenericRespSuccessMessage(
|
||||||
|
ToPeerId: scpMessage.FromPeerId!,
|
||||||
|
FromPeerId: scpMessage.ToPeerId!,
|
||||||
|
PayloadType: scpMessage.PayloadType!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 获取 StartChartTalkStatus 的唯一实例
|
// 获取 StartChartTalkStatus 的唯一实例
|
||||||
StartChartTalkStatus talkStatus = StartChartTalkStatus.instance;
|
StartChartTalkStatus talkStatus = StartChartTalkStatus.instance;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool checkGenericRespSuccess(GenericResp genericResp) {
|
bool checkGenericRespSuccess(GenericResp genericResp) {
|
||||||
if (genericResp == null) return false;
|
if (genericResp == null) return false;
|
||||||
final code = genericResp.code;
|
final code = genericResp.code;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:convert/convert.dart';
|
import 'package:convert/convert.dart';
|
||||||
import 'package:fast_rsa/fast_rsa.dart' as fastRsa;
|
import 'package:fast_rsa/fast_rsa.dart' as fastRsa;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pointycastle/export.dart' as pc;
|
import 'package:pointycastle/export.dart' as pc;
|
||||||
import 'package:star_lock/app_settings/app_settings.dart';
|
import 'package:star_lock/app_settings/app_settings.dart';
|
||||||
@ -22,6 +23,7 @@ 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/entity/star_chart_register_node_entity.dart';
|
||||||
import 'package:star_lock/talk/startChart/handle/scp_message_handle.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/handle/scp_message_handler_factory.dart';
|
||||||
|
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||||
import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart';
|
import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart';
|
||||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||||
import 'package:star_lock/tools/baseGetXController.dart';
|
import 'package:star_lock/tools/baseGetXController.dart';
|
||||||
@ -70,6 +72,8 @@ class StartChartManage {
|
|||||||
Timer? talkPingTimer; // 发送通话保持消息定时器
|
Timer? talkPingTimer; // 发送通话保持消息定时器
|
||||||
int talkExpectIntervalTime = 1; // 发送通话预期数据的消息间隔(s)
|
int talkExpectIntervalTime = 1; // 发送通话预期数据的消息间隔(s)
|
||||||
Timer? talkExpectTimer; // 发送通话预期消息定时器
|
Timer? talkExpectTimer; // 发送通话预期消息定时器
|
||||||
|
int talkDataIntervalTime = 10; // 通话数据的消息间隔(ms)
|
||||||
|
Timer? talkDataTimer; // 发送通话数据消息定时器
|
||||||
|
|
||||||
// 默认通话的期望数据格式
|
// 默认通话的期望数据格式
|
||||||
TalkExpect defaultTalkExpect = TalkExpect(
|
TalkExpect defaultTalkExpect = TalkExpect(
|
||||||
@ -77,6 +81,9 @@ class StartChartManage {
|
|||||||
audioType: [TalkExpect_AudioTypeE.G711],
|
audioType: [TalkExpect_AudioTypeE.G711],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 默认通话数据
|
||||||
|
TalkData defaultTalkData = TalkData();
|
||||||
|
|
||||||
String relayPeerId = ''; // 中继peerId
|
String relayPeerId = ''; // 中继peerId
|
||||||
|
|
||||||
// 获取 StartChartTalkStatus 的唯一实例
|
// 获取 StartChartTalkStatus 的唯一实例
|
||||||
@ -228,6 +235,17 @@ class StartChartManage {
|
|||||||
await _sendMessage(message: message);
|
await _sendMessage(message: message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发送对讲数据
|
||||||
|
Future<void> sendTalkDataMessage({required TalkData talkData}) async {
|
||||||
|
// 组装上线消息
|
||||||
|
final message = MessageCommand.talkDataMessage(
|
||||||
|
FromPeerId: FromPeerId,
|
||||||
|
ToPeerId: ToPeerId,
|
||||||
|
talkData: talkData,
|
||||||
|
);
|
||||||
|
await _sendMessage(message: message);
|
||||||
|
}
|
||||||
|
|
||||||
// 发送心跳包消息
|
// 发送心跳包消息
|
||||||
void _sendHeartbeatMessage() async {
|
void _sendHeartbeatMessage() async {
|
||||||
if (_heartBeatTimerRunning) {
|
if (_heartBeatTimerRunning) {
|
||||||
@ -764,6 +782,108 @@ class StartChartManage {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 通话数据定时器
|
||||||
|
void startTalkDataTimer() async {
|
||||||
|
// 如果已经启动了就不运行
|
||||||
|
if (talkDataTimer != null) return;
|
||||||
|
// 读取 assets 文件
|
||||||
|
final ByteData data = await rootBundle.load('assets/talk.h264');
|
||||||
|
final List<int> byteData = data.buffer.asUint8List();
|
||||||
|
int current = 0;
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
final List<int> chunks = extractChunks(byteData);
|
||||||
|
talkDataTimer ??= Timer.periodic(
|
||||||
|
Duration(
|
||||||
|
milliseconds: talkDataIntervalTime,
|
||||||
|
),
|
||||||
|
(Timer timer) {
|
||||||
|
if (current >= chunks.length) {
|
||||||
|
print('数据已经发完');
|
||||||
|
start = 0;
|
||||||
|
end = 0;
|
||||||
|
current = 0;
|
||||||
|
timer.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 提取 NALU 边界并生成 chunks
|
||||||
|
end = chunks[current];
|
||||||
|
current++;
|
||||||
|
List<int> frameData = byteData.sublist(start, end);
|
||||||
|
if (frameData.length == 0) timer.cancel();
|
||||||
|
defaultTalkData = TalkData(
|
||||||
|
content: frameData,
|
||||||
|
contentType: TalkData_ContentTypeE.H264,
|
||||||
|
);
|
||||||
|
start = end;
|
||||||
|
// 发送童话数据
|
||||||
|
sendTalkDataMessage(talkData: defaultTalkData);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int> extractChunks(List<int> byteData) {
|
||||||
|
int i = 0;
|
||||||
|
int length = byteData.length;
|
||||||
|
int naluCount = 0;
|
||||||
|
int value;
|
||||||
|
int state = 0;
|
||||||
|
int lastIndex = 0;
|
||||||
|
List<int> result = [];
|
||||||
|
const minNaluPerChunk = 22; // 每个数据块包含的最小NALU数量
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
value = byteData[i++];
|
||||||
|
// finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
|
||||||
|
switch (state) {
|
||||||
|
case 0:
|
||||||
|
if (value == 0) {
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (value == 0) {
|
||||||
|
state = 2;
|
||||||
|
} else {
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
if (value == 0) {
|
||||||
|
state = 3;
|
||||||
|
} else if (value == 1 && i < length) {
|
||||||
|
if (lastIndex > 0) {
|
||||||
|
naluCount++;
|
||||||
|
}
|
||||||
|
if (naluCount >= minNaluPerChunk) {
|
||||||
|
result.add(lastIndex - state - 1);
|
||||||
|
naluCount = 0;
|
||||||
|
}
|
||||||
|
state = 0;
|
||||||
|
lastIndex = i;
|
||||||
|
} else {
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (naluCount > 0) {
|
||||||
|
result.add(lastIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止发送通话数据
|
||||||
|
void stopTalkDataTimer() {
|
||||||
|
talkDataTimer?.cancel();
|
||||||
|
talkDataTimer = null; // 清除定时器引用
|
||||||
|
}
|
||||||
|
|
||||||
// 停止发送通话期望数据
|
// 停止发送通话期望数据
|
||||||
void stopTalkExpectMessageTimer() {
|
void stopTalkExpectMessageTimer() {
|
||||||
talkExpectTimer?.cancel();
|
talkExpectTimer?.cancel();
|
||||||
@ -781,6 +901,7 @@ class StartChartManage {
|
|||||||
stopTalkPingMessageTimer();
|
stopTalkPingMessageTimer();
|
||||||
stopHeartbeat();
|
stopHeartbeat();
|
||||||
stopReStartOnlineStartChartServer();
|
stopReStartOnlineStartChartServer();
|
||||||
|
stopTalkDataTimer();
|
||||||
await Storage.removerRelayInfo();
|
await Storage.removerRelayInfo();
|
||||||
await Storage.removerStarChartRegisterNodeInfo();
|
await Storage.removerStarChartRegisterNodeInfo();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user