import 'dart:convert'; import 'dart:typed_data'; import 'package:fis_common/env/env.dart'; import 'package:fis_common/web/shell_api_hepler.dart'; import 'package:flutter/foundation.dart'; import 'package:vid/us/vid_us_data_http_reader.dart'; import 'package:vid/us/vid_us_data_reader.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 'reader.dart'; import 'shell/reader.dart'; abstract class AsyncVidImageDataBase { late AsyncVidDataReaderBase _reader; late List _imagePositionList; late int _version; late int _imageCount; late VidUsProbe _probe; late VidUsImageFormat _imageFormat; late Uint8List _extendedData; late int _readHeaderTimeout; late int _readImageTimeout; late bool _initialized; late bool _closed; final Duration delayDuration = const Duration(milliseconds: 10); static final String vid_header = "VINNO IMAGE DATA"; /// Gets the version of this image data. int get version => _version; /// Gets the image count of this image data. int get imageCount => _imageCount; /// Gets the probe information. VidUsProbe get probe => _probe; /// Gets the image format of this image data. VidUsImageFormat get imageFormat => _imageFormat; /// Gets the extended data. Uint8List get extendedData => _extendedData; /// Gets the initialized state. bool get initialized => _initialized; /// Create a VINNO Image Data. /// [readHeaderTimeout] is the timeout value of reading header and probe information etc..., we need give as much time as possible /// to let the reader read the header. /// [readImageTimeout] is the timeout value of reading one frame. /// [minChunkSize] The min download chunk size, set to a large value may speedup the download speed, but will cause the progress not smooth. AsyncVidImageDataBase( String url, { DownloadCallback? downloadCallback, int readHeaderTimeout = 3000, int readImageTimeout = 500, }) { _initialized = false; _closed = false; _readHeaderTimeout = readHeaderTimeout; _readImageTimeout = readImageTimeout; _reader = createReader(url, downloadCallback: downloadCallback); } @protected AsyncVidDataReaderBase createReader(String url, {DownloadCallback? downloadCallback}); ///Get the downloaded data of this HttpVidUsImageData Uint8List getDownloadedData() { return _reader.toBytes(); } void setReadHeaderTimeout(int timeout) => _readHeaderTimeout = timeout; void setReadImageTimeout(int timeout) => _readImageTimeout = timeout; ///Initialize the HttpVidUsImageData, must be called firstly. Future initialize() async { if (!_initialized) { String header; if (_reader.isVrd) { header = vid_header; } else { header = await _readHeader(); } if (header != vid_header) { throw Exception("The input data is not a VID data."); } if (_reader.isVrd) { _version = await _getVersion(); _probe = await _getProbe(); _imageFormat = await _getImageFormat(); _extendedData = await _getExtendedData(); _imageCount = await _getImageCount(); print('_imageCount:$_imageCount'); } else { _version = await _readVersion(); //Get probe info _probe = await _readProbe(); _imageFormat = await _readImageFormat(); _extendedData = await _readExtendedData(); _imagePositionList = []; var imagePositionListData = await _readImagePositionListData(); _imageCount = imagePositionListData.length ~/ 8; var imagePositionReader = VidUsDataReader(imagePositionListData); for (var i = 0; i < _imageCount; i++) { _imagePositionList.add(imagePositionReader.readInt64V2()); } } _initialized = true; } return _initialized; } ///Close the HttpVidUsImageData, it will force close the download operation and reading operation. void close() { if (_closed) return; _closed = true; _reader.close(); } T getReader() => _reader as T; ///Read header from http data. Future _readHeader() async { var timeout = 0; while (!_closed) { try { var header = _reader.readString(); return header; } catch (ex) { if (ex is NotReadyException) { if (timeout >= _readHeaderTimeout) { throw ReadTimeoutException(); } await Future.delayed(delayDuration); timeout += delayDuration.inMilliseconds; } else { rethrow; } } } throw AlreadyClosedException(); } ///Read the version from http data. Future _readVersion() async { var timeout = 0; while (!_closed) { try { var version = _reader.readInt(); return version; } catch (ex) { if (ex is NotReadyException) { if (timeout >= _readHeaderTimeout) { throw ReadTimeoutException(); } await Future.delayed(delayDuration); timeout += delayDuration.inMilliseconds; } else { rethrow; } } } throw AlreadyClosedException(); } ///Read the probe info from http data. Future _readProbe() async { var timeout = 0; while (!_closed) { try { var probeData = _reader.readBytes(); return VidUsProbe.fromBytes(probeData); } catch (ex) { if (ex is NotReadyException) { if (timeout >= _readHeaderTimeout) { throw ReadTimeoutException(); } await Future.delayed(delayDuration); timeout += delayDuration.inMilliseconds; } else { rethrow; } } } throw AlreadyClosedException(); } ///Read the image format from http data. Future _readImageFormat() async { var timeout = 0; while (!_closed) { try { return VidUsImageFormat.values[_reader.readInt()]; } catch (ex) { if (ex is NotReadyException) { if (timeout >= _readHeaderTimeout) { throw ReadTimeoutException(); } await Future.delayed(delayDuration); timeout += delayDuration.inMilliseconds; } else { rethrow; } } } throw AlreadyClosedException(); } ///Read extended data from http data. Future _readExtendedData() async { var timeout = 0; while (!_closed) { try { return _reader.readBytes(); } catch (ex) { if (ex is NotReadyException) { if (timeout >= _readHeaderTimeout) { throw ReadTimeoutException(); } await Future.delayed(delayDuration); timeout += delayDuration.inMilliseconds; } else { rethrow; } } } throw AlreadyClosedException(); } ///Read the image positions data from http data. Future _readImagePositionListData() async { var timeout = 0; while (!_closed) { try { return _reader.readBytes(); } catch (ex) { if (ex is NotReadyException) { if (timeout >= _readHeaderTimeout) { throw ReadTimeoutException(); } await Future.delayed(delayDuration); timeout += delayDuration.inMilliseconds; } else { rethrow; } } } throw AlreadyClosedException(); } /// Get one image from the vid. Future getImage(int index) async { if (index >= _imageCount || index < 0) { throw Exception("Can not find image Data"); } //Jump to image. var timeout = 0; while (!_closed) { try { print('_reader.readBytes $index'); Uint8List imageData; if (_reader.isVrd) { imageData = _reader.getImageBytes(index); if (imageData.isEmpty) { _reader.skipToFrame(index); throw Exception("Can not find image Data"); } } else { imageData = _reader.readBytes(_imagePositionList[index]); } print('_reader.readBytes $index done'); return VidUsImage.fromBytes(imageData); } catch (ex) { print('_reader.readBytes $index ex:$ex'); if (ex is NotReadyException) { if (timeout >= _readImageTimeout) { throw ReadTimeoutException(); } await Future.delayed(delayDuration); timeout += delayDuration.inMilliseconds; } else { rethrow; } } } throw AlreadyClosedException(); } Future _getVersion() async { final result = await ShellApiHelper.call( 'getVersion', { 'Url': _reader.url, }, ); print('_getVersion:$result'); return result; } Future _getProbe() async { final result = await ShellApiHelper.call( 'getVinnoProbe', { 'Url': _reader.url, }, ); var probe = base64Decode(result); var probeResult = VidUsProbe.fromBytes(probe); print("probe : ${probeResult.name} ${probeResult.type}"); return probeResult; } Future _getImageFormat() async { final result = await ShellApiHelper.call( 'getImageFormat', { 'Url': _reader.url, }, ); return VidUsImageFormat.values .firstWhere((element) => element.index == result); } Future _getExtendedData() async { final result = await ShellApiHelper.call( 'getExtendedData', { 'Url': _reader.url, }, ); return base64Decode(result); } Future _getImageCount() async { final result = await ShellApiHelper.call( 'getImageCount', { 'Url': _reader.url, }, ); return result; } }