fix:调整发送g711音频数据、增加回声消除、增大缓冲区、调整A律解码效果
This commit is contained in:
parent
b876f608e4
commit
911396e1f3
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<int> 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 帧
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user