|
|
|
@ -14,10 +14,15 @@ class CallTranslationDebugController extends GetxController { |
|
|
|
// 持久化键 |
|
|
|
static const String _kSendInterval = 'call_trans_debug_send_interval'; |
|
|
|
static const String _kBundleFrameCount = 'call_trans_debug_bundle_frames'; |
|
|
|
static const String _kUplinkBitrate = 'call_trans_debug_uplink_bitrate'; |
|
|
|
static const String _kDownlinkBitrate = 'call_trans_debug_downlink_bitrate'; |
|
|
|
|
|
|
|
// 默认值(与原生默认保持一致) |
|
|
|
static const int defaultSendIntervalMs = 100; |
|
|
|
static const int defaultBundleFrameCount = 4; |
|
|
|
// 上行/下行默认每声道码率(kbps):对应上行 packetSize=40、下行 packetSize=80 |
|
|
|
static const int defaultUplinkBitrateKbps = 16; |
|
|
|
static const int defaultDownlinkBitrateKbps = 16; |
|
|
|
|
|
|
|
// 取值范围 |
|
|
|
static const int minSendIntervalMs = 20; |
|
|
|
@ -26,12 +31,44 @@ class CallTranslationDebugController extends GetxController { |
|
|
|
static const int minBundleFrameCount = 1; |
|
|
|
static const int maxBundleFrameCount = 20; |
|
|
|
|
|
|
|
// 可选码率档位(每声道 kbps)。取偶数以保证换算出的 packetSize 为整数字节。 |
|
|
|
static const List<int> bitrateOptionsKbps = [8, 12, 16, 20, 24, 32]; |
|
|
|
// 设备端 Opus 单帧固定时长(ms),packetSize 随码率线性变化。 |
|
|
|
static const int frameDurationMs = 20; |
|
|
|
|
|
|
|
/// 音频下行发送间隔(ms) |
|
|
|
final RxInt sendIntervalMs = defaultSendIntervalMs.obs; |
|
|
|
|
|
|
|
/// 下行合包帧数 |
|
|
|
final RxInt bundleFrameCount = defaultBundleFrameCount.obs; |
|
|
|
|
|
|
|
/// 上行编码码率(每声道 kbps)——麦克风→设备 |
|
|
|
final RxInt uplinkBitrateKbps = defaultUplinkBitrateKbps.obs; |
|
|
|
|
|
|
|
/// 下行解码码率(每声道 kbps)——设备→本机播放,需与设备固件编码码率一致 |
|
|
|
final RxInt downlinkBitrateKbps = defaultDownlinkBitrateKbps.obs; |
|
|
|
|
|
|
|
/// 上行 packetSize:每声道独立 mono 流,每帧字节数 = 码率 × 帧时长 ÷ 8 |
|
|
|
int get uplinkPacketSize => uplinkBitrateKbps.value * frameDurationMs ~/ 8; |
|
|
|
|
|
|
|
/// 下行 packetSize:立体声合并流(左右各一声道),每帧字节数 = 2 × 码率 × 帧时长 ÷ 8 |
|
|
|
int get downlinkPacketSize => |
|
|
|
2 * downlinkBitrateKbps.value * frameDurationMs ~/ 8; |
|
|
|
|
|
|
|
/// 由 packetSize 反推每声道码率(kbps),用于同步原生返回值 |
|
|
|
int _uplinkBitrateFromPacket(int packet) => packet * 8 ~/ frameDurationMs; |
|
|
|
int _downlinkBitrateFromPacket(int packet) => |
|
|
|
packet * 8 ~/ (2 * frameDurationMs); |
|
|
|
|
|
|
|
/// 就近吸附到最接近的合法档位 |
|
|
|
int _snapBitrate(int kbps) { |
|
|
|
var best = bitrateOptionsKbps.first; |
|
|
|
for (final o in bitrateOptionsKbps) { |
|
|
|
if ((o - kbps).abs() < (best - kbps).abs()) best = o; |
|
|
|
} |
|
|
|
return best; |
|
|
|
} |
|
|
|
|
|
|
|
/// 通话翻译编解码是否已开启 |
|
|
|
RxBool get isCodecActive => _bleManager.isCodecActiveRx; |
|
|
|
|
|
|
|
@ -50,6 +87,8 @@ class CallTranslationDebugController extends GetxController { |
|
|
|
void _loadFromStorage() { |
|
|
|
final savedInterval = _storage.read<int>(_kSendInterval); |
|
|
|
final savedFrames = _storage.read<int>(_kBundleFrameCount); |
|
|
|
final savedUplink = _storage.read<int>(_kUplinkBitrate); |
|
|
|
final savedDownlink = _storage.read<int>(_kDownlinkBitrate); |
|
|
|
if (savedInterval != null) { |
|
|
|
sendIntervalMs.value = |
|
|
|
savedInterval.clamp(minSendIntervalMs, maxSendIntervalMs); |
|
|
|
@ -58,6 +97,12 @@ class CallTranslationDebugController extends GetxController { |
|
|
|
bundleFrameCount.value = |
|
|
|
savedFrames.clamp(minBundleFrameCount, maxBundleFrameCount); |
|
|
|
} |
|
|
|
if (savedUplink != null) { |
|
|
|
uplinkBitrateKbps.value = _snapBitrate(savedUplink); |
|
|
|
} |
|
|
|
if (savedDownlink != null) { |
|
|
|
downlinkBitrateKbps.value = _snapBitrate(savedDownlink); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 用原生当前实际值同步显示(不覆盖用户本地保存的偏好,仅在无本地保存时参考) |
|
|
|
@ -79,6 +124,19 @@ class CallTranslationDebugController extends GetxController { |
|
|
|
nf.clamp(minBundleFrameCount, maxBundleFrameCount); |
|
|
|
} |
|
|
|
} |
|
|
|
if (_storage.read<int>(_kUplinkBitrate) == null) { |
|
|
|
final up = params['uplinkPacketSize']; |
|
|
|
if (up != null && up > 0) { |
|
|
|
uplinkBitrateKbps.value = _snapBitrate(_uplinkBitrateFromPacket(up)); |
|
|
|
} |
|
|
|
} |
|
|
|
if (_storage.read<int>(_kDownlinkBitrate) == null) { |
|
|
|
final dp = params['downlinkPacketSize']; |
|
|
|
if (dp != null && dp > 0) { |
|
|
|
downlinkBitrateKbps.value = |
|
|
|
_snapBitrate(_downlinkBitrateFromPacket(dp)); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
Logger.e('CALL_TRANS_DEBUG', '同步原生参数失败: $e'); |
|
|
|
} |
|
|
|
@ -96,22 +154,38 @@ class CallTranslationDebugController extends GetxController { |
|
|
|
value.clamp(minBundleFrameCount, maxBundleFrameCount); |
|
|
|
} |
|
|
|
|
|
|
|
void setUplinkBitrate(int kbps) { |
|
|
|
uplinkBitrateKbps.value = _snapBitrate(kbps); |
|
|
|
} |
|
|
|
|
|
|
|
void setDownlinkBitrate(int kbps) { |
|
|
|
downlinkBitrateKbps.value = _snapBitrate(kbps); |
|
|
|
} |
|
|
|
|
|
|
|
/// 恢复默认值 |
|
|
|
void resetDefaults() { |
|
|
|
sendIntervalMs.value = defaultSendIntervalMs; |
|
|
|
bundleFrameCount.value = defaultBundleFrameCount; |
|
|
|
uplinkBitrateKbps.value = defaultUplinkBitrateKbps; |
|
|
|
downlinkBitrateKbps.value = defaultDownlinkBitrateKbps; |
|
|
|
} |
|
|
|
|
|
|
|
/// 保存并下发参数到原生(运行时立即生效) |
|
|
|
/// 保存并下发参数到原生 |
|
|
|
/// 注意:packetSize(码率)在编解码器创建时确定,运行中修改需停止后重新「应用并启动」才生效。 |
|
|
|
Future<void> applyParams() async { |
|
|
|
_storage.write(_kSendInterval, sendIntervalMs.value); |
|
|
|
_storage.write(_kBundleFrameCount, bundleFrameCount.value); |
|
|
|
_storage.write(_kUplinkBitrate, uplinkBitrateKbps.value); |
|
|
|
_storage.write(_kDownlinkBitrate, downlinkBitrateKbps.value); |
|
|
|
final ok = await _bleManager.setCallTranslationDebugParams( |
|
|
|
sendIntervalMs: sendIntervalMs.value, |
|
|
|
bundleFrameCount: bundleFrameCount.value, |
|
|
|
uplinkPacketSize: uplinkPacketSize, |
|
|
|
downlinkPacketSize: downlinkPacketSize, |
|
|
|
); |
|
|
|
Logger.i('CALL_TRANS_DEBUG', |
|
|
|
'下发调试参数 interval=${sendIntervalMs.value}ms frames=${bundleFrameCount.value} result=$ok'); |
|
|
|
'下发调试参数 interval=${sendIntervalMs.value}ms frames=${bundleFrameCount.value} ' |
|
|
|
'up=${uplinkBitrateKbps.value}kbps/${uplinkPacketSize}B down=${downlinkBitrateKbps.value}kbps/${downlinkPacketSize}B result=$ok'); |
|
|
|
Get.snackbar( |
|
|
|
'通话翻译调试', |
|
|
|
ok ? '参数已应用' : '参数下发失败(请确认设备已连接)', |
|
|
|
|