melon.yin 2 жил өмнө
parent
commit
1af91defcb

+ 0 - 1
lib/index.dart

@@ -1,4 +1,3 @@
 export 'view/player/player.dart';
 export 'view/player/control_board/control_board.dart';
 export 'view/player/controller.dart';
-export 'view/frame_view/frame_view.dart';

+ 12 - 3
lib/interfaces/process/player/play_controller.dart

@@ -18,6 +18,9 @@ abstract class IPlayerController {
   /// 数据通道
   VidDataChannel get dataChannel;
 
+  /// 链接
+  String get url;
+
   Future<void> load();
 
   /// 播放
@@ -30,8 +33,14 @@ abstract class IPlayerController {
   void dispose();
 
   /// 事件处理
-  late FEventHandler<VidPlayerEvent> eventHandler;
+  late final FEventHandler<VidPlayerEvent> eventHandler;
+
+  /// 首帧事件处理
+  late final FEventHandler<VidUsImage> firstFrameLoaded;
+
+  /// 单帧加载状态变更事件
+  late final FEventHandler<bool> frameLoadStateChanged;
 
-  /// 当前帧信息返回的事件处理
-  late FEventHandler<VidUsImage> currentFrameHandler;
+  /// 发生错误事件
+  late final FEventHandler<String?> errorOccured;
 }

+ 0 - 3
lib/interfaces/process/workspace/measure_controller.dart

@@ -14,9 +14,6 @@ abstract class IMeasureController {
   /// 当前vid链接地址
   String get currentVidUrl;
 
-  /// Vid数据宿主
-  VidDataHost get dataHost;
-
   /// 播放控制器
   IPlayerController get playerController;
 

+ 0 - 7
lib/main.dart

@@ -7,7 +7,6 @@ import 'package:fis_jsonrpc/rpc.dart';
 import 'package:fis_measure/measure_page_test.dart';
 import 'package:fis_measure/process/layout/configuration.dart';
 import 'package:fis_measure/view/measure/measure_view.dart';
-import 'package:fis_vid/data_channel/test.dart';
 import 'package:fis_vid/index.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
@@ -15,7 +14,6 @@ import 'package:get/get.dart';
 import 'package:fis_i18n/i18n.dart';
 
 import 'item_create_test.dart';
-import 'third_vid_test.dart';
 
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
@@ -25,10 +23,6 @@ void main() async {
   await _initI18n();
   await LayoutConfiguration.ins.loadData();
   // LayoutConfiguration.ins.getRect("")
-  final rpc = JsonRpcProxy();
-  if (FPlatform.isMacOS || FPlatform.isWindows) {
-    VidDataHostEnv.setPlatformGetter(() => rpc.platform);
-  }
   runApp(const MyApp());
 }
 
@@ -76,7 +70,6 @@ class _MyHomePageState extends State<MyHomePage> {
               onPressed: () {
                 // ThirdPartVidTest().run();
                 // ItemCreateTest().run();
-                VidChannelTest().run();
               },
               child: const Text("Function Test"),
             ),

+ 9 - 15
lib/process/workspace/measure_controller.dart

@@ -23,7 +23,6 @@ typedef MeasureImagesFetchFunc = Future<List<ExamImageInfo>> Function(
 
 class MeasureController implements IMeasureController {
   late final ExamInfo _examInfo;
-  VidDataHost? _vidDataHost;
   IPlayerController? _playerController;
   IApplication? _application;
   int _recursive = 0;
@@ -43,9 +42,6 @@ class MeasureController implements IMeasureController {
   @override
   String get currentVidUrl => examInfo.selectedImage.url;
 
-  @override
-  VidDataHost get dataHost => _vidDataHost!;
-
   @override
   bool get canDoMeasure => playerController.status == VidPlayStatus.pause;
 
@@ -77,7 +73,6 @@ class MeasureController implements IMeasureController {
     _playerController?.dispose();
     Get.delete<IApplication>();
     Get.delete<IPlayerController>();
-    _vidDataHost?.release();
   }
 
   /// 轮询下载接口
@@ -107,16 +102,14 @@ class MeasureController implements IMeasureController {
 
   void _onExamImageSelectedChanged(Object sender, int index) {
     final url = currentVidUrl;
+    _playerController?.firstFrameLoaded.removeListener(_onFrameLoaded);
     _playerController?.dispose();
-    _vidDataHost?.frameLoaded.dispose();
     Get.delete<IPlayerController>();
-    // Get.delete<IApplication>();
 
-    _vidDataHost = VidDataHost(url);
-    _playerController =
-        Get.put<IPlayerController>(VidPlayerController(dataHost: dataHost));
+    _playerController = Get.put<IPlayerController>(VidPlayerController(url));
 
-    dataHost.frameLoaded.addListener(_onFrameLoaded);
+    // dataHost.frameLoaded.addListener(_onFrameLoaded);// TODO: host->player
+    _playerController!.firstFrameLoaded.addListener(_onFrameLoaded);
     _recursivePlayer(sender);
   }
 
@@ -140,13 +133,14 @@ class MeasureController implements IMeasureController {
   }
 
   Future<void> _buildApplication() async {
-    final is3rd = dataHost.probe.name == Application.C_VID_THIRDPART_NAME;
+    final dataChannel = playerController.dataChannel;
+    final probe = dataChannel.probe;
+    final is3rd = probe.name == Application.C_VID_THIRDPART_NAME;
     IApplication app;
     if (is3rd) {
-      final vidData = await dataHost.getData();
-      app = ThirdPartApplication(dataHost.probe, vidData!.extendedData);
+      app = ThirdPartApplication(probe, dataChannel.extendedData);
     } else {
-      app = Application(dataHost.probe);
+      app = Application(probe);
     }
     Get.delete<IApplication>();
     _application = Get.put<IApplication>(app);

+ 0 - 31
lib/third_vid_test.dart

@@ -1,31 +0,0 @@
-import 'package:fis_measure/interfaces/date_types/rect_region.dart';
-import 'package:fis_measure/process/visual/v2d_visual.dart';
-import 'package:fis_vid/data_host/data_host.dart';
-import 'package:fis_vid_ext/vid_value_element.dart';
-import 'package:fis_vid_ext/vid_extended_data.dart';
-
-class ThirdPartVidTest {
-  void run() async {
-    final dataHost = VidDataHost(
-        "http://cdn-bj.fis.plus/81FFF8E5E078473FA687FBE81C4869B1.vid");
-    await dataHost.load();
-    final vidData = await dataHost.getData();
-    final extData = vidData!.getStructExtendedData();
-    if (extData != null) {
-      for (var key in extData.data.keys) {
-        final val = extData.data[key] as VidValueElement;
-        print('${key.group}-${key.element}: ${val.value}');
-      }
-    }
-    final frame = vidData.getImage(0);
-    for (var visual in frame.visuals) {
-      final v = V2dVisual(visual, RectRegion.fill(0, 0, 1, 1));
-      final a = v.visualAreas.first;
-      print(a.displayRegion);
-      print(a.mode.name);
-      if (a.viewport != null) {
-        print(a.viewport!.region);
-      }
-    }
-  }
-}

+ 0 - 3
lib/view/frame_view/frame_view.dart

@@ -1,3 +0,0 @@
-export 'stub/frame_view.dart'
-    if (dart.library.html) 'web/frame_view.dart'
-    if (dart.library.io) 'native/frame_view.dart';

+ 0 - 16
lib/view/frame_view/interface/frame_view.dart

@@ -1,16 +0,0 @@
-import 'dart:typed_data';
-import 'package:flutter/cupertino.dart';
-
-abstract class VidFrameViewInterface extends Widget {
-  const VidFrameViewInterface(
-    this.data, {
-    Key? key,
-    this.width,
-    this.height,
-  }) : super(key: key);
-
-  final Uint8List data;
-
-  final double? width;
-  final double? height;
-}

+ 0 - 34
lib/view/frame_view/native/frame_view.dart

@@ -1,34 +0,0 @@
-import 'dart:typed_data';
-
-import 'package:fis_measure/view/frame_view/interface/frame_view.dart';
-import 'package:flutter/material.dart';
-
-class VidFrameView extends StatelessWidget implements VidFrameViewInterface {
-  const VidFrameView(
-    this.data, {
-    Key? key,
-    this.width,
-    this.height,
-  }) : super(key: key);
-
-  @override
-  final Uint8List data;
-
-  @override
-  final double? height;
-
-  @override
-  final double? width;
-
-  @override
-  Widget build(BuildContext context) {
-    return Image.memory(
-      data,
-      width: width,
-      height: height,
-      isAntiAlias: true,
-      fit: BoxFit.contain,
-      gaplessPlayback: true,
-    );
-  }
-}

+ 0 - 28
lib/view/frame_view/stub/frame_view.dart

@@ -1,28 +0,0 @@
-import 'dart:typed_data';
-
-import 'package:fis_measure/view/frame_view/interface/frame_view.dart';
-import 'package:flutter/material.dart';
-
-class VidFrameView extends StatelessWidget implements VidFrameViewInterface {
-  const VidFrameView(
-    this.data, {
-    Key? key,
-    this.width,
-    this.height,
-  }) : super(key: key);
-
-  @override
-  final Uint8List data;
-
-  @override
-  final double? height;
-
-  @override
-  final double? width;
-
-  @override
-  Widget build(BuildContext context) {
-    // TODO: implement build
-    throw UnimplementedError();
-  }
-}

+ 0 - 32
lib/view/frame_view/web/browser.dart

@@ -1,32 +0,0 @@
-part of 'frame_view.dart';
-
-class _VidFrameViewBroswer extends StatelessWidget
-    implements VidFrameViewInterface {
-  const _VidFrameViewBroswer(
-    this.data, {
-    Key? key,
-    this.width,
-    this.height,
-  }) : super(key: key);
-
-  @override
-  final Uint8List data;
-
-  @override
-  final double? height;
-
-  @override
-  final double? width;
-
-  @override
-  Widget build(BuildContext context) {
-    return Image.memory(
-      data,
-      width: width,
-      height: height,
-      isAntiAlias: true,
-      fit: BoxFit.contain,
-      gaplessPlayback: true,
-    );
-  }
-}

+ 0 - 42
lib/view/frame_view/web/frame_view.dart

@@ -1,42 +0,0 @@
-import 'dart:typed_data';
-
-import 'package:fis_common/env/env.dart';
-import 'package:fis_measure/view/frame_view/interface/frame_view.dart';
-import 'package:fis_vid/common/env.dart';
-import 'package:flutter/material.dart';
-
-part 'browser.dart';
-part 'shell.dart';
-
-class VidFrameView extends StatelessWidget implements VidFrameViewInterface {
-  const VidFrameView(
-    this.data, {
-    Key? key,
-    this.width,
-    this.height,
-  }) : super(key: key);
-
-  @override
-  final Uint8List data;
-
-  @override
-  final double? height;
-
-  @override
-  final double? width;
-
-  @override
-  Widget build(BuildContext context) {
-    return VidDataHostEnv.isShell
-        ? _VidFrameViewShell(
-            data,
-            width: width,
-            height: height,
-          )
-        : _VidFrameViewBroswer(
-            data,
-            width: width,
-            height: height,
-          );
-  }
-}

+ 0 - 32
lib/view/frame_view/web/shell.dart

@@ -1,32 +0,0 @@
-part of 'frame_view.dart';
-
-class _VidFrameViewShell extends StatelessWidget
-    implements VidFrameViewInterface {
-  const _VidFrameViewShell(
-    this.data, {
-    Key? key,
-    this.width,
-    this.height,
-  }) : super(key: key);
-
-  @override
-  final Uint8List data;
-
-  @override
-  final double? height;
-
-  @override
-  final double? width;
-
-  @override
-  Widget build(BuildContext context) {
-    return Image.memory(
-      data,
-      width: width,
-      height: height,
-      isAntiAlias: true,
-      fit: BoxFit.contain,
-      gaplessPlayback: true,
-    );
-  }
-}

+ 5 - 5
lib/view/measure/measure_tool.dart

@@ -90,7 +90,7 @@ class LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
   }
 
   /// 获取当前图像的测量项,从第一帧取
-  void currentFrameHandler(Object sender, VidUsImage e) async {
+  void firstFrameLoaded(Object sender, VidUsImage e) async {
     List<String> getModes = [];
     for (var element in e.visuals[0].modes) {
       getModes.add(element.type.toString().split('.')[1]);
@@ -150,8 +150,8 @@ class LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
 
   @override
   void initState() {
-    playerController.currentFrameHandler.addListener(
-      (currentFrameHandler),
+    playerController.firstFrameLoaded.addListener(
+      (firstFrameLoaded),
     );
     measureData.getItemMetaListChanged.addListener(getItemMetaListChanged);
     measureData.itemMetaListChanged.addListener(itemMetaList);
@@ -162,8 +162,8 @@ class LeftSiderSelectMeasureState extends FState<LeftSiderSelectMeasure> {
   @override
   dispose() {
     super.dispose();
-    playerController.currentFrameHandler.removeListener(
-      (currentFrameHandler),
+    playerController.firstFrameLoaded.removeListener(
+      (firstFrameLoaded),
     );
     measureData.getItemMetaListChanged.removeListener(getItemMetaListChanged);
     measureData.itemMetaListChanged.removeListener(itemMetaList);

+ 1 - 0
lib/view/mobile_view/mobile_control_board/mobile_control_board.dart

@@ -6,6 +6,7 @@ import 'package:fis_jsonrpc/services/aIDiagnosis.m.dart';
 import 'package:fis_measure/interfaces/process/player/play_controller.dart';
 import 'package:fis_measure/process/workspace/measure_data_controller.dart';
 import 'package:fis_measure/view/paint/ai_patint_controller.dart';
+import 'package:fis_measure/view/player/control_board/streaming_progress_bar.dart';
 import 'package:fis_measure/view/player/controller.dart';
 import 'package:fis_measure/view/player/events.dart';
 import 'package:fis_ui/index.dart';

+ 9 - 8
lib/view/mobile_view/mobile_control_board/progress_bar.dart

@@ -114,14 +114,15 @@ class _ProgressBarState extends State<_ProgressBar> {
           children: [
             Container(
               margin: const EdgeInsets.fromLTRB(10, 0, 10, 0),
-              child: Slider(
-                max: max,
-                value: index,
-                onChanged: (v) {
-                  controller.pause();
-                  controller.gotoFrame(v.toInt());
-                },
-              ),
+              child: const StreamingProgressBar(),
+              // child: Slider(
+              //   max: max,
+              //   value: index,
+              //   onChanged: (v) {
+              //     controller.pause();
+              //     controller.gotoFrame(v.toInt());
+              //   },
+              // ),
             ),
             Container(
               margin: const EdgeInsets.fromLTRB(10, 0, 10, 0),

+ 1 - 0
lib/view/player/control_board/control_board.dart

@@ -13,6 +13,7 @@ import 'package:flutter/material.dart';
 import 'package:get/get.dart';
 import '../controller.dart';
 import '../events.dart';
+import 'streaming_progress_bar.dart';
 
 part 'play_btn.dart';
 part 'prev_btn.dart';

+ 1 - 92
lib/view/player/control_board/progress_bar.dart

@@ -155,7 +155,7 @@ class _ProgressBarState extends State<_ProgressBar> {
                 left: marginSpace,
                 right: marginSpace,
               ),
-              child: _StreamingProgressBar(),
+              child: const StreamingProgressBar(),
               // child: Slider(
               //   max: max,
               //   value: index,
@@ -468,94 +468,3 @@ class _CursorTrack extends StatelessWidget {
     );
   }
 }
-
-class _StreamingProgressBar extends StatefulWidget {
-  @override
-  State<StatefulWidget> createState() => _StreamingProgressBarState();
-}
-
-class _StreamingProgressBarState extends State<_StreamingProgressBar> {
-  final controller = Get.find<IPlayerController>() as VidPlayerController;
-
-  int totalCount = 0;
-  int currentIndex = 0;
-  double bufferedProgress = 0;
-
-  int get bufferedCount {
-    return (bufferedProgress * totalCount).toInt();
-  }
-
-  @override
-  void initState() {
-    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
-      if (mounted) {
-        controller.eventHandler.addListener(_onControllerEvent);
-        controller.dataChannel.downloadProgressChanged
-            .addListener(_onDownloadProgressChanged);
-      }
-    });
-    super.initState();
-  }
-
-  @override
-  void dispose() {
-    controller.dataChannel.downloadProgressChanged
-        .removeListener(_onDownloadProgressChanged);
-    controller.eventHandler.removeListener(_onControllerEvent);
-    super.dispose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return ProgressBar(
-      progress: Duration(milliseconds: currentIndex),
-      total: Duration(milliseconds: totalCount),
-      buffered: Duration(milliseconds: bufferedCount),
-      // baseBarColor: Colors.blueGrey,
-      // progressBarColor: Colors.blue,
-      // bufferedBarColor: Colors.pink[300],
-      barHeight: 16,
-      thumbRadius: (16 / 2) * 1.4,
-      thumbColor: Colors.white,
-      timeLabelLocation: TimeLabelLocation.none,
-      onSeek: (timeStamp) {
-        setState(() {
-          currentIndex = timeStamp.inMilliseconds;
-          controller.gotoFrame(currentIndex);
-        });
-      },
-      onDragUpdate: (details) {
-        final timeStamp = details.timeStamp;
-        setState(() {
-          currentIndex = timeStamp.inMilliseconds;
-          controller.gotoFrame(currentIndex);
-        });
-      },
-    );
-  }
-
-  void _update() {
-    totalCount = controller.totalFramesCount;
-    currentIndex = controller.currentFrameIndex;
-    if (mounted) {
-      setState(() {});
-    }
-  }
-
-  void _onDownloadProgressChanged(Object sender, VidDownloadProgressInfo e) {
-    if (bufferedProgress != e.progress) {
-      bufferedProgress = e.progress;
-      if (mounted) {
-        setState(() {});
-      }
-    }
-  }
-
-  void _onControllerEvent(Object sender, VidPlayerEvent e) {
-    if (e is VidPlayerFrameIndexChangeEvent) {
-      if (currentIndex != e.index) {
-        _update();
-      }
-    }
-  }
-}

+ 99 - 0
lib/view/player/control_board/streaming_progress_bar.dart

@@ -0,0 +1,99 @@
+import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
+import 'package:fis_measure/interfaces/process/player/play_controller.dart';
+import 'package:fis_measure/view/player/controller.dart';
+import 'package:fis_measure/view/player/events.dart';
+import 'package:fis_vid/data_channel/progress_info.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+class StreamingProgressBar extends StatefulWidget {
+  const StreamingProgressBar({super.key});
+
+  @override
+  State<StatefulWidget> createState() => _StreamingProgressBarState();
+}
+
+class _StreamingProgressBarState extends State<StreamingProgressBar> {
+  final controller = Get.find<IPlayerController>() as VidPlayerController;
+
+  int totalCount = 0;
+  int currentIndex = 0;
+  double bufferedProgress = 0;
+
+  int get bufferedCount {
+    return (bufferedProgress * totalCount).toInt();
+  }
+
+  @override
+  void initState() {
+    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+      controller.eventHandler.addListener(_onControllerEvent);
+      controller.dataChannel.downloadProgressChanged
+          .addListener(_onDownloadProgressChanged);
+    });
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    controller.dataChannel.downloadProgressChanged
+        .removeListener(_onDownloadProgressChanged);
+    controller.eventHandler.removeListener(_onControllerEvent);
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return ProgressBar(
+      progress: Duration(milliseconds: currentIndex),
+      total: Duration(milliseconds: totalCount),
+      buffered: Duration(milliseconds: bufferedCount),
+      // baseBarColor: Colors.blueGrey,
+      // progressBarColor: Colors.blue,
+      // bufferedBarColor: Colors.pink[300],
+      barHeight: 16,
+      thumbRadius: (16 / 2) * 1.4,
+      thumbColor: Colors.white,
+      timeLabelLocation: TimeLabelLocation.none,
+      onSeek: (timeStamp) {
+        setState(() {
+          currentIndex = timeStamp.inMilliseconds;
+          controller.gotoFrame(currentIndex);
+        });
+      },
+      onDragUpdate: (details) {
+        final timeStamp = details.timeStamp;
+        setState(() {
+          currentIndex = timeStamp.inMilliseconds;
+          controller.gotoFrame(currentIndex);
+        });
+      },
+    );
+  }
+
+  void _update() {
+    totalCount = controller.totalFramesCount;
+    currentIndex = controller.currentFrameIndex;
+    if (mounted) {
+      setState(() {});
+    }
+  }
+
+  void _onDownloadProgressChanged(Object sender, VidDownloadProgressInfo e) {
+    if (bufferedProgress != e.progress) {
+      bufferedProgress = e.progress;
+      print('Progress: $bufferedProgress');
+      if (mounted) {
+        setState(() {});
+      }
+    }
+  }
+
+  void _onControllerEvent(Object sender, VidPlayerEvent e) {
+    if (e is VidPlayerFrameIndexChangeEvent) {
+      if (currentIndex != e.index) {
+        _update();
+      }
+    }
+  }
+}

+ 75 - 47
lib/view/player/controller.dart

@@ -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;
   }

+ 2 - 2
pubspec.lock

@@ -194,8 +194,8 @@ packages:
     dependency: "direct main"
     description:
       path: "."
-      ref: d13048d65a
-      resolved-ref: d13048d65aa641d60785fd551c8865863ed197c0
+      ref: "1.0.4"
+      resolved-ref: "25fb4f44307b61d123476d0298020b2ccd1a38d0"
       url: "http://git.ius.plus:88/melon.yin/fis_lib_vid.git"
     source: git
     version: "0.0.1"

+ 2 - 2
pubspec.yaml

@@ -57,7 +57,7 @@ dependencies:
   fis_vid:
     git:
       url: http://git.ius.plus:88/melon.yin/fis_lib_vid.git
-      ref: ^1.0.3
+      ref: ^1.0.4
   fis_vid_ext:
     git:
       url: http://git.ius.plus/melon.yin/fis_lib_vid_ext.git
@@ -89,7 +89,7 @@ dependency_overrides:
   fis_vid:
     git:
       url: http://git.ius.plus:88/melon.yin/fis_lib_vid.git
-      ref: d13048d65a
+      ref: 1.0.4
     # path: ../fis_lib_vid
   fis_i18n:
     git: