attach method

Stream<Uint8List> attach(
  1. Stream<List<int>> dataResponse
)

Attach this RxPhoto to the Frame's dataResponse characteristic stream.

Implementation

Stream<Uint8List> attach(Stream<List<int>> dataResponse) {
  // TODO check for illegal state - attach() already called on this RxPhoto etc?
  // might be possible though after a clean close(), do I want to prevent it?

  // qualityLevel must be valid (10, 25, 50, 100)
  if (!jpegHeaderMap.containsKey(qualityLevel)) {
    throw Exception(
        'Invalid quality level for jpeg: $qualityLevel - must be one of: ${jpegHeaderMap.keys}');
  }

  // the image data as a list of bytes that accumulates with each packet
  List<int> imageData = List.empty(growable: true);
  int rawOffset = 0;

  // add the jpeg header bytes for this quality level (623 bytes)
  imageData.addAll(jpegHeaderMap[qualityLevel]!);

  // the subscription to the underlying data stream
  StreamSubscription<List<int>>? dataResponseSubs;

  // Our stream controller that transforms/accumulates the raw data into images (as bytes)
  _controller = StreamController();

  _controller!.onListen = () {
    _log.fine('ImageDataResponse stream subscribed');
    dataResponseSubs = dataResponse
        .where(
            (data) => data[0] == nonFinalChunkFlag || data[0] == finalChunkFlag)
        .listen((data) {
      if (data[0] == nonFinalChunkFlag) {
        imageData += data.sublist(1);
        rawOffset += data.length - 1;
      }
      // the last chunk has a first byte of 8 so stop after this
      else if (data[0] == finalChunkFlag) {
        imageData += data.sublist(1);
        rawOffset += data.length - 1;

        // add the jpeg footer bytes (2 bytes)
        imageData.addAll(jpegFooter);

        // When full image data is received,
        // rotate the image counter-clockwise 90 degrees to make it upright
        // unless requested otherwise (to save processing)
        if (upright) {
          image_lib.Image? im = image_lib.decodeJpg(Uint8List.fromList(imageData));
          im = image_lib.copyRotate(im!, angle: 270);
          // emit the rotated jpeg bytes
          _controller!.add(image_lib.encodeJpg(im));
        }
        else {
          // emit the original rotation jpeg bytes
          _controller!.add(Uint8List.fromList(imageData));
        }

        // clear the buffer
        imageData.clear();
        rawOffset = 0;

        // and close the stream
        _controller!.close();
      }
      _log.finer(() => 'Chunk size: ${data.length - 1}, rawOffset: $rawOffset');
    }, onDone: _controller!.close, onError: _controller!.addError);
    _log.fine('Controller being listened to');
  };

  _controller!.onCancel = () {
    _log.fine('ImageDataResponse stream unsubscribed');
    dataResponseSubs?.cancel();
    _controller!.close();
  };

  return _controller!.stream;
}