This commit is contained in:
魏少阳 2024-04-02 17:30:49 +08:00
commit 95769e6ca8
5 changed files with 205 additions and 171 deletions

View File

@ -164,7 +164,7 @@ class _LockDetailPageState extends State<LockDetailPage>
Stack(children: [ Stack(children: [
Container( Container(
width: 1.sw, width: 1.sw,
height: 1.sh - ScreenUtil().statusBarHeight * 2, height: 1.sh - ScreenUtil().statusBarHeight,
color: Colors.white, color: Colors.white,
child: Column( child: Column(
children: [ children: [

View File

@ -8,7 +8,6 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:star_lock/talk/call/callTalk.dart'; import 'package:star_lock/talk/call/callTalk.dart';
import 'package:star_lock/talk/udp/udp_talkClass.dart'; import 'package:star_lock/talk/udp/udp_talkClass.dart';
import '../../../../talk/call/g711.dart';
import '../../../../talk/udp/udp_manage.dart'; import '../../../../talk/udp/udp_manage.dart';
import '../../../../talk/udp/udp_senderManage.dart'; import '../../../../talk/udp/udp_senderManage.dart';
import '../../../../tools/baseGetXController.dart'; import '../../../../tools/baseGetXController.dart';
@ -113,19 +112,6 @@ class LockMonitoringLogic extends BaseGetXController {
udpHangUpAction(); udpHangUpAction();
state.hangUpSeconds++; state.hangUpSeconds++;
// //
// if (UDPTalkClass().isEndCall == true) {
// print('{$clickIndex}已经挂断成功');
// state.hangUpTimer.cancel();
// return;
// }
// if (UDPTalkClass().status == 6) {
// print('{$clickIndex}被叫中,已经挂断成功');
// state.hangUpTimer.cancel();
// return;
// }
// 6 // 6
if (state.hangUpSeconds >= 6) { if (state.hangUpSeconds >= 6) {
state.hangUpTimer.cancel(); // state.hangUpTimer.cancel(); //
@ -177,87 +163,77 @@ class LockMonitoringLogic extends BaseGetXController {
Get.back(); Get.back();
} }
Future<void> _readG711Data() async { //
String filePath = 'assets/s10-g711.bin'; Future<void> startProcessing() async {
List<int> audioData = await G711().readAssetFile(filePath); state.isButtonDisabled.value = true;
// Get.log('发送读取711文件数据为:$audioData');// :$audioData
// return;
// print('发送读取711文件数据长度为:${audioData.length}');// :$audioData
if (audioData.isNotEmpty) {
//
// pcmBytes = G711().convertList(audioData);
// print('发送转换pcmBytes数据长度为:${pcmBytes.length}');
int start = 0; state.voiceProcessor?.addFrameListener(_onFrame);
int length = 320; state.voiceProcessor?.addErrorListener(_onError);
while (start < audioData.length) { try {
// await Future.delayed(const Duration(milliseconds: 50)); if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) {
await state.voiceProcessor?.start(state.frameLength, state.sampleRate);
int end = (start + length > audioData.length) bool? isRecording = await state.voiceProcessor?.isRecording();
? audioData.length state.isProcessing.value = isRecording!;
: start + length; } else {
List<int> sublist = audioData.sublist(start, end); state.errorMessage.value = "Recording permission not granted";
sendRecordData({
"bytes": sublist,
// "udpSendDataFrameNumber": 0,
"lockID": UDPManage().lockId,
"lockIP": UDPManage().host,
"userMobile": await state.userUid,
"userMobileIP": await state.userMobileIP,
});
print(sublist);
start += length;
} }
print('G711数据发送完成'); } on PlatformException catch (ex) {
} else { state.errorMessage.value = "Failed to start recorder: $ex";
print('Failed to read audio data.'); } finally {
state.isButtonDisabled.value = false;
} }
} }
Future<void> startProcessing() async { double _calculateVolumeLevel(List<int> frame) {
frameListener(List<int> frame) async { double rms = 0.0;
for (int i = 0; i < frame.length; i++) { for (int sample in frame) {
frame[i] = linearToULaw(frame[i]); rms += pow(sample, 2);
}
await Future.delayed(const Duration(milliseconds: 50));
sendRecordData({
"bytes": frame,
// "udpSendDataFrameNumber": 0,
"lockID": UDPManage().lockId,
"lockIP": UDPManage().host,
"userMobile": await state.userUid,
"userMobileIP": await state.userMobileIP,
});
} }
rms = sqrt(rms / frame.length);
errorListener(VoiceProcessorException error) { double dbfs = 20 * log(rms / 32767.0) / log(10);
print("VoiceProcessorException: $error"); double normalizedValue = (dbfs + state.dbOffset) / state.dbOffset;
return normalizedValue.clamp(0.0, 1.0);
}
Future<void> _onFrame(List<int> frame) async {
double volumeLevel = _calculateVolumeLevel(frame);
if (state.volumeHistory.value.length == state.volumeHistoryCapacity) {
state.volumeHistory.value.removeAt(0);
} }
state.volumeHistory.value.add(volumeLevel);
; state.smoothedVolumeValue.value =
state.voiceProcessor?.addFrameListener(frameListener); state.volumeHistory.value.reduce((a, b) => a + b) /
state.voiceProcessor?.addErrorListener(errorListener); state.volumeHistory.value.length;
try { List<int> pcmBytes = listLinearToULaw(frame);
if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) { await Future.delayed(const Duration(milliseconds: 100));
await state.voiceProcessor?.start(320, 8000); sendRecordData({
bool? isRecording = await state.voiceProcessor?.isRecording(); "bytes": pcmBytes,
} else {} // "udpSendDataFrameNumber": 0,
} on PlatformException catch (ex) { "lockID": UDPManage().lockId,
Get.log("PlatformException: $ex"); "lockIP": UDPManage().host,
} finally {} "userMobile": await state.userUid,
"userMobileIP": await state.userMobileIP,
});
}
void _onError(VoiceProcessorException error) {
state.errorMessage.value = error.message!;
} }
Future<void> stopProcessing() async { Future<void> stopProcessing() async {
state.isButtonDisabled.value = true;
try { try {
await state.voiceProcessor?.stop(); await state.voiceProcessor?.stop();
} on PlatformException catch (ex) { } on PlatformException catch (ex) {
Get.log("PlatformException: $ex"); state.errorMessage.value = "Failed to stop recorder: $ex";
} finally {} } finally {
} bool? isRecording = await state.voiceProcessor?.isRecording();
state.isProcessing.value = isRecording!;
void onError(Object e) { state.isButtonDisabled.value = false;
print(e); }
} }
sendRecordData(Map<String, dynamic> args) async { sendRecordData(Map<String, dynamic> args) async {
@ -360,6 +336,15 @@ class LockMonitoringLogic extends BaseGetXController {
// UDPManage().sendData(topBytes); // UDPManage().sendData(topBytes);
} }
List<int> listLinearToULaw(List<int> pcmList) {
List<int> uLawList = [];
for (int pcmVal in pcmList) {
int uLawVal = linearToULaw(pcmVal);
uLawList.add(uLawVal);
}
return uLawList;
}
// pcm // pcm
int linearToULaw(int pcmVal) { int linearToULaw(int pcmVal) {
int mask; int mask;
@ -404,18 +389,6 @@ class LockMonitoringLogic extends BaseGetXController {
return size; return size;
} }
double _calculateVolumeLevel(List<int> frame) {
double rms = 0.0;
for (int sample in frame) {
rms += pow(sample, 2);
}
rms = sqrt(rms / frame.length);
double dbfs = 20 * log(rms / 32767.0) / log(10);
double normalizedValue = (dbfs + 50) / 50;
return normalizedValue.clamp(0.0, 1.0);
}
Future<bool> getPermissionStatus() async { Future<bool> getPermissionStatus() async {
Permission permission = Permission.microphone; Permission permission = Permission.microphone;
//granted denied permanentlyDenied //granted denied permanentlyDenied

View File

@ -1,9 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:star_lock/talk/call/callTalk.dart'; import 'package:star_lock/talk/call/callTalk.dart';
import '../../../../app_settings/app_colors.dart'; import '../../../../app_settings/app_colors.dart';
@ -36,85 +41,88 @@ class _LockMonitoringPageState extends State<LockMonitoringPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PopScope( return PopScope(
canPop: false, canPop: false,
child: Container( child: RepaintBoundary(
width: 1.sw, key: state.globalKey,
height: 1.sh, child: Container(
color: Colors.white, width: 1.sw,
child: Stack( height: 1.sh,
children: [ color: Colors.white,
Obx(() { child: Stack(
if (state.listPhotoData.value.isEmpty || children: [
state.listPhotoData.value.length < 10) { Obx(() {
return Container(color: Colors.transparent); if (state.listPhotoData.value.isEmpty ||
} else { state.listPhotoData.value.length < 10) {
return Image.memory( return Container(color: Colors.transparent);
state.listPhotoData.value, } else {
gaplessPlayback: true, return Image.memory(
width: 1.sw, state.listPhotoData.value,
height: 1.sh, gaplessPlayback: true,
fit: BoxFit.cover, width: 1.sw,
errorBuilder: (context, error, stackTrace) { height: 1.sh,
return Container(color: Colors.transparent); fit: BoxFit.cover,
}, errorBuilder: (context, error, stackTrace) {
); return Container(color: Colors.transparent);
} },
}), );
Positioned( }
top: ScreenUtil().statusBarHeight + 30.h,
width: 1.sw,
child: Obx(() {
var sec = (state.oneMinuteTime.value % 60)
.toString()
.padLeft(2, '0');
var min = (state.oneMinuteTime.value ~/ 60)
.toString()
.padLeft(2, '0');
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("$min:$sec",
style: TextStyle(
fontSize: 26.sp, color: Colors.white)),
// SizedBox(width: 30.w),
// GestureDetector(
// onTap: () {
// Get.back();
// },
// child: Container(
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(25.h)),
// padding: EdgeInsets.all(10.w),
// child: Image(
// width: 40.w,
// height: 40.w,
// image: const AssetImage("images/icon_left_black.png"),
// ),
// ),
// ),
]);
}), }),
), Positioned(
Positioned( top: ScreenUtil().statusBarHeight + 30.h,
bottom: 10.w, width: 1.sw,
child: Container( child: Obx(() {
width: 1.sw - 30.w * 2, var sec = (state.oneMinuteTime.value % 60)
// height: 300.h, .toString()
margin: EdgeInsets.all(30.w), .padLeft(2, '0');
decoration: BoxDecoration( var min = (state.oneMinuteTime.value ~/ 60)
color: const Color(0xC83C3F41), .toString()
borderRadius: BorderRadius.circular(20.h)), .padLeft(2, '0');
child: Column( return Row(
children: [ mainAxisAlignment: MainAxisAlignment.center,
SizedBox(height: 20.h), children: [
bottomTopBtnWidget(), Text("$min:$sec",
SizedBox(height: 20.h), style: TextStyle(
bottomBottomBtnWidget(), fontSize: 26.sp, color: Colors.white)),
SizedBox(height: 20.h), // SizedBox(width: 30.w),
], // GestureDetector(
), // onTap: () {
)) // Get.back();
], // },
// child: Container(
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(25.h)),
// padding: EdgeInsets.all(10.w),
// child: Image(
// width: 40.w,
// height: 40.w,
// image: const AssetImage("images/icon_left_black.png"),
// ),
// ),
// ),
]);
}),
),
Positioned(
bottom: 10.w,
child: Container(
width: 1.sw - 30.w * 2,
// height: 300.h,
margin: EdgeInsets.all(30.w),
decoration: BoxDecoration(
color: const Color(0xC83C3F41),
borderRadius: BorderRadius.circular(20.h)),
child: Column(
children: [
SizedBox(height: 20.h),
bottomTopBtnWidget(),
SizedBox(height: 20.h),
bottomBottomBtnWidget(),
SizedBox(height: 20.h),
],
),
))
],
),
), ),
)); ));
} }
@ -144,6 +152,7 @@ class _LockMonitoringPageState extends State<LockMonitoringPage> {
// //
GestureDetector( GestureDetector(
onTap: () { onTap: () {
captureAndSavePng();
// Get.toNamed(Routers.monitoringRealTimeScreenPage); // Get.toNamed(Routers.monitoringRealTimeScreenPage);
}, },
child: Container( child: Container(
@ -198,13 +207,16 @@ class _LockMonitoringPageState extends State<LockMonitoringPage> {
state.udpStatus.value = 9; state.udpStatus.value = 9;
} }
// logic.readG711Data(); // logic.readG711Data();
logic.startProcessing(); if (state.isProcessing.value == false) {
logic.startProcessing();
}
}, longPressUp: () async { }, longPressUp: () async {
// //
print("onLongPressUp"); print("onLongPressUp");
if (state.udpStatus.value == 9) { if (state.udpStatus.value == 9) {
state.udpStatus.value = 8; state.udpStatus.value = 8;
} }
logic.stopProcessing();
})), })),
bottomBtnItemWidget( bottomBtnItemWidget(
"images/main/icon_lockDetail_hangUp.png", "挂断", Colors.red, () async { "images/main/icon_lockDetail_hangUp.png", "挂断", Colors.red, () async {
@ -337,8 +349,44 @@ class _LockMonitoringPageState extends State<LockMonitoringPage> {
}); });
} }
Future<void> captureAndSavePng() async {
try {
if (state.globalKey.currentContext == null) {
print('截图失败: 未找到当前上下文');
return;
}
RenderRepaintBoundary boundary = state.globalKey.currentContext!
.findRenderObject() as RenderRepaintBoundary;
ui.Image image = await boundary.toImage();
ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
if (byteData == null) {
print('截图失败: 图像数据为空');
return;
}
Uint8List pngBytes = byteData.buffer.asUint8List();
//
final directory = await getApplicationDocumentsDirectory();
final imagePath = '${directory.path}/screenshot.png';
//
File imgFile = File(imagePath);
await imgFile.writeAsBytes(pngBytes);
//
await ImageGallerySaver.saveFile(imagePath);
print('截图保存路径: $imagePath');
logic.showToast('截图已保存到相册');
} catch (e) {
print('截图失败: $e');
}
}
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
logic.stopProcessing();
} }
} }

View File

@ -23,7 +23,19 @@ class LockMonitoringState {
var listPhotoData = Uint8List(0).obs; // var listPhotoData = Uint8List(0).obs; //
var listAudioData = <int>[].obs; // var listAudioData = <int>[].obs; //
//
late final VoiceProcessor? voiceProcessor; late final VoiceProcessor? voiceProcessor;
var isProcessing = false.obs; //
var isButtonDisabled = false.obs; //
final int frameLength = 320; //320
final int sampleRate = 8000; //8000
final int volumeHistoryCapacity = 5; //
final double dbOffset = 50.0; //
var volumeHistory = <double>[].obs; //
var smoothedVolumeValue = 0.0.obs; //
var errorMessage = ''.obs;
GlobalKey globalKey = GlobalKey();
late Timer oneMinuteTimeTimer = late Timer oneMinuteTimeTimer =
Timer(const Duration(seconds: 1), () {}); // 60 Timer(const Duration(seconds: 1), () {}); // 60

View File

@ -99,6 +99,7 @@ class CommandUDPReciverManager {
EasyLoading.showToast("开门成功", duration: 2000.milliseconds); EasyLoading.showToast("开门成功", duration: 2000.milliseconds);
} else { } else {
print("开门失败"); print("开门失败");
EasyLoading.showToast("开门失败", duration: 2000.milliseconds);
} }
} }
break; break;