diff --git a/lib/talk/call/g711.dart b/lib/talk/call/g711.dart index d497f9c7..d792cec8 100755 --- a/lib/talk/call/g711.dart +++ b/lib/talk/call/g711.dart @@ -24,16 +24,28 @@ class G711 { int decodeG711(int encodedValue, bool isALaw) { if (isALaw) { // A律解码 - encodedValue = ~encodedValue; - int t = ((encodedValue & 0x0F) << 3) + 0x84; - t <<= (encodedValue & 0x70) >> 4; - return (encodedValue & 0x80) != 0 ? 0x84 - t : t - 0x84; + encodedValue ^= 0x55; // 反转最高位 + int sign = encodedValue & 0x80; // 符号位 + int exponent = (encodedValue & 0x70) >> 4; // 指数位 + int mantissa = encodedValue & 0x0F; // 尾数位 + + int pcmSample = (mantissa << 4) + 0x84; // 计算量化值 + pcmSample <<= exponent; // 根据指数位移位 + pcmSample = (sign == 0) ? pcmSample : -pcmSample; // 处理符号位 + + return pcmSample; } else { // μ律解码 - encodedValue = ~encodedValue; - int t = ((encodedValue & 0x0F) << 3) + 0x84; - t <<= (encodedValue & 0x70) >> 4; - return (encodedValue & 0x80) != 0 ? 0x84 - t : t - 0x84; + encodedValue ^= 0xFF; // 反转最高位 + int sign = encodedValue & 0x80; // 符号位 + int exponent = (encodedValue & 0x70) >> 4; // 指数位 + int mantissa = encodedValue & 0x0F; // 尾数位 + + int pcmSample = (mantissa << 3) + 0x84; // 计算量化值 + pcmSample <<= exponent + 1; // 根据指数位移位(μ律需要额外加1) + pcmSample = (sign == 0) ? pcmSample : -pcmSample; // 处理符号位 + + return pcmSample; } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart index 30f3178a..86d1e9b5 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:get/get.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'; @@ -34,6 +35,7 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle talkDataOverTimeTimerManager.cancel(); EasyLoading.showToast('已挂断'); + Get.back(); } @override diff --git a/lib/talk/startChart/views/talkView/talk_view_logic.dart b/lib/talk/startChart/views/talkView/talk_view_logic.dart index e6a17ae1..7c4d8fd1 100644 --- a/lib/talk/startChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/startChart/views/talkView/talk_view_logic.dart @@ -41,11 +41,12 @@ class TalkViewLogic extends BaseGetXController { final TalkViewState state = TalkViewState(); final LockDetailState lockDetailState = Get.put(LockDetailLogic()).state; Timer? _syncTimer; // 音视频播放刷新率定时器 + Timer? _audioTimer; // 音视频播放刷新率定时器 int _startTime = 0; // 开始播放时间戳,用于判断帧数据中的时间戳位置 - int bufferSize = 20; // 缓冲区大小(以帧为单位) - int audioBufferSize = 640; // 缓冲区大小(以帧为单位) - final List frameTimestamps = []; // 帧时间戳用于计算 FPS - int frameIntervalMs = 35; // 初始帧间隔设置为45毫秒(约22FPS) + int bufferSize = 50; // 缓冲区大小(以帧为单位) + + int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) + int audioFrameIntervalMs = 20; // 初始帧间隔设置为45毫秒(约22FPS) int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS) int maxFrameIntervalMs = 100; // 最大帧间隔(约1 FPS) // int maxFrameIntervalMs = 100; // 最大帧间隔(约10 FPS) @@ -53,11 +54,11 @@ class TalkViewLogic extends BaseGetXController { /// 初始化音频播放器 void _initFlutterPcmSound() { const int sampleRate = 8000; - FlutterPcmSound.setLogLevel(LogLevel.verbose); + FlutterPcmSound.setLogLevel(LogLevel.none); FlutterPcmSound.setup(sampleRate: sampleRate, channelCount: 1); // 设置 feed 阈值 if (Platform.isAndroid) { - FlutterPcmSound.setFeedThreshold(sampleRate ~/ 2); // Android 平台的特殊处理 + FlutterPcmSound.setFeedThreshold(1024); // Android 平台的特殊处理 } else { FlutterPcmSound.setFeedThreshold(sampleRate ~/ 32); // 非 Android 平台的处理 } @@ -88,7 +89,7 @@ class TalkViewLogic extends BaseGetXController { // 判断数据类型,进行分发处理 switch (contentType) { case TalkData_ContentTypeE.G711: - if (state.audioBuffer.length >= audioBufferSize) { + if (state.audioBuffer.length >= bufferSize) { state.audioBuffer.removeAt(0); // 丢弃最旧的数据 // readAudioBufferSize.removeAt(0); // 丢弃最旧的数据 } @@ -130,14 +131,17 @@ class TalkViewLogic extends BaseGetXController { /// 播放音频数据 void _playAudioData(TalkData talkData) async { - // final list = G711().convertList(talkData.content); - final list = G711().decodeAndDenoise(talkData.content, true, 8000, 300, 50); - // // 将 PCM 数据转换为 PcmArrayInt16 - final PcmArrayInt16 fromList = PcmArrayInt16.fromList(list); - FlutterPcmSound.feed(fromList); - if (!state.isPlaying.value) { - FlutterPcmSound.play(); - state.isPlaying.value = true; + if (state.isOpenVoice.value) { + // final list = G711().convertList(talkData.content); + final list = G711().convertList(talkData.content); + // final list = G711().decodeAndDenoise(talkData.content, true,8000, 300, 50); + // // 将 PCM 数据转换为 PcmArrayInt16 + final PcmArrayInt16 fromList = PcmArrayInt16.fromList(list); + FlutterPcmSound.feed(fromList); + if (!state.isPlaying.value) { + FlutterPcmSound.play(); + state.isPlaying.value = true; + } } } @@ -154,8 +158,6 @@ class TalkViewLogic extends BaseGetXController { Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { // 动态调整帧间隔 _adjustFrameInterval(); - - _playFrames(); }); }); } @@ -184,29 +186,37 @@ class TalkViewLogic extends BaseGetXController { _syncTimer?.cancel(); _syncTimer = Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { - _playFrames(); + // 播放视频帧 + _playVideoFrames(); + }); + + _audioTimer?.cancel(); + _audioTimer = + Timer.periodic(Duration(milliseconds: audioFrameIntervalMs), (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); + } + } }); } } - void _playFrames() { + void _playVideoFrames() { 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); - } - } - // 播放合适的视频帧 // 跳帧策略:如果缓冲区中有多个帧,且它们的时间戳都在当前时间之前,则播放最新的帧 int maxFramesToProcess = 5; // 每次最多处理 5 帧