diff --git a/lib/talk/starChart/views/native/talk_view_native_decode_logic.dart b/lib/talk/starChart/views/native/talk_view_native_decode_logic.dart index b58ebe56..5ec8ae91 100644 --- a/lib/talk/starChart/views/native/talk_view_native_decode_logic.dart +++ b/lib/talk/starChart/views/native/talk_view_native_decode_logic.dart @@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; +import 'package:flutter_sound/flutter_sound.dart'; +import 'package:flutter_sound/public/flutter_sound_recorder.dart'; import 'package:flutter_voice_processor/flutter_voice_processor.dart'; import 'package:gallery_saver/gallery_saver.dart'; import 'package:get/get.dart'; @@ -51,7 +53,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { int bufferSize = 25; // 初始化为默认大小 - int audioBufferSize = 2; // 音频默认缓冲2帧 + int audioBufferSize = 20; // 音频默认缓冲2帧 // 回绕阈值,动态调整,frameSeq较小时阈值也小 int _getFrameSeqRolloverThreshold(int lastSeq) { @@ -144,11 +146,11 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { FlutterPcmSound.setLogLevel(LogLevel.none); FlutterPcmSound.setup(sampleRate: sampleRate, channelCount: 1); // 设置 feed 阈值 - if (Platform.isAndroid) { - FlutterPcmSound.setFeedThreshold(1024); // Android 平台的特殊处理 - } else { - FlutterPcmSound.setFeedThreshold(2000); // 非 Android 平台的处理 - } + // if (Platform.isAndroid) { + // FlutterPcmSound.setFeedThreshold(1024); // Android 平台的特殊处理 + // } else { + // FlutterPcmSound.setFeedThreshold(4096); // 非 Android 平台的处理 + // } } /// 挂断 @@ -499,15 +501,18 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { /// 播放音频数据 void _playAudioData(TalkData talkData) async { if (state.isOpenVoice.value && state.isLoading.isFalse) { - final list = - G711().decodeAndDenoise(talkData.content, true, 8000, 300, 150); - // // 将 PCM 数据转换为 PcmArrayInt16 - final PcmArrayInt16 fromList = PcmArrayInt16.fromList(list); + List encodedData = G711Tool.decode(talkData.content, 0); // 0表示A-law + // 将 PCM 数据转换为 PcmArrayInt16 + final PcmArrayInt16 fromList = PcmArrayInt16.fromList(encodedData); FlutterPcmSound.feed(fromList); if (!state.isPlaying.value) { + AppLog.log('play'); FlutterPcmSound.play(); state.isPlaying.value = true; } + } else if (state.isOpenVoice.isFalse) { + FlutterPcmSound.pause(); + state.isPlaying.value = false; } } @@ -573,8 +578,6 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { void onInit() { super.onInit(); - // 启动监听音视频数据流 - _startListenTalkData(); // 启动监听对讲状态 _startListenTalkStatus(); // 在没有监听成功之前赋值一遍状态 @@ -596,6 +599,9 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { // 初始化H264帧缓冲区 state.h264FrameBuffer.clear(); state.isProcessingFrame = false; + + // 启动监听音视频数据流 + _startListenTalkData(); } @override @@ -639,7 +645,9 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { // 清空已解码I帧集合 _decodedIFrames.clear(); - + _startProcessingAudioTimer?.cancel(); + _startProcessingAudioTimer = null; + _bufferedAudioFrames.clear(); super.onClose(); } @@ -652,33 +660,12 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { /// 更新发送预期数据 void updateTalkExpect() { - // 清晰度与VideoTypeE的映射 - final Map qualityToVideoType = { - '标清': VideoTypeE.H264, - '高清': VideoTypeE.H264_720P, - // 可扩展更多清晰度 - }; - TalkExpectReq talkExpectReq = TalkExpectReq(); state.isOpenVoice.value = !state.isOpenVoice.value; - // 根据当前清晰度动态设置videoType - VideoTypeE currentVideoType = - qualityToVideoType[state.currentQuality.value] ?? VideoTypeE.H264; - if (!state.isOpenVoice.value) { - talkExpectReq = TalkExpectReq( - videoType: [currentVideoType], - audioType: [], - ); - showToast('已静音'.tr); + if (state.isOpenVoice.isTrue) { + FlutterPcmSound.play(); } else { - talkExpectReq = TalkExpectReq( - videoType: [currentVideoType], - audioType: [AudioTypeE.G711], - ); + FlutterPcmSound.pause(); } - - /// 修改发送预期数据 - StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer( - talkExpect: talkExpectReq); } /// 截图并保存到相册 @@ -762,8 +749,11 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { state.voiceProcessor = VoiceProcessor.instance; } + Timer? _startProcessingAudioTimer; + //开始录音 Future startProcessingAudio() async { + try { if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) { await state.voiceProcessor?.start(state.frameLength, state.sampleRate); @@ -781,7 +771,6 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { } on PlatformException catch (ex) { // state.errorMessage.value = 'Failed to start recorder: $ex'; } - state.isOpenVoice.value = false; } /// 停止录音 @@ -803,51 +792,10 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { } finally { final bool? isRecording = await state.voiceProcessor?.isRecording(); state.isRecordingAudio.value = isRecording!; - state.isOpenVoice.value = true; } - } - -// 音频帧处理 - Future _onFrame(List frame) async { - // 添加最大缓冲限制 - if (_bufferedAudioFrames.length > state.frameLength * 3) { - _bufferedAudioFrames.clear(); // 清空过多积累的数据 - return; - } - - // 首先应用固定增益提升基础音量 - List amplifiedFrame = _applyGain(frame, 1.6); - // 编码为G711数据 - List encodedData = G711Tool.encode(amplifiedFrame, 0); // 0表示A-law - _bufferedAudioFrames.addAll(encodedData); - // 使用相对时间戳 - final int ms = DateTime.now().millisecondsSinceEpoch % 1000000; // 使用循环时间戳 - int getFrameLength = state.frameLength; - if (Platform.isIOS) { - getFrameLength = state.frameLength * 2; - } - - // 添加发送间隔控制 - if (_bufferedAudioFrames.length >= state.frameLength) { - try { - await StartChartManage().sendTalkDataMessage( - talkData: TalkData( - content: _bufferedAudioFrames, - contentType: TalkData_ContentTypeE.G711, - durationMs: ms, - ), - ); - } finally { - _bufferedAudioFrames.clear(); // 确保清理缓冲区 - } - } else { - _bufferedAudioFrames.addAll(encodedData); - } - } - -// 错误监听 - void _onError(VoiceProcessorException error) { - AppLog.log(error.message!); + _startProcessingAudioTimer?.cancel(); + _startProcessingAudioTimer = null; + _bufferedAudioFrames.clear(); } // 添加音频增益处理方法 @@ -873,453 +821,98 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { return result; } - - /// 追加写入一帧到h264文件(需传入帧数据和帧类型frameType) - Future _appendH264FrameToFile( - List frameData, TalkDataH264Frame_FrameTypeE frameType) async { - try { - if (state.h264File == null) { - await _initH264File(); - } - // NALU分割函数,返回每个NALU的完整字节数组 - List> splitNalus(List data) { - List> nalus = []; - int i = 0; - while (i < data.length - 3) { - int start = -1; - int next = -1; - if (data[i] == 0x00 && data[i + 1] == 0x00) { - if (data[i + 2] == 0x01) { - start = i; - i += 3; - } else if (i + 3 < data.length && - data[i + 2] == 0x00 && - data[i + 3] == 0x01) { - start = i; - i += 4; - } else { - i++; - continue; - } - next = i; - while (next < data.length - 3) { - if (data[next] == 0x00 && - data[next + 1] == 0x00 && - ((data[next + 2] == 0x01) || - (data[next + 2] == 0x00 && data[next + 3] == 0x01))) { - break; - } - next++; - } - nalus.add(data.sublist(start, next)); - i = next; - } else { - i++; - } - } - int nalusTotalLen = - nalus.isNotEmpty ? nalus.fold(0, (p, n) => p + n.length) : 0; - if (nalus.isEmpty && data.isNotEmpty) { - nalus.add(data); - } else if (nalus.isNotEmpty && nalusTotalLen < data.length) { - nalus.add(data.sublist(nalusTotalLen)); - } - return nalus; - } - - // 优化:I帧前只缓存SPS/PPS/IDR,首次写入严格顺序 - if (!_hasWrittenFirstIFrame) { - final nalus = splitNalus(frameData); - List> spsList = []; - List> ppsList = []; - List> idrList = []; - for (final nalu in nalus) { - int offset = 0; - if (nalu.length >= 4 && nalu[0] == 0x00 && nalu[1] == 0x00) { - if (nalu[2] == 0x01) - offset = 3; - else if (nalu[2] == 0x00 && nalu[3] == 0x01) offset = 4; - } - if (nalu.length > offset) { - int naluType = nalu[offset] & 0x1F; - if (naluType == 7) { - spsList.add(nalu); - // AppLog.log('SPS内容: ' + - // nalu - // .map((b) => b.toRadixString(16).padLeft(2, '0')) - // .join(' ')); - } else if (naluType == 8) { - ppsList.add(nalu); - // AppLog.log('PPS内容: ' + - // nalu - // .map((b) => b.toRadixString(16).padLeft(2, '0')) - // .join(' ')); - } else if (naluType == 5) { - idrList.add(nalu); - } - // 其他类型不缓存也不写入头部 - } - } - // 只在首次I帧写入前缓存,严格顺序写入 - if (spsList.isNotEmpty && ppsList.isNotEmpty && idrList.isNotEmpty) { - for (final sps in spsList) { - await _writeSingleFrameToFile(_ensureStartCode(sps)); - // AppLog.log('写入顺序: SPS'); - } - for (final pps in ppsList) { - await _writeSingleFrameToFile(_ensureStartCode(pps)); - // AppLog.log('写入顺序: PPS'); - } - for (final idr in idrList) { - await _writeSingleFrameToFile(_ensureStartCode(idr)); - // AppLog.log('写入顺序: IDR'); - } - _hasWrittenFirstIFrame = true; - } else { - // 未收齐SPS/PPS/IDR则继续缓存,等待下次I帧 - if (spsList.isNotEmpty) _preIFrameCache.addAll(spsList); - if (ppsList.isNotEmpty) _preIFrameCache.addAll(ppsList); - if (idrList.isNotEmpty) _preIFrameCache.addAll(idrList); - } - } else { - // 首帧后只写入IDR和P帧 - final nalus = splitNalus(frameData); - for (final nalu in nalus) { - int offset = 0; - if (nalu.length >= 4 && nalu[0] == 0x00 && nalu[1] == 0x00) { - if (nalu[2] == 0x01) - offset = 3; - else if (nalu[2] == 0x00 && nalu[3] == 0x01) offset = 4; - } - if (nalu.length > offset) { - int naluType = nalu[offset] & 0x1F; - if (naluType == 5) { - await _writeSingleFrameToFile(_ensureStartCode(nalu)); - // AppLog.log('写入顺序: IDR'); - } else if (naluType == 1) { - await _writeSingleFrameToFile(_ensureStartCode(nalu)); - // AppLog.log('写入顺序: P帧'); - } else if (naluType == 7) { - // AppLog.log('遇到新SPS,已忽略'); - } else if (naluType == 8) { - // AppLog.log('遇到新PPS,已忽略'); - } - // 其他类型不写入 - } - } - } - } catch (e) { - AppLog.log('写入H264帧到文件失败: $e'); + static const int chunkSize = 320; // 每次发送320字节(10ms G.711) + static const int intervalMs = 40; // 每40ms发送一次(4个chunk) + void _sendAudioChunk(Timer timer) async { + if (_bufferedAudioFrames.length < chunkSize) { + // 数据不足,等待下一周期 + return; } + + // 截取前 chunkSize 个字节 + final chunk = _bufferedAudioFrames.sublist(0, chunkSize); + // 更新缓冲区:移除已发送部分 + _bufferedAudioFrames.removeRange(0, chunkSize); + + // 获取时间戳(相对时间) + final int ms = DateTime.now().millisecondsSinceEpoch % 1000000; + + print('Send chunk ${timer.tick}: ${chunk.take(10).toList()}...'); + + await StartChartManage().sendTalkDataMessage( + talkData: TalkData( + content: chunk, + contentType: TalkData_ContentTypeE.G711, + durationMs: ms, + ), + ); } - // 统一NALU起始码为0x00000001 - List _ensureStartCode(List nalu) { - if (nalu.length >= 4 && - nalu[0] == 0x00 && - nalu[1] == 0x00 && - nalu[2] == 0x00 && - nalu[3] == 0x01) { - return nalu; - } else if (nalu.length >= 3 && - nalu[0] == 0x00 && - nalu[1] == 0x00 && - nalu[2] == 0x01) { - return [0x00, 0x00, 0x00, 0x01] + nalu.sublist(3); - } else { - return [0x00, 0x00, 0x00, 0x01] + nalu; +// 音频帧处理 + Future _onFrame(List frame) async { + final applyGain = _applyGain(frame, 1.6); + + // 编码为G711数据 + List encodedData = G711Tool.encode(applyGain, 0); // 0表示A-law + _bufferedAudioFrames.addAll(encodedData); + + + // 启动定时发送器(仅启动一次) + if (_startProcessingAudioTimer == null && _bufferedAudioFrames.length > chunkSize) { + _startProcessingAudioTimer = Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk); } + // if (_startProcessingAudioTimer == null && + // _bufferedAudioFrames.length > 320) { + // // 每 10ms 发送一次 320 长度的数据 + // const int intervalMs = 40; + // const int chunkSize = 320; + // _startProcessingAudioTimer = + // Timer.periodic(Duration(milliseconds: intervalMs), (timer) async { + // // 从 _bufferedAudioFrames 中截取 320 个数据(循环发送) + // int startIndex = (timer.tick - 1) * chunkSize; // tick 从 1 开始 + // int endIndex = startIndex + chunkSize; + // // 使用相对时间戳 + // final int ms = + // DateTime.now().millisecondsSinceEpoch % 1000000; // 使用循环时间戳 + // + // // 循环使用数据(防止越界) + // List chunk; + // if (endIndex <= _bufferedAudioFrames.length) { + // chunk = _bufferedAudioFrames.sublist(startIndex, endIndex); + // } else { + // // 超出范围时循环 + // chunk = []; + // while (chunk.length < chunkSize) { + // int remaining = chunkSize - chunk.length; + // int take = endIndex > _bufferedAudioFrames.length + // ? _bufferedAudioFrames.length - + // (startIndex % _bufferedAudioFrames.length) + // : remaining; + // take = take.clamp(0, remaining); + // int start = startIndex % _bufferedAudioFrames.length; + // chunk.addAll(_bufferedAudioFrames.sublist(start, + // (start + take).clamp(start, _bufferedAudioFrames.length))); + // startIndex += take; + // } + // } + // // 示例:打印前10个数据 + // print('Send chunk ${timer.tick}: ${chunk.take(10).toList()}...'); + // await StartChartManage().sendTalkDataMessage( + // talkData: TalkData( + // content: chunk, + // contentType: TalkData_ContentTypeE.G711, + // durationMs: ms, + // ), + // ); + // }); + // } } - /// 实际写入单帧到文件(带NALU头判断) - Future _writeSingleFrameToFile(List frameData) async { - bool hasNaluHeader = false; - if (frameData.length >= 4 && - frameData[0] == 0x00 && - frameData[1] == 0x00 && - ((frameData[2] == 0x01) || - (frameData[2] == 0x00 && frameData[3] == 0x01))) { - hasNaluHeader = true; - } - if (hasNaluHeader) { - await state.h264File!.writeAsBytes(frameData, mode: FileMode.append); - } else { - final List naluHeader = [0x00, 0x00, 0x01]; - await state.h264File! - .writeAsBytes(naluHeader + frameData, mode: FileMode.append); - } +// 错误监听 + void _onError(VoiceProcessorException error) { + AppLog.log(error.message!); } - /// 初始化h264文件 - Future _initH264File() async { - try { - if (state.h264File != null) return; - // 获取Download目录 - Directory? downloadsDir; - if (Platform.isAndroid) { - // Android 10+ 推荐用getExternalStorageDirectory() - downloadsDir = await getExternalStorageDirectory(); - // 兼容部分ROM,优先用Download - final downloadPath = '/storage/emulated/0/Download'; - if (Directory(downloadPath).existsSync()) { - downloadsDir = Directory(downloadPath); - } - } else { - downloadsDir = await getApplicationDocumentsDirectory(); - } - final filePath = - '${downloadsDir!.path}/video_${DateTime.now().millisecondsSinceEpoch}.h264'; - state.h264FilePath = filePath; - state.h264File = File(filePath); - if (!await state.h264File!.exists()) { - await state.h264File!.create(recursive: true); - } - AppLog.log('H264文件初始化: $filePath'); - } catch (e) { - AppLog.log('H264文件初始化失败: $e'); - } - } - - /// 关闭h264文件 - Future _closeH264File() async { - try { - if (state.h264File != null) { - AppLog.log('H264文件已关闭: ${state.h264FilePath ?? ''}'); - } - state.h264File = null; - state.h264FilePath = null; - _preIFrameCache.clear(); - _hasWrittenFirstIFrame = false; - } catch (e) { - AppLog.log('关闭H264文件时出错: $e'); - } - } - - /// 从I帧数据中分割NALU并将SPS/PPS优先放入缓冲区(用于缓冲区发送) - void _extractAndBufferSpsPpsForBuffer( - List frameData, int durationMs, int frameSeq, int frameSeqI) { - List> splitNalus(List data) { - List> nalus = []; - int i = 0; - while (i < data.length - 3) { - int start = -1; - int next = -1; - if (data[i] == 0x00 && data[i + 1] == 0x00) { - if (data[i + 2] == 0x01) { - start = i; - i += 3; - } else if (i + 3 < data.length && - data[i + 2] == 0x00 && - data[i + 3] == 0x01) { - start = i; - i += 4; - } else { - i++; - continue; - } - next = i; - while (next < data.length - 3) { - if (data[next] == 0x00 && - data[next + 1] == 0x00 && - ((data[next + 2] == 0x01) || - (data[next + 2] == 0x00 && data[next + 3] == 0x01))) { - break; - } - next++; - } - nalus.add(data.sublist(start, next)); - i = next; - } else { - i++; - } - } - int nalusTotalLen = - nalus.isNotEmpty ? nalus.fold(0, (p, n) => p + n.length) : 0; - if (nalus.isEmpty && data.isNotEmpty) { - nalus.add(data); - } else if (nalus.isNotEmpty && nalusTotalLen < data.length) { - nalus.add(data.sublist(nalusTotalLen)); - } - return nalus; - } - - final nalus = splitNalus(frameData); - for (final nalu in nalus) { - int offset = 0; - if (nalu.length >= 4 && nalu[0] == 0x00 && nalu[1] == 0x00) { - if (nalu[2] == 0x01) - offset = 3; - else if (nalu[2] == 0x00 && nalu[3] == 0x01) offset = 4; - } - if (nalu.length > offset) { - int naluType = nalu[offset] & 0x1F; - if (naluType == 7) { - // SPS - hasSps = true; - // 只在首次或内容变化时更新缓存 - if (spsCache == null || !_listEquals(spsCache!, nalu)) { - spsCache = List.from(nalu); - } - } else if (naluType == 8) { - // PPS - hasPps = true; - if (ppsCache == null || !_listEquals(ppsCache!, nalu)) { - ppsCache = List.from(nalu); - } - } - } - } - } - - // 新增:List比较工具 - bool _listEquals(List a, List b) { - if (a.length != b.length) return false; - for (int i = 0; i < a.length; i++) { - if (a[i] != b[i]) return false; - } - return true; - } - - // 新增:I帧处理方法 - // void _handleIFrameWithSpsPpsAndIdr( - // List frameData, int durationMs, int frameSeq, int frameSeqI) { - // // 清空缓冲区,丢弃I帧前所有未处理帧(只保留SPS/PPS/I帧) - // state.h264FrameBuffer.clear(); - // _extractAndBufferSpsPpsForBuffer( - // frameData, durationMs, frameSeq, frameSeqI); - // // 只要缓存有SPS/PPS就先写入,再写I帧本体(只写IDR) - // if (spsCache == null || ppsCache == null) { - // // 没有SPS/PPS缓存,丢弃本次I帧 - // return; - // } - // // 先写入SPS/PPS - // _addFrameToBuffer(spsCache!, TalkDataH264Frame_FrameTypeE.I, durationMs, - // frameSeq, frameSeqI); - // _addFrameToBuffer(ppsCache!, TalkDataH264Frame_FrameTypeE.I, durationMs, - // frameSeq, frameSeqI); - // // 分割I帧包,只写入IDR(type 5) - // List> nalus = []; - // int i = 0; - // List data = frameData; - // while (i < data.length - 3) { - // int start = -1; - // int next = -1; - // if (data[i] == 0x00 && data[i + 1] == 0x00) { - // if (data[i + 2] == 0x01) { - // start = i; - // i += 3; - // } else if (i + 3 < data.length && - // data[i + 2] == 0x00 && - // data[i + 3] == 0x01) { - // start = i; - // i += 4; - // } else { - // i++; - // continue; - // } - // next = i; - // while (next < data.length - 3) { - // if (data[next] == 0x00 && - // data[next + 1] == 0x00 && - // ((data[next + 2] == 0x01) || - // (data[next + 2] == 0x00 && data[next + 3] == 0x01))) { - // break; - // } - // next++; - // } - // nalus.add(data.sublist(start, next)); - // i = next; - // } else { - // i++; - // } - // } - // int nalusTotalLen = - // nalus.isNotEmpty ? nalus.fold(0, (p, n) => p + n.length) : 0; - // if (nalus.isEmpty && data.isNotEmpty) { - // nalus.add(data); - // } else if (nalus.isNotEmpty && nalusTotalLen < data.length) { - // nalus.add(data.sublist(nalusTotalLen)); - // } - // for (final nalu in nalus) { - // int offset = 0; - // if (nalu.length >= 4 && nalu[0] == 0x00 && nalu[1] == 0x00) { - // if (nalu[2] == 0x01) - // offset = 3; - // else if (nalu[2] == 0x00 && nalu[3] == 0x01) offset = 4; - // } - // if (nalu.length > offset) { - // int naluType = nalu[offset] & 0x1F; - // if (naluType == 5) { - // _addFrameToBuffer(nalu, TalkDataH264Frame_FrameTypeE.I, durationMs, - // frameSeq, frameSeqI); - // } - // } - // } - // } - - // 新增:P帧处理方法 - // void _handlePFrame( - // List frameData, int durationMs, int frameSeq, int frameSeqI) { - // // 只写入P帧(type 1) - // List> nalus = []; - // int i = 0; - // List data = frameData; - // while (i < data.length - 3) { - // int start = -1; - // int next = -1; - // if (data[i] == 0x00 && data[i + 1] == 0x00) { - // if (data[i + 2] == 0x01) { - // start = i; - // i += 3; - // } else if (i + 3 < data.length && - // data[i + 2] == 0x00 && - // data[i + 3] == 0x01) { - // start = i; - // i += 4; - // } else { - // i++; - // continue; - // } - // next = i; - // while (next < data.length - 3) { - // if (data[next] == 0x00 && - // data[next + 1] == 0x00 && - // ((data[next + 2] == 0x01) || - // (data[next + 2] == 0x00 && data[next + 3] == 0x01))) { - // break; - // } - // next++; - // } - // nalus.add(data.sublist(start, next)); - // i = next; - // } else { - // i++; - // } - // } - // int nalusTotalLen = - // nalus.isNotEmpty ? nalus.fold(0, (p, n) => p + n.length) : 0; - // if (nalus.isEmpty && data.isNotEmpty) { - // nalus.add(data); - // } else if (nalus.isNotEmpty && nalusTotalLen < data.length) { - // nalus.add(data.sublist(nalusTotalLen)); - // } - // for (final nalu in nalus) { - // int offset = 0; - // if (nalu.length >= 4 && nalu[0] == 0x00 && nalu[1] == 0x00) { - // if (nalu[2] == 0x01) - // offset = 3; - // else if (nalu[2] == 0x00 && nalu[3] == 0x01) offset = 4; - // } - // if (nalu.length > offset) { - // int naluType = nalu[offset] & 0x1F; - // if (naluType == 1) { - // _addFrameToBuffer(nalu, TalkDataH264Frame_FrameTypeE.P, durationMs, - // frameSeq, frameSeqI); - // } - // } - // } - // } - // 切换清晰度的方法,后续补充具体实现 void onQualityChanged(String quality) async { state.currentQuality.value = quality; @@ -1432,6 +1025,9 @@ class TalkViewNativeDecodeLogic extends BaseGetXController { // 判断数据类型,进行分发处理 switch (contentType) { case TalkData_ContentTypeE.G711: + if (!state.isOpenVoice.value) { + return; + } if (state.audioBuffer.length >= audioBufferSize) { state.audioBuffer.removeAt(0); // 丢弃最旧的数据 }