diff --git a/assets/html/h264.html b/assets/html/h264.html index 97143565..63303e3c 100644 --- a/assets/html/h264.html +++ b/assets/html/h264.html @@ -56,7 +56,7 @@ }, flushingTime: 0, // 禁用自动刷新 clearBuffer: false, // 保留解码缓存 - maxBufferLength: 2, + fps:20, onReady: () => { console.log('播放器初始化完成'); // 通知Flutter端准备就绪 diff --git a/lib/main/lockDetail/lockDetail/lockDetail_state.dart b/lib/main/lockDetail/lockDetail/lockDetail_state.dart index 5e168524..760f1289 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_state.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_state.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart'; import '../../../blue/io_reply.dart'; import '../../lockMian/entity/lockListInfo_entity.dart'; @@ -9,7 +10,7 @@ import '../../lockMian/entity/lockListInfo_entity.dart'; class LockDetailState { Rx keyInfos = LockListInfoItemEntity().obs; - + final Rx lockSetInfoData = LockSetInfoData().obs; late StreamSubscription replySubscription; StreamSubscription? lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent; StreamSubscription? LockSetChangeSetRefreshLockDetailWithTypeSubscription; diff --git a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart index 763d25ca..c350b336 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart @@ -8,6 +8,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:network_info_plus/network_info_plus.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:star_lock/appRouters.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/blue/io_gateway/io_gateway_configuringWifi.dart'; import 'package:star_lock/blue/io_gateway/io_gateway_getStatus.dart'; @@ -68,7 +69,12 @@ class ConfiguringWifiLogic extends BaseGetXController { secretKey: secretKey, peerId: peerId, ); - Get.close(2); + + if (state.pageName.value == 'lockSet') { + Get.close(2); + } else { + Get.offAllNamed(Routers.starLockMain); + } }); } } diff --git a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_page.dart b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_page.dart index 55a6a02c..89ea1ee1 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_page.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_page.dart @@ -47,7 +47,8 @@ class _ConfiguringWifiPageState extends State SubmitBtn( btnName: '确定'.tr, onClick: () { - logic.senderConfiguringWifiAction(); + FocusScope.of(context).requestFocus(FocusNode()); + logic.senderConfiguringWifiAction(); }, ), ], diff --git a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_state.dart b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_state.dart index 33707851..062ebf51 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_state.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_state.dart @@ -9,6 +9,7 @@ class ConfiguringWifiState{ ConfiguringWifiState() { var map = Get.arguments; lockSetInfoData.value = map['lockSetInfoData']; + pageName.value = map['pageName']; lockBasicInfo.value = lockSetInfoData.value.lockBasicInfo!; if (map['wifiName'] != null) { wifiName.value = map['wifiName']; @@ -20,6 +21,7 @@ class ConfiguringWifiState{ Rx lockBasicInfo = LockBasicInfo().obs; RxString wifiName = ''.obs; + RxString pageName = ''.obs; RxBool ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 RxInt sureBtnState = 0.obs;// 0普通状态(可用) 1连接中(不可用) diff --git a/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_page.dart b/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_page.dart index 937c2e8a..bd976e9d 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_page.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_page.dart @@ -24,11 +24,18 @@ class _WifiListPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - appBar: TitleAppBar( + return WillPopScope( + onWillPop: () async { + if (state.pageName.value == 'lockSet') { + return true; + } + return false; + }, + child: Scaffold( + backgroundColor: Colors.white, + appBar: TitleAppBar( barTitle: 'WIFI列表'.tr, - haveBack: true, + haveBack: state.pageName.value == 'lockSet', actionsList: [ TextButton( child: Text( @@ -38,45 +45,50 @@ class _WifiListPageState extends State { onPressed: logic.senderGetWifiListWifiAction, ), ], - backgroundColor: AppColors.mainColor), - body: Column( - children: [ - Expanded( - child: Obx(() => state.wifiNameDataList.value.isNotEmpty - ? ListView.builder( - itemCount: state.wifiNameDataList.value.length, - itemBuilder: (BuildContext c, int index) { - Map wifiNameStr = state.wifiNameDataList.value[index]; - return _messageListItem( - wifiNameStr['wifiName'], wifiNameStr['rssi'], () { - Get.toNamed(Routers.configuringWifiPage, arguments: { - 'lockSetInfoData': state.lockSetInfoData.value, - 'wifiName': wifiNameStr['wifiName'], + backgroundColor: AppColors.mainColor, + ), + body: Column( + children: [ + Expanded( + child: Obx(() => state.wifiNameDataList.value.isNotEmpty + ? ListView.builder( + itemCount: state.wifiNameDataList.value.length, + itemBuilder: (BuildContext c, int index) { + Map wifiNameStr = state.wifiNameDataList.value[index]; + return _messageListItem( + wifiNameStr['wifiName'], wifiNameStr['rssi'], () { + Get.toNamed(Routers.configuringWifiPage, + arguments: { + 'lockSetInfoData': + state.lockSetInfoData.value, + 'wifiName': wifiNameStr['wifiName'], + 'pageName': state.pageName.value, + }); }); + }) + : NoData( + noDataHeight: 1.sh - + ScreenUtil().statusBarHeight - + ScreenUtil().bottomBarHeight - + 64.h)), + ), + SubmitBtn( + btnName: '手动配网'.tr, + fontSize: 28.sp, + borderRadius: 20.w, + padding: EdgeInsets.only(top: 25.w, bottom: 25.w), + onClick: () { + Get.toNamed(Routers.configuringWifiPage, + arguments: { + 'lockSetInfoData': state.lockSetInfoData.value }); - }) - : NoData( - noDataHeight: 1.sh - - ScreenUtil().statusBarHeight - - ScreenUtil().bottomBarHeight - - 64.h)), - ), - SubmitBtn( - btnName: '手动配网'.tr, - fontSize: 28.sp, - borderRadius: 20.w, - padding: EdgeInsets.only(top: 25.w, bottom: 25.w), - onClick: () { - Get.toNamed(Routers.configuringWifiPage, - arguments: { - 'lockSetInfoData': state.lockSetInfoData.value - }); - }), - SizedBox( - height: 64.h, - ) - ], - )); + }), + SizedBox( + height: 64.h, + ) + ], + )), + ); } Widget _messageListItem(String wifiName, String rssi, Function() action) { diff --git a/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_state.dart b/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_state.dart index ca058136..d640b496 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_state.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/wifiList/wifiList_state.dart @@ -1,18 +1,22 @@ - import 'package:get/get.dart'; import '../../lockSet/lockSetInfo_entity.dart'; -class WifiListState{// 0普通状态(可用) 1连接中(不可用) +class WifiListState { + // 0普通状态(可用) 1连接中(不可用) WifiListState() { final map = Get.arguments; lockSetInfoData.value = map['lockSetInfoData']; + pageName.value = map['pageName']; lockBasicInfo.value = lockSetInfoData.value.lockBasicInfo!; } - final RxList> wifiNameDataList = >[].obs; + + final RxList> wifiNameDataList = + >[].obs; Rx lockSetInfoData = LockSetInfoData().obs; Rx lockBasicInfo = LockBasicInfo().obs; RxBool ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 RxInt sureBtnState = 0.obs; -} \ No newline at end of file + RxString pageName = ''.obs; +} diff --git a/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart b/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart index dbea2c40..a0011308 100755 --- a/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart +++ b/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart @@ -595,10 +595,10 @@ class _LockSetPageState extends State isHaveLine: true, isHaveDirection: true, action: () { - Get.toNamed(Routers.wifiListPage, - arguments: { - 'lockSetInfoData': state.lockSetInfoData.value - }); + Get.toNamed(Routers.wifiListPage, arguments: { + 'lockSetInfoData': state.lockSetInfoData.value, + 'pageName': 'lockSet' + }); // Get.toNamed(Routers.configuringWifiPage, arguments: { // 'lockSetInfoData': state.lockSetInfoData.value // }); diff --git a/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart b/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart index 619d0539..ca1259cb 100755 --- a/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart +++ b/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart @@ -170,7 +170,7 @@ class _VideoLogDetailPageState extends State { _buildImageItem(RecordListData recordData) { return RotatedBox( - quarterTurns: 1, + quarterTurns: -1, child: Image.network( recordData.imagesUrl!, fit: BoxFit.cover, diff --git a/lib/main/lockMian/entity/lockListInfo_entity.dart b/lib/main/lockMian/entity/lockListInfo_entity.dart index b3b46464..81838ba6 100755 --- a/lib/main/lockMian/entity/lockListInfo_entity.dart +++ b/lib/main/lockMian/entity/lockListInfo_entity.dart @@ -345,6 +345,8 @@ class LockFeature { this.isSupportCatEye, this.isSupportBackupBattery, this.isNoSupportedBlueBroadcast, + this.wifiLockType, + this.wifi, }); LockFeature.fromJson(Map json) { @@ -360,6 +362,8 @@ class LockFeature { isSupportCatEye = json['isSupportCatEye']; isSupportBackupBattery = json['isSupportBackupBattery']; isNoSupportedBlueBroadcast = json['isNoSupportedBlueBroadcast']; + wifiLockType = json['wifiLockType']; + wifi = json['wifi']; } int? password; @@ -374,6 +378,8 @@ class LockFeature { int? isSupportCatEye; int? isSupportBackupBattery; int? isNoSupportedBlueBroadcast; + int? wifiLockType; + int? wifi; Map toJson() { final Map data = {}; @@ -389,6 +395,8 @@ class LockFeature { data['isSupportCatEye'] = isSupportCatEye; data['isSupportBackupBattery'] = isSupportBackupBattery; data['isNoSupportedBlueBroadcast'] = isNoSupportedBlueBroadcast; + data['wifiLockType'] = wifiLockType; + data['wifi'] = wifi; return data; } } diff --git a/lib/mine/addLock/saveLock/saveLock_logic.dart b/lib/mine/addLock/saveLock/saveLock_logic.dart index 7e8ae377..ce1cc799 100755 --- a/lib/mine/addLock/saveLock/saveLock_logic.dart +++ b/lib/mine/addLock/saveLock/saveLock_logic.dart @@ -1,11 +1,12 @@ - import 'dart:async'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:get/get.dart'; import 'package:star_lock/apm/apm_helper.dart'; +import 'package:star_lock/appRouters.dart'; import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_logic.dart'; +import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart'; import 'package:star_lock/mine/addLock/saveLock/entity/SaveLockEntity.dart'; import '../../../app_settings/app_settings.dart'; @@ -408,10 +409,11 @@ class SaveLockLogic extends BaseGetXController { final String getMobile = (await Storage.getMobile())!; ApmHelper.instance.trackEvent('save_lock_result', { - 'lock_name':BlueManage().connectDeviceName, - 'account':getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, - 'date':DateTool().getNowDateWithType(1), - 'save_lock_result':'成功', + 'lock_name': BlueManage().connectDeviceName, + 'account': + getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, + 'date': DateTool().getNowDateWithType(1), + 'save_lock_result': '成功', }); backAction(); // await senderCustomPasswords(); @@ -424,10 +426,11 @@ class SaveLockLogic extends BaseGetXController { final String getMobile = (await Storage.getMobile())!; ApmHelper.instance.trackEvent('save_lock_result', { - 'lock_name':BlueManage().connectDeviceName, - 'account':getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, - 'date':DateTool().getNowDateWithType(1), - 'save_lock_result':'${entity.errorCode}--${entity.errorMsg}', + 'lock_name': BlueManage().connectDeviceName, + 'account': + getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, + 'date': DateTool().getNowDateWithType(1), + 'save_lock_result': '${entity.errorCode}--${entity.errorMsg}', }); } } @@ -482,20 +485,52 @@ class SaveLockLogic extends BaseGetXController { // ); // } - void backAction() { + void backAction() async { eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true)); BlueManage().disconnect(); - Future.delayed(const Duration(seconds: 1), () { - Get.close(state.isFromMap == 1 ? (CommonDataManage().seletLockType == 0 ? 4 : 5) : (CommonDataManage().seletLockType == 0 ? 5 : 6)); - }); - //刚刚配对完,需要对开锁页锁死 2 秒 - Future.delayed(const Duration(milliseconds: 200), () { - if (Get.isRegistered()) { - Get.find() - .functionBlocker - .countdownProhibited(duration: const Duration(seconds: 2)); + + // 查询锁设置信息 + final LockSetInfoEntity entity = + await ApiRepository.to.getLockSettingInfoDataIsNotLoadingIcon( + lockId: state.lockId.toString(), + ); + if (entity.errorCode!.codeIsSuccessful) { + state.lockSetInfoData.value = entity.data!; + if (state.lockSetInfoData.value.lockFeature?.wifi == 1) { + // await Future.delayed(const Duration(seconds: 1), () {c + // Get.close(state.isFromMap == 1 + // ? (CommonDataManage().seletLockType == 0 ? 4 : 5) + // : (CommonDataManage().seletLockType == 0 ? 5 : 6)); + // }); + // //刚刚配对完,需要对开锁页锁死 2 秒 + // await Future.delayed(const Duration(milliseconds: 200), () { + // if (Get.isRegistered()) { + // Get.find() + // .functionBlocker + // .countdownProhibited(duration: const Duration(seconds: 2)); + // } + // }); + // 如果是wifi锁,需要配置WIFI + Get.toNamed(Routers.wifiListPage, arguments: { + 'lockSetInfoData': state.lockSetInfoData.value, + 'pageName': 'saveLock' + }); + } else { + Future.delayed(const Duration(seconds: 1), () { + Get.close(state.isFromMap == 1 + ? (CommonDataManage().seletLockType == 0 ? 4 : 5) + : (CommonDataManage().seletLockType == 0 ? 5 : 6)); + }); + //刚刚配对完,需要对开锁页锁死 2 秒 + Future.delayed(const Duration(milliseconds: 200), () { + if (Get.isRegistered()) { + Get.find() + .functionBlocker + .countdownProhibited(duration: const Duration(seconds: 2)); + } + }); } - }); + } } @override diff --git a/lib/mine/addLock/saveLock/saveLock_state.dart b/lib/mine/addLock/saveLock/saveLock_state.dart index 564fe3d3..1a14896c 100755 --- a/lib/mine/addLock/saveLock/saveLock_state.dart +++ b/lib/mine/addLock/saveLock/saveLock_state.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart'; import '../../../blue/blue_manage.dart'; @@ -25,7 +26,7 @@ class SaveLockState { RxString aliName = ''.obs; RxInt pwdTimestamp = 0.obs; RxMap addressInfo = {}.obs; - + final Rx lockSetInfoData = LockSetInfoData().obs; TextEditingController aliNameController = TextEditingController(); FocusNode focusNode = FocusNode(); diff --git a/lib/mine/mineSet/mineSet/mineSet_page.dart b/lib/mine/mineSet/mineSet/mineSet_page.dart index cf62b035..7f639d2b 100755 --- a/lib/mine/mineSet/mineSet/mineSet_page.dart +++ b/lib/mine/mineSet/mineSet/mineSet_page.dart @@ -292,21 +292,20 @@ class _MineSetPageState extends State SizedBox( height: 10.h, ), - Obx(() { - // AppLog.log('state.currentLanguageName: ${state.currentLanguageName} state.currentLanguage.value: ${state.currentLanguage.value}'); - return CommonItem( - leftTitel: '多语言'.tr, - rightTitle: state.currentLanguageName, - isHaveLine: true, - isHaveDirection: true, - action: () async { - // Get.toNamed(Routers.mineMultiLanguagePage); - await Get.toNamed(Routers.mineMultiLanguagePage)!.then((value) { - state.currentLanguage.value = value['currentLanguage']; - setState(() {}); - }); + + // AppLog.log('state.currentLanguageName: ${state.currentLanguageName} state.currentLanguage.value: ${state.currentLanguage.value}'); + CommonItem( + leftTitel: '多语言'.tr, + rightTitle: state.currentLanguageName, + isHaveLine: true, + isHaveDirection: true, + action: () async { + // Get.toNamed(Routers.mineMultiLanguagePage); + await Get.toNamed(Routers.mineMultiLanguagePage)!.then((value) { + state.currentLanguage.value = value['currentLanguage']; + setState(() {}); }); - }), + }), /* 2024-01-12 会议确定去掉“锁屏” by DaisyWu Obx(() => CommonItem( leftTitel: TranslationLoader.lanKeys!.lockScreen!.tr, diff --git a/lib/network/api_provider.dart b/lib/network/api_provider.dart index c69dce39..2139c1af 100755 --- a/lib/network/api_provider.dart +++ b/lib/network/api_provider.dart @@ -423,6 +423,15 @@ class ApiProvider extends BaseProvider { 'lockId': lockId, })); + // 获取所有锁设置信息 + Future getLockSettingInfoDataIsNotLoadingIcon(String lockId) => + post( + getLockSettingURL.toUrl, + jsonEncode({ + 'lockId': lockId, + }), + isUnShowLoading: true); + // 删除锁 Future deletLockInfo(int lockId) => post( deletLockURL.toUrl, diff --git a/lib/network/api_repository.dart b/lib/network/api_repository.dart index 5b0b5cef..6ef31dcd 100755 --- a/lib/network/api_repository.dart +++ b/lib/network/api_repository.dart @@ -486,6 +486,12 @@ class ApiRepository { final res = await apiProvider.getLockSettingInfoData(lockId); return LockSetInfoEntity.fromJson(res.body); } + // 获取所有锁设置信息(不显示加载框) + Future getLockSettingInfoDataIsNotLoadingIcon( + {required String lockId}) async { + final res = await apiProvider.getLockSettingInfoDataIsNotLoadingIcon(lockId); + return LockSetInfoEntity.fromJson(res.body); + } // 删除锁 Future deletOwnerLockData({required int lockId}) async { diff --git a/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart index a95b1c78..cb00a0d3 100644 --- a/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart @@ -160,6 +160,8 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle final TalkDataH264Frame talkDataH264Frame = TalkDataH264Frame(); talkDataH264Frame.mergeFromBuffer(talkData.content); frameHandler.handleFrame(talkDataH264Frame); + AppLog.log( + "帧:${talkDataH264Frame.frameType},帧序号:${talkDataH264Frame.frameSeq},对应I帧序号:${talkDataH264Frame.frameSeqI}"); } /// 处理图片数据 diff --git a/lib/talk/starChart/handle/other/h264_frame_handler.dart b/lib/talk/starChart/handle/other/h264_frame_handler.dart index 5eaa27df..2b4ea1ee 100644 --- a/lib/talk/starChart/handle/other/h264_frame_handler.dart +++ b/lib/talk/starChart/handle/other/h264_frame_handler.dart @@ -1,52 +1,51 @@ +import 'dart:collection'; +import 'dart:typed_data'; + import 'package:star_lock/app_settings/app_settings.dart'; import '../../proto/talk_data_h264_frame.pb.dart'; class H264FrameHandler { - final Map _frameBuffer = {}; + final LinkedHashMap _frameBuffer = LinkedHashMap(); final void Function(List frameData) onCompleteFrame; - int _lastProcessedSeq = -1; + + final LinkedHashMap _frameTypeIndex = LinkedHashMap(); H264FrameHandler({required this.onCompleteFrame}); void handleFrame(TalkDataH264Frame frame) { // 存储帧 _frameBuffer[frame.frameSeq] = frame; + _frameTypeIndex[frame.frameSeq] = frame.frameType; // 检查是否可以组装完整的 GOP (Group of Pictures) _tryAssembleFrames(frame.frameSeq); } void _tryAssembleFrames(int currentSeq) { - // 找到连续的帧序列 - final List sortedSeqs = _frameBuffer.keys.toList()..sort(); final List framesToProcess = []; + int? startFrameSeq; // 从当前帧开始向前找到最近的 I 帧或 P 帧 - int? startFrameSeq; - for (var seq in sortedSeqs.reversed) { - final frame = _frameBuffer[seq]; - if (frame?.frameType == TalkDataH264Frame_FrameTypeE.I) { + for (int seq = currentSeq; seq >= 0; seq--) { + final frameType = _frameTypeIndex[seq]; + if (frameType == null) continue; + if (frameType == TalkDataH264Frame_FrameTypeE.I) { startFrameSeq = seq; break; - } else if (frame?.frameType == TalkDataH264Frame_FrameTypeE.P) { - // 检查 P 帧是否有对应的 I 帧 - if (_frameBuffer.containsKey(frame?.frameSeqI)) { + } else if (frameType == TalkDataH264Frame_FrameTypeE.P) { + if (_frameBuffer.containsKey(_frameBuffer[seq]!.frameSeqI)) { startFrameSeq = seq; break; } else { - // 丢弃没有对应 I 帧的 P 帧 _frameBuffer.remove(seq); + _frameTypeIndex.remove(seq); } } } if (startFrameSeq != null) { - // 收集从 I 帧或 P 帧开始的连续帧 - int expectedSeq = startFrameSeq; - for (var seq in sortedSeqs.where((s) => s >= startFrameSeq!)) { - if (seq != expectedSeq) break; + for (int seq = startFrameSeq; _frameBuffer.containsKey(seq); seq++) { framesToProcess.add(seq); - expectedSeq++; } if (framesToProcess.isNotEmpty) { @@ -57,28 +56,50 @@ class H264FrameHandler { } } - void _clearOldFrames(int currentSeq) { - // 清理比当前帧序列旧的帧 - _frameBuffer.removeWhere((seq, frame) => seq < currentSeq - 200); // 调整阈值 - } - void _processFrames(List frameSeqs) { // 按顺序组装帧数据 - final List assembledData = []; + // final List assembledData = []; + // + // for (var seq in frameSeqs) { + // final frame = _frameBuffer[seq]!; + // assembledData.addAll(frame.frameData); + // + // // 处理完后从缓冲区移除 + // _frameBuffer.remove(seq); + // } + // + // // 回调完整的帧数据 + // onCompleteFrame(assembledData); + // Calculate the total length of the assembled data + int totalLength = frameSeqs.fold( + 0, (sum, seq) => sum + _frameBuffer[seq]!.frameData.length); + + // Allocate a buffer for the assembled data + final assembledData = Uint8List(totalLength); + int offset = 0; for (var seq in frameSeqs) { final frame = _frameBuffer[seq]!; - assembledData.addAll(frame.frameData); + assembledData.setRange( + offset, offset + frame.frameData.length, frame.frameData); + offset += frame.frameData.length; - // 处理完后从缓冲区移除 + // Remove the frame from the buffer after processing _frameBuffer.remove(seq); + _frameTypeIndex.remove(seq); } - // 回调完整的帧数据 + // Callback with the complete frame data onCompleteFrame(assembledData); } void clear() { _frameBuffer.clear(); } + + void _clearOldFrames(int currentSeq) { + // 清理比当前帧序列旧的帧 + _frameBuffer.removeWhere((seq, frame) => seq < currentSeq - 200); // 调整阈值 + _frameTypeIndex.removeWhere((seq, frameType) => seq < currentSeq - 200); + } } diff --git a/lib/talk/starChart/star_chart_manage.dart b/lib/talk/starChart/star_chart_manage.dart index 91617794..1e53b7f0 100644 --- a/lib/talk/starChart/star_chart_manage.dart +++ b/lib/talk/starChart/star_chart_manage.dart @@ -112,7 +112,7 @@ class StartChartManage { // 默认通话的期望数据格式 TalkExpectReq _defaultTalkExpect = TalkExpectReq( - videoType: [VideoTypeE.H264], + videoType: [VideoTypeE.IMAGE], audioType: [AudioTypeE.G711], ); @@ -1119,7 +1119,7 @@ class StartChartManage { void reSetDefaultTalkExpect() { _defaultTalkExpect = TalkExpectReq( - videoType: [VideoTypeE.H264], + videoType: [VideoTypeE.IMAGE], audioType: [AudioTypeE.G711], ); } @@ -1131,7 +1131,7 @@ class StartChartManage { /// 修改预期接收到的数据 void sendOnlyImageVideoTalkExpectData() { final talkExpectReq = TalkExpectReq( - videoType: [VideoTypeE.H264], + videoType: [VideoTypeE.IMAGE], audioType: [], ); changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer( @@ -1141,7 +1141,7 @@ class StartChartManage { /// 修改预期接收到的数据 void sendImageVideoAndG711AudioTalkExpectData() { final talkExpectReq = TalkExpectReq( - videoType: [VideoTypeE.H264], + videoType: [VideoTypeE.IMAGE], audioType: [AudioTypeE.G711], ); changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer( diff --git a/lib/talk/starChart/views/talkView/talk_view_logic.dart b/lib/talk/starChart/views/talkView/talk_view_logic.dart index 5033bca3..4a64ebe9 100644 --- a/lib/talk/starChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/starChart/views/talkView/talk_view_logic.dart @@ -46,6 +46,8 @@ class TalkViewLogic extends BaseGetXController { int audioFrameIntervalMs = 20; // 初始帧间隔设置为45毫秒(约22FPS) int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS) int maxFrameIntervalMs = 100; // 最大帧间隔(约1 FPS) + // 定义音频帧缓冲和发送函数 + List _bufferedAudioFrames = []; /// 初始化音频播放器 void _initFlutterPcmSound() { @@ -533,15 +535,17 @@ class TalkViewLogic extends BaseGetXController { //开始录音 Future startProcessingAudio() async { - // 增加录音帧监听器和错误监听器 - state.voiceProcessor?.addFrameListener(_onFrame); - state.voiceProcessor?.addErrorListener(_onError); try { if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) { await state.voiceProcessor?.start(state.frameLength, state.sampleRate); final bool? isRecording = await state.voiceProcessor?.isRecording(); state.isRecordingAudio.value = isRecording!; state.startRecordingAudioTime.value = DateTime.now(); + + // 增加录音帧监听器和错误监听器 + state.voiceProcessor + ?.addFrameListeners([_onFrame]); + state.voiceProcessor?.addErrorListener(_onError); } else { // state.errorMessage.value = 'Recording permission not granted'; } @@ -576,23 +580,23 @@ class TalkViewLogic extends BaseGetXController { // 音频帧处理 Future _onFrame(List frame) async { - // 预处理和转码操作放到异步计算线程 - // final processedFrame = await compute(preprocessAudio, frame); - // final list = listLinearToALaw(processedFrame); final List processedFrame = preprocessAudio(frame); final List list = listLinearToALaw(processedFrame); + _bufferedAudioFrames.addAll(list); final int ms = DateTime.now().millisecondsSinceEpoch - state.startRecordingAudioTime.value.millisecondsSinceEpoch; - // 发送音频数据到UDP - await StartChartManage().sendTalkDataMessage( - talkData: TalkData( - content: list, - contentType: TalkData_ContentTypeE.G711, - durationMs: ms, - ), - ); + Future.delayed(const Duration(milliseconds: 1000)).whenComplete(() async { + // 发送音频数据到UDP + await StartChartManage().sendTalkDataMessage( + talkData: TalkData( + content: list, + contentType: TalkData_ContentTypeE.G711, + durationMs: ms, + ), + ); + }); } // 错误监听 @@ -662,9 +666,31 @@ class TalkViewLogic extends BaseGetXController { // return processedList; // } - List listLinearToALaw(List pcmList) { - final List aLawList = []; + List adjustVolume(List pcmList, double volume) { + final List adjustedPcmList = []; for (int pcmVal in pcmList) { + // 调整音量 + int adjustedPcmVal = (pcmVal * volume).round(); + + // 裁剪到 16-bit PCM 范围 + if (adjustedPcmVal > 32767) { + adjustedPcmVal = 32767; + } else if (adjustedPcmVal < -32768) { + adjustedPcmVal = -32768; + } + + adjustedPcmList.add(adjustedPcmVal); + } + return adjustedPcmList; + } + + List listLinearToALaw(List pcmList) { + // 先调节音量 + final List adjustedPcmList = adjustVolume(pcmList, 5.0); + + // 再进行 A-law 编码 + final List aLawList = []; + for (int pcmVal in adjustedPcmList) { final int aLawVal = linearToALaw(pcmVal); aLawList.add(aLawVal); } diff --git a/lib/talk/starChart/views/talkView/talk_view_state.dart b/lib/talk/starChart/views/talkView/talk_view_state.dart index 7e56836a..25538093 100644 --- a/lib/talk/starChart/views/talkView/talk_view_state.dart +++ b/lib/talk/starChart/views/talkView/talk_view_state.dart @@ -37,7 +37,7 @@ class TalkViewState { RxList listAudioData = [].obs; //得到的音频流字节数据 GlobalKey globalKey = GlobalKey(); - Timer? oneMinuteTimeTimer; // 定时器超过60秒关闭当前界面 + Timer? oneMinuteTimeTimer; // 定时器超过60秒关闭当前界面 RxInt oneMinuteTime = 0.obs; // 定时器秒数 // 定时器如果发送了接听的命令 而没收到回复就每秒重复发送10次 @@ -89,4 +89,4 @@ class TalkViewState { RxBool isLongPressing = false.obs; // 旋转角度(以弧度为单位) RxBool hasAudioData = false.obs; // 是否有音频数据 RxInt lastAudioTimestamp = 0.obs; // 最后接收到的音频数据的时间戳 -} \ No newline at end of file +}