diff --git a/assets/demo.h264 b/assets/demo.h264
new file mode 100644
index 00000000..eb04d7b3
Binary files /dev/null and b/assets/demo.h264 differ
diff --git a/assets/html/h264.html b/assets/html/h264.html
new file mode 100644
index 00000000..431b5c38
--- /dev/null
+++ b/assets/html/h264.html
@@ -0,0 +1,2684 @@
+
+
+
+
+
+ play
+
+
+
+
+
+
+
+
+
diff --git a/assets/talk.h264 b/assets/talk.h264
new file mode 100644
index 00000000..5a1f2802
Binary files /dev/null and b/assets/talk.h264 differ
diff --git a/lib/appRouters.dart b/lib/appRouters.dart
index 12fbb302..f4bfb8ef 100755
--- a/lib/appRouters.dart
+++ b/lib/appRouters.dart
@@ -2,6 +2,7 @@ import 'package:get/get.dart';
import 'package:star_lock/common/safetyVerification/safetyVerification_binding.dart';
import 'package:star_lock/flavors.dart';
import 'package:star_lock/login/forgetPassword/starLock_forgetPassword_xhj_page.dart';
+import 'package:star_lock/login/login/JMuxerApp.dart';
import 'package:star_lock/login/login/starLock_login_xhj_page.dart';
import 'package:star_lock/login/register/starLock_register_binding.dart';
import 'package:star_lock/login/register/starLock_register_xhj_page.dart';
@@ -397,6 +398,7 @@ abstract class Routers {
static const String ownedKeyListPage = '/ownedKeyListPage'; //拥有的钥匙
static const String starLockLoginPage = '/StarLockLoginPage'; // 登录
+ static const String LocalHtmlPage = '/LocalHtmlPage'; // LocalHtmlPage
static const String starLockRegisterPage = '/StarLockRegisterPage'; // 注册
static const String starLockForgetPasswordPage =
'/StarLockForgetPasswordPage'; // 忘记密码
@@ -638,6 +640,9 @@ abstract class AppRouters {
page: () => F.sw(
skyCall: () => const StarLockLoginPage(),
xhjCall: () => const StarLockLoginXHJPage()),
+ ),GetPage(
+ name: Routers.LocalHtmlPage,
+ page: () => LocalHtmlPage(),
),
GetPage(
name: Routers.starLockRegisterPage,
diff --git a/lib/login/login/JMuxerApp.dart b/lib/login/login/JMuxerApp.dart
new file mode 100644
index 00000000..61d809ee
--- /dev/null
+++ b/lib/login/login/JMuxerApp.dart
@@ -0,0 +1,137 @@
+import 'dart:convert';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart' show ByteData, Uint8List, rootBundle;
+import 'package:webview_flutter/webview_flutter.dart';
+
+class LocalHtmlPage extends StatefulWidget {
+ @override
+ _LocalHtmlPageState createState() => _LocalHtmlPageState();
+}
+
+class _LocalHtmlPageState extends State {
+ late final WebViewController _controller;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..enableZoom(false)
+ ..addJavaScriptChannel(
+ 'Flutter',
+ onMessageReceived: (message) {
+ print("来自 HTML 的消息: ${message.message}");
+ },
+ );
+
+ // 加载本地 HTML
+ _loadLocalHtml();
+ _sendFramesToHtml();
+ }
+
+ void _sendFramesToHtml() async {
+ // 读取 assets/demo.h264 文件
+ final ByteData data = await rootBundle.load('assets/talk.h264');
+ final List byteData = data.buffer.asUint8List();
+
+ int offset = 0;
+ int frameSize = 0;
+
+ // 根据 H.264 数据的帧结构来逐帧读取并发送
+ while (offset < byteData.length) {
+ // 获取每一帧的大小
+ frameSize = getFrameSize(byteData, offset);
+
+ if (frameSize == 0) {
+ print("No more frames or error in frame size calculation.");
+ break; // 如果没有更多的帧,或者无法计算帧的大小,则退出循环
+ }
+
+ // 提取当前帧数据
+ List frameData = byteData.sublist(offset, offset + frameSize);
+
+ // 将当前帧数据发送到 WebView 中的 feedDataFromFlutter 函数
+ String jsCode = "feedDataFromFlutter(${frameData});";
+ await _controller.runJavaScript(jsCode);
+
+ // 更新偏移量,继续发送下一个帧
+ offset += frameSize;
+
+ // 控制帧率,模拟每秒 22 帧播放(或者根据你的视频帧率调整)
+ await Future.delayed(Duration(milliseconds: (1000 / 22).toInt()));
+ }
+ }
+
+ int getFrameSize(List data, int offset) {
+ // 查找第一个 Start Code (0x000001 或 0x00000001)
+ const List startCode1 = [0, 0, 0, 1]; // Start Code: 0x000001
+ const List startCode2 = [
+ 0,
+ 0,
+ 1
+ ]; // Start Code: 0x000001 (0x00000001 version)
+
+ // 查找下一个 Start Code 的位置
+ int startCodeStart = -1;
+ int startCodeEnd = -1;
+ for (int i = offset; i < data.length - 3; i++) {
+ // 判断是否匹配 Start Code (0x000001 或 0x00000001)
+ if (_matchesStartCode(data, i, startCode1)) {
+ startCodeStart = i;
+ startCodeEnd = i + startCode1.length;
+ break;
+ } else if (_matchesStartCode(data, i, startCode2)) {
+ startCodeStart = i;
+ startCodeEnd = i + startCode2.length;
+ break;
+ }
+ }
+
+ // 如果找不到有效的 Start Code,返回 0(结束循环)
+ if (startCodeStart == -1) return 0;
+
+ // 查找下一个 Start Code 的位置
+ int nextStartCodeStart = -1;
+ for (int i = startCodeEnd; i < data.length - 3; i++) {
+ if (_matchesStartCode(data, i, startCode1) ||
+ _matchesStartCode(data, i, startCode2)) {
+ nextStartCodeStart = i;
+ break;
+ }
+ }
+
+ // 如果找不到下一个 Start Code,说明当前是最后一个 NAL 单元
+ if (nextStartCodeStart == -1) {
+ return data.length - startCodeStart;
+ }
+
+ // 计算并返回当前 NAL 单元的大小
+ return nextStartCodeStart - startCodeStart;
+ }
+
+// 检查数据是否匹配 Start Code
+ bool _matchesStartCode(List data, int index, List startCode) {
+ for (int i = 0; i < startCode.length; i++) {
+ if (data[index + i] != startCode[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ Future _loadLocalHtml() async {
+ final String fileHtmlContent =
+ await rootBundle.loadString('assets/html/h264.html');
+ _controller.loadHtmlString(fileHtmlContent);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("加载本地 HTML"),
+ ),
+ body: WebViewWidget(controller: _controller),
+ );
+ }
+}
diff --git a/lib/login/login/starLock_login_page.dart b/lib/login/login/starLock_login_page.dart
index b9e9acaf..d32a0609 100755
--- a/lib/login/login/starLock_login_page.dart
+++ b/lib/login/login/starLock_login_page.dart
@@ -238,18 +238,23 @@ class _StarLockLoginPageState extends State {
SubmitBtn(
btnName: '发送回声测试消息',
onClick: () {
- StartChartManage().sendEchoMessage(
- ToPeerId: '3phX8Ng2cZHz5NtP8xAf6nYy2z1BYytoejgjoHrWMGhH');
+ StartChartManage().sendEchoMessage();
},
),
SubmitBtn(
btnName: '发送对讲请求1',
onClick: () {
StartChartManage().sendCallRequestMessage(
- ToPeerId: 'CqKJUADeUZsHrSD4SEVnPBr73fbsHNUXCEq9kyCtE3wp',
+ ToPeerId: '3QvLvD3cBnM358bCdccUpGBBYicepEskPXEsh8KvTvY2',
);
},
),
+ SubmitBtn(
+ btnName: '跳转至html',
+ onClick: () {
+ Get.toNamed(Routers.LocalHtmlPage);
+ },
+ ),
SizedBox(height: 50.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart
index c2f4aad4..434dbb8b 100644
--- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart
+++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart
@@ -36,7 +36,6 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle
stopRingtone();
// 设置状态为接听中
talkStatus.setDuringCall();
- stopRingtone();
}
}
diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart
index fb1090ac..28376433 100644
--- a/lib/talk/startChart/start_chart_manage.dart
+++ b/lib/talk/startChart/start_chart_manage.dart
@@ -176,6 +176,7 @@ class StartChartManage {
// 发送上线消息
Future _sendOnlineMessage() async {
+ _log(text: '发送上线消息');
if (isOnlineStartChartServer) {
_log(text: '星图已上线,请勿重复发送上线消息');
return;
@@ -190,6 +191,10 @@ class StartChartManage {
// 发送对讲请求消息
Future sendCallRequestMessage({required String ToPeerId}) async {
+ if (talkStatus.status == TalkStatus.duringCall) {
+ _log(text: '已经在通话中,请勿重复发送对讲请求');
+ return;
+ }
// 组装上线消息
final message = MessageCommand.talkRequestMessage(
FromPeerId: FromPeerId,
@@ -220,12 +225,13 @@ class StartChartManage {
}
// 发送回声测试消息
- void sendEchoMessage({required String ToPeerId}) async {
+ void sendEchoMessage() async {
final message = MessageCommand.echoMessage(
- ToPeerId: ToPeerId,
+ ToPeerId: echoPeerId,
FromPeerId: FromPeerId,
);
await _sendMessage(message: message);
+ _log(text: '发送回声测试消息');
}
// 发送网关初始化消息
@@ -704,7 +710,6 @@ class StartChartManage {
seconds: talkPingIntervalTime,
),
(Timer timer) async {
- // 重新发送上线消息
await sendTalkPingMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
diff --git a/lib/talk/startChart/start_chart_talk_status.dart b/lib/talk/startChart/start_chart_talk_status.dart
index 31cda259..504e6aae 100644
--- a/lib/talk/startChart/start_chart_talk_status.dart
+++ b/lib/talk/startChart/start_chart_talk_status.dart
@@ -4,6 +4,7 @@ import 'package:star_lock/talk/startChart/events/talk_status_change_event.dart';
enum TalkStatus {
waitingAnswer, // 等待接听
+ waitingData, // 等待数据
duringCall, // 通话中
rejected, // 被拒绝
uninitialized, // 未初始化
diff --git a/pubspec.yaml b/pubspec.yaml
index 3109fdae..e844e3e2 100755
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -298,6 +298,9 @@ flutter:
- images/mine/
- images/lockType/
- assets/
+ - assets/html/h264.html
+ - assets/demo.h264
+ - assets/talk.h264
- lan/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware