Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Conversation

@PerLycke
Copy link
Contributor

@PerLycke PerLycke commented Oct 27, 2019

…ble for video recording instead of still image capture.

Description

Existing behaviour creates a request which prioritizes image quality over frame rate and results in a choppy and / or freezing streaming session on many devices. Creating a request suitable for video recording means that a stable frame rate is used, and post-processing is set for recording quality. It noticeably improves streaming images on a variety of devices.

Related Issues

Fixes flutter/flutter#43566

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • My PR includes unit or integration tests for all changed/updated/fixed behaviors (See Contributor Guide).
  • All existing and new tests are passing.
  • I updated/added relevant documentation (doc comments with ///).
  • The analyzer (flutter analyze) does not report any problems on my PR.
  • I read and followed the Flutter Style Guide.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I signed the CLA.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Does your PR require plugin users to manually update their apps to accommodate your change?

  • Yes, this is a breaking change (please indicate a breaking change in CHANGELOG.md and increment major revision).
  • No, this is not a breaking change.

Copy link
Contributor

@mklim mklim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! Appreciate you finding and fixing this issue. If you don't have time to keep working on this PR, let us know and someone from the Flutter team will be happy to shepherd it forward from here.

This fix LG, but all PRs need to have automated tests before we can merge them into the tree, see Tree Hygiene #4.

Unfortunately testing this is going to be painful. The resulting behavior isn't something we can meaningfully verify. I think our only bet is to write an interaction test using JUnit for the Java code. Not ideal, but it will work. Then the test itself is going to be a little tricky to implement becausecameraDevice isn't exposed anywhere, and we'd basically want to put in a mock or spy cameraDevice and verify that it has createCaptureRequest called with the right template type when we call startPreviewWithImageStream on Camera.java. I don't mind adding a package private setTestCameraDevice method to Camera.java just to get the mock initialized in our test, but again that's not ideal. I think it would be possible to write a nicer test here with a broader refactor of the class and how it gets cameraDevice, but I don't think it would be trivial or necessarily worth inducing all that risk. So just the simple interaction test with a new setter is good for this PR.

Thanks again for the contribution and the fix. Let us know if you don't have time to keep working on this, and someone from our team will be happy to help land it.

public void startPreviewWithImageStream(EventChannel imageStreamChannel)
throws CameraAccessException {
createCaptureSession(CameraDevice.TEMPLATE_STILL_CAPTURE, imageStreamReader.getSurface());
createCaptureSession(CameraDevice.TEMPLATE_RECORD, imageStreamReader.getSurface());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think maybe it would make even more sense to use TEMPLATE_PREVIEW? That setting is optimized for a high FPS without much post processing, which I think makes sense here. RECORD is very similar but it has a "stable" FPS with more post processing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested with both but using TEMPLATE_PREVIEW breaks trying to detect objects in a CameraImage using the ml kit on my test devices. With TEMPLATE_RECORD it both fixes the streaming issues and keeps the functionality intact. @mehmetf might have more insight in this as I know he looked in to improving streaming.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pinging me here. We forked this plugin for Google and have made a number of improvements. Glad to see similar efforts upstream:

  • TEMPLATE_RECORD is indeed what we use now for preview.
  • We also start a different thread for image stream handler (that we dispose when image stream is stopped).
  • We drain the image queue while waiting for processing to finish. This allows preview to be smooth even if the stream does not deliver high fps (which is often unneeded).

Our code looks a bit like this:

public void startPreviewWithImageStream(
      ImageReader imageStreamReader, StreamProcessor<Image> processor)
      throws CameraAccessException {
    imageStreamHandler = CameraUtils.startThread("imageStreamThread");
    createCaptureSession(CameraDevice.TEMPLATE_RECORD, imageStreamReader.getSurface());
    imageStreamReader.setOnImageAvailableListener(getImageStreamListener(processor), imageStreamHandler);
  }

  public void stopPreviewWithImageStream(ImageReader imageStreamReader)
      throws CameraAccessException {
    CameraUtils.stopThread(imageStreamHandler);
    imageStreamReader.setOnImageAvailableListener(null, null);
    startPreview();
  }

  private ImageReader.OnImageAvailableListener getImageStreamListener(
      StreamProcessor<Image> processor) {
    return reader -> {
      Image img = reader.acquireLatestImage();
      if (img == null) {
        return;
      }
      if (openImage.getAndSet(true)) {
        img.close();
        return;
      }
      imageStreamHandler.post(
          (Runnable)
              () -> {
                try {
                  Uninterruptibles.getUninterruptibly(processor.apply(img));
                } catch (ExecutionException e) {
                  Log.e(TAG, "Exception from image processing", e);
                } finally {
                  openImage.set(false);
                  img.close();
                }
              });
    };
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Mehmet, I hope some of those improvements can find its way to this plugin later. As of now, and regarding this PR, changing TEMPLATE_STILL_CAPTURE to TEMPLATE_RECORD singlehandedly fixes streaming on several devices. There's not much documentation around this in particular, but seems needed

@PerLycke
Copy link
Contributor Author

@mklim I've added a basic test for streaming on Android. If I got something wrong, or if more sophisticated tests are needed, I will need to hand this over to someone in the Flutter team.

Copy link
Contributor

@mklim mklim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a basic test for streaming on Android. If I got something wrong, or if more sophisticated tests are needed, I will need to hand this over to someone in the Flutter team.

This doesn't technically test the specific change in this PR, but I'm going to LGTM it because it tests the streaming feature overall and is an improvement over what we currently have. Thanks for the improvement and for adding tests!

lgtm

@mklim mklim merged commit 89c4816 into flutter:master Oct 29, 2019
mormih pushed a commit to mormih/plugins that referenced this pull request Nov 17, 2019
flutter#2239)

Existing behaviour creates a request which prioritizes image quality over frame rate and results in a choppy and / or freezing streaming session on many devices. Creating a request suitable for video recording means that a stable frame rate is used, and post-processing is set for recording quality. It noticeably improves streaming images on a variety of devices.
sungmin-park pushed a commit to sungmin-park/flutter-plugins that referenced this pull request Dec 17, 2019
flutter#2239)

Existing behaviour creates a request which prioritizes image quality over frame rate and results in a choppy and / or freezing streaming session on many devices. Creating a request suitable for video recording means that a stable frame rate is used, and post-processing is set for recording quality. It noticeably improves streaming images on a variety of devices.
@tigrenok00
Copy link

Hi, did this make into latest stable version? We're still experiencing performance issues with CameraPreview freezing while streaming images with High resolution, and I thought this might fix this.
Thanks

@PerLycke
Copy link
Contributor Author

@tigrenok00 It's included yes, but it won't enable streaming images on high resolution on all devices. It fixes so that you can use recommended resolution on all devices (afaik) without notable issues. Before this patch camera plugin was unusable on a variety of devices, even on lower resolutions.

Akachu pushed a commit to Akachu/flutter_camera that referenced this pull request Apr 27, 2020
flutter#2239)

Existing behaviour creates a request which prioritizes image quality over frame rate and results in a choppy and / or freezing streaming session on many devices. Creating a request suitable for video recording means that a stable frame rate is used, and post-processing is set for recording quality. It noticeably improves streaming images on a variety of devices.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[camera] ml_vision example app freezes on Note 10+ with CameraAccessException

5 participants