fix:增加p2p消息协议代码、优化对讲时应用卡死、调整StreamController处理数据时的逻辑、调整在寻找图片帧时的代码逻辑

This commit is contained in:
liyi 2025-01-14 13:43:12 +08:00
parent 641ff4a2b6
commit 7a0e8f9e28
14 changed files with 413 additions and 232 deletions

View File

@ -2745,7 +2745,9 @@ class ApiProvider extends BaseProvider {
jsonEncode({
'deviceType': deviceType,
'deviceMac': deviceMac,
}));
}),
isShowNetworkErrorMsg: false,
isUnShowLoading: true);
}
extension ExtensionString on String {

View File

@ -2739,4 +2739,7 @@ class ApiRepository {
);
return DeviceNetwork.fromJson(res.body);
}
}

View File

@ -42,12 +42,10 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle
@override
void handleResp(ScpMessage scpMessage) async {
final BleResp bleResp = scpMessage.Payload;
AppLog.log('收到蓝牙消息回复:${bleResp.structData}');
// List<int>
String hexString = bleResp.structData
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
.join(' ');
AppLog.log('蓝牙透传回复:$hexString');
AppLog.log('收到蓝牙透传回复:$hexString');
await CommandReciverManager.appDataReceive(bleResp.structData);
@ -57,8 +55,6 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle
AppLog.log('收到开门请求命令回复');
_replyOpenLock(reply);
}
});
}
@ -146,7 +142,7 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle
lockDetailLogic.lockReportLockSuccessfullyUploadData();
lockDetailLogic.resetOpenDoorState();
lockDetailState.animationController!.stop();
lockDetailState.animationController?.stop();
//
lockDetailLogic.getLockRecordLastUploadDataTime();

View File

@ -3,7 +3,7 @@ import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
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/p2p/p2p_manage.dart';
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart';
import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart';
@ -68,7 +68,7 @@ class UdpRbcuInfoHandler extends ScpMessageBaseHandle
/// rbcuInfo消息
void _handleResultRbcuInfo(RbcuInfo rbcuInfo) {
P2pManage().communicationObjectRbcuInfo = rbcuInfo;
startChartManage.rbcuInfo = rbcuInfo;
startChartManage.stopSendingRbcuInfoMessages();
}
}

View File

@ -0,0 +1,57 @@
import 'package:star_lock/login/selectCountryRegion/common/index.dart';
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
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/proto/generic.pb.dart';
import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart';
import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart';
class UdpRbcuProBeHandler extends ScpMessageBaseHandle
implements ScpMessageHandler {
@override
deserializePayload(
{required int payloadType,
required int messageType,
required List<int> byte,
int? offset,
int? PayloadLength,
int? spTotal,
int? spIndex,
int? messageId}) {
if (messageType == MessageTypeConstant.Resp) {
final GenericResp genericResp = GenericResp();
genericResp.mergeFromBuffer(byte);
return genericResp;
} else if (messageType == MessageTypeConstant.Req) {
final RbcuProbe rbcuProbe = RbcuProbe();
rbcuProbe.mergeFromBuffer(byte);
return rbcuProbe;
} else {
String payload = utf8.decode(byte);
return payload;
}
}
@override
void handleInvalidReq(ScpMessage scpMessage) {
// TODO: implement handleInvalidReq
}
@override
void handleRealTimeData(ScpMessage scpMessage) {
// TODO: implement handleRealTimeData
}
@override
void handleReq(ScpMessage scpMessage) {
// rbcuprobe
final RbcuProbe rbcuProbe = scpMessage.Payload;
//
startChartManage.rbcuProbe = rbcuProbe;
}
@override
void handleResp(ScpMessage scpMessage) {}
}

View File

