controller.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. // ignore_for_file: constant_identifier_names
  2. import 'dart:async';
  3. import 'dart:collection';
  4. import 'dart:convert';
  5. import 'package:fis_common/event/event_type.dart';
  6. import 'package:fis_common/logger/logger.dart';
  7. import 'package:fis_i18n/i18n.dart';
  8. import 'package:fis_jsonrpc/rpc.dart';
  9. import 'package:fis_measure/interfaces/date_types/rect_region.dart';
  10. import 'package:fis_measure/interfaces/process/player/play_controller.dart';
  11. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  12. import 'package:fis_measure/process/workspace/rpc_bridge.dart';
  13. import 'package:fis_measure/view/player/buffer_waiter.dart';
  14. import 'package:fis_vid/data_channel/channel.dart';
  15. import 'package:flutter/foundation.dart';
  16. import 'package:get/get.dart';
  17. import 'package:vid/us/vid_us_image.dart';
  18. import 'package:vid/us/vid_us_image_data.dart';
  19. import 'package:vid/us/vid_us_probe.dart';
  20. import 'package:vid/us/vid_us_visual_area_type.dart';
  21. import 'enums.dart';
  22. import 'events.dart';
  23. /// Vid播放器控制器
  24. class VidPlayerController extends ChangeNotifier implements IPlayerController {
  25. /// Vid播放器控制器
  26. ///
  27. /// [url] Vid文件链接
  28. VidPlayerController(String url) {
  29. _url = url;
  30. _dataChannel = VidDataChannel.create(url);
  31. _bufferWaiter = VidBufferWaiter(_dataChannel);
  32. _isFirstFrame = true;
  33. eventHandler = FEventHandler<VidPlayerEvent>();
  34. frameUpdated = FEventHandler<VidUsImage>();
  35. firstFrameLoaded = FEventHandler<VidUsImage>();
  36. frameLoadStateChanged = FEventHandler<bool>();
  37. errorOccured = FEventHandler<String?>();
  38. }
  39. static const _CAN_PLAY_STATUS_ARR = [
  40. VidPlayStatus.ready,
  41. VidPlayStatus.pause
  42. ];
  43. static const _HAS_VIEW_STATUS_ARR = [VidPlayStatus.play, VidPlayStatus.pause];
  44. @override
  45. late final FEventHandler<VidPlayerEvent> eventHandler;
  46. @override
  47. late final FEventHandler<VidUsImage> frameUpdated;
  48. @override
  49. late final FEventHandler<VidUsImage> firstFrameLoaded;
  50. @override
  51. late final FEventHandler<bool> frameLoadStateChanged;
  52. @override
  53. late final FEventHandler<String?> errorOccured;
  54. final enableLoopChanged = FEventHandler<bool>();
  55. final crossFrameAnchorsUpdated = FEventHandler<List<int>>();
  56. /// 控制器释放触发事件
  57. final disposeOccured = FEventHandler<void>();
  58. late final String _url;
  59. late final VidDataChannel _dataChannel;
  60. late final VidBufferWaiter _bufferWaiter;
  61. _PlayAssistant? _playAssistant;
  62. VidPlayStatus _status = VidPlayStatus.init;
  63. int _frameIndex = -1;
  64. VidUsImage? _frame;
  65. bool _disposed = false;
  66. bool _isFirstFrame = false;
  67. bool _loading = false;
  68. bool _enableLoop = false;
  69. List<int> _crossFrameAnchors = [];
  70. /// 跨帧锚点
  71. List<int> get crossFrameAnchors => UnmodifiableListView(_crossFrameAnchors);
  72. set crossFrameAnchors(List<int> value) {
  73. _crossFrameAnchors = value;
  74. crossFrameAnchorsUpdated.emit(this, crossFrameAnchors);
  75. }
  76. /// 是否开启循环播放
  77. bool get enableLoop => _enableLoop;
  78. set enableLoop(bool val) {
  79. if (val != _enableLoop) {
  80. _enableLoop = val;
  81. enableLoopChanged.emit(this, val);
  82. }
  83. }
  84. @override
  85. String get url => _url;
  86. @override
  87. VidDataChannel get dataChannel => _dataChannel;
  88. @override
  89. bool get disposed => _disposed;
  90. @override
  91. VidPlayStatus get status => _status;
  92. @override
  93. VidUsImage? get currentFrame => _frame;
  94. /// Whether the player is playing
  95. bool get playing => status == VidPlayStatus.play;
  96. /// Whether the player can play
  97. bool get canPlay => _CAN_PLAY_STATUS_ARR.contains(status);
  98. /// Whether the player should has view
  99. @override
  100. bool get hasView => _HAS_VIEW_STATUS_ARR.contains(status);
  101. /// Current viewed frame index
  102. int get currentFrameIndex => _frameIndex;
  103. /// Total frames count of current vid
  104. int get totalFramesCount => _dataChannel.imageCount;
  105. double get frameRate => _dataChannel.probe.frameRate;
  106. VidUsProbe get probe => _dataChannel.probe;
  107. bool get isSingleFrame => totalFramesCount == 1;
  108. /// 是否播放结束
  109. bool get isEndOfPlay => currentFrameIndex == totalFramesCount - 1;
  110. /// 当前播放器亮度 初始值为 0
  111. double get brightness => _brightness;
  112. double _brightness = 0.0;
  113. /// 当前播放器对比度 初始值为 1
  114. double get contrast => _contrast;
  115. double _contrast = 1.0;
  116. /// vid头信息尺寸(含扩展)
  117. int get vidHeaderSize => _bufferWaiter.vidHeaderSize;
  118. @override
  119. Future<bool> load() async {
  120. final loaded = await _dataChannel.load(10 * 1000);
  121. if (loaded) {
  122. _bufferWaiter.init();
  123. _setStatus(VidPlayStatus.ready);
  124. logger.i(
  125. "Vid load successed. ImageCount:${_dataChannel.imageCount}, FrameRate:${_dataChannel.probe.frameRate} Url: $url");
  126. } else {
  127. _setStatus(VidPlayStatus.loadFail);
  128. logger.i("Vid load failed. Url: $url");
  129. }
  130. return loaded;
  131. }
  132. @override
  133. void play() {
  134. if (playing) return;
  135. if (!canPlay) return;
  136. if (isEndOfPlay) {
  137. _frameIndex = -1;
  138. }
  139. if (isSingleFrame) {
  140. locateTo(0);
  141. _setStatus(VidPlayStatus.pause);
  142. } else {
  143. _playAssistant ??= _PlayAssistant(this);
  144. _playAssistant!.play();
  145. _setStatus(VidPlayStatus.play);
  146. }
  147. }
  148. @override
  149. void pause() {
  150. if (!playing) {
  151. if (_status != VidPlayStatus.pause) {
  152. _setStatus(VidPlayStatus.pause);
  153. }
  154. return;
  155. }
  156. _playAssistant?.pause();
  157. _setStatus(VidPlayStatus.pause);
  158. }
  159. /// Pause and view next frame
  160. Future<bool> gotoNextFrame() {
  161. return locateTo(currentFrameIndex + 1);
  162. }
  163. /// Pause and view prev frame
  164. Future<bool> gotoPrevFrame() {
  165. return locateTo(currentFrameIndex - 1);
  166. }
  167. @override
  168. Future<bool> locateTo(int index) async {
  169. if (index < 0 || index >= totalFramesCount) return false;
  170. pause();
  171. _loading = false;
  172. gotoFrame(index);
  173. return true;
  174. }
  175. /// View target indexed frame
  176. ///
  177. /// [index] frame index
  178. Future<bool> gotoFrame(int index) async {
  179. if (index < 0 || index >= totalFramesCount) return false;
  180. if (_loading) return false;
  181. _frameIndex = index;
  182. _loading = true;
  183. _updateFrameLoadState(true);
  184. final start = DateTime.now();
  185. final result = await _waitUpdateFrame();
  186. final end = DateTime.now();
  187. if (result) {
  188. _updateFrameLoadState(false);
  189. final spendTime = end.difference(start).inMilliseconds;
  190. _bufferWaiter.recordFrameSpendTime(spendTime);
  191. }
  192. // ///[TODO] 半自动 半自动自动测量获取的ai数据
  193. // _getHalfAutoAiResult();
  194. return result;
  195. }
  196. /// Set frame brightness
  197. ///
  198. /// [value] brightness value
  199. void setBrightness(int value) {
  200. final brightnessCount = value / 100;
  201. if (brightnessCount < -1 || brightnessCount > 1) {
  202. return;
  203. }
  204. _brightness = brightnessCount * 255;
  205. final fliterMatrix = <double>[
  206. contrast, 0, 0, 0, brightness, // red
  207. 0, contrast, 0, 0, brightness, // green
  208. 0, 0, contrast, 0, brightness, // blue
  209. 0, 0, 0, 1, 0, // alpha // alpha
  210. ];
  211. eventHandler.emit(this, VidPlayerFilterChangeEvent(fliterMatrix));
  212. _reloadFrame();
  213. }
  214. /// Set frame contrast
  215. ///
  216. /// [value] contrast value
  217. void setContrast(int value) {
  218. double contrastCount = 1;
  219. if (value < 0) {
  220. contrastCount = (value + 100) / 100;
  221. } else if (value >= 0) {
  222. contrastCount = value / 100 * 9 + 1;
  223. }
  224. if (contrastCount < 0 || contrastCount > 10) {
  225. return;
  226. }
  227. _contrast = contrastCount;
  228. final fliterMatrix = <double>[
  229. contrast, 0, 0, 0, brightness, // red
  230. 0, contrast, 0, 0, brightness, // green
  231. 0, 0, contrast, 0, brightness, // blue
  232. 0, 0, 0, 1, 0, // alpha
  233. ];
  234. eventHandler.emit(this, VidPlayerFilterChangeEvent(fliterMatrix));
  235. _reloadFrame();
  236. }
  237. void setFilterMatrix(List<double> matrix) {
  238. eventHandler.emit(this, VidPlayerFilterChangeEvent(matrix));
  239. }
  240. /// 重置图像增益
  241. void resetTone() {
  242. setBrightness(0);
  243. setContrast(0);
  244. eventHandler.emit(this, VidPlayResetToneEvent());
  245. }
  246. void _reloadFrame() {
  247. gotoFrame(currentFrameIndex);
  248. }
  249. void _updateFrameLoadState(bool val) {
  250. _loading = val;
  251. frameLoadStateChanged.emit(this, _loading);
  252. }
  253. void _emitErrorOccured([String? msg]) {
  254. errorOccured.emit(this, msg);
  255. }
  256. /// 等待更新帧
  257. Future<bool> _waitUpdateFrame() async {
  258. if (_disposed) return false;
  259. Future<bool> _fetchOnce() async {
  260. int timeout = 500;
  261. if (!isSingleFrame) {
  262. // 视频:一帧刷新时长,buffer 10ms 处理渲染
  263. timeout = _playAssistant!._playIntervalMillSeconds - 10;
  264. }
  265. final image = await _dataChannel.getImage(currentFrameIndex, timeout);
  266. if (playing || _bufferWaiter.channel.isBufferedDone) {
  267. // 未缓冲完成且暂停中 不允许 跳到下一帧
  268. _frame = image;
  269. emitFrameUpdate();
  270. }
  271. return true;
  272. }
  273. try {
  274. return await _fetchOnce();
  275. } catch (e) {
  276. if (e is ReadTimeoutException) {
  277. try {
  278. // 等待一次缓存
  279. await _waitFrameBufferFluently();
  280. return await _fetchOnce();
  281. } catch (e) {
  282. _emitErrorOccured(i18nBook.measure.frameLoadTimeout.t);
  283. }
  284. } else {
  285. _emitErrorOccured(i18nBook.measure.frameLoadError.t);
  286. }
  287. }
  288. return false;
  289. }
  290. Future<void> _waitFrameBufferFluently() async {
  291. if (isSingleFrame) {
  292. await _bufferWaiter.waitSingleVid();
  293. } else {
  294. await _bufferWaiter.waitBuffer(_frameIndex);
  295. }
  296. }
  297. /// 获取半自动ai的结果
  298. void _getHalfAutoAiResult() async {
  299. final application = Get.find<IApplication>();
  300. RectRegion? rectRegion;
  301. application.visuals.first.visualAreas.firstWhereOrNull((element) {
  302. if (element.visualAreaType == VidUsVisualAreaType.Doppler) {
  303. rectRegion = element.layoutRegion;
  304. return true;
  305. }
  306. return false;
  307. });
  308. if (currentFrame != null && rectRegion != null) {
  309. var a = await getAutoTraceImageData(
  310. imageInfo: VetAutoTraceImageDTO(
  311. imageBase64String: base64Encode(currentFrame!.imageData),
  312. height: currentFrame!.height,
  313. width: currentFrame!.width,
  314. sKColorType: 4,
  315. ),
  316. rectInfo: VetAutoTraceRectDTO(
  317. left: ((rectRegion?.left ?? 0) * currentFrame!.width).toInt(),
  318. top: ((rectRegion?.top ?? 0) * currentFrame!.height).toInt(),
  319. right: ((rectRegion?.right ?? 0) * currentFrame!.width).toInt(),
  320. bottom: ((rectRegion?.bottom ?? 0) * currentFrame!.height).toInt(),
  321. height: ((rectRegion?.height ?? 0) * currentFrame!.height).toInt(),
  322. width: ((rectRegion?.width ?? 0) * currentFrame!.width).toInt(),
  323. ),
  324. );
  325. print("🍭🍭🍭🍭🍭");
  326. print(a);
  327. print("🍭🍭🍭🍭🍭");
  328. }
  329. }
  330. /// [TODO] 需要拆出去
  331. Future<VetAutoTraceImageResult?> getAutoTraceImageData({
  332. required VetAutoTraceImageDTO imageInfo,
  333. VetAutoTraceRectDTO? rectInfo,
  334. }) async {
  335. try {
  336. return await RPCBridge.ins.rpc.aIDiagnosis.vetAutoTraceImageAsync(
  337. VetAutoTraceImageRequest(
  338. token: RPCBridge.ins.userToken,
  339. imageInfo: imageInfo,
  340. rectInfo: rectInfo,
  341. ),
  342. );
  343. } catch (e) {
  344. logger.e('Project getAutoTraceImageData ex:', e);
  345. return null;
  346. }
  347. }
  348. /// [Carotid] ✅用于设置颈动脉单帧展示
  349. void set2DMeasureFrame(VidUsImage frame) {
  350. _emitFrameUpdated(frame);
  351. }
  352. /// [Carotid] ✅用于重置播放器
  353. void resetCurrentFrame() {
  354. _frameIndex = -1;
  355. play();
  356. }
  357. void emitFrameUpdate() {
  358. if (_isFirstFrame) {
  359. firstFrameLoaded.emit(this, _frame!);
  360. _isFirstFrame = false;
  361. resetTone();
  362. }
  363. _emitFrameUpdated();
  364. }
  365. void _emitFrameUpdated([VidUsImage? frame]) {
  366. frameUpdated.emit(this, frame ?? _frame!);
  367. final f = frame ?? _frame!;
  368. eventHandler.emit(
  369. this,
  370. VidPlayerFrameIndexChangeEvent(f.index, f.imageData, f.width, f.height),
  371. );
  372. }
  373. void _setStatus(VidPlayStatus value) {
  374. _status = value;
  375. _notifyStatus();
  376. }
  377. void _notifyStatus() {
  378. eventHandler.emit(this, VidPlayerStatusChangeEvent(status));
  379. }
  380. void _stop({bool needNotify = true}) {
  381. _playAssistant?.pause();
  382. if (needNotify) {
  383. _setStatus(VidPlayStatus.stop);
  384. }
  385. }
  386. @override
  387. void dispose() {
  388. disposeOccured.emit(this, null);
  389. _disposed = true;
  390. _stop(needNotify: false);
  391. eventHandler.dispose();
  392. _dataChannel.close();
  393. super.dispose();
  394. }
  395. /// 已禁用,请通过eventHandler监听事件
  396. @override
  397. void addListener(VoidCallback listener) {
  398. throw UnsupportedError(
  399. "method `addListener` has been limited.Pls use `eventHandler.addListener`.");
  400. }
  401. /// 已禁用,请通过eventHandler监听事件
  402. @override
  403. void removeListener(VoidCallback listener) {
  404. throw UnsupportedError(
  405. "method `removeListener` has been limited.Pls use `eventHandler.removeListener`.");
  406. }
  407. }
  408. class _PlayAssistant {
  409. _PlayAssistant(this.owner);
  410. final VidPlayerController owner;
  411. bool _ready = false;
  412. late double _frameRate;
  413. int get _playInterval => 1000 * 1000 ~/ _frameRate;
  414. int get _playIntervalMillSeconds => _playInterval ~/ 1000.0;
  415. Timer? _timer;
  416. void play() {
  417. if (!_ready) {
  418. _prepare();
  419. }
  420. if (_timer != null) {
  421. pause();
  422. }
  423. bool waiting = false;
  424. final duration = Duration(microseconds: _playInterval);
  425. // final duration = const Duration(milliseconds: 1000 ~/ 10);
  426. _timer = Timer.periodic(duration, (timer) async {
  427. if (waiting) return;
  428. waiting = true;
  429. final result = await owner.gotoFrame(owner.currentFrameIndex + 1);
  430. if (_timer == null) {
  431. return; // 已取消播放
  432. }
  433. waiting = false;
  434. if (result) {
  435. if (owner.currentFrameIndex == owner.totalFramesCount - 1) {
  436. // 播放到最后一帧
  437. if (owner.enableLoop) {
  438. pause();
  439. // 循环播放
  440. owner._frameIndex = -1;
  441. play();
  442. } else {
  443. owner.pause();
  444. }
  445. }
  446. }
  447. });
  448. }
  449. void pause() {
  450. _timer?.cancel();
  451. _timer = null;
  452. }
  453. void _prepare() {
  454. _frameRate = owner._dataChannel.probe.frameRate;
  455. _ready = true;
  456. }
  457. }