-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[camerax] Implement resolution configuration #3799
Changes from 26 commits
44d7828
6281eb5
9a74cd1
03ef376
2a5c0fc
9001ab0
fd19de6
c9b62ef
725be10
6389ae1
16770de
a8144fa
aba4c34
ca5604a
474745c
b778894
8d2cb3a
a145afe
fd3ade2
1dc6fbd
fad7cbc
08edc4c
1def519
5bab620
caf819e
c775756
fabaab7
810cb54
60a47ed
474d84c
6f45ecb
5c31de4
e4134bd
bb35a61
5ac1c7c
860b4db
5ea328e
a163f0e
2a9671e
0178493
ff9bd3a
396091c
206fbb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,11 +2,13 @@ | |
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'dart:async'; | ||
import 'dart:io'; | ||
import 'dart:ui'; | ||
|
||
import 'package:camera_android_camerax/camera_android_camerax.dart'; | ||
import 'package:camera_android_camerax_example/camera_controller.dart'; | ||
import 'package:camera_android_camerax_example/camera_image.dart'; | ||
import 'package:camera_platform_interface/camera_platform_interface.dart'; | ||
import 'package:flutter/painting.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
@@ -19,6 +21,46 @@ void main() { | |
CameraPlatform.instance = AndroidCameraCameraX(); | ||
}); | ||
|
||
final Map<ResolutionPreset, Size> presetExpectedSizes = | ||
<ResolutionPreset, Size>{ | ||
ResolutionPreset.low: const Size(240, 320), | ||
ResolutionPreset.medium: const Size(480, 720), | ||
ResolutionPreset.high: const Size(720, 1280), | ||
ResolutionPreset.veryHigh: const Size(1080, 1920), | ||
ResolutionPreset.ultraHigh: const Size(2160, 3840), | ||
// Don't bother checking for max here since it could be anything. | ||
}; | ||
|
||
/// Verify that [actual] has dimensions that are at least as large as | ||
camsim99 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns | ||
/// whether the dimensions exactly match. | ||
bool assertExpectedDimensions(Size expectedSize, Size actual) { | ||
expect(actual.shortestSide, lessThanOrEqualTo(expectedSize.shortestSide)); | ||
expect(actual.longestSide, lessThanOrEqualTo(expectedSize.longestSide)); | ||
return actual.shortestSide == expectedSize.shortestSide && | ||
actual.longestSide == expectedSize.longestSide; | ||
} | ||
|
||
// This tests that the capture is no bigger than the preset, since we have | ||
// automatic code to fall back to smaller sizes when we need to. Returns | ||
// whether the image is exactly the desired resolution. | ||
Future<bool> testCaptureImageResolution( | ||
CameraController controller, ResolutionPreset preset) async { | ||
final Size expectedSize = presetExpectedSizes[preset]!; | ||
|
||
// Take Picture | ||
final XFile file = await controller.takePicture(); | ||
|
||
// Load picture | ||
final File fileImage = File(file.path); | ||
final Image image = await decodeImageFromList(fileImage.readAsBytesSync()); | ||
|
||
// Verify image dimensions are as expected | ||
expect(image, isNotNull); | ||
return assertExpectedDimensions( | ||
expectedSize, Size(image.height.toDouble(), image.width.toDouble())); | ||
} | ||
|
||
testWidgets('availableCameras only supports valid back or front cameras', | ||
(WidgetTester tester) async { | ||
final List<CameraDescription> availableCameras = | ||
|
@@ -31,27 +73,94 @@ void main() { | |
} | ||
}); | ||
|
||
testWidgets('takePictures stores a valid image in memory', | ||
testWidgets('Capture specific image resolutions', | ||
(WidgetTester tester) async { | ||
final List<CameraDescription> availableCameras = | ||
final List<CameraDescription> cameras = | ||
await CameraPlatform.instance.availableCameras(); | ||
if (availableCameras.isEmpty) { | ||
if (cameras.isEmpty) { | ||
return; | ||
} | ||
for (final CameraDescription cameraDescription in availableCameras) { | ||
final CameraController controller = | ||
CameraController(cameraDescription, ResolutionPreset.high); | ||
await controller.initialize(); | ||
for (final CameraDescription cameraDescription in cameras) { | ||
bool previousPresetExactlySupported = true; | ||
for (final MapEntry<ResolutionPreset, Size> preset | ||
in presetExpectedSizes.entries) { | ||
final CameraController controller = | ||
CameraController(cameraDescription, preset.key); | ||
await controller.initialize(); | ||
final bool presetExactlySupported = | ||
await testCaptureImageResolution(controller, preset.key); | ||
expect(!(!previousPresetExactlySupported && presetExactlySupported), | ||
isTrue, | ||
reason: | ||
'The camera took higher resolution pictures at a lower resolution.'); | ||
previousPresetExactlySupported = presetExactlySupported; | ||
await controller.dispose(); | ||
} | ||
} | ||
}); | ||
|
||
testWidgets('Preview takes expected resolution from preset', | ||
(WidgetTester tester) async { | ||
final List<CameraDescription> cameras = | ||
await CameraPlatform.instance.availableCameras(); | ||
if (cameras.isEmpty) { | ||
return; | ||
} | ||
for (final CameraDescription cameraDescription in cameras) { | ||
bool previousPresetExactlySupported = true; | ||
for (final MapEntry<ResolutionPreset, Size> preset | ||
in presetExpectedSizes.entries) { | ||
final CameraController controller = | ||
CameraController(cameraDescription, preset.key); | ||
|
||
await controller.initialize(); | ||
|
||
while (controller.value.previewSize == null) { | ||
// Wait for preview size to update. | ||
} | ||
|
||
final bool presetExactlySupported = assertExpectedDimensions( | ||
presetExpectedSizes[preset.key]!, controller.value.previewSize!); | ||
expect(!(!previousPresetExactlySupported && presetExactlySupported), | ||
isTrue, | ||
reason: 'The preview has a lower resolution than that specified.'); | ||
previousPresetExactlySupported = presetExactlySupported; | ||
await controller.dispose(); | ||
} | ||
} | ||
}); | ||
|
||
// Take Picture | ||
final XFile file = await controller.takePicture(); | ||
testWidgets('Images from streaming have expected resolution from preset', | ||
(WidgetTester tester) async { | ||
final List<CameraDescription> cameras = | ||
await CameraPlatform.instance.availableCameras(); | ||
if (cameras.isEmpty) { | ||
return; | ||
} | ||
for (final CameraDescription cameraDescription in cameras) { | ||
bool previousPresetExactlySupported = true; | ||
for (final MapEntry<ResolutionPreset, Size> preset | ||
in presetExpectedSizes.entries) { | ||
final CameraController controller = | ||
CameraController(cameraDescription, preset.key); | ||
final Completer<CameraImage> imageCompleter = Completer<CameraImage>(); | ||
await controller.initialize(); | ||
await controller.startImageStream((CameraImage image) { | ||
imageCompleter.complete(image); | ||
controller.stopImageStream(); | ||
}); | ||
|
||
// Try loading picture | ||
final File fileImage = File(file.path); | ||
final Image image = | ||
await decodeImageFromList(fileImage.readAsBytesSync()); | ||
final CameraImage image = await imageCompleter.future; | ||
final bool presetExactlySupported = assertExpectedDimensions( | ||
presetExpectedSizes[preset.key]!, | ||
camsim99 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Size(image.height.toDouble(), image.width.toDouble())); | ||
expect(!(!previousPresetExactlySupported && presetExactlySupported), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This expect is checking that (for a specific camera on the device) Can you add a comment either way explaining the purpose, as its not immediately clear to me from reading the test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment and also re-wrote the boolean since it's overcomplicated. By the way, this is the integration test in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replaced |
||
isTrue, | ||
reason: 'The preview has a lower resolution than that specified.'); | ||
previousPresetExactlySupported = presetExactlySupported; | ||
|
||
expect(image, isNotNull); | ||
await controller.dispose(); | ||
} | ||
} | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a fix for Long -> Integer casting issues that the integration test revealed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was the issue? Its not clear to me why the previous code would fail - it looks like the logical change here is to go from: calling
intValue()
on theLong
, to: casting to aNumber
and then using theintValue()
implementation forNumber
instead.But I would expect either of those to work, and if they both did, would prefer the former. Does this only work with the cast to
Number
added in?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using your former suggestion, this is an example of the error I got: https://firebase.corp.google.com/project/flutter-infra-staging/testlab/histories/bh.90e767c88242d274/matrices/6437969997948729501/executions/bs.a725e88a067fbf4d/testcases/2/test-cases.
I'm open to suggestions, but I could not find a fix and after investigating, it's unclear why I'm getting the error and only in the Firebase test lab like that. May be something with Java versions but unsure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out this is an issue because I was trying to index an array with a Long, most likely. After talking to @reidbaker, I'm going to take his suggestion and rework how I send a
Quality
from Dart to Java rather than trying to send indices of enums that representQuality
s.