From b876f608e4e261565090715b4c6a26e9b6be055a Mon Sep 17 00:00:00 2001 From: liyi Date: Thu, 16 Jan 2025 14:02:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E8=B0=83=E6=95=B4=E5=8F=91=E9=80=81g711?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E6=95=B0=E6=8D=AE=E3=80=81=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=9B=9E=E5=A3=B0=E6=B6=88=E9=99=A4=E3=80=81=E5=A2=9E=E5=A4=A7?= =?UTF-8?q?=E7=BC=93=E5=86=B2=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lockDetail/lockDetail_page.dart | 31 +-- .../configuringWifi_logic.dart | 3 - lib/talk/call/g711.dart | 59 ++-- lib/talk/startChart/start_chart_manage.dart | 2 +- .../views/talkView/talk_view_logic.dart | 119 ++++---- .../views/talkView/talk_view_page.dart | 255 +++++++++--------- .../views/talkView/talk_view_state.dart | 2 + 7 files changed, 200 insertions(+), 271 deletions(-) diff --git a/lib/main/lockDetail/lockDetail/lockDetail_page.dart b/lib/main/lockDetail/lockDetail/lockDetail_page.dart index e61b1d3a..91cd15a5 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_page.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_page.dart @@ -1599,33 +1599,8 @@ class _LockDetailPageState extends State } Future _handleLockMonitor() async { - final lockId = state.keyInfos.value.lockId; - final LockSetInfoEntity entity = - await ApiRepository.to.getLockSettingInfoData( - lockId: lockId.toString(), - ); - if (entity.errorCode!.codeIsSuccessful) { - final LockSetInfoData data = entity.data!; - final mac = data.lockBasicInfo?.mac; - if (mac != null && mac.isNotEmpty) { - final DeviceNetwork deviceNetworkInfo = await ApiRepository.to - .getDeviceNetwork(deviceType: 2, deviceMac: mac); - if (deviceNetworkInfo.data?.wifiName == null) { - // 未找到配网信息 - logic.showToast('请先进行配网'); - return; - } else { - final peerId = deviceNetworkInfo?.data?.peerId; - if (peerId == null || peerId.isEmpty) { - throw Exception('设备peerId为空'); - } - // 设置锁的peerID - StartChartManage().lockPeerId = peerId; - // 发送监控id - StartChartManage().startCallRequestMessageTimer( - ToPeerId: StartChartManage().lockPeerId ?? ''); - } - } - } + // 发送监控id + StartChartManage().startCallRequestMessageTimer( + ToPeerId: StartChartManage().lockPeerId ?? ''); } } diff --git a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart index 9baf2577..44c1fcbd 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart @@ -148,7 +148,6 @@ class ConfiguringWifiLogic extends BaseGetXController { } state.sureBtnState.value = 1; - showEasyLoading(); showBlueConnetctToastTimer(action: () { dismissEasyLoading(); state.sureBtnState.value = 0; @@ -198,8 +197,6 @@ class ConfiguringWifiLogic extends BaseGetXController { gatewayConfigurationStr: state.getGatewayConfigurationStr, ); } else if (connectionState == BluetoothConnectionState.disconnected) { - dismissEasyLoading(); - cancelBlueConnetctToastTimer(); state.sureBtnState.value = 0; if (state.ifCurrentScreen.value == true) { showBlueConnetctToast(); diff --git a/lib/talk/call/g711.dart b/lib/talk/call/g711.dart index 23b7f9fa..d497f9c7 100755 --- a/lib/talk/call/g711.dart +++ b/lib/talk/call/g711.dart @@ -3,54 +3,12 @@ import 'dart:math'; import 'package:flutter/services.dart'; class G711 { - - List _aLawTable = [ - 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 - ]; - Future> readAssetFile(String assetPath) async { final ByteData data = await rootBundle.load(assetPath); final List bytes = data.buffer.asUint8List(); return bytes; } - List encodeALaw(List pcmSamples) { - final List aLawSamples = []; - - for (final sample in pcmSamples) { - // 将 16 位 PCM 样本归一化为 13 位有符号整数 - int normalizedSample = sample >> 3; - - // 获取样本的符号位 - int sign = (normalizedSample & 0x8000) != 0 ? 0x80 : 0x00; - - // 取绝对值 - normalizedSample = normalizedSample.abs(); - - // 查找编码表中的段 - int segment = _aLawTable[normalizedSample >> 8]; - - // 计算量化值 - int quantizedValue = (normalizedSample >> (segment + 3)) & 0x0F; - - // 生成 A-law 编码 - int aLawSample = sign | (segment << 4) | quantizedValue; - - // 添加到结果列表 - aLawSamples.add(aLawSample); - } - - return aLawSamples; - } - - int ALawToLinear(int aVal) { // 取反 aVal = ~aVal; @@ -107,6 +65,18 @@ class G711 { .toList(); } + List removeEcho(List audioData, int delaySamples, double decay) { + final List processedData = []; + for (int i = 0; i < audioData.length; i++) { + int sample = audioData[i]; + if (i >= delaySamples) { + sample -= (audioData[i - delaySamples] * decay).round(); + } + processedData.add(sample); + } + return processedData; + } + /// 解码并降噪 List decodeAndDenoise(List encodedData, bool isALaw, int sampleRate, double cutoffFreq, int threshold) { @@ -121,7 +91,10 @@ class G711 { // 噪声门 List denoisedData = noiseGate(filteredData, threshold); - return denoisedData; + // 回声消除处理 + final List processedData = removeEcho(denoisedData, 160, 0.5); + + return processedData; } //711解码为pcm数据 diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index f29d052d..df512c64 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -390,7 +390,7 @@ class StartChartManage { // 如果已经处于等待接听状态就不发送 if (talkStatus.status != TalkStatus.proactivelyCallWaitingAnswer) { // 停止播放铃声 - AudioPlayerManager().playRingtone(); + // AudioPlayerManager().playRingtone(); Get.toNamed( Routers.starChartTalkView, ); diff --git a/lib/talk/startChart/views/talkView/talk_view_logic.dart b/lib/talk/startChart/views/talkView/talk_view_logic.dart index 49bef73a..e6a17ae1 100644 --- a/lib/talk/startChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/startChart/views/talkView/talk_view_logic.dart @@ -31,8 +31,7 @@ import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/startChart/start_chart_manage.dart'; import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart'; -import 'package:star_lock/talk/udp/udp_manage.dart'; -import 'package:star_lock/talk/udp/udp_senderManage.dart'; + import 'package:star_lock/tools/bugly/bugly_tool.dart'; import 'package:star_lock/tools/storage.dart'; @@ -44,8 +43,9 @@ class TalkViewLogic extends BaseGetXController { Timer? _syncTimer; // 音视频播放刷新率定时器 int _startTime = 0; // 开始播放时间戳,用于判断帧数据中的时间戳位置 int bufferSize = 20; // 缓冲区大小(以帧为单位) + int audioBufferSize = 640; // 缓冲区大小(以帧为单位) final List frameTimestamps = []; // 帧时间戳用于计算 FPS - int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) + int frameIntervalMs = 35; // 初始帧间隔设置为45毫秒(约22FPS) int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS) int maxFrameIntervalMs = 100; // 最大帧间隔(约1 FPS) // int maxFrameIntervalMs = 100; // 最大帧间隔(约10 FPS) @@ -53,11 +53,11 @@ class TalkViewLogic extends BaseGetXController { /// 初始化音频播放器 void _initFlutterPcmSound() { const int sampleRate = 8000; - FlutterPcmSound.setLogLevel(LogLevel.none); + FlutterPcmSound.setLogLevel(LogLevel.verbose); FlutterPcmSound.setup(sampleRate: sampleRate, channelCount: 1); // 设置 feed 阈值 if (Platform.isAndroid) { - FlutterPcmSound.setFeedThreshold(-1); // Android 平台的特殊处理 + FlutterPcmSound.setFeedThreshold(sampleRate ~/ 2); // Android 平台的特殊处理 } else { FlutterPcmSound.setFeedThreshold(sampleRate ~/ 32); // 非 Android 平台的处理 } @@ -88,10 +88,14 @@ class TalkViewLogic extends BaseGetXController { // 判断数据类型,进行分发处理 switch (contentType) { case TalkData_ContentTypeE.G711: - if (state.audioBuffer.length >= bufferSize) { + if (state.audioBuffer.length >= audioBufferSize) { state.audioBuffer.removeAt(0); // 丢弃最旧的数据 + // readAudioBufferSize.removeAt(0); // 丢弃最旧的数据 } state.audioBuffer.add(talkData); // 添加新数据 + + // readAudioBufferSize.add(talkData.content); + break; case TalkData_ContentTypeE.Image: if (state.videoBuffer.length >= bufferSize) { @@ -128,7 +132,6 @@ 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); @@ -145,44 +148,14 @@ class TalkViewLogic extends BaseGetXController { /// 启动播放 void _startPlayback() { - int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) - Future.delayed(Duration(milliseconds: 800), () { _startTime = DateTime.now().millisecondsSinceEpoch; _syncTimer ??= Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { - final currentTime = DateTime.now().millisecondsSinceEpoch; - final elapsedTime = currentTime - _startTime; - // 动态调整帧间隔 _adjustFrameInterval(); - // 播放合适的音频帧 - if (state.audioBuffer.isNotEmpty && - state.audioBuffer.first.durationMs <= elapsedTime) { - // 判断音频开关是否打开 - if (state.isOpenVoice.value) { - AppLog.log('播放音频:${state.audioBuffer[0]}'); - _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)); - } - } + _playFrames(); }); }); } @@ -211,42 +184,46 @@ class TalkViewLogic extends BaseGetXController { _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); - } - } - - // 播放合适的视频帧 - // 跳帧策略:如果缓冲区中有多个帧,且它们的时间戳都在当前时间之前,则播放最新的帧 - 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++; - } + _playFrames(); }); } } + void _playFrames() { + 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 帧 + 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++; + } + } + /// 修改网络状态 void updateNetworkStatus(int currentTimestamp) { if (state.lastFrameTimestamp.value != 0) { @@ -474,8 +451,6 @@ class TalkViewLogic extends BaseGetXController { // 停止播放音频 _stopPlayG711Data(); stopProcessingAudio(); - // 状态错误,返回页面 - Get.back(); } /// 更新发送预期数据 diff --git a/lib/talk/startChart/views/talkView/talk_view_page.dart b/lib/talk/startChart/views/talkView/talk_view_page.dart index dac55d6d..6d5da3a1 100644 --- a/lib/talk/startChart/views/talkView/talk_view_page.dart +++ b/lib/talk/startChart/views/talkView/talk_view_page.dart @@ -62,143 +62,150 @@ class _TalkViewPageState extends State @override Widget build(BuildContext context) { - return SizedBox( - width: 1.sw, - height: 1.sh, - child: Stack( - alignment: Alignment.center, - children: [ - Obx( - () { - final screenWidth = MediaQuery.of(context).size.width; - final screenHeight = MediaQuery.of(context).size.height; + return WillPopScope( + onWillPop: () async { + // 返回 false 表示禁止退出 + return false; + }, + child: SizedBox( + width: 1.sw, + height: 1.sh, + child: Stack( + alignment: Alignment.center, + children: [ + Obx( + () { + final screenWidth = MediaQuery.of(context).size.width; + final screenHeight = MediaQuery.of(context).size.height; - final logicalWidth = MediaQuery.of(context).size.width; - final logicalHeight = MediaQuery.of(context).size.height; - final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; + final logicalWidth = MediaQuery.of(context).size.width; + final logicalHeight = MediaQuery.of(context).size.height; + final devicePixelRatio = + MediaQuery.of(context).devicePixelRatio; - // 计算物理像素值 - final physicalWidth = logicalWidth * devicePixelRatio; - final physicalHeight = logicalHeight * devicePixelRatio; + // 计算物理像素值 + final physicalWidth = logicalWidth * devicePixelRatio; + final physicalHeight = logicalHeight * devicePixelRatio; - // 旋转后的图片尺寸 - final rotatedImageWidth = 480; // 原始高度 - final rotatedImageHeight = 864; // 原始宽度 + // 旋转后的图片尺寸 + final rotatedImageWidth = 480; // 原始高度 + final rotatedImageHeight = 864; // 原始宽度 - // 计算缩放比例 - final scaleWidth = physicalWidth / rotatedImageWidth; - final scaleHeight = physicalHeight / rotatedImageHeight; - final scale = max(scaleWidth, scaleHeight); // 选择较大的缩放比例 + // 计算缩放比例 + final scaleWidth = physicalWidth / rotatedImageWidth; + final scaleHeight = physicalHeight / rotatedImageHeight; + final scale = max(scaleWidth, scaleHeight); // 选择较大的缩放比例 - return state.listData.value.isEmpty - ? Image.asset( - 'images/main/monitorBg.png', - width: screenWidth, - height: screenHeight, - fit: BoxFit.cover, - ) - : PopScope( - canPop: false, - child: RepaintBoundary( - key: state.globalKey, - child: Transform.rotate( - angle: - state.rotateAngle.value * (pi / 180), // 旋转 90 度 - child: Transform.scale( - scale: scale, // 动态计算的缩放比例 - child: Image.memory( - state.listData.value, - gaplessPlayback: true, - fit: BoxFit.cover, - filterQuality: FilterQuality.high, - errorBuilder: ( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return Container(color: Colors.transparent); - }, + return state.listData.value.isEmpty + ? Image.asset( + 'images/main/monitorBg.png', + width: screenWidth, + height: screenHeight, + fit: BoxFit.cover, + ) + : PopScope( + canPop: false, + child: RepaintBoundary( + key: state.globalKey, + child: Transform.rotate( + angle: + state.rotateAngle.value * (pi / 180), // 旋转 90 度 + child: Transform.scale( + scale: scale, // 动态计算的缩放比例 + child: Image.memory( + state.listData.value, + gaplessPlayback: true, + fit: BoxFit.cover, + filterQuality: FilterQuality.high, + errorBuilder: ( + BuildContext context, + Object error, + StackTrace? stackTrace, + ) { + return Container(color: Colors.transparent); + }, + ), ), ), ), - ), - ); - }, - ), - Obx(() => state.listData.value.isEmpty - ? Positioned( - bottom: 300.h, - child: Text( - '正在创建安全连接...'.tr, - style: TextStyle(color: Colors.black, fontSize: 26.sp), - )) - : Container()), - Positioned( - bottom: 10.w, - child: Container( - width: 1.sw - 30.w * 2, - // height: 300.h, - margin: EdgeInsets.all(30.w), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.2), - borderRadius: BorderRadius.circular(20.h)), - child: Column( - children: [ - SizedBox(height: 20.h), - bottomTopBtnWidget(), - SizedBox(height: 20.h), - bottomBottomBtnWidget(), - SizedBox(height: 20.h), - ], + ); + }, + ), + Obx(() => state.listData.value.isEmpty + ? Positioned( + bottom: 300.h, + child: Text( + '正在创建安全连接...'.tr, + style: TextStyle(color: Colors.black, fontSize: 26.sp), + )) + : Container()), + Positioned( + bottom: 10.w, + child: Container( + width: 1.sw - 30.w * 2, + // height: 300.h, + margin: EdgeInsets.all(30.w), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.2), + borderRadius: BorderRadius.circular(20.h)), + child: Column( + children: [ + SizedBox(height: 20.h), + bottomTopBtnWidget(), + SizedBox(height: 20.h), + bottomBottomBtnWidget(), + SizedBox(height: 20.h), + ], + ), ), ), - ), - // Positioned( - // top: 100.h, - // left: 10.w, - // child: Obx( - // () => Text( - // 'FPS:${state.fps.value}', - // style: TextStyle( - // fontSize: 30.sp, - // color: Colors.orange, - // fontWeight: FontWeight.bold), - // ), - // ), - // ), - Obx(() => state.listData.value.isEmpty - ? buildRotationTransition() - : Container()), + // Positioned( + // top: 100.h, + // left: 10.w, + // child: Obx( + // () => Text( + // 'FPS:${state.fps.value}', + // style: TextStyle( + // fontSize: 30.sp, + // color: Colors.orange, + // fontWeight: FontWeight.bold), + // ), + // ), + // ), + Obx(() => state.listData.value.isEmpty + ? buildRotationTransition() + : 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), - ), - ], + 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()), - ], + ) + : Container()), + ], + ), ), ); } diff --git a/lib/talk/startChart/views/talkView/talk_view_state.dart b/lib/talk/startChart/views/talkView/talk_view_state.dart index 8c1ffbd7..6ffeae75 100644 --- a/lib/talk/startChart/views/talkView/talk_view_state.dart +++ b/lib/talk/startChart/views/talkView/talk_view_state.dart @@ -87,4 +87,6 @@ class TalkViewState { List> recordingAudioAllFrames = >[]; // 录制音频的所有帧 RxInt rotateAngle = 0.obs; // 旋转角度(以弧度为单位) RxBool isLongPressing = false.obs; // 旋转角度(以弧度为单位) + RxBool hasAudioData = false.obs; // 是否有音频数据 + RxInt lastAudioTimestamp = 0.obs; // 最后接收到的音频数据的时间戳 }