记录接收到第一帧的时间,防止挂断后自动重连,从接收第一帧到关闭loading耗时
This commit is contained in:
parent
ce350aecca
commit
a2801fe613
@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math'; // 添加Random类支持
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -89,6 +90,35 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
|
|
||||||
int? lastDecodedIFrameSeq;
|
int? lastDecodedIFrameSeq;
|
||||||
|
|
||||||
|
// 新增:记录最近I帧的时间戳,用于检测画面大幅变动
|
||||||
|
DateTime? _lastIFrameTime;
|
||||||
|
|
||||||
|
// 添加一个标志来记录是否是第一个H264帧
|
||||||
|
bool _isFirstH264FrameReceived = true;
|
||||||
|
|
||||||
|
// 记录接收到第一帧的时间
|
||||||
|
DateTime? _firstFrameReceivedTime;
|
||||||
|
|
||||||
|
// 记录开始时间用于计算耗时
|
||||||
|
static DateTime? _monitorStartTime;
|
||||||
|
|
||||||
|
// 设置开始时间的方法
|
||||||
|
static void setMonitorStartTime(DateTime startTime) {
|
||||||
|
_monitorStartTime = startTime;
|
||||||
|
AppLog.log('监控启动时间已记录: $startTime');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算并打印耗时的方法
|
||||||
|
static void printH264ReceiveTime() {
|
||||||
|
if (_monitorStartTime != null) {
|
||||||
|
final Duration duration = DateTime.now().difference(_monitorStartTime!);
|
||||||
|
AppLog.log('从点击监控到接收H264数据耗时: ${duration.inMilliseconds} 毫秒 (${duration.inSeconds}.${duration.inMilliseconds % 1000} 秒)');
|
||||||
|
|
||||||
|
// 重置开始时间,避免重复计算
|
||||||
|
_monitorStartTime = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化视频解码器
|
// 初始化视频解码器
|
||||||
Future<void> _initVideoDecoder() async {
|
Future<void> _initVideoDecoder() async {
|
||||||
try {
|
try {
|
||||||
@ -96,33 +126,38 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
int width = StartChartManage().videoWidth;
|
int width = StartChartManage().videoWidth;
|
||||||
int height = StartChartManage().videoHeight;
|
int height = StartChartManage().videoHeight;
|
||||||
|
|
||||||
if(Platform.isIOS){
|
// ios第一次点击监控没画面
|
||||||
// ios第一次点击监控没画面
|
if (Platform.isIOS && (width == 0 || height == 0)) {
|
||||||
if (width == 0 || height == 0) {
|
// 使用Future.microtask代替延时等待,提高响应速度
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
const maxAttempts = 20; // 最多等待2秒 (20 * 100ms)
|
const maxAttempts = 10; // 减少等待次数,提高响应速度
|
||||||
|
|
||||||
while ((width == 0 || height == 0) && attempts < maxAttempts) {
|
while ((width == 0 || height == 0) && attempts < maxAttempts) {
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.microtask(() async {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50)); // 减少等待时间
|
||||||
width = StartChartManage().videoWidth;
|
width = StartChartManage().videoWidth;
|
||||||
height = StartChartManage().videoHeight;
|
height = StartChartManage().videoHeight;
|
||||||
attempts++;
|
});
|
||||||
}
|
attempts++;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果仍然没有获取到参数,使用默认值
|
// 如果仍然没有获取到参数,使用默认值
|
||||||
if (width == 0 || height == 0) {
|
if (width == 0 || height == 0) {
|
||||||
width = 864;
|
width = 864;
|
||||||
height = 480;
|
height = 480;
|
||||||
AppLog.log('使用默认视频参数: ${width}x$height');
|
AppLog.log('使用默认视频参数: ${width}x$height');
|
||||||
} else {
|
} else {
|
||||||
AppLog.log('获取到视频参数: ${width}x$height');
|
AppLog.log('获取到视频参数: ${width}x$height');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 确保宽高为偶数,符合H264标准
|
||||||
|
width = (width / 2).floor() * 2;
|
||||||
|
height = (height / 2).floor() * 2;
|
||||||
|
|
||||||
// 创建解码器配置
|
// 创建解码器配置
|
||||||
final config = VideoDecoderConfig(
|
final config = VideoDecoderConfig(
|
||||||
width: width,
|
width: width,
|
||||||
// 实际视频宽度
|
|
||||||
height: height,
|
height: height,
|
||||||
codecType: 'h264',
|
codecType: 'h264',
|
||||||
);
|
);
|
||||||
@ -133,6 +168,16 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
AppLog.log('视频解码器初始化成功:textureId=$textureId');
|
AppLog.log('视频解码器初始化成功:textureId=$textureId');
|
||||||
VideoDecodePlugin.setOnFrameRenderedListener((textureId) {
|
VideoDecodePlugin.setOnFrameRenderedListener((textureId) {
|
||||||
AppLog.log('已经开始渲染=======');
|
AppLog.log('已经开始渲染=======');
|
||||||
|
|
||||||
|
// 计算并打印从接收第一帧到关闭loading的耗时
|
||||||
|
if (_firstFrameReceivedTime != null) {
|
||||||
|
final Duration renderToLoadingDuration = DateTime.now().difference(_firstFrameReceivedTime!);
|
||||||
|
AppLog.log('从接收第一帧到关闭loading耗时: ${renderToLoadingDuration.inMilliseconds} 毫秒 (${renderToLoadingDuration.inSeconds}.${renderToLoadingDuration.inMilliseconds % 1000} 秒)');
|
||||||
|
|
||||||
|
// 重置时间记录,避免重复计算
|
||||||
|
_firstFrameReceivedTime = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 只有真正渲染出首帧时才关闭loading
|
// 只有真正渲染出首帧时才关闭loading
|
||||||
Future.microtask(() => state.isLoading.value = false);
|
Future.microtask(() => state.isLoading.value = false);
|
||||||
});
|
});
|
||||||
@ -144,7 +189,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
AppLog.log('初始化视频解码器错误: $e');
|
AppLog.log('初始化视频解码器错误: $e');
|
||||||
// 如果初始化失败,延迟后重试
|
// 如果初始化失败,延迟后重试
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
await Future.delayed(const Duration(milliseconds: 500)); // 减少重试等待时间
|
||||||
if (!Get.isRegistered<TalkViewNativeDecodeLogic>()) {
|
if (!Get.isRegistered<TalkViewNativeDecodeLogic>()) {
|
||||||
return; // 如果控制器已经被销毁,不再重试
|
return; // 如果控制器已经被销毁,不再重试
|
||||||
}
|
}
|
||||||
@ -633,6 +678,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
_startProcessingAudioTimer?.cancel();
|
_startProcessingAudioTimer?.cancel();
|
||||||
_startProcessingAudioTimer = null;
|
_startProcessingAudioTimer = null;
|
||||||
_bufferedAudioFrames.clear();
|
_bufferedAudioFrames.clear();
|
||||||
|
// 停止监控请求定时器,防止挂断后自动重连
|
||||||
|
StartChartManage().stopCallRequestMessageTimer();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -899,8 +946,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
state.textureId.value = null;
|
state.textureId.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待一小段时间确保资源释放完成
|
// 减少等待时间,提高响应速度
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
await Future.delayed(Duration(milliseconds: 50));
|
||||||
|
|
||||||
// 创建新的解码器配置
|
// 创建新的解码器配置
|
||||||
final config = VideoDecoderConfig(
|
final config = VideoDecoderConfig(
|
||||||
@ -967,14 +1014,50 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
// 处理H264帧
|
// 处理H264帧
|
||||||
if (state.textureId.value != null) {
|
if (state.textureId.value != null) {
|
||||||
if (talkDataH264Frame != null) {
|
if (talkDataH264Frame != null) {
|
||||||
_addFrameToBuffer(
|
// 记录第一个H264帧接收时间并计算耗时
|
||||||
talkData.content,
|
if (_isFirstH264FrameReceived) {
|
||||||
talkDataH264Frame.frameType,
|
AppLog.log('第一个H264帧接收时间: ${DateTime.now()}');
|
||||||
talkData.durationMs,
|
|
||||||
talkDataH264Frame.frameSeq,
|
// 计算并打印从点击监控到接收H264数据的耗时
|
||||||
talkDataH264Frame.frameSeqI,
|
TalkViewNativeDecodeLogic.printH264ReceiveTime();
|
||||||
scpMessage!,
|
|
||||||
);
|
// 记录接收到第一帧的时间,用于计算到关闭loading的耗时
|
||||||
|
_firstFrameReceivedTime = DateTime.now();
|
||||||
|
|
||||||
|
_isFirstH264FrameReceived = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建包含帧数据和类型的Map
|
||||||
|
final Map<String, dynamic> frameMap = {
|
||||||
|
'frameData': talkData.content,
|
||||||
|
'frameType': talkDataH264Frame.frameType,
|
||||||
|
'frameSeq': talkDataH264Frame.frameSeq,
|
||||||
|
'frameSeqI': talkDataH264Frame.frameSeqI,
|
||||||
|
'pts': talkData.durationMs,
|
||||||
|
'scpMessage': scpMessage!,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果缓冲区超出最大大小,优先丢弃P/B帧
|
||||||
|
if (state.h264FrameBuffer.length >= state.maxFrameBufferSize) {
|
||||||
|
// 首先尝试快速查找P帧
|
||||||
|
int pbIndex = -1;
|
||||||
|
for (int i = 0; i < state.h264FrameBuffer.length; i++) {
|
||||||
|
if (state.h264FrameBuffer[i]['frameType'] == TalkDataH264Frame_FrameTypeE.P) {
|
||||||
|
pbIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbIndex != -1) {
|
||||||
|
state.h264FrameBuffer.removeAt(pbIndex);
|
||||||
|
} else {
|
||||||
|
// 如果没有找到P帧,则移除最旧的帧
|
||||||
|
state.h264FrameBuffer.removeAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将帧添加到缓冲区
|
||||||
|
state.h264FrameBuffer.add(frameMap);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AppLog.log('无法处理H264帧:textureId为空');
|
AppLog.log('无法处理H264帧:textureId为空');
|
||||||
|
|||||||
@ -109,12 +109,12 @@ class TalkViewNativeDecodeState {
|
|||||||
|
|
||||||
// H264帧缓冲区相关
|
// H264帧缓冲区相关
|
||||||
final List<Map<String, dynamic>> h264FrameBuffer = <Map<String, dynamic>>[]; // H264帧缓冲区,存储帧数据和类型
|
final List<Map<String, dynamic>> h264FrameBuffer = <Map<String, dynamic>>[]; // H264帧缓冲区,存储帧数据和类型
|
||||||
int maxFrameBufferSize = 3; // 最大缓冲区大小,减小以降低延迟
|
int maxFrameBufferSize = 2; // 最大缓冲区大小,减小以降低延迟
|
||||||
final int targetFps = 25; // 目标解码帧率,只是为了快速填充native的缓冲区
|
final int targetFps = 25; // 目标解码帧率,只是为了快速填充native的缓冲区
|
||||||
final int adaptiveBufferSizeMin = 2; // 自适应缓冲区最小大小
|
final int adaptiveBufferSizeMin = 2; // 自适应缓冲区最小大小
|
||||||
final int adaptiveBufferSizeMax = 6; // 自适应缓冲区最大大小
|
final int adaptiveBufferSizeMax = 6; // 自适应缓冲区最大大小
|
||||||
final int networkQualityCheckIntervalMs = 2000; // 网络质量检查间隔(毫秒)
|
final int networkQualityCheckIntervalMs = 2000; // 网络质量检查间隔(毫秒)
|
||||||
final int frameProcessIntervalMs = 10; // 帧处理间隔(毫秒),提高响应速度
|
int frameProcessIntervalMs = 10; // 帧处理间隔(毫秒),提高响应速度
|
||||||
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