123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- // ignore_for_file: constant_identifier_names
- import 'dart:async';
- import 'dart:collection';
- import 'dart:convert';
- import 'package:fis_common/event/event_type.dart';
- import 'package:fis_common/logger/logger.dart';
- import 'package:fis_i18n/i18n.dart';
- import 'package:fis_jsonrpc/rpc.dart';
- import 'package:fis_measure/interfaces/date_types/rect_region.dart';
- import 'package:fis_measure/interfaces/process/player/play_controller.dart';
- import 'package:fis_measure/interfaces/process/workspace/application.dart';
- import 'package:fis_measure/process/primitives/multi_method/dop_trace_disp/data.dart';
- import 'package:fis_measure/process/workspace/rpc_bridge.dart';
- import 'package:fis_measure/view/player/buffer_waiter.dart';
- import 'package:fis_vid/data_channel/channel.dart';
- import 'package:flutter/foundation.dart';
- import 'package:get/get.dart';
- import 'package:vid/us/vid_us_image.dart';
- import 'package:vid/us/vid_us_image_data.dart';
- import 'package:vid/us/vid_us_probe.dart';
- import 'package:vid/us/vid_us_visual_area_type.dart';
- import 'enums.dart';
- import 'events.dart';
- /// Vid播放器控制器
- class VidPlayerController extends ChangeNotifier implements IPlayerController {
- /// Vid播放器控制器
- ///
- /// [url] Vid文件链接
- VidPlayerController(String url) {
- _url = url;
- _dataChannel = VidDataChannel.create(url);
- _bufferWaiter = VidBufferWaiter(_dataChannel);
- _isFirstFrame = true;
- eventHandler = FEventHandler<VidPlayerEvent>();
- frameUpdated = FEventHandler<VidUsImage>();
- firstFrameLoaded = FEventHandler<VidUsImage>();
- frameLoadStateChanged = FEventHandler<bool>();
- errorOccured = FEventHandler<String?>();
- }
- static const _CAN_PLAY_STATUS_ARR = [
- VidPlayStatus.ready,
- VidPlayStatus.pause
- ];
- static const _HAS_VIEW_STATUS_ARR = [VidPlayStatus.play, VidPlayStatus.pause];
- @override
- late final FEventHandler<VidPlayerEvent> eventHandler;
- @override
- late final FEventHandler<VidUsImage> frameUpdated;
- @override
- late final FEventHandler<VidUsImage> firstFrameLoaded;
- @override
- late final FEventHandler<bool> frameLoadStateChanged;
- @override
- late final FEventHandler<String?> errorOccured;
- final enableLoopChanged = FEventHandler<bool>();
- final crossFrameAnchorsUpdated = FEventHandler<List<int>>();
- /// 控制器释放触发事件
- final disposeOccured = FEventHandler<void>();
- late final String _url;
- late final VidDataChannel _dataChannel;
- late final VidBufferWaiter _bufferWaiter;
- _PlayAssistant? _playAssistant;
- VidPlayStatus _status = VidPlayStatus.init;
- int _frameIndex = -1;
- VidUsImage? _frame;
- bool _disposed = false;
- bool _isFirstFrame = false;
- bool _loading = false;
- bool _enableLoop = false;
- List<int> _crossFrameAnchors = [];
- /// 跨帧锚点
- List<int> get crossFrameAnchors => UnmodifiableListView(_crossFrameAnchors);
- set crossFrameAnchors(List<int> value) {
- _crossFrameAnchors = value;
- crossFrameAnchorsUpdated.emit(this, crossFrameAnchors);
- }
- /// 是否开启循环播放
- bool get enableLoop => _enableLoop;
- set enableLoop(bool val) {
- if (val != _enableLoop) {
- _enableLoop = val;
- enableLoopChanged.emit(this, val);
- }
- }
- @override
- String get url => _url;
- @override
- VidDataChannel get dataChannel => _dataChannel;
- @override
- bool get disposed => _disposed;
- @override
- VidPlayStatus get status => _status;
- @override
- VidUsImage? get currentFrame => _frame;
- /// Whether the player is playing
- bool get playing => status == VidPlayStatus.play;
- /// Whether the player can play
- bool get canPlay => _CAN_PLAY_STATUS_ARR.contains(status);
- /// Whether the player should has view
- @override
- bool get hasView => _HAS_VIEW_STATUS_ARR.contains(status);
- /// Current viewed frame index
- int get currentFrameIndex => _frameIndex;
- /// Total frames count of current vid
- int get totalFramesCount => _dataChannel.imageCount;
- double get frameRate => _dataChannel.probe.frameRate;
- VidUsProbe get probe => _dataChannel.probe;
- bool get isSingleFrame => totalFramesCount == 1;
- /// 是否播放结束
- bool get isEndOfPlay => currentFrameIndex == totalFramesCount - 1;
- /// 当前播放器亮度 初始值为 0
- double get brightness => _brightness;
- double _brightness = 0.0;
- /// 当前播放器对比度 初始值为 1
- double get contrast => _contrast;
- double _contrast = 1.0;
- /// vid头信息尺寸(含扩展)
- int get vidHeaderSize => _bufferWaiter.vidHeaderSize;
- @override
- Future<bool> load() async {
- final loaded = await _dataChannel.load(10 * 1000);
- if (loaded) {
- _bufferWaiter.init();
- _setStatus(VidPlayStatus.ready);
- logger.i(
- "Vid load successed. ImageCount:${_dataChannel.imageCount}, FrameRate:${_dataChannel.probe.frameRate} Url: $url");
- } else {
- _setStatus(VidPlayStatus.loadFail);
- logger.i("Vid load failed. Url: $url");
- }
- return loaded;
- }
- @override
- void play() {
- if (playing) return;
- if (!canPlay) return;
- if (isEndOfPlay) {
- _frameIndex = -1;
- }
- if (isSingleFrame) {
- locateTo(0);
- _setStatus(VidPlayStatus.pause);
- } else {
- _playAssistant ??= _PlayAssistant(this);
- _playAssistant!.play();
- _setStatus(VidPlayStatus.play);
- }
- }
- @override
- void pause() {
- if (!playing) {
- if (_status != VidPlayStatus.pause) {
- _setStatus(VidPlayStatus.pause);
- }
- return;
- }
- _playAssistant?.pause();
- _setStatus(VidPlayStatus.pause);
- }
- /// Pause and view next frame
- Future<bool> gotoNextFrame() {
- return locateTo(currentFrameIndex + 1);
- }
- /// Pause and view prev frame
- Future<bool> gotoPrevFrame() {
- return locateTo(currentFrameIndex - 1);
- }
- @override
- Future<bool> locateTo(int index) async {
- if (index < 0 || index >= totalFramesCount) return false;
- pause();
- _loading = false;
- gotoFrame(index);
- return true;
- }
- /// View target indexed frame
- ///
- /// [index] frame index
- Future<bool> gotoFrame(int index) async {
- if (index < 0 || index >= totalFramesCount) return false;
- if (_loading) return false;
- _frameIndex = index;
- _loading = true;
- _updateFrameLoadState(true);
- final start = DateTime.now();
- final result = await _waitUpdateFrame();
- final end = DateTime.now();
- if (result) {
- _updateFrameLoadState(false);
- final spendTime = end.difference(start).inMilliseconds;
- _bufferWaiter.recordFrameSpendTime(spendTime);
- }
- ///[TODO] 半自动 半自动自动测量获取的ai数据
- _getHalfAutoAiResult();
- return result;
- }
- /// Set frame brightness
- ///
- /// [value] brightness value
- void setBrightness(int value) {
- final brightnessCount = value / 100;
- if (brightnessCount < -1 || brightnessCount > 1) {
- return;
- }
- _brightness = brightnessCount * 255;
- final fliterMatrix = <double>[
- contrast, 0, 0, 0, brightness, // red
- 0, contrast, 0, 0, brightness, // green
- 0, 0, contrast, 0, brightness, // blue
- 0, 0, 0, 1, 0, // alpha // alpha
- ];
- eventHandler.emit(this, VidPlayerFilterChangeEvent(fliterMatrix));
- _reloadFrame();
- }
- /// Set frame contrast
- ///
- /// [value] contrast value
- void setContrast(int value) {
- double contrastCount = 1;
- if (value < 0) {
- contrastCount = (value + 100) / 100;
- } else if (value >= 0) {
- contrastCount = value / 100 * 9 + 1;
- }
- if (contrastCount < 0 || contrastCount > 10) {
- return;
- }
- _contrast = contrastCount;
- final fliterMatrix = <double>[
- contrast, 0, 0, 0, brightness, // red
- 0, contrast, 0, 0, brightness, // green
- 0, 0, contrast, 0, brightness, // blue
- 0, 0, 0, 1, 0, // alpha
- ];
- eventHandler.emit(this, VidPlayerFilterChangeEvent(fliterMatrix));
- _reloadFrame();
- }
- void setFilterMatrix(List<double> matrix) {
- eventHandler.emit(this, VidPlayerFilterChangeEvent(matrix));
- }
- /// 重置图像增益
- void resetTone() {
- setBrightness(0);
- setContrast(0);
- eventHandler.emit(this, VidPlayResetToneEvent());
- }
- 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;
- Future<bool> _fetchOnce() async {
- int timeout = 500;
- if (!isSingleFrame) {
- // 视频:一帧刷新时长,buffer 10ms 处理渲染
- timeout = _playAssistant!._playIntervalMillSeconds - 10;
- }
- final image = await _dataChannel.getImage(currentFrameIndex, timeout);
- if (playing || _bufferWaiter.channel.isBufferedDone) {
- // 未缓冲完成且暂停中 不允许 跳到下一帧
- _frame = image;
- emitFrameUpdate();
- }
- return true;
- }
- try {
- return await _fetchOnce();
- } catch (e) {
- if (e is ReadTimeoutException) {
- try {
- // 等待一次缓存
- await _waitFrameBufferFluently();
- return await _fetchOnce();
- } catch (e) {
- _emitErrorOccured(i18nBook.measure.frameLoadTimeout.t);
- }
- } else {
- _emitErrorOccured(i18nBook.measure.frameLoadError.t);
- }
- }
- return false;
- }
- Future<void> _waitFrameBufferFluently() async {
- if (isSingleFrame) {
- await _bufferWaiter.waitSingleVid();
- } else {
- await _bufferWaiter.waitBuffer(_frameIndex);
- }
- }
- /// 获取半自动ai的结果
- void _getHalfAutoAiResult() async {
- final application = Get.find<IApplication>();
- RectRegion? rectRegion;
- application.visuals.first.visualAreas.firstWhereOrNull((element) {
- if (element.visualAreaType == VidUsVisualAreaType.Doppler) {
- rectRegion = element.layoutRegion;
- return true;
- }
- return false;
- });
- if (currentFrame != null && rectRegion != null) {
- VetAutoTraceImageResult? autoTraceImageData = await getAutoTraceImageData(
- imageInfo: VetAutoTraceImageDTO(
- imageBase64String: base64Encode(currentFrame!.imageData),
- height: currentFrame!.height,
- width: currentFrame!.width,
- sKColorType: 4,
- ),
- rectInfo: VetAutoTraceRectDTO(
- left: ((rectRegion?.left ?? 0) * currentFrame!.width).toInt(),
- top: ((rectRegion?.top ?? 0) * currentFrame!.height).toInt(),
- right: ((rectRegion?.right ?? 0) * currentFrame!.width).toInt(),
- bottom: ((rectRegion?.bottom ?? 0) * currentFrame!.height).toInt(),
- height: ((rectRegion?.height ?? 0) * currentFrame!.height).toInt(),
- width: ((rectRegion?.width ?? 0) * currentFrame!.width).toInt(),
- ),
- );
- TraceListData.setVetAutoTraceImageResult(autoTraceImageData);
- }
- }
- /// [TODO] 需要拆出去
- Future<VetAutoTraceImageResult?> getAutoTraceImageData({
- required VetAutoTraceImageDTO imageInfo,
- VetAutoTraceRectDTO? rectInfo,
- }) async {
- try {
- return await RPCBridge.ins.rpc.aIDiagnosis.vetAutoTraceImageAsync(
- VetAutoTraceImageRequest(
- token: RPCBridge.ins.userToken,
- imageInfo: imageInfo,
- rectInfo: rectInfo,
- ),
- );
- } catch (e) {
- logger.e('Project getAutoTraceImageData ex:', e);
- return null;
- }
- }
- /// [Carotid] ✅用于设置颈动脉单帧展示
- void set2DMeasureFrame(VidUsImage frame) {
- _emitFrameUpdated(frame);
- }
- /// [Carotid] ✅用于重置播放器
- void resetCurrentFrame() {
- _frameIndex = -1;
- play();
- }
- void emitFrameUpdate() {
- if (_isFirstFrame) {
- firstFrameLoaded.emit(this, _frame!);
- _isFirstFrame = false;
- resetTone();
- }
- _emitFrameUpdated();
- }
- void _emitFrameUpdated([VidUsImage? frame]) {
- frameUpdated.emit(this, frame ?? _frame!);
- final f = frame ?? _frame!;
- eventHandler.emit(
- this,
- VidPlayerFrameIndexChangeEvent(f.index, f.imageData, f.width, f.height),
- );
- }
- void _setStatus(VidPlayStatus value) {
- _status = value;
- _notifyStatus();
- }
- void _notifyStatus() {
- eventHandler.emit(this, VidPlayerStatusChangeEvent(status));
- }
- void _stop({bool needNotify = true}) {
- _playAssistant?.pause();
- if (needNotify) {
- _setStatus(VidPlayStatus.stop);
- }
- }
- @override
- void dispose() {
- disposeOccured.emit(this, null);
- _disposed = true;
- _stop(needNotify: false);
- eventHandler.dispose();
- _dataChannel.close();
- super.dispose();
- }
- /// 已禁用,请通过eventHandler监听事件
- @override
- void addListener(VoidCallback listener) {
- throw UnsupportedError(
- "method `addListener` has been limited.Pls use `eventHandler.addListener`.");
- }
- /// 已禁用,请通过eventHandler监听事件
- @override
- void removeListener(VoidCallback listener) {
- throw UnsupportedError(
- "method `removeListener` has been limited.Pls use `eventHandler.removeListener`.");
- }
- }
- class _PlayAssistant {
- _PlayAssistant(this.owner);
- final VidPlayerController owner;
- bool _ready = false;
- late double _frameRate;
- int get _playInterval => 1000 * 1000 ~/ _frameRate;
- int get _playIntervalMillSeconds => _playInterval ~/ 1000.0;
- Timer? _timer;
- void play() {
- if (!_ready) {
- _prepare();
- }
- if (_timer != null) {
- pause();
- }
- bool waiting = false;
- final duration = Duration(microseconds: _playInterval);
- // final duration = const Duration(milliseconds: 1000 ~/ 10);
- _timer = Timer.periodic(duration, (timer) async {
- if (waiting) return;
- waiting = true;
- final result = await owner.gotoFrame(owner.currentFrameIndex + 1);
- if (_timer == null) {
- return; // 已取消播放
- }
- waiting = false;
- if (result) {
- if (owner.currentFrameIndex == owner.totalFramesCount - 1) {
- // 播放到最后一帧
- if (owner.enableLoop) {
- pause();
- // 循环播放
- owner._frameIndex = -1;
- play();
- } else {
- owner.pause();
- }
- }
- }
- });
- }
- void pause() {
- _timer?.cancel();
- _timer = null;
- }
- void _prepare() {
- _frameRate = owner._dataChannel.probe.frameRate;
- _ready = true;
- }
- }
|