合并代码后,优化视频/设备时区/鸿蒙手机bug
This commit is contained in:
parent
c167b254fd
commit
f9ee17de21
@ -153,7 +153,7 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
|
|||||||
visible: state.lockSetInfoData.value.lockFeature?.wifi == 1,
|
visible: state.lockSetInfoData.value.lockFeature?.wifi == 1,
|
||||||
child: CommonItem(
|
child: CommonItem(
|
||||||
leftTitel: '设备时区'.tr,
|
leftTitel: '设备时区'.tr,
|
||||||
rightTitle: state.lockSetInfoData.value.lockBasicInfo?.timezoneName,
|
rightTitle: _convertTimezoneToUTCFormat(state.lockSetInfoData.value.lockBasicInfo?.timezoneName),
|
||||||
allHeight: 70.h,
|
allHeight: 70.h,
|
||||||
isHaveLine: true,
|
isHaveLine: true,
|
||||||
),
|
),
|
||||||
@ -244,4 +244,112 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
|
|||||||
// 线性映射: percentage = (rssi + 100) * 100 / 70
|
// 线性映射: percentage = (rssi + 100) * 100 / 70
|
||||||
return ((clampedRssi + 100) * 100 ~/ 70).clamp(0, 100);
|
return ((clampedRssi + 100) * 100 ~/ 70).clamp(0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加时区名称转换方法
|
||||||
|
String _convertTimezoneToUTCFormat(String? timezoneName) {
|
||||||
|
if (timezoneName == null || timezoneName.isEmpty) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果已经是UTC格式,直接返回
|
||||||
|
if (timezoneName.startsWith('UTC')) {
|
||||||
|
return timezoneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理常见的时区名称转换
|
||||||
|
final Map<String, String> timezoneMap = {
|
||||||
|
// 亚洲时区 (UTC+)
|
||||||
|
'Asia/Shanghai': 'UTC+8',
|
||||||
|
'Asia/Taipei': 'UTC+8',
|
||||||
|
'Asia/Singapore': 'UTC+8',
|
||||||
|
'Asia/Hong_Kong': 'UTC+8',
|
||||||
|
'Asia/Macau': 'UTC+8',
|
||||||
|
'Asia/Seoul': 'UTC+9',
|
||||||
|
'Asia/Tokyo': 'UTC+9',
|
||||||
|
'Asia/Dubai': 'UTC+4',
|
||||||
|
'Asia/Kolkata': 'UTC+5:30',
|
||||||
|
'Asia/Bangkok': 'UTC+7',
|
||||||
|
'Asia/Jakarta': 'UTC+7',
|
||||||
|
'Asia/Kuala_Lumpur': 'UTC+8',
|
||||||
|
'Asia/Manila': 'UTC+8',
|
||||||
|
'Asia/Karachi': 'UTC+5',
|
||||||
|
'Asia/Tehran': 'UTC+3:30',
|
||||||
|
'Asia/Baghdad': 'UTC+3',
|
||||||
|
'Asia/Beirut': 'UTC+2',
|
||||||
|
'Asia/Jerusalem': 'UTC+2',
|
||||||
|
'Asia/Damascus': 'UTC+3',
|
||||||
|
'Asia/Amman': 'UTC+3',
|
||||||
|
'Asia/Baku': 'UTC+4',
|
||||||
|
'Asia/Yerevan': 'UTC+4',
|
||||||
|
'Asia/Tbilisi': 'UTC+4',
|
||||||
|
|
||||||
|
// 欧洲时区
|
||||||
|
'Europe/London': 'UTC+0',
|
||||||
|
'Europe/Paris': 'UTC+1',
|
||||||
|
'Europe/Berlin': 'UTC+1',
|
||||||
|
'Europe/Rome': 'UTC+1',
|
||||||
|
'Europe/Madrid': 'UTC+1',
|
||||||
|
'Europe/Amsterdam': 'UTC+1',
|
||||||
|
'Europe/Brussels': 'UTC+1',
|
||||||
|
'Europe/Vienna': 'UTC+1',
|
||||||
|
'Europe/Stockholm': 'UTC+1',
|
||||||
|
'Europe/Oslo': 'UTC+1',
|
||||||
|
'Europe/Copenhagen': 'UTC+1',
|
||||||
|
'Europe/Warsaw': 'UTC+1',
|
||||||
|
'Europe/Prague': 'UTC+1',
|
||||||
|
'Europe/Budapest': 'UTC+1',
|
||||||
|
'Europe/Athens': 'UTC+2',
|
||||||
|
'Europe/Helsinki': 'UTC+2',
|
||||||
|
'Europe/Riga': 'UTC+2',
|
||||||
|
'Europe/Tallinn': 'UTC+2',
|
||||||
|
'Europe/Vilnius': 'UTC+2',
|
||||||
|
'Europe/Sofia': 'UTC+2',
|
||||||
|
'Europe/Bucharest': 'UTC+2',
|
||||||
|
'Europe/Istanbul': 'UTC+3',
|
||||||
|
'Europe/Minsk': 'UTC+3',
|
||||||
|
'Europe/Moscow': 'UTC+3',
|
||||||
|
|
||||||
|
// 北美时区 (UTC-)
|
||||||
|
'America/New_York': 'UTC-5',
|
||||||
|
'America/Los_Angeles': 'UTC-8',
|
||||||
|
'America/Chicago': 'UTC-6',
|
||||||
|
'America/Denver': 'UTC-7',
|
||||||
|
'America/Phoenix': 'UTC-7',
|
||||||
|
'America/Toronto': 'UTC-5',
|
||||||
|
'America/Montreal': 'UTC-5',
|
||||||
|
'America/Vancouver': 'UTC-8',
|
||||||
|
'America/Edmonton': 'UTC-7',
|
||||||
|
'America/Halifax': 'UTC-4',
|
||||||
|
'America/St_Johns': 'UTC-3:30',
|
||||||
|
|
||||||
|
// 南美时区
|
||||||
|
'America/Sao_Paulo': 'UTC-3',
|
||||||
|
'America/Buenos_Aires': 'UTC-3',
|
||||||
|
'America/Santiago': 'UTC-4',
|
||||||
|
'America/Lima': 'UTC-5',
|
||||||
|
'America/Bogota': 'UTC-5',
|
||||||
|
'America/Caracas': 'UTC-4',
|
||||||
|
'America/Mexico_City': 'UTC-6',
|
||||||
|
|
||||||
|
// 大洋洲时区
|
||||||
|
'Australia/Sydney': 'UTC+10',
|
||||||
|
'Australia/Melbourne': 'UTC+10',
|
||||||
|
'Australia/Brisbane': 'UTC+10',
|
||||||
|
'Australia/Perth': 'UTC+8',
|
||||||
|
'Australia/Adelaide': 'UTC+9:30',
|
||||||
|
'Pacific/Auckland': 'UTC+12',
|
||||||
|
'Pacific/Fiji': 'UTC+12',
|
||||||
|
|
||||||
|
// 非洲时区
|
||||||
|
'Africa/Cairo': 'UTC+2',
|
||||||
|
'Africa/Johannesburg': 'UTC+2',
|
||||||
|
'Africa/Lagos': 'UTC+1',
|
||||||
|
'Africa/Nairobi': 'UTC+3',
|
||||||
|
'Africa/Casablanca': 'UTC+1',
|
||||||
|
'Africa/Tunis': 'UTC+1',
|
||||||
|
'Africa/Algiers': 'UTC+1',
|
||||||
|
};
|
||||||
|
|
||||||
|
return timezoneMap[timezoneName] ?? timezoneName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,8 +26,11 @@ class SelectLockTypeLogic extends BaseGetXController {
|
|||||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
// 检查是否为华为设备且系统版本包含HarmonyOS标识,例如'HUAWEI'开头或特定系统属性等。
|
// 更准确地检测鸿蒙系统
|
||||||
return androidInfo.brand == 'HONOR' || androidInfo.version.sdkInt >= 30; // 注意:具体API可能需要更新以适配最新鸿蒙系统版本。
|
return androidInfo.brand == 'HUAWEI' ||
|
||||||
|
androidInfo.brand == 'HONOR' ||
|
||||||
|
androidInfo.manufacturer == 'HUAWEI' ||
|
||||||
|
androidInfo.version.release.contains('HarmonyOS');
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -38,19 +41,33 @@ class SelectLockTypeLogic extends BaseGetXController {
|
|||||||
if (!Platform.isIOS) {
|
if (!Platform.isIOS) {
|
||||||
final bool locationRequest = await PermissionDialog.request(Permission.location);
|
final bool locationRequest = await PermissionDialog.request(Permission.location);
|
||||||
final bool bluetoothRequest = await PermissionDialog.requestBluetooth();
|
final bool bluetoothRequest = await PermissionDialog.requestBluetooth();
|
||||||
|
// 添加存储权限请求(相册权限)
|
||||||
|
final bool storageRequest = await PermissionDialog.request(Permission.storage);
|
||||||
|
|
||||||
bool isHarmonyOS = await checkIfHarmonyOS();
|
bool isHarmonyOS = await checkIfHarmonyOS();
|
||||||
// 鸿蒙系统
|
// 鸿蒙系统
|
||||||
if(isHarmonyOS){
|
if(isHarmonyOS){
|
||||||
Get.snackbar('提示', '如您是鸿蒙系统,请下拉手动开启系统的“位置信息”,否则无法搜索到锁哦');
|
print('鸿蒙手机提示----');
|
||||||
}
|
if (!bluetoothRequest || !locationRequest || !storageRequest) {
|
||||||
if (!bluetoothRequest || !locationRequest) {
|
return;
|
||||||
return;
|
}
|
||||||
|
} else {
|
||||||
|
if (!bluetoothRequest || !locationRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Get.toNamed(Routers.nearbyLockPage);
|
Get.toNamed(Routers.nearbyLockPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> requestHarmonyOSPermissions() async {
|
||||||
|
// 鸿蒙系统可能需要分别请求不同权限
|
||||||
|
await Permission.storage.request(); // 存储权限(包含相册访问)
|
||||||
|
await Permission.photos.request(); // 照片权限(如果可用)
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|||||||
@ -44,6 +44,31 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
|
|
||||||
int audioBufferSize = 20; // 音频默认缓冲2帧
|
int audioBufferSize = 20; // 音频默认缓冲2帧
|
||||||
|
|
||||||
|
int _frameProcessCount = 0;
|
||||||
|
int _lastFrameProcessTime = 0;
|
||||||
|
double _actualFps = 0.0;
|
||||||
|
void _monitorFrameProcessingPerformance() {
|
||||||
|
_frameProcessCount++;
|
||||||
|
final now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|
||||||
|
if (now - _lastFrameProcessTime >= 1000) { // 每秒统计一次
|
||||||
|
_actualFps = _frameProcessCount.toDouble();
|
||||||
|
_frameProcessCount = 0;
|
||||||
|
_lastFrameProcessTime = now;
|
||||||
|
|
||||||
|
// 根据实际处理能力调整目标帧率
|
||||||
|
if (_actualFps < state.targetFps * 0.7) {
|
||||||
|
// 处理能力不足,降低目标帧率
|
||||||
|
state.targetFps = (state.targetFps * 0.9).clamp(15.0, 60.0) as int;
|
||||||
|
_startFrameProcessTimer();
|
||||||
|
} else if (_actualFps > state.targetFps * 1.2 && state.targetFps < 30.0) {
|
||||||
|
// 处理能力充足,可以提高帧率
|
||||||
|
state.targetFps = (state.targetFps * 1.1).clamp(15.0, 30.0) as int;
|
||||||
|
_startFrameProcessTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 回绕阈值,动态调整,frameSeq较小时阈值也小
|
// 回绕阈值,动态调整,frameSeq较小时阈值也小
|
||||||
int _getFrameSeqRolloverThreshold(int lastSeq) {
|
int _getFrameSeqRolloverThreshold(int lastSeq) {
|
||||||
if (lastSeq > 2000) {
|
if (lastSeq > 2000) {
|
||||||
@ -237,6 +262,23 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据缓冲区长度动态调整最大缓冲区大小
|
||||||
|
if (state.h264FrameBuffer.length > state.maxFrameBufferSize * 0.8) {
|
||||||
|
// 缓冲区接近满载,更积极地丢弃帧
|
||||||
|
while (state.h264FrameBuffer.length >= state.maxFrameBufferSize * 0.9) {
|
||||||
|
// 优先丢弃旧的P帧
|
||||||
|
int pbIndex = state.h264FrameBuffer.indexWhere((f) =>
|
||||||
|
f['frameType'] == TalkDataH264Frame_FrameTypeE.P &&
|
||||||
|
f['frameSeq'] < frameSeq - 100);
|
||||||
|
|
||||||
|
if (pbIndex != -1) {
|
||||||
|
state.h264FrameBuffer.removeAt(pbIndex);
|
||||||
|
} else {
|
||||||
|
// 如果没有找到合适的P帧,移除最旧的帧
|
||||||
|
state.h264FrameBuffer.removeAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 将帧添加到缓冲区
|
// 将帧添加到缓冲区
|
||||||
state.h264FrameBuffer.add(frameMap);
|
state.h264FrameBuffer.add(frameMap);
|
||||||
}
|
}
|
||||||
@ -259,8 +301,20 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
/// 从缓冲区处理下一帧
|
/// 从缓冲区处理下一帧
|
||||||
/// 从缓冲区处理下一帧
|
/// 从缓冲区处理下一帧
|
||||||
void _processNextFrameFromBuffer() async {
|
void _processNextFrameFromBuffer() async {
|
||||||
|
_monitorFrameProcessingPerformance();
|
||||||
final startTime = DateTime.now().microsecondsSinceEpoch;
|
final startTime = DateTime.now().microsecondsSinceEpoch;
|
||||||
|
|
||||||
|
// 动态调整处理策略基于缓冲区长度
|
||||||
|
final bufferLength = state.h264FrameBuffer.length;
|
||||||
|
// 如果缓冲区过长,提高处理频率
|
||||||
|
if (bufferLength > 30 && state.targetFps < 60) {
|
||||||
|
_adjustFrameProcessFrequency(state.targetFps * 1.5);
|
||||||
|
}
|
||||||
|
// 如果缓冲区较短,可以适当降低处理频率节省资源
|
||||||
|
else if (bufferLength < 10 && state.targetFps > 25) {
|
||||||
|
_adjustFrameProcessFrequency(state.targetFps * 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
// 避免重复处理
|
// 避免重复处理
|
||||||
if (state.isProcessingFrame) {
|
if (state.isProcessingFrame) {
|
||||||
return;
|
return;
|
||||||
@ -273,17 +327,22 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 优先查找I帧,按frameSeq最小的I帧消费
|
// 优先查找I帧,按frameSeq最小的I帧消费
|
||||||
final iFrames = state.h264FrameBuffer.where((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.I).toList();
|
final iFrames = state.h264FrameBuffer.where((f) =>
|
||||||
iFrames.sort((a, b) => (a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
|
f['frameType'] == TalkDataH264Frame_FrameTypeE.I).toList();
|
||||||
|
iFrames.sort((a, b) =>
|
||||||
|
(a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
|
||||||
|
|
||||||
if (iFrames.isNotEmpty) {
|
if (iFrames.isNotEmpty) {
|
||||||
final minIFrame = iFrames.first;
|
final minIFrame = iFrames.first;
|
||||||
final minIFrameSeq = minIFrame['frameSeq'];
|
final minIFrameSeq = minIFrame['frameSeq'];
|
||||||
final targetIndex = state.h264FrameBuffer.indexWhere(
|
final targetIndex = state.h264FrameBuffer.indexWhere(
|
||||||
(f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.I && f['frameSeq'] == minIFrameSeq,
|
(f) =>
|
||||||
|
f['frameType'] == TalkDataH264Frame_FrameTypeE.I &&
|
||||||
|
f['frameSeq'] == minIFrameSeq,
|
||||||
);
|
);
|
||||||
state.isProcessingFrame = true;
|
state.isProcessingFrame = true;
|
||||||
final Map<String, dynamic>? frameMap = state.h264FrameBuffer.removeAt(targetIndex);
|
final Map<String, dynamic>? frameMap = state.h264FrameBuffer.removeAt(
|
||||||
|
targetIndex);
|
||||||
if (frameMap == null) {
|
if (frameMap == null) {
|
||||||
state.isProcessingFrame = false;
|
state.isProcessingFrame = false;
|
||||||
return;
|
return;
|
||||||
@ -293,7 +352,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
final int? frameSeq = frameMap['frameSeq'];
|
final int? frameSeq = frameMap['frameSeq'];
|
||||||
final int? frameSeqI = frameMap['frameSeqI'];
|
final int? frameSeqI = frameMap['frameSeqI'];
|
||||||
final int? pts = frameMap['pts'];
|
final int? pts = frameMap['pts'];
|
||||||
if (frameData == null || frameType == null || frameSeq == null || frameSeqI == null || pts == null) {
|
if (frameData == null || frameType == null || frameSeq == null ||
|
||||||
|
frameSeqI == null || pts == null) {
|
||||||
state.isProcessingFrame = false;
|
state.isProcessingFrame = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -315,19 +375,41 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.isProcessingFrame = true;
|
||||||
|
|
||||||
|
// 使用更高效的查找方式,避免每次都排序整个列表
|
||||||
|
Map<String, dynamic>? frameToProcess;
|
||||||
|
int frameIndex = -1;
|
||||||
|
|
||||||
// 没有I帧时,只消费refIFrameSeq等于lastDecodedIFrameSeq的P帧
|
// 没有I帧时,只消费refIFrameSeq等于lastDecodedIFrameSeq的P帧
|
||||||
if (lastDecodedIFrameSeq != null) {
|
if (lastDecodedIFrameSeq != null) {
|
||||||
|
// 先查找与上一个I帧关联的P帧
|
||||||
|
for (int i = 0; i < state.h264FrameBuffer.length; i++) {
|
||||||
|
final frame = state.h264FrameBuffer[i];
|
||||||
|
if (frame['frameType'] == TalkDataH264Frame_FrameTypeE.P &&
|
||||||
|
frame['frameSeqI'] == lastDecodedIFrameSeq) {
|
||||||
|
frameToProcess = frame;
|
||||||
|
frameIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
final validPFrames =
|
final validPFrames =
|
||||||
state.h264FrameBuffer.where((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.P && f['frameSeqI'] == lastDecodedIFrameSeq).toList();
|
state.h264FrameBuffer.where((f) =>
|
||||||
|
f['frameType'] == TalkDataH264Frame_FrameTypeE.P &&
|
||||||
|
f['frameSeqI'] == lastDecodedIFrameSeq).toList();
|
||||||
if (validPFrames.isNotEmpty) {
|
if (validPFrames.isNotEmpty) {
|
||||||
validPFrames.sort((a, b) => (a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
|
validPFrames.sort((a, b) =>
|
||||||
|
(a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
|
||||||
final minPFrame = validPFrames.first;
|
final minPFrame = validPFrames.first;
|
||||||
final targetIndex = state.h264FrameBuffer.indexWhere(
|
final targetIndex = state.h264FrameBuffer.indexWhere(
|
||||||
(f) =>
|
(f) =>
|
||||||
f['frameType'] == TalkDataH264Frame_FrameTypeE.P && f['frameSeq'] == minPFrame['frameSeq'] && f['frameSeqI'] == lastDecodedIFrameSeq,
|
f['frameType'] == TalkDataH264Frame_FrameTypeE.P &&
|
||||||
|
f['frameSeq'] == minPFrame['frameSeq'] &&
|
||||||
|
f['frameSeqI'] == lastDecodedIFrameSeq,
|
||||||
);
|
);
|
||||||
state.isProcessingFrame = true;
|
state.isProcessingFrame = true;
|
||||||
final Map<String, dynamic>? frameMap = state.h264FrameBuffer.removeAt(targetIndex);
|
final Map<String, dynamic>? frameMap = state.h264FrameBuffer.removeAt(
|
||||||
|
targetIndex);
|
||||||
if (frameMap == null) {
|
if (frameMap == null) {
|
||||||
state.isProcessingFrame = false;
|
state.isProcessingFrame = false;
|
||||||
return;
|
return;
|
||||||
@ -337,7 +419,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
final int? frameSeq = frameMap['frameSeq'];
|
final int? frameSeq = frameMap['frameSeq'];
|
||||||
final int? frameSeqI = frameMap['frameSeqI'];
|
final int? frameSeqI = frameMap['frameSeqI'];
|
||||||
final int? pts = frameMap['pts'];
|
final int? pts = frameMap['pts'];
|
||||||
if (frameData == null || frameType == null || frameSeq == null || frameSeqI == null || pts == null) {
|
if (frameData == null || frameType == null || frameSeq == null ||
|
||||||
|
frameSeqI == null || pts == null) {
|
||||||
state.isProcessingFrame = false;
|
state.isProcessingFrame = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -358,8 +441,35 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 其他情况不消费,等待I帧到来
|
// 如果没有找到合适的P帧,查找最早的I帧
|
||||||
|
if (frameToProcess == null) {
|
||||||
|
int earliestIframeIndex = -1;
|
||||||
|
int earliestIframeSeq = 999999;
|
||||||
|
|
||||||
|
for (int i = 0; i < state.h264FrameBuffer.length; i++) {
|
||||||
|
final frame = state.h264FrameBuffer[i];
|
||||||
|
if (frame['frameType'] == TalkDataH264Frame_FrameTypeE.I) {
|
||||||
|
final frameSeq = frame['frameSeq'] as int;
|
||||||
|
if (frameSeq < earliestIframeSeq) {
|
||||||
|
earliestIframeSeq = frameSeq;
|
||||||
|
frameToProcess = frame;
|
||||||
|
earliestIframeIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frameToProcess != null) {
|
||||||
|
frameIndex = earliestIframeIndex;
|
||||||
|
}
|
||||||
|
// 其他情况不消费,等待I帧到来
|
||||||
|
} // 处理选中的帧
|
||||||
|
if (frameToProcess != null && frameIndex >= 0) {
|
||||||
|
final Map<String, dynamic> frameMap = state.h264FrameBuffer.removeAt(
|
||||||
|
frameIndex);
|
||||||
|
// ... 帧处理逻辑
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
state.isProcessingFrame = false;
|
||||||
final endTime = DateTime.now().microsecondsSinceEpoch;
|
final endTime = DateTime.now().microsecondsSinceEpoch;
|
||||||
final durationMs = (endTime - startTime) / 1000.0;
|
final durationMs = (endTime - startTime) / 1000.0;
|
||||||
// 可选:只在耗时较长时打印(例如 > 5ms)
|
// 可选:只在耗时较长时打印(例如 > 5ms)
|
||||||
@ -369,6 +479,18 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
// AppLog.log('Frame processing took ${durationMs.toStringAsFixed(2)} ms');
|
// AppLog.log('Frame processing took ${durationMs.toStringAsFixed(2)} ms');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
final endTime = DateTime.now().microsecondsSinceEpoch;
|
||||||
|
final durationMs = (endTime - startTime) / 1000.0;
|
||||||
|
|
||||||
|
// 记录处理时间,用于性能分析
|
||||||
|
if (durationMs > 16.67) { // 超过一帧的时间(60fps)
|
||||||
|
AppLog.log('帧处理耗时过长: ${durationMs.toStringAsFixed(2)} ms, 缓冲区长度: ${state.h264FrameBuffer.length}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _adjustFrameProcessFrequency(double newFps) {
|
||||||
|
state.targetFps = newFps.clamp(20.0, 60.0) as int;
|
||||||
|
_startFrameProcessTimer(); // 重新启动定时器
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 停止帧处理定时器
|
/// 停止帧处理定时器
|
||||||
@ -823,6 +945,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
state.currentQuality.value = quality;
|
state.currentQuality.value = quality;
|
||||||
TalkExpectReq talkExpectReq = StartChartManage().getDefaultTalkExpect();
|
TalkExpectReq talkExpectReq = StartChartManage().getDefaultTalkExpect();
|
||||||
final audioType = talkExpectReq.audioType;
|
final audioType = talkExpectReq.audioType;
|
||||||
|
// 立即显示loading状态
|
||||||
|
state.isLoading.value = true;
|
||||||
int width = 864;
|
int width = 864;
|
||||||
int height = 480;
|
int height = 480;
|
||||||
switch (quality) {
|
switch (quality) {
|
||||||
@ -844,14 +968,19 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 立即预设解码器尺寸
|
||||||
|
_pendingResetWidth = width;
|
||||||
|
_pendingResetHeight = height;
|
||||||
|
// 立即重置解码器而不是等待回绕检测
|
||||||
|
await _resetDecoderForNewStream(width, height);
|
||||||
/// 修改发送预期数据
|
/// 修改发送预期数据
|
||||||
StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: talkExpectReq);
|
StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: talkExpectReq);
|
||||||
|
|
||||||
// 不立即loading,继续解码旧流帧,等待frameSeq回绕检测
|
// 不立即loading,继续解码旧流帧,等待frameSeq回绕检测
|
||||||
// 仅重置frameSeq回绕检测标志
|
// 仅重置frameSeq回绕检测标志
|
||||||
_pendingStreamReset = false;
|
// _pendingStreamReset = false;
|
||||||
_pendingResetWidth = width;
|
// _pendingResetWidth = width;
|
||||||
_pendingResetHeight = height;
|
// _pendingResetHeight = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initHdOptions() {
|
void _initHdOptions() {
|
||||||
@ -867,6 +996,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
// 新增:重置解码器方法
|
// 新增:重置解码器方法
|
||||||
Future<void> _resetDecoderForNewStream(int width, int height) async {
|
Future<void> _resetDecoderForNewStream(int width, int height) async {
|
||||||
try {
|
try {
|
||||||
|
// 显示加载状态
|
||||||
|
state.isLoading.value = true;
|
||||||
// 先停止帧处理定时器
|
// 先停止帧处理定时器
|
||||||
_stopFrameProcessTimer();
|
_stopFrameProcessTimer();
|
||||||
|
|
||||||
@ -877,7 +1008,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 等待一小段时间确保资源释放完成
|
// 等待一小段时间确保资源释放完成
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
await Future.delayed(Duration(milliseconds: 50));
|
||||||
|
|
||||||
// 创建新的解码器配置
|
// 创建新的解码器配置
|
||||||
final config = VideoDecoderConfig(
|
final config = VideoDecoderConfig(
|
||||||
@ -906,6 +1037,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
_decodedIFrames.clear();
|
_decodedIFrames.clear();
|
||||||
state.h264FrameBuffer.clear();
|
state.h264FrameBuffer.clear();
|
||||||
state.isProcessingFrame = false;
|
state.isProcessingFrame = false;
|
||||||
|
_lastFrameSeq = null;
|
||||||
hasSps = false;
|
hasSps = false;
|
||||||
hasPps = false;
|
hasPps = false;
|
||||||
spsCache = null;
|
spsCache = null;
|
||||||
|
|||||||
@ -110,7 +110,7 @@ class TalkViewNativeDecodeState {
|
|||||||
// H264帧缓冲区相关
|
// H264帧缓冲区相关
|
||||||
final List<Map<String, dynamic>> h264FrameBuffer = <Map<String, dynamic>>[]; // H264帧缓冲区,存储帧数据和类型
|
final List<Map<String, dynamic>> h264FrameBuffer = <Map<String, dynamic>>[]; // H264帧缓冲区,存储帧数据和类型
|
||||||
final int maxFrameBufferSize = 25; // 最大缓冲区大小
|
final int maxFrameBufferSize = 25; // 最大缓冲区大小
|
||||||
final int targetFps = 25; // 目标解码帧率,只是为了快速填充native的缓冲区
|
int targetFps = 25; // 目标解码帧率,只是为了快速填充native的缓冲区
|
||||||
Timer? frameProcessTimer; // 帧处理定时器
|
Timer? frameProcessTimer; // 帧处理定时器
|
||||||
bool isProcessingFrame = false; // 是否正在处理帧
|
bool isProcessingFrame = false; // 是否正在处理帧
|
||||||
int lastProcessedTimestamp = 0; // 上次处理帧的时间戳
|
int lastProcessedTimestamp = 0; // 上次处理帧的时间戳
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user