@ -128,10 +128,10 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
/// g711音频数据
void _handleVideoG711(TalkData talkData) {
try {
final g711Data = talkData.content;
// pcm数据
List<int> pcmBytes = G711().convertList(g711Data);
talkData.content = pcmBytes;
// final g711Data = talkData.content;
// // pcm数据
// List<int> pcmBytes = G711().convertList(g711Data);
// talkData.content = pcmBytes;
talkDataRepository.addTalkData(talkData);
} catch (e) {
print('Error decoding G.711 to PCM: $e');
@ -142,34 +142,69 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
//
List<Uint8List> frames = [];
// (0xFFD8 , 0xFFD9 )
int startIdx = 0;
while (startIdx < payload.length - 1) {
// 0xFFD8
startIdx = payload.indexOf(0xFF, startIdx);
if (startIdx == -1 || startIdx + 1 >= payload.length) break;
if (payload[startIdx + 1] == 0xD8) {
// 0xFFD8
int endIdx = startIdx + 2;
while (endIdx < payload.length - 1) {
endIdx = payload.indexOf(0xFF, endIdx);
if (endIdx == -1 || endIdx + 1 >= payload.length) break;
if (payload[endIdx + 1] == 0xD9) {
// 0xFFD9
Uint8List frame = payload.sublist(startIdx, endIdx + 2);
frames.add(frame);
startIdx = endIdx + 2; //
// payload
int i = 0;
while (i < payload.length - 1) {
// 0xFFD8
if (payload[i] == 0xFF && payload[i + 1] == 0xD8) {
int startIdx = i;
i += 2; //
// 0xFFD9
while (i < payload.length - 1) {
if (payload[i] == 0xFF && payload[i + 1] == 0xD9) {
// 0xFFD9
int endIdx = i + 2;
// 使 Uint8List.view
frames.add(
Uint8List.view(payload.buffer, startIdx, endIdx - startIdx));
i = endIdx; //
break;
} else {
endIdx += 1; //
i += 1; //
}
}
} else {
startIdx += 1; //
i += 1; //
}
}
//
return frames;
}
// Future<List<Uint8List>> _processCompletePayload(Uint8List payload) async {
// //
// List<Uint8List> frames = [];
//
// // (0xFFD8 , 0xFFD9 )
// int startIdx = 0;
// while (startIdx < payload.length - 1) {
// // 0xFFD8
// startIdx = payload.indexOf(0xFF, startIdx);
// if (startIdx == -1 || startIdx + 1 >= payload.length) break;
// if (payload[startIdx + 1] == 0xD8) {
// // 0xFFD8
// int endIdx = startIdx + 2;
// while (endIdx < payload.length - 1) {
// endIdx = payload.indexOf(0xFF, endIdx);
// if (endIdx == -1 || endIdx + 1 >= payload.length) break;
// if (payload[endIdx + 1] == 0xD9) {
// // 0xFFD9
// Uint8List frame = payload.sublist(startIdx, endIdx + 2);
// frames.add(frame);
// startIdx = endIdx + 2; //
// break;
// } else {
// endIdx += 1; //
// }
// }
// } else {
// startIdx += 1; //
// }
// }
//
// //
// return frames;
// }
}

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
@ -31,6 +32,8 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle
talkeRequestOverTimeTimerManager.cancel();
talkePingOverTimeTimerManager.cancel();
talkDataOverTimeTimerManager.cancel();
EasyLoading.showToast('已挂断');
}
@override

View File

@ -4,7 +4,17 @@ import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
class TalkDataRepository {
//
TalkDataRepository._();
TalkDataRepository._() {
_talkDataStreamController = StreamController<TalkData>.broadcast(
onListen: () {
_isListening = true;
},
onCancel: () {
_isListening = false;
},
sync: false, //
);
}
// 使 _instance
static final TalkDataRepository _instance = TalkDataRepository._();
@ -13,18 +23,37 @@ class TalkDataRepository {
static TalkDataRepository get instance => _instance;
// StreamController
final StreamController<TalkData> _talkDataStreamController = StreamController<TalkData>.broadcast();
late final StreamController<TalkData> _talkDataStreamController;
bool _isListening = false;
// Stream
Stream<TalkData> get talkDataStream => _talkDataStreamController.stream;
Stream<TalkData> get talkDataStream =>
_talkDataStreamController.stream.transform(
StreamTransformer<TalkData, TalkData>.fromHandlers(
handleData: (TalkData data, EventSink<TalkData> sink) {
// 100
if (_buffer.length >= 100) {
_buffer.removeAt(0); //
}
_buffer.add(data);
sink.add(data);
},
),
);
final List<TalkData> _buffer = []; //
// TalkData Stream
void addTalkData(TalkData talkData) async {
_talkDataStreamController.add(talkData);
if (_isListening) {
Future.microtask(() {
_talkDataStreamController.add(talkData);
});
}
}
// StreamController
void dispose() {
_talkDataStreamController.close();
}
}
}

View File

@ -57,6 +57,8 @@ class ScpMessageHandlerFactory {
return UdpTalkHangUpHandler();
case PayloadTypeConstant.RbcuInfo:
return UdpRbcuInfoHandler();
case PayloadTypeConstant.RbcuProbe:
return UdpRbcuInfoHandler();
default:
return UnKnowPayloadTypeHandler();
}

View File

@ -1,108 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart';
class P2pManage {
//
static const String testMessage = 'Hello'; //
static const int localPort = 0; // 0
static const Duration connectionTimeout = Duration(seconds: 5); //
static const List<String> localNetworkPrefixes = [
'192.168.',
'10.',
'172.'
]; // IP
RbcuInfo? communicationObjectRbcuInfo;
void init() {
//
}
// address IP
List<Map<String, String>> parseRemoteAddresses() {
final addresses = communicationObjectRbcuInfo?.address ?? [];
return addresses.map((addr) {
final parts = addr.split(':');
return {'ip': parts[0], 'port': parts[1]};
}).toList();
}
// IP
bool _isLocalNetworkIp(String ip) {
for (final prefix in localNetworkPrefixes) {
if (ip.startsWith(prefix)) {
// 172.16.x.x 172.31.x.x
if (prefix == '172.') {
final secondOctet = int.tryParse(ip.split('.')[1]) ?? 0;
if (secondOctet >= 16 && secondOctet <= 31) {
return true;
}
} else {
return true;
}
}
}
return false;
}
// UDP
Future<void> createUdpConnection() async {
final addresses = parseRemoteAddresses();
// 使 IP
addresses.sort((a, b) {
final isALocal = _isLocalNetworkIp(a['ip']!);
final isBLocal = _isLocalNetworkIp(b['ip']!);
if (isALocal && !isBLocal) return -1; // a IP
if (!isALocal && isBLocal) return 1; // b IP
return 0; //
});
//
for (final addr in addresses) {
final ip = addr['ip']!;
final port = int.parse(addr['port']!);
try {
final socket = await RawDatagramSocket.bind(
InternetAddress.anyIPv4, localPort); //
//
final testData = testMessage.codeUnits;
socket.send(testData, InternetAddress(ip), port);
//
final completer = Completer<void>();
//
socket.listen((event) {
if (event == RawSocketEvent.read) {
final datagram = socket.receive();
if (datagram != null) {
final response = String.fromCharCodes(datagram.data);
print('收到来自 $ip:$port 的响应: $response');
completer.complete(); // Future
}
}
});
//
try {
await completer.future.timeout(connectionTimeout);
print('成功连接到 $ip:$port');
return; // 退
} on TimeoutException {
print('连接到 $ip:$port 超时');
} finally {
socket.close(); // socket
}
} catch (e) {
print('连接到 $ip:$port 失败: $e');
}
}
print('无法连接到任何地址');
}
}

View File

@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:fixnum/fixnum.dart';
import 'package:get/get.dart';
@ -29,6 +31,7 @@ import 'package:star_lock/talk/startChart/handle/other/talke_request_over_time_t
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/proto/rbcu.pb.dart';
import 'package:star_lock/talk/startChart/proto/rbcu.pbserver.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.pbserver.dart';
@ -86,9 +89,14 @@ class StartChartManage {
int _defaultIntervalTime = 1; // (s)
Timer? talkDataTimer; //
Timer? rbcuInfoTimer; // p2p地址交换定时器
Timer? rbcuProbeTimer; // p2p打洞包
Timer? rbcuConfirmTimer; // p2p打洞确认包
String _rbcuSessionId = ''; // p2p SessionId
Timer? talkRequestTimer; //
final int maxAttempts = 15; //
RbcuInfo? rbcuInfo;
RbcuProbe? rbcuProbe;
RbcuConfirm? rbcuConfirm;
final int _maxPayloadSize = 8 * 1024; //
//
@ -254,6 +262,7 @@ class StartChartManage {
) // "udp://"
.cast<
String>(); // Iterable<String>// Iterable<String?> Iterable<String>
_rbcuSessionId = uuid;
final RbcuInfo rbcuInfo = RbcuInfo(
sessionId: uuid,
name: uuid,
@ -261,6 +270,7 @@ class StartChartManage {
time: int64Timestamp,
isResp: isResp,
);
final message = MessageCommand.genericRbcuInfoMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
@ -270,6 +280,44 @@ class StartChartManage {
_sendMessage(message: message);
}
// RbcuProbe
void _sendRbcuProbeMessage() async {
//
String generateRandomString(int length) {
const chars =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
final random = Random();
return String.fromCharCodes(
List.generate(
length, (index) => chars.codeUnitAt(random.nextInt(chars.length))),
);
}
final info = rbcuInfo = RbcuInfo(address: ['119.137.55.161:62289']);
if (info != null && info.address != null && info.address.length > 0) {
info.address.forEach((element) {
// element
final parts = element.split(':');
final host = parts[0]; // IP
final port = int.tryParse(parts[1]) ?? 0; // 0
final RbcuProbe rbcuProbe = RbcuProbe(
sessionId: _rbcuSessionId,
data: generateRandomString(100),
targetAddress: element);
final rbcuProBeBuffer = rbcuProbe.writeToBuffer();
_sendNatMessage(message: rbcuProBeBuffer, host: host, port: port);
});
}
}
// RbcuConfirm
void _sendRbcuConfirmMessage() async {
RbcuConfirm(
sessionId: _rbcuSessionId,
);
}
//
void startSendingRbcuInfoMessages({required String ToPeerId}) {
// 1 _sendRbcuInfoMessage
@ -280,12 +328,43 @@ class StartChartManage {
});
}
//
void startSendingRbcuProbeTMessages() {
// 1 _sendRbcuInfoMessage
rbcuProbeTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// RbcuProbe
_sendRbcuProbeMessage();
});
}
//
void startSendingRbcuConfirmTMessages() {
rbcuConfirmTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// RbcuProbe
_sendRbcuConfirmMessage();
});
}
//
void stopSendingRbcuInfoMessages() {
rbcuInfoTimer?.cancel(); //
rbcuInfoTimer = null;
}
//
void stopSendingRbcuProBeMessages() {
rbcuProbeTimer?.cancel(); //
rbcuProbeTimer = null;
}
//
void stopSendingRbcuConfirmMessages() {
rbcuConfirmTimer?.cancel(); //
rbcuConfirmTimer = null;
}
// RbcuInfo
void replyRbcuInfoMessage({required String ToPeerId}) {
_sendRbcuInfoMessage(ToPeerId: ToPeerId, isResp: true);
@ -345,7 +424,9 @@ class StartChartManage {
}
//
//
Future<void> sendTalkDataMessage({required TalkData talkData}) async {
String toPeerId = lockPeerId;
final List<int> payload = talkData.content;
//
final int totalPackets = (payload.length / _maxPayloadSize).ceil();
@ -361,10 +442,10 @@ class StartChartManage {
// messageID
final messageId =
MessageCommand.getNextMessageId(ToPeerId, increment: false);
MessageCommand.getNextMessageId(toPeerId, increment: false);
//
final message = MessageCommand.talkDataMessage(
ToPeerId: 'D78Fo4CjNzUXz8DxuUhLtcRpnGFXhSzhzs191XzhJttS',
ToPeerId: toPeerId,
FromPeerId: FromPeerId,
payload: packet,
SpTotal: totalPackets,
@ -375,7 +456,7 @@ class StartChartManage {
await _sendMessage(message: message);
}
// id
MessageCommand.getNextMessageId(ToPeerId);
MessageCommand.getNextMessageId(toPeerId);
}
//
@ -603,6 +684,20 @@ class StartChartManage {
}
}
//
Future<void> _sendNatMessage(
{required List<int> message,
required String host,
required int port}) async {
_log(text: '发送nat消息');
var result = await _udpSocket?.send(message, InternetAddress(host), port);
if (result != message.length) {
throw StartChartMessageException(
'❌Udp send data error----> $result ${message.length}');
// _udpSocket = null;
}
}
//
Future<StarChartRegisterNodeEntity> _requestStarChartRegisterNode() async {
//
@ -960,6 +1055,8 @@ class StartChartManage {
stopReStartOnlineStartChartServer();
stopTalkDataTimer();
stopSendingRbcuInfoMessages();
stopSendingRbcuProBeMessages();
stopSendingRbcuConfirmMessages();
_resetData();
await Storage.removerRelayInfo();
await Storage.removerStarChartRegisterNodeInfo();

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
@ -23,6 +24,7 @@ import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_logic.dart';
import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_state.dart';
import 'package:star_lock/main/lockDetail/lockDetail/lockNetToken_entity.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/talk/call/g711.dart';
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
@ -43,18 +45,18 @@ class TalkViewLogic extends BaseGetXController {
final LockDetailState lockDetailState = Get.put(LockDetailLogic()).state;
Timer? _syncTimer; //
int _startTime = 0; //
final int bufferSize = 20; //
int bufferSize = 20; //
final List<int> frameTimestamps = []; // FPS
int frameIntervalMs = 45; // 4522FPS
int minFrameIntervalMs = 30; // 33 FPS
int maxFrameIntervalMs = 500; // 1 FPS
int maxFrameIntervalMs = 100; // 1 FPS
// int maxFrameIntervalMs = 100; // 10 FPS
///
void _initFlutterPcmSound() {
const int sampleRate = 44100;
FlutterPcmSound.setLogLevel(LogLevel.verbose);
FlutterPcmSound.setup(sampleRate: sampleRate, channelCount: 2);
const int sampleRate = 8000;
FlutterPcmSound.setLogLevel(LogLevel.none);
FlutterPcmSound.setup(sampleRate: sampleRate, channelCount: 1);
// feed
if (Platform.isAndroid) {
FlutterPcmSound.setFeedThreshold(-1); // Android
@ -82,24 +84,22 @@ class TalkViewLogic extends BaseGetXController {
//
void _startListenTalkData() {
state.talkDataRepository.talkDataStream.listen((TalkData talkData) {
state.talkDataRepository.talkDataStream.listen((TalkData talkData) async {
final contentType = talkData.contentType;
int currentTimestamp = DateTime.now().millisecondsSinceEpoch;
//
switch (contentType) {
case TalkData_ContentTypeE.G711:
if (state.audioBuffer.length < bufferSize) {
state.audioBuffer.add(talkData);
if (state.audioBuffer.length >= bufferSize) {
state.audioBuffer.removeAt(0); //
}
// print('收到音频数据');
state.audioBuffer.add(talkData); //
break;
case TalkData_ContentTypeE.Image:
if (state.videoBuffer.length < bufferSize) {
state.videoBuffer.add(talkData);
if (state.videoBuffer.length >= bufferSize) {
state.videoBuffer.removeAt(0); //
}
// print('talkData durationMs-->:${talkData.durationMs}');
state.videoBuffer.add(talkData); //
///
// updateNetworkStatus(currentTimestamp);
break;
@ -127,9 +127,11 @@ class TalkViewLogic extends BaseGetXController {
}
///
void _playAudioData(TalkData talkData) {
void _playAudioData(TalkData talkData) async {
final list = G711().decodeAndDenoise(talkData.content, true, 8000, 100, 50);
// PCM PcmArrayInt16
final PcmArrayInt16 fromList = PcmArrayInt16.fromList(talkData.content);
final PcmArrayInt16 fromList = PcmArrayInt16.fromList(list);
FlutterPcmSound.feed(fromList);
if (!state.isPlaying.value) {
FlutterPcmSound.play();
@ -138,7 +140,7 @@ class TalkViewLogic extends BaseGetXController {
}
///
void _playVideoData(TalkData talkData) {
void _playVideoData(TalkData talkData) async {
state.listData.value = Uint8List.fromList(talkData.content);
}
@ -153,8 +155,6 @@ class TalkViewLogic extends BaseGetXController {
final currentTime = DateTime.now().millisecondsSinceEpoch;
final elapsedTime = currentTime - _startTime;
// elapsedTime
// AppLog.log('Elapsed Time: $elapsedTime ms');
//
_adjustFrameInterval();
@ -163,6 +163,7 @@ class TalkViewLogic extends BaseGetXController {
state.audioBuffer.first.durationMs <= elapsedTime) {
//
if (state.isOpenVoice.value) {
AppLog.log('播放音频:${state.audioBuffer[0]}');
_playAudioData(state.audioBuffer.removeAt(0));
} else {
//
@ -189,6 +190,13 @@ class TalkViewLogic extends BaseGetXController {
///
void _adjustFrameInterval() {
int newFrameIntervalMs = frameIntervalMs;
if (state.networkStatus.value == NetworkStatus.lagging) {
bufferSize = 30; //
} else {
bufferSize = 20; //
}
if (state.videoBuffer.length < 10 && frameIntervalMs < maxFrameIntervalMs) {
//
frameIntervalMs += 5;
@ -197,38 +205,47 @@ class TalkViewLogic extends BaseGetXController {
//
frameIntervalMs -= 5;
}
_syncTimer?.cancel();
_syncTimer =
Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) {
final currentTime = DateTime.now().millisecondsSinceEpoch;
final elapsedTime = currentTime - _startTime;
//
if (newFrameIntervalMs != frameIntervalMs) {
frameIntervalMs = newFrameIntervalMs;
//
_syncTimer?.cancel();
_syncTimer =
Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) {
final currentTime = DateTime.now().millisecondsSinceEpoch;
final elapsedTime = currentTime - _startTime;
//
if (state.audioBuffer.isNotEmpty &&
state.audioBuffer.first.durationMs <= elapsedTime) {
//
if (state.isOpenVoice.value) {
_playAudioData(state.audioBuffer.removeAt(0));
} else {
//
//
//
state.audioBuffer.removeAt(0);
//
if (state.audioBuffer.isNotEmpty &&
state.audioBuffer.first.durationMs <= elapsedTime) {
//
if (state.isOpenVoice.value) {
_playAudioData(state.audioBuffer.removeAt(0));
} else {
//
//
//
state.audioBuffer.removeAt(0);
}
}
}
//
//
while (state.videoBuffer.isNotEmpty &&
state.videoBuffer.first.durationMs <= elapsedTime) {
//
if (state.videoBuffer.length > 1) {
state.videoBuffer.removeAt(0);
} else {
_playVideoData(state.videoBuffer.removeAt(0));
//
//
int maxFramesToProcess = 5; // 5
int processedFrames = 0;
while (state.videoBuffer.isNotEmpty &&
state.videoBuffer.first.durationMs <= elapsedTime &&
processedFrames < maxFramesToProcess) {
if (state.videoBuffer.length > 1) {
state.videoBuffer.removeAt(0);
} else {
_playVideoData(state.videoBuffer.removeAt(0));
}
processedFrames++;
}
}
});
});
}
}
///
@ -238,11 +255,11 @@ class TalkViewLogic extends BaseGetXController {
if (frameInterval > 500 && frameInterval <= 1000) {
// 5001
state.networkStatus.value = NetworkStatus.lagging;
showNetworkStatus("Network is lagging");
// showNetworkStatus("Network is lagging");
} else if (frameInterval > 1000) {
// 1
state.networkStatus.value = NetworkStatus.delayed;
showNetworkStatus("Network is delayed");
// showNetworkStatus("Network is delayed");
} else {
state.networkStatus.value = NetworkStatus.normal;
state.alertCount.value = 0; //
@ -436,12 +453,13 @@ class TalkViewLogic extends BaseGetXController {
@override
void onClose() {
_stopPlayG711Data();
state.listData.value = Uint8List(0);
state.audioBuffer.clear();
state.videoBuffer.clear();
_syncTimer?.cancel();
_syncTimer = null;
_stopPlayG711Data(); //
state.listData.value = Uint8List(0); //
state.audioBuffer.clear(); //
state.videoBuffer.clear(); //
_syncTimer?.cancel(); //
_syncTimer = null; //
super.onClose();
}
///
@ -563,7 +581,7 @@ class TalkViewLogic extends BaseGetXController {
// _concatenateFrames(state.recordingAudioAllFrames); //
final List<int> pcmBytes = _listLinearToULaw(frame);
//
StartChartManage().sendTalkDataMessage(
await StartChartManage().sendTalkDataMessage(
talkData: TalkData(
content: pcmBytes,
contentType: TalkData_ContentTypeE.G711,
@ -571,6 +589,7 @@ class TalkViewLogic extends BaseGetXController {
state.startRecordingAudioTime.value.millisecondsSinceEpoch,
),
);
AppLog.log('发送音频数据');
}
void _onError(VoiceProcessorException error) {

View File

@ -168,7 +168,36 @@ class _TalkViewPageState extends State<TalkViewPage>
// ),
Obx(() => state.listData.value.isEmpty
? buildRotationTransition()
: Container())
: Container()),
Obx(() => state.isLongPressing.value
? Positioned(
top: 80.h,
left: 0,
right: 0,
child: Center(
child: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7),
borderRadius: BorderRadius.circular(10.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.mic, color: Colors.white, size: 24.w),
SizedBox(width: 10.w),
Text(
'正在说话...',
style:
TextStyle(fontSize: 20.sp, color: Colors.white),
),
],
),
),
),
)
: Container()),
],
),
);
@ -273,11 +302,13 @@ class _TalkViewPageState extends State<TalkViewPage>
if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
print('开始录音');
logic.startProcessingAudio();
state.isLongPressing.value = true;
}
},
longPressUp: () async {
print('停止录音');
logic.stopProcessingAudio();
state.isLongPressing.value = false;
},
onClick: () async {
if (state.talkStatus.value ==
@ -300,8 +331,8 @@ class _TalkViewPageState extends State<TalkViewPage>
AppColors.mainColor,
onClick: () {
// if (UDPManage().remoteUnlock == 1) {
logic.udpOpenDoorAction();
// showDeletPasswordAlertDialog(context);
logic.udpOpenDoorAction();
// showDeletPasswordAlertDialog(context);
// } else {
// logic.showToast('请在锁设置中开启远程开锁'.tr);
// }
@ -334,36 +365,43 @@ class _TalkViewPageState extends State<TalkViewPage>
}
}
Widget bottomBtnItemWidget(String iconUrl, String name, Color backgroundColor,
{required Function() onClick,
Function()? longPress,
Function()? longPressUp}) {
Widget bottomBtnItemWidget(
String iconUrl,
String name,
Color backgroundColor, {
required Function() onClick,
Function()? longPress,
Function()? longPressUp,
}) {
double wh = 80.w;
return GestureDetector(
onTap: onClick,
onLongPress: longPress,
onLongPressUp: longPressUp,
child: SizedBox(
height: 140.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: wh,
height: wh,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular((wh + 10.w * 2) / 2)),
padding: EdgeInsets.all(20.w),
child: Image.asset(iconUrl, fit: BoxFit.fitWidth),
height: 140.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: wh,
height: wh,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular((wh + 10.w * 2) / 2),
),
SizedBox(height: 20.w),
Expanded(
child: Text(name,
style: TextStyle(fontSize: 20.sp, color: Colors.white),
textAlign: TextAlign.center))
],
)),
padding: EdgeInsets.all(20.w),
child: Image.asset(iconUrl, fit: BoxFit.fitWidth),
),
SizedBox(height: 20.w),
Expanded(
child: Text(name,
style: TextStyle(fontSize: 20.sp, color: Colors.white),
textAlign: TextAlign.center),
)
],
),
),
);
}

View File

@ -6,6 +6,7 @@ import 'package:flutter_voice_processor/flutter_voice_processor.dart';
import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:get/state_manager.dart';
import 'package:network_info_plus/network_info_plus.dart';
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart';
@ -54,7 +55,13 @@ class TalkViewState {
//
List<TalkData> audioBuffer = <TalkData>[].obs;
List<TalkData> audioBuffer2 = <TalkData>[].obs;
List<TalkData> activeAudioBuffer =<TalkData>[].obs;
List<TalkData> activeVideoBuffer =<TalkData>[].obs;
List<TalkData> videoBuffer = <TalkData>[].obs;
List<TalkData> videoBuffer2 = <TalkData>[].obs;
RxBool isPlaying = false.obs; //
Rx<TalkStatus> talkStatus = TalkStatus.none.obs; //
// startChartTalkStatus
@ -80,4 +87,5 @@ class TalkViewState {
final int sampleRate = 8000; //8000
List<List<int>> recordingAudioAllFrames = <List<int>>[]; //
RxInt rotateAngle = 0.obs; //
RxBool isLongPressing = false.obs; //
}