|
@@ -5,9 +5,9 @@ import 'package:fis_common/func/func_proxy.dart';
|
|
|
import 'package:fis_measure/interfaces/process/player/play_controller.dart';
|
|
|
import 'package:fis_ui/index.dart';
|
|
|
import 'package:fis_vid/data_channel/channel.dart';
|
|
|
-import 'package:fis_vid/data_host/data_host.dart';
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
import 'package:vid/us/vid_us_image.dart';
|
|
|
+import 'package:vid/us/vid_us_image_data.dart';
|
|
|
|
|
|
import 'enums.dart';
|
|
|
import 'events.dart';
|
|
@@ -16,12 +16,15 @@ import 'events.dart';
|
|
|
class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
/// Vid播放器控制器
|
|
|
///
|
|
|
- /// [dataHost] Vid文件宿主
|
|
|
- VidPlayerController({
|
|
|
- required VidDataHost dataHost,
|
|
|
- }) {
|
|
|
- _dataHost = dataHost;
|
|
|
- _dataChannel = VidDataChannel.create(dataHost.url);
|
|
|
+ /// [url] Vid文件链接
|
|
|
+ VidPlayerController(String url) {
|
|
|
+ _url = url;
|
|
|
+ _dataChannel = VidDataChannel.create(url);
|
|
|
+ _isFirstFrame = true;
|
|
|
+ eventHandler = FEventHandler<VidPlayerEvent>();
|
|
|
+ firstFrameLoaded = FEventHandler<VidUsImage>();
|
|
|
+ frameLoadStateChanged = FEventHandler<bool>();
|
|
|
+ errorOccured = FEventHandler<String?>();
|
|
|
}
|
|
|
// ignore: constant_identifier_names
|
|
|
static const _CAN_PLAY_STATUS_ARR = [
|
|
@@ -33,12 +36,18 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
static const _HAS_VIEW_STATUS_ARR = [VidPlayStatus.play, VidPlayStatus.pause];
|
|
|
|
|
|
@override
|
|
|
- var eventHandler = FEventHandler<VidPlayerEvent>();
|
|
|
+ late final FEventHandler<VidPlayerEvent> eventHandler;
|
|
|
|
|
|
@override
|
|
|
- var currentFrameHandler = FEventHandler<VidUsImage>();
|
|
|
+ late final FEventHandler<VidUsImage> firstFrameLoaded;
|
|
|
|
|
|
- late final VidDataHost _dataHost;
|
|
|
+ @override
|
|
|
+ late final FEventHandler<bool> frameLoadStateChanged;
|
|
|
+
|
|
|
+ @override
|
|
|
+ late final FEventHandler<String?> errorOccured;
|
|
|
+
|
|
|
+ late final String _url;
|
|
|
late final VidDataChannel _dataChannel;
|
|
|
|
|
|
_PlayAssistant? _playAssistant;
|
|
@@ -47,9 +56,11 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
int _frameIndex = -1;
|
|
|
VidUsImage? _frame;
|
|
|
bool _disposed = false;
|
|
|
+ bool _isFirstFrame = false;
|
|
|
+ bool _loading = false;
|
|
|
|
|
|
- ///该图像是否需要获取测量项和注释项
|
|
|
- bool ifNeedInit = true;
|
|
|
+ @override
|
|
|
+ String get url => _url;
|
|
|
|
|
|
@override
|
|
|
VidDataChannel get dataChannel => _dataChannel;
|
|
@@ -80,7 +91,6 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
|
|
|
/// Total frames count of current vid
|
|
|
int get totalFramesCount => _dataChannel.imageCount;
|
|
|
- // int get totalFramesCount => _dataHost.frameCount;
|
|
|
|
|
|
bool get isSingleFrame => totalFramesCount == 1;
|
|
|
|
|
@@ -107,14 +117,7 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
|
|
|
@override
|
|
|
Future<void> load() async {
|
|
|
- ifNeedInit = true;
|
|
|
- // final info = await _dataHost.load();
|
|
|
- // if (info == null) {
|
|
|
- // setStatus(VidPlayStatus.loadFail);
|
|
|
- // } else {
|
|
|
- // _setStatus(VidPlayStatus.ready);
|
|
|
- // }
|
|
|
- final loaded = await _dataChannel.load(3000);
|
|
|
+ final loaded = await _dataChannel.load(3 * 1000);
|
|
|
if (loaded) {
|
|
|
_setStatus(VidPlayStatus.ready);
|
|
|
} else {
|
|
@@ -134,6 +137,7 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
gotoFrame(0);
|
|
|
_setStatus(VidPlayStatus.pause);
|
|
|
} else {
|
|
|
+ print("Frame count: $totalFramesCount");
|
|
|
_playAssistant ??= _PlayAssistant(this);
|
|
|
_playAssistant!.play();
|
|
|
|
|
@@ -167,9 +171,16 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
Future<bool> gotoFrame(int index) async {
|
|
|
if (index < 0 || index >= totalFramesCount) return false;
|
|
|
|
|
|
+ if (_loading) return false;
|
|
|
+
|
|
|
_frameIndex = index;
|
|
|
- _updateFrame();
|
|
|
- return true;
|
|
|
+ _loading = true;
|
|
|
+ _updateFrameLoadState(true);
|
|
|
+ final result = await _waitUpdateFrame();
|
|
|
+ if (result) {
|
|
|
+ _updateFrameLoadState(false);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
/// Set frame brightness
|
|
@@ -192,7 +203,7 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
0, 0, 0, 1, 0, // alpha // alpha
|
|
|
];
|
|
|
eventHandler.emit(this, VidPlayerFilterChangeEvent(fliterMatrix));
|
|
|
- _updateFrame();
|
|
|
+ _reloadFrame();
|
|
|
}
|
|
|
|
|
|
/// Set frame contrast
|
|
@@ -216,7 +227,7 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
0, 0, 0, 1, 0, // alpha
|
|
|
];
|
|
|
eventHandler.emit(this, VidPlayerFilterChangeEvent(fliterMatrix));
|
|
|
- _updateFrame();
|
|
|
+ _reloadFrame();
|
|
|
}
|
|
|
|
|
|
void setFilterMatrix(List<double> matrix) {
|
|
@@ -230,12 +241,35 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
eventHandler.emit(this, VidPlayResetToneEvent());
|
|
|
}
|
|
|
|
|
|
- void _updateFrame() {
|
|
|
- _handleUpdateFrame
|
|
|
- .throttle(
|
|
|
- timeout: _playAssistant?._playIntervalMillSeconds ?? 200,
|
|
|
- )
|
|
|
- .call();
|
|
|
+ void _reloadFrame() {
|
|
|
+ gotoFrame(currentFrameIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ void _updateFrameLoadState(bool val) {
|
|
|
+ _loading = val;
|
|
|
+ frameLoadStateChanged.emit(this, _loading);
|
|
|
+ }
|
|
|
+
|
|
|
+ void _emitErrorOccured([String? msg]) {
|
|
|
+ errorOccured.emit(this, msg);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 等待更新帧
|
|
|
+ Future<bool> _waitUpdateFrame() async {
|
|
|
+ if (_disposed) return false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ _frame = await _dataChannel.getImage(currentFrameIndex, 500);
|
|
|
+ emitFrameUpdate();
|
|
|
+ return true;
|
|
|
+ } catch (e) {
|
|
|
+ if (e is ReadTimeoutException) {
|
|
|
+ _emitErrorOccured("FrameLoadTimeout");
|
|
|
+ } else {
|
|
|
+ _emitErrorOccured("FrameLoadError");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
/// [Carotid] ✅用于设置颈动脉单帧展示
|
|
@@ -257,17 +291,10 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
play();
|
|
|
}
|
|
|
|
|
|
- Future<void> _handleUpdateFrame() async {
|
|
|
- if (_disposed) return;
|
|
|
-
|
|
|
- _frame = await _dataChannel.getImage(currentFrameIndex);
|
|
|
- emitFrameUpdate();
|
|
|
- }
|
|
|
-
|
|
|
void emitFrameUpdate() {
|
|
|
- if (ifNeedInit) {
|
|
|
- currentFrameHandler.emit(this, _frame!);
|
|
|
- ifNeedInit = false;
|
|
|
+ if (_isFirstFrame) {
|
|
|
+ firstFrameLoaded.emit(this, _frame!);
|
|
|
+ _isFirstFrame = false;
|
|
|
}
|
|
|
|
|
|
eventHandler.emit(
|
|
@@ -302,6 +329,7 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
|
|
|
_disposed = true;
|
|
|
_stop(needNotify: false);
|
|
|
eventHandler.dispose();
|
|
|
+ _dataChannel.close();
|
|
|
super.dispose();
|
|
|
}
|
|
|
|
|
@@ -344,11 +372,13 @@ class _PlayAssistant {
|
|
|
}
|
|
|
final duration = Duration(microseconds: _playInterval);
|
|
|
// final duration = const Duration(milliseconds: 1000 ~/ 10);
|
|
|
- _timer = Timer.periodic(duration, (timer) {
|
|
|
+ _timer = Timer.periodic(duration, (timer) async {
|
|
|
// print('play at ${DateTime.now()}');
|
|
|
- owner.gotoFrame(owner.currentFrameIndex + 1);
|
|
|
- if (owner.currentFrameIndex == owner.totalFramesCount - 1) {
|
|
|
- owner.pause();
|
|
|
+ final result = await owner.gotoFrame(owner.currentFrameIndex + 1);
|
|
|
+ if (result) {
|
|
|
+ if (owner.currentFrameIndex == owner.totalFramesCount - 1) {
|
|
|
+ owner.pause();
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
}
|
|
@@ -356,11 +386,9 @@ class _PlayAssistant {
|
|
|
void pause() {
|
|
|
_timer?.cancel();
|
|
|
_timer = null;
|
|
|
- print('stop at ${DateTime.now()}');
|
|
|
}
|
|
|
|
|
|
void _prepare() {
|
|
|
- // _frameRate = owner._dataHost.probe.frameRate;
|
|
|
_frameRate = owner._dataChannel.probe.frameRate;
|
|
|
_ready = true;
|
|
|
}
|