style:整理代码
This commit is contained in:
parent
b7da72a1ce
commit
23b8257779
BIN
assets/demo.h264
BIN
assets/demo.h264
Binary file not shown.
@ -2,7 +2,6 @@ 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';
|
||||
@ -398,7 +397,6 @@ 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'; // 忘记密码
|
||||
@ -640,9 +638,6 @@ abstract class AppRouters {
|
||||
page: () => F.sw(
|
||||
skyCall: () => const StarLockLoginPage(),
|
||||
xhjCall: () => const StarLockLoginXHJPage()),
|
||||
),GetPage<dynamic>(
|
||||
name: Routers.LocalHtmlPage,
|
||||
page: () => LocalHtmlPage(),
|
||||
),
|
||||
GetPage<dynamic>(
|
||||
name: Routers.starLockRegisterPage,
|
||||
|
||||
@ -2,22 +2,35 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show ByteData, Uint8List, rootBundle;
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:star_lock/app_settings/app_colors.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart';
|
||||
import 'package:star_lock/tools/titleAppBar.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class LocalHtmlPage extends StatefulWidget {
|
||||
class H264WebView extends StatefulWidget {
|
||||
@override
|
||||
_LocalHtmlPageState createState() => _LocalHtmlPageState();
|
||||
_H264WebViewState createState() => _H264WebViewState();
|
||||
}
|
||||
|
||||
class _LocalHtmlPageState extends State<LocalHtmlPage> {
|
||||
class _H264WebViewState extends State<H264WebView> {
|
||||
late final WebViewController _controller;
|
||||
Timer? timer;
|
||||
Timer? _sendTimer;
|
||||
|
||||
// 私有缓冲区,外部无法直接访问
|
||||
final List<int> _buffer = [];
|
||||
|
||||
// 发送数据至html文件间隔时间
|
||||
final int sendDataToHtmlIntervalTime = 820;
|
||||
|
||||
// 通话数据流的单例流数据处理类
|
||||
final TalkDataRepository talkDataRepository = TalkDataRepository.instance;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..enableZoom(false)
|
||||
@ -30,135 +43,52 @@ class _LocalHtmlPageState extends State<LocalHtmlPage> {
|
||||
|
||||
// 加载本地 HTML
|
||||
_loadLocalHtml();
|
||||
simulateStreamFromAsset();
|
||||
_sendFramesToHtml();
|
||||
}
|
||||
|
||||
void _sendFramesToHtml() async {
|
||||
// 读取 assets/demo.h264 文件
|
||||
void simulateStreamFromAsset() async {
|
||||
// 读取 assets 文件
|
||||
final ByteData data = await rootBundle.load('assets/talk.h264');
|
||||
final List<int> byteData = data.buffer.asUint8List();
|
||||
|
||||
// 提取 NALU 边界并生成 chunks
|
||||
final List<int> chunks = extractChunks(byteData);
|
||||
final int total = chunks.length;
|
||||
|
||||
int current = 0;
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
List<int> buffer = []; // 用于累积帧数据的缓冲区
|
||||
|
||||
|
||||
// 创建一个定时器,每800毫秒触发一次
|
||||
timer ??= Timer.periodic(Duration(milliseconds: 800), (timer) async {
|
||||
if (current >= total) {
|
||||
current = 0;
|
||||
final List<int> chunks = extractChunks(byteData);
|
||||
// 定时器控制发送数据块的节奏
|
||||
timer ??= Timer.periodic(Duration(milliseconds: 10), (timer) {
|
||||
if (current >= chunks.length) {
|
||||
print('数据已经发完,重新进行发送');
|
||||
start = 0;
|
||||
print("All frames sent. Restarting from the beginning.");
|
||||
end = 0;
|
||||
current = 0;
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// 提取 NALU 边界并生成 chunks
|
||||
end = chunks[current];
|
||||
current++;
|
||||
|
||||
// 累积当前帧数据到缓冲区
|
||||
List<int> frameData = byteData.sublist(start, end);
|
||||
buffer.addAll(frameData);
|
||||
if (frameData.length == 0) timer.cancel();
|
||||
talkDataRepository.addTalkData(frameData);
|
||||
start = end;
|
||||
|
||||
// 发送累积的数据
|
||||
if (buffer.isNotEmpty) {
|
||||
await _sendBufferedData(buffer);
|
||||
buffer.clear(); // 清空缓冲区
|
||||
}
|
||||
|
||||
// 打印进度信息(可选)
|
||||
if (current % 50 == 0) {
|
||||
print("I am serving, no problem!");
|
||||
}
|
||||
if (current == 0) {
|
||||
print("Started from first chunk...");
|
||||
}
|
||||
|
||||
// 如果所有帧都已处理完毕,取消定时器
|
||||
if (current >= total) {
|
||||
timer.cancel();
|
||||
print('All frames sent. Stopping timer...');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int getFrameSize(List<int> data, int offset) {
|
||||
// 查找第一个 Start Code (0x000001 或 0x00000001)
|
||||
const List<int> startCode1 = [0, 0, 0, 1]; // Start Code: 0x000001
|
||||
const List<int> 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;
|
||||
void _sendFramesToHtml() async {
|
||||
// 接收到流数据,保存到缓冲区
|
||||
talkDataRepository.talkDataStream.listen((event) async {
|
||||
_buffer.addAll(event);
|
||||
});
|
||||
// 缓冲800ms的数据,定时发送
|
||||
_sendTimer ??= Timer.periodic(
|
||||
Duration(milliseconds: sendDataToHtmlIntervalTime), (timer) async {
|
||||
// 发送累积的数据
|
||||
if (_buffer.isNotEmpty) {
|
||||
await _sendBufferedData(_buffer);
|
||||
_buffer.clear(); // 清空缓冲区
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找不到有效的 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<int> data, int index, List<int> startCode) {
|
||||
for (int i = 0; i < startCode.length; i++) {
|
||||
if (data[index + i] != startCode[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _loadLocalHtml() async {
|
||||
final String fileHtmlContent =
|
||||
await rootBundle.loadString('assets/html/h264.html');
|
||||
_controller.loadHtmlString(fileHtmlContent);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: WebViewWidget(controller: _controller),
|
||||
);
|
||||
}
|
||||
|
||||
_sendBufferedData(List<int> buffer) async {
|
||||
String jsCode = "feedDataFromFlutter(${buffer});";
|
||||
await _controller.runJavaScript(jsCode);
|
||||
});
|
||||
}
|
||||
|
||||
// 提取 NALU 边界并生成 chunks
|
||||
@ -170,7 +100,7 @@ class _LocalHtmlPageState extends State<LocalHtmlPage> {
|
||||
int state = 0;
|
||||
int lastIndex = 0;
|
||||
List<int> result = [];
|
||||
const minNaluPerChunk = 30; // 每个数据块包含的最小NALU数量
|
||||
const minNaluPerChunk = 22; // 每个数据块包含的最小NALU数量
|
||||
|
||||
while (i < length) {
|
||||
value = byteData[i++];
|
||||
@ -215,13 +145,36 @@ class _LocalHtmlPageState extends State<LocalHtmlPage> {
|
||||
result.add(lastIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 加载html文件
|
||||
Future<void> _loadLocalHtml() async {
|
||||
final String fileHtmlContent =
|
||||
await rootBundle.loadString('assets/html/h264.html');
|
||||
_controller.loadHtmlString(fileHtmlContent);
|
||||
}
|
||||
|
||||
// 发送数据给js处理
|
||||
_sendBufferedData(List<int> buffer) async {
|
||||
String jsCode = "feedDataFromFlutter(${buffer});";
|
||||
await _controller.runJavaScript(jsCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WebViewWidget(controller: _controller);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timer?.cancel();
|
||||
timer=null;
|
||||
timer = null;
|
||||
_sendTimer?.cancel();
|
||||
timer = null;
|
||||
// talkDataRepository.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user