fix:完成切换清晰度逻辑
This commit is contained in:
parent
069ef1b592
commit
90f94e1a9a
@ -35,6 +35,8 @@ import 'package:star_lock/talk/starChart/views/native/talk_view_native_decode_st
|
|||||||
import 'package:star_lock/talk/starChart/views/talkView/talk_view_state.dart';
|
import 'package:star_lock/talk/starChart/views/talkView/talk_view_state.dart';
|
||||||
import 'package:star_lock/tools/G711Tool.dart';
|
import 'package:star_lock/tools/G711Tool.dart';
|
||||||
import 'package:star_lock/tools/bugly/bugly_tool.dart';
|
import 'package:star_lock/tools/bugly/bugly_tool.dart';
|
||||||
|
import 'package:star_lock/tools/commonDataManage.dart';
|
||||||
|
import 'package:star_lock/tools/storage.dart';
|
||||||
import 'package:video_decode_plugin/video_decode_plugin.dart';
|
import 'package:video_decode_plugin/video_decode_plugin.dart';
|
||||||
|
|
||||||
import '../../../../tools/baseGetXController.dart';
|
import '../../../../tools/baseGetXController.dart';
|
||||||
@ -75,6 +77,16 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
// 新增:记录上一个已接收的frameSeq
|
// 新增:记录上一个已接收的frameSeq
|
||||||
int? _lastFrameSeq;
|
int? _lastFrameSeq;
|
||||||
|
|
||||||
|
// 新增:frameSeq回绕检测标志
|
||||||
|
bool _pendingStreamReset = false;
|
||||||
|
|
||||||
|
// 新增:记录切换时的宽高参数
|
||||||
|
int _pendingResetWidth = 864;
|
||||||
|
int _pendingResetHeight = 480;
|
||||||
|
|
||||||
|
// 新增:等待新I帧状态
|
||||||
|
bool _waitingForIFrame = false;
|
||||||
|
|
||||||
// 初始化视频解码器
|
// 初始化视频解码器
|
||||||
Future<void> _initVideoDecoder() async {
|
Future<void> _initVideoDecoder() async {
|
||||||
try {
|
try {
|
||||||
@ -89,12 +101,12 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
// 初始化解码器并获取textureId
|
// 初始化解码器并获取textureId
|
||||||
final textureId = await VideoDecodePlugin.initDecoder(config);
|
final textureId = await VideoDecodePlugin.initDecoder(config);
|
||||||
if (textureId != null) {
|
if (textureId != null) {
|
||||||
state.textureId.value = textureId;
|
Future.microtask(() => state.textureId.value = textureId);
|
||||||
AppLog.log('视频解码器初始化成功:textureId=$textureId');
|
AppLog.log('视频解码器初始化成功:textureId=$textureId');
|
||||||
|
|
||||||
VideoDecodePlugin.setOnFrameRenderedListener((textureId) {
|
VideoDecodePlugin.setOnFrameRenderedListener((textureId) {
|
||||||
state.isLoading.value = false;
|
|
||||||
AppLog.log('已经开始渲染=======');
|
AppLog.log('已经开始渲染=======');
|
||||||
|
// 只有真正渲染出首帧时才关闭loading
|
||||||
|
Future.microtask(() => state.isLoading.value = false);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
AppLog.log('视频解码器初始化失败');
|
AppLog.log('视频解码器初始化失败');
|
||||||
@ -146,12 +158,53 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
int frameSeq,
|
int frameSeq,
|
||||||
int frameSeqI,
|
int frameSeqI,
|
||||||
) {
|
) {
|
||||||
// 只允许frameSeq严格递增,乱序或重复帧直接丢弃
|
// 检测frameSeq回绕,且为I帧
|
||||||
if (_lastFrameSeq != null && frameSeq <= _lastFrameSeq!) {
|
if (!_pendingStreamReset &&
|
||||||
AppLog.log('丢弃乱序或重复帧: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq');
|
_lastFrameSeq != null &&
|
||||||
return;
|
frameType == TalkDataH264Frame_FrameTypeE.I &&
|
||||||
|
frameSeq < _lastFrameSeq!) {
|
||||||
|
// 检测到新流I帧,进入loading并重置所有本地状态
|
||||||
|
AppLog.log(
|
||||||
|
'检测到新流I帧,frameSeq回绕,进入loading并重置: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq');
|
||||||
|
Future.microtask(() => state.isLoading.value = true);
|
||||||
|
_pendingStreamReset = true;
|
||||||
|
// 先暂停帧处理定时器,防止竞态
|
||||||
|
_stopFrameProcessTimer();
|
||||||
|
// 先释放并重新初始化解码器
|
||||||
|
_resetDecoderForNewStream(_pendingResetWidth, _pendingResetHeight);
|
||||||
|
// 重置所有本地状态
|
||||||
|
_lastFrameSeq = null;
|
||||||
|
_decodedIFrames.clear();
|
||||||
|
state.h264FrameBuffer.clear();
|
||||||
|
// 再恢复帧处理定时器
|
||||||
|
_startFrameProcessTimer();
|
||||||
|
// 不return,直接用该I帧初始化解码器并解码
|
||||||
|
// 继续往下执行
|
||||||
|
}
|
||||||
|
// 如果处于pendingStreamReset,等待新I帧
|
||||||
|
if (_pendingStreamReset) {
|
||||||
|
if (frameType == TalkDataH264Frame_FrameTypeE.I) {
|
||||||
|
// 收到新流I帧,关闭loading,恢复正常解码
|
||||||
|
AppLog.log('收到新流I帧,关闭loading: frameSeq=$frameSeq');
|
||||||
|
//Future.microtask(() => state.isLoading.value = false);
|
||||||
|
_pendingStreamReset = false;
|
||||||
|
_lastFrameSeq = frameSeq;
|
||||||
|
_decodedIFrames.clear();
|
||||||
|
_decodedIFrames.add(frameSeq);
|
||||||
|
// 继续往下执行,直接用该I帧解码
|
||||||
|
} else {
|
||||||
|
// 等待新流I帧期间,丢弃所有非I帧
|
||||||
|
AppLog.log('等待新流I帧,丢弃非I帧: frameSeq=$frameSeq, frameType=$frameType');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 正常流程
|
||||||
|
if (_lastFrameSeq != null && frameSeq <= _lastFrameSeq!) {
|
||||||
|
AppLog.log('丢弃乱序或重复帧: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_lastFrameSeq = frameSeq;
|
||||||
}
|
}
|
||||||
_lastFrameSeq = frameSeq;
|
|
||||||
// 创建包含帧数据和类型的Map
|
// 创建包含帧数据和类型的Map
|
||||||
final Map<String, dynamic> frameMap = {
|
final Map<String, dynamic> frameMap = {
|
||||||
'frameData': frameData,
|
'frameData': frameData,
|
||||||
@ -163,8 +216,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
|
|
||||||
// 如果缓冲区超出最大大小,优先丢弃P/B帧
|
// 如果缓冲区超出最大大小,优先丢弃P/B帧
|
||||||
while (state.h264FrameBuffer.length >= state.maxFrameBufferSize) {
|
while (state.h264FrameBuffer.length >= state.maxFrameBufferSize) {
|
||||||
int pbIndex = state.h264FrameBuffer.indexWhere((f) =>
|
int pbIndex = state.h264FrameBuffer
|
||||||
f['frameType'] == TalkDataH264Frame_FrameTypeE.P);
|
.indexWhere((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.P);
|
||||||
if (pbIndex != -1) {
|
if (pbIndex != -1) {
|
||||||
state.h264FrameBuffer.removeAt(pbIndex);
|
state.h264FrameBuffer.removeAt(pbIndex);
|
||||||
} else {
|
} else {
|
||||||
@ -209,29 +262,31 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 取出最早的帧
|
// 取出最早的帧
|
||||||
final Map<String, dynamic> frameMap = state.h264FrameBuffer.removeAt(0);
|
final Map<String, dynamic>? frameMap = state.h264FrameBuffer.isNotEmpty
|
||||||
final List<int> frameData = frameMap['frameData'];
|
? state.h264FrameBuffer.removeAt(0)
|
||||||
final TalkDataH264Frame_FrameTypeE frameType = frameMap['frameType'];
|
: null;
|
||||||
final int frameSeq = frameMap['frameSeq'];
|
if (frameMap == null) {
|
||||||
final int frameSeqI = frameMap['frameSeqI'];
|
state.isProcessingFrame = false;
|
||||||
int pts = frameMap['pts'];
|
return;
|
||||||
// int pts = DateTime.now().millisecondsSinceEpoch;
|
}
|
||||||
|
final List<int>? frameData = frameMap['frameData'];
|
||||||
if (frameType == TalkDataH264Frame_FrameTypeE.P) {
|
final TalkDataH264Frame_FrameTypeE? frameType = frameMap['frameType'];
|
||||||
// 以frameSeqI为I帧序号标识
|
final int? frameSeq = frameMap['frameSeq'];
|
||||||
if (!(_decodedIFrames.contains(frameSeqI))) {
|
final int? frameSeqI = frameMap['frameSeqI'];
|
||||||
AppLog.log('丢弃P帧:未收到对应I帧,frameSeqI=${frameSeqI}');
|
final int? pts = frameMap['pts'];
|
||||||
return;
|
if (frameData == null ||
|
||||||
}
|
frameType == null ||
|
||||||
} else if (frameType == TalkDataH264Frame_FrameTypeE.I) {
|
frameSeq == null ||
|
||||||
// 记录已解码I帧序号
|
frameSeqI == null ||
|
||||||
_decodedIFrames.add(frameSeq);
|
pts == null) {
|
||||||
|
state.isProcessingFrame = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 解码器未初始化或textureId为null时跳过
|
||||||
|
if (state.textureId.value == null) {
|
||||||
|
state.isProcessingFrame = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// 实时写入h264文件
|
|
||||||
// _appendH264FrameToFile(frameData, frameType);
|
|
||||||
|
|
||||||
// final timestamp = DateTime.now().millisecondsSinceEpoch;
|
|
||||||
// final timestamp64 = timestamp is int ? timestamp : timestamp.toInt();
|
|
||||||
await VideoDecodePlugin.sendFrame(
|
await VideoDecodePlugin.sendFrame(
|
||||||
frameData: frameData,
|
frameData: frameData,
|
||||||
frameType: frameType == TalkDataH264Frame_FrameTypeE.I ? 0 : 1,
|
frameType: frameType == TalkDataH264Frame_FrameTypeE.I ? 0 : 1,
|
||||||
@ -462,6 +517,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
// 初始化视频解码器
|
// 初始化视频解码器
|
||||||
_initVideoDecoder();
|
_initVideoDecoder();
|
||||||
|
|
||||||
|
_initHdOptions();
|
||||||
// 初始化H264帧缓冲区
|
// 初始化H264帧缓冲区
|
||||||
state.h264FrameBuffer.clear();
|
state.h264FrameBuffer.clear();
|
||||||
state.isProcessingFrame = false;
|
state.isProcessingFrame = false;
|
||||||
@ -490,7 +546,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
// 释放视频解码器资源
|
// 释放视频解码器资源
|
||||||
if (state.textureId.value != null) {
|
if (state.textureId.value != null) {
|
||||||
VideoDecodePlugin.releaseDecoder();
|
VideoDecodePlugin.releaseDecoder();
|
||||||
state.textureId.value = null;
|
Future.microtask(() => state.textureId.value = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消数据流监听
|
// 取消数据流监听
|
||||||
@ -577,36 +633,42 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 远程开锁
|
// 远程开锁
|
||||||
Future<void> remoteOpenLock() async {
|
Future<void> remoteOpenLock() async {
|
||||||
|
final LockListInfoItemEntity currentKeyInfo =
|
||||||
|
CommonDataManage().currentKeyInfo;
|
||||||
|
|
||||||
|
var lockId = currentKeyInfo.lockId ?? 0;
|
||||||
|
var remoteUnlock = currentKeyInfo.lockSetting?.remoteUnlock ?? 0;
|
||||||
|
|
||||||
final lockPeerId = StartChartManage().lockPeerId;
|
final lockPeerId = StartChartManage().lockPeerId;
|
||||||
final lockListPeerId = StartChartManage().lockListPeerId;
|
final LockListInfoGroupEntity? lockListInfoGroupEntity =
|
||||||
int lockId = lockDetailState.keyInfos.value.lockId ?? 0;
|
await Storage.getLockMainListData();
|
||||||
|
if (lockListInfoGroupEntity != null) {
|
||||||
// 如果锁列表获取到peerId,代表有多个锁,使用锁列表的peerId
|
lockListInfoGroupEntity!.groupList?.forEach((element) {
|
||||||
// 从列表中遍历出对应的peerId
|
final lockList = element.lockList;
|
||||||
lockListPeerId.forEach((element) {
|
if (lockList != null && lockList.length != 0) {
|
||||||
if (element.network?.peerId == lockPeerId) {
|
for (var lockInfo in lockList) {
|
||||||
lockId = element.lockId ?? 0;
|
final peerId = lockInfo.network?.peerId;
|
||||||
}
|
if (peerId != null && peerId != '') {
|
||||||
});
|
if (peerId == lockPeerId) {
|
||||||
|
lockId = lockInfo.lockId ?? 0;
|
||||||
final LockSetInfoEntity lockSetInfoEntity =
|
remoteUnlock = lockInfo.lockSetting?.remoteUnlock ?? 0;
|
||||||
await ApiRepository.to.getLockSettingInfoData(
|
}
|
||||||
lockId: lockId.toString(),
|
}
|
||||||
);
|
}
|
||||||
if (lockSetInfoEntity.errorCode!.codeIsSuccessful) {
|
|
||||||
if (lockSetInfoEntity.data?.lockFeature?.remoteUnlock == 1 &&
|
|
||||||
lockSetInfoEntity.data?.lockSettingInfo?.remoteUnlock == 1) {
|
|
||||||
final LoginEntity entity = await ApiRepository.to
|
|
||||||
.remoteOpenLock(lockId: lockId.toString(), timeOut: 60);
|
|
||||||
if (entity.errorCode!.codeIsSuccessful) {
|
|
||||||
showToast('已开锁'.tr);
|
|
||||||
StartChartManage().lockListPeerId = [];
|
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
showToast('该锁的远程开锁功能未启用'.tr);
|
}
|
||||||
|
if (remoteUnlock == 1) {
|
||||||
|
final LoginEntity entity = await ApiRepository.to
|
||||||
|
.remoteOpenLock(lockId: lockId.toString(), timeOut: 60);
|
||||||
|
if (entity.errorCode!.codeIsSuccessful) {
|
||||||
|
showToast('已开锁'.tr);
|
||||||
|
StartChartManage().lockListPeerId = [];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
showToast('该锁的远程开锁功能未启用'.tr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1172,4 +1234,81 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 切换清晰度的方法,后续补充具体实现
|
||||||
|
void onQualityChanged(String quality) async {
|
||||||
|
state.currentQuality.value = quality;
|
||||||
|
TalkExpectReq talkExpectReq = StartChartManage().getDefaultTalkExpect();
|
||||||
|
final audioType = talkExpectReq.audioType;
|
||||||
|
int width = 864;
|
||||||
|
int height = 480;
|
||||||
|
switch (quality) {
|
||||||
|
case '高清':
|
||||||
|
talkExpectReq = TalkExpectReq(
|
||||||
|
videoType: [VideoTypeE.H264_720P],
|
||||||
|
audioType: audioType,
|
||||||
|
);
|
||||||
|
width = 1280;
|
||||||
|
height = 720;
|
||||||
|
break;
|
||||||
|
case '标清':
|
||||||
|
talkExpectReq = TalkExpectReq(
|
||||||
|
videoType: [VideoTypeE.H264],
|
||||||
|
audioType: audioType,
|
||||||
|
);
|
||||||
|
width = 864;
|
||||||
|
height = 480;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改发送预期数据
|
||||||
|
StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
|
||||||
|
talkExpect: talkExpectReq);
|
||||||
|
|
||||||
|
// 不立即loading,继续解码旧流帧,等待frameSeq回绕检测
|
||||||
|
// 仅重置frameSeq回绕检测标志
|
||||||
|
_pendingStreamReset = false;
|
||||||
|
_pendingResetWidth = width;
|
||||||
|
_pendingResetHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initHdOptions() {
|
||||||
|
TalkExpectReq talkExpectReq = StartChartManage().getDefaultTalkExpect();
|
||||||
|
final videoType = talkExpectReq.videoType;
|
||||||
|
if (videoType.contains(VideoTypeE.H264)) {
|
||||||
|
state.currentQuality.value = '标清';
|
||||||
|
} else if (videoType.contains(VideoTypeE.H264_720P)) {
|
||||||
|
state.currentQuality.value = '高清';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:重置解码器方法
|
||||||
|
Future<void> _resetDecoderForNewStream(int width, int height) async {
|
||||||
|
try {
|
||||||
|
if (state.textureId.value != null) {
|
||||||
|
await VideoDecodePlugin.releaseDecoder();
|
||||||
|
Future.microtask(() => state.textureId.value = null);
|
||||||
|
}
|
||||||
|
final config = VideoDecoderConfig(
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
codecType: 'h264',
|
||||||
|
);
|
||||||
|
final textureId = await VideoDecodePlugin.initDecoder(config);
|
||||||
|
if (textureId != null) {
|
||||||
|
Future.microtask(() => state.textureId.value = textureId);
|
||||||
|
AppLog.log('frameSeq回绕后解码器初始化成功:textureId=$textureId');
|
||||||
|
VideoDecodePlugin.setOnFrameRenderedListener((textureId) {
|
||||||
|
AppLog.log('已经开始渲染=======');
|
||||||
|
// 只有真正渲染出首帧时才关闭loading
|
||||||
|
Future.microtask(() => state.isLoading.value = false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
AppLog.log('frameSeq回绕后解码器初始化失败');
|
||||||
|
}
|
||||||
|
_startFrameProcessTimer();
|
||||||
|
} catch (e) {
|
||||||
|
AppLog.log('frameSeq回绕时解码器初始化错误: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,40 +97,42 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
|
|||||||
final double scaleWidth = physicalWidth / rotatedImageWidth;
|
final double scaleWidth = physicalWidth / rotatedImageWidth;
|
||||||
final double scaleHeight = physicalHeight / rotatedImageHeight;
|
final double scaleHeight = physicalHeight / rotatedImageHeight;
|
||||||
max(scaleWidth, scaleHeight); // 选择较大的缩放比例
|
max(scaleWidth, scaleHeight); // 选择较大的缩放比例
|
||||||
return state.isLoading.isTrue
|
// 防御性处理:只要loading中或textureId为null,优先渲染loading/占位
|
||||||
? Image.asset(
|
if (state.isLoading.isTrue || state.textureId.value == null) {
|
||||||
'images/main/monitorBg.png',
|
return Image.asset(
|
||||||
width: screenWidth,
|
'images/main/monitorBg.png',
|
||||||
height: screenHeight,
|
width: screenWidth,
|
||||||
fit: BoxFit.cover,
|
height: screenHeight,
|
||||||
)
|
fit: BoxFit.cover,
|
||||||
: Positioned.fill(
|
);
|
||||||
child: PopScope(
|
} else {
|
||||||
canPop: false,
|
return Positioned.fill(
|
||||||
child: RepaintBoundary(
|
child: PopScope(
|
||||||
key: state.globalKey,
|
canPop: false,
|
||||||
child: SizedBox.expand(
|
child: RepaintBoundary(
|
||||||
child: RotatedBox(
|
key: state.globalKey,
|
||||||
// 解码器不支持硬件旋转,使用RotatedBox
|
child: SizedBox.expand(
|
||||||
quarterTurns:
|
child: RotatedBox(
|
||||||
startChartManage.rotateAngle ~/ 90,
|
// 解码器不支持硬件旋转,使用RotatedBox
|
||||||
child: Platform.isIOS
|
quarterTurns: startChartManage.rotateAngle ~/ 90,
|
||||||
? Transform.scale(
|
child: Platform.isIOS
|
||||||
scale: 1.008, // 轻微放大,消除iOS白边
|
? Transform.scale(
|
||||||
child: Texture(
|
scale: 1.008, // 轻微放大,消除iOS白边
|
||||||
textureId: state.textureId.value!,
|
child: Texture(
|
||||||
filterQuality: FilterQuality.medium,
|
textureId: state.textureId.value!,
|
||||||
),
|
filterQuality: FilterQuality.medium,
|
||||||
)
|
),
|
||||||
: Texture(
|
)
|
||||||
textureId: state.textureId.value!,
|
: Texture(
|
||||||
filterQuality: FilterQuality.medium,
|
textureId: state.textureId.value!,
|
||||||
),
|
filterQuality: FilterQuality.medium,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -295,6 +297,62 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(width: 50.w),
|
||||||
|
// 清晰度切换按钮
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
// 弹出底部弹出层,选择清晰度
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20.w)),
|
||||||
|
),
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final List<String> qualities = ['高清', '标清'];
|
||||||
|
return SafeArea(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: qualities.map((q) {
|
||||||
|
return Obx(() => InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
logic.onQualityChanged(q);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 18.w),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
q,
|
||||||
|
style: TextStyle(
|
||||||
|
color: state.currentQuality.value == q
|
||||||
|
? AppColors.mainColor
|
||||||
|
: Colors.black,
|
||||||
|
fontWeight: state.currentQuality.value == q
|
||||||
|
? FontWeight.bold
|
||||||
|
: FontWeight.normal,
|
||||||
|
fontSize: 28.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Icon(Icons.high_quality_outlined, color: Colors.white, size: 38.w),
|
||||||
|
),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -117,4 +117,7 @@ class TalkViewNativeDecodeState {
|
|||||||
// H264文件保存相关
|
// H264文件保存相关
|
||||||
String? h264FilePath;
|
String? h264FilePath;
|
||||||
File? h264File;
|
File? h264File;
|
||||||
|
|
||||||
|
// 当前清晰度选项,初始为'高清'
|
||||||
|
RxString currentQuality = '高清'.obs; // 可选:高清、标清、流畅
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user