Skip to content

Commit c9ee666

Browse files
committed
Add support for YUV_420_888 Image format.
Casting the bytes to a type directly is not possible, thus allocating a new texture is necessary (and costly). But on the bright side, we avoid the conversion inside the camera plugin [0]. Or any other custom conversion code in dart, which is likely more costly. [0] https://github.com/flutter/packages/blob/d1fd6232ec33cd5a25aa762e605c494afced812f/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/ImageStreamReaderUtils.java#L35
1 parent da48532 commit c9ee666

File tree

14 files changed

+220
-108
lines changed

14 files changed

+220
-108
lines changed

.github/workflows/code-analysis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
distribution: temurin
2525
- uses: subosito/[email protected]
2626
with:
27-
flutter-version: '3.24.0'
27+
flutter-version: '3.27.3'
2828
cache: true
2929

3030
- name: Install dependencies for google_ml_kit

packages/example/lib/vision_detector_views/camera_view.dart

+38-13
Original file line numberDiff line numberDiff line change
@@ -358,27 +358,52 @@ class _CameraViewState extends State<CameraView> {
358358

359359
// get image format
360360
final format = InputImageFormatValue.fromRawValue(image.format.raw);
361-
// validate format depending on platform
362-
// only supported formats:
363-
// * nv21 for Android
364-
// * bgra8888 for iOS
365-
if (format == null ||
366-
(Platform.isAndroid && format != InputImageFormat.nv21) ||
367-
(Platform.isIOS && format != InputImageFormat.bgra8888)) return null;
361+
if (format == null) {
362+
print('could not find format from raw value: $image.format.raw');
363+
return null;
364+
}
365+
// Validate format depending on platform
366+
final androidSupportedFormats = [
367+
InputImageFormat.nv21,
368+
InputImageFormat.yv12,
369+
InputImageFormat.yuv_420_888
370+
];
371+
if ((Platform.isAndroid && !androidSupportedFormats.contains(format)) ||
372+
(Platform.isIOS && format != InputImageFormat.bgra8888)) {
373+
print('image format is not supported: $format');
374+
return null;
375+
}
368376

369-
// since format is constraint to nv21 or bgra8888, both only have one plane
370-
if (image.planes.length != 1) return null;
371-
final plane = image.planes.first;
377+
// Compile a flat list of all image data. For image formats with multiple planes,
378+
// takes some copying.
379+
final Uint8List bytes = image.planes.length == 1
380+
? image.planes.first.bytes
381+
: _concatenatePlanes(image);
372382

373383
// compose InputImage using bytes
374384
return InputImage.fromBytes(
375-
bytes: plane.bytes,
385+
bytes: bytes,
376386
metadata: InputImageMetadata(
377387
size: Size(image.width.toDouble(), image.height.toDouble()),
378388
rotation: rotation, // used only in Android
379-
format: format, // used only in iOS
380-
bytesPerRow: plane.bytesPerRow, // used only in iOS
389+
format: format,
390+
bytesPerRow: image.planes.first.bytesPerRow, // used only in iOS
381391
),
382392
);
383393
}
394+
395+
Uint8List _concatenatePlanes(CameraImage image) {
396+
int length = 0;
397+
for (final Plane p in image.planes) {
398+
length += p.bytes.length;
399+
}
400+
401+
final Uint8List bytes = Uint8List(length);
402+
int offset = 0;
403+
for (final Plane p in image.planes) {
404+
bytes.setRange(offset, offset + p.bytes.length, p.bytes);
405+
offset += p.bytes.length;
406+
}
407+
return bytes;
408+
}
384409
}

packages/example/pubspec.lock

+37-45
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,8 @@ packages:
2525
url: "https://pub.dev"
2626
source: hosted
2727
version: "0.11.0+2"
28-
camera_android:
29-
dependency: "direct main"
30-
description:
31-
name: camera_android
32-
sha256: "19b7226387218864cb2388e1ad5db7db50d065222f5511254b03fc397dd21a5e"
33-
url: "https://pub.dev"
34-
source: hosted
35-
version: "0.10.9+17"
3628
camera_android_camerax:
37-
dependency: transitive
29+
dependency: "direct main"
3830
description:
3931
name: camera_android_camerax
4032
sha256: "2bb0724371bae3c0889d7e0b1665357e4aa6ba6c8d32ffa3e178098ba81ed3df"
@@ -45,18 +37,18 @@ packages:
4537
dependency: transitive
4638
description:
4739
name: camera_avfoundation
48-
sha256: "7c28969a975a7eb2349bc2cb2dfe3ad218a33dba9968ecfb181ce08c87486655"
40+
sha256: "3f81ee3e88a79b0b010f0233d42625926299551b05d5dc995267a0b35bc33247"
4941
url: "https://pub.dev"
5042
source: hosted
51-
version: "0.9.17+3"
43+
version: "0.9.18"
5244
camera_platform_interface:
5345
dependency: transitive
5446
description:
5547
name: camera_platform_interface
56-
sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061
48+
sha256: "953e7baed3a7c8fae92f7200afeb2be503ff1a17c3b4e4ed7b76f008c2810a31"
5749
url: "https://pub.dev"
5850
source: hosted
59-
version: "2.8.0"
51+
version: "2.9.0"
6052
camera_web:
6153
dependency: transitive
6254
description:
@@ -117,10 +109,10 @@ packages:
117109
dependency: transitive
118110
description:
119111
name: file_selector_linux
120-
sha256: "712ce7fab537ba532c8febdb1a8f167b32441e74acd68c3ccb2e36dcb52c4ab2"
112+
sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
121113
url: "https://pub.dev"
122114
source: hosted
123-
version: "0.9.3"
115+
version: "0.9.3+2"
124116
file_selector_macos:
125117
dependency: transitive
126118
description:
@@ -162,18 +154,18 @@ packages:
162154
dependency: "direct main"
163155
description:
164156
name: flutter_pdfview
165-
sha256: "6b625b32a9102780236554dff42f2d798b4627704ab4a3153c07f2134a52b697"
157+
sha256: "2e3fa359524e9865ec25a64593b65092b4a9974c5871228c1a771300a003d150"
166158
url: "https://pub.dev"
167159
source: hosted
168-
version: "1.3.3"
160+
version: "1.4.0"
169161
flutter_plugin_android_lifecycle:
170162
dependency: transitive
171163
description:
172164
name: flutter_plugin_android_lifecycle
173-
sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
165+
sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e"
174166
url: "https://pub.dev"
175167
source: hosted
176-
version: "2.0.23"
168+
version: "2.0.24"
177169
flutter_test:
178170
dependency: "direct dev"
179171
description: flutter
@@ -308,10 +300,10 @@ packages:
308300
dependency: transitive
309301
description:
310302
name: http_parser
311-
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
303+
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
312304
url: "https://pub.dev"
313305
source: hosted
314-
version: "4.0.2"
306+
version: "4.1.2"
315307
image_picker:
316308
dependency: "direct main"
317309
description:
@@ -324,26 +316,26 @@ packages:
324316
dependency: transitive
325317
description:
326318
name: image_picker_android
327-
sha256: d3e5e00fdfeca8fd4ffb3227001264d449cc8950414c2ff70b0e06b9c628e643
319+
sha256: b62d34a506e12bb965e824b6db4fbf709ee4589cf5d3e99b45ab2287b008ee0c
328320
url: "https://pub.dev"
329321
source: hosted
330-
version: "0.8.12+15"
322+
version: "0.8.12+20"
331323
image_picker_for_web:
332324
dependency: transitive
333325
description:
334326
name: image_picker_for_web
335-
sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50"
327+
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
336328
url: "https://pub.dev"
337329
source: hosted
338-
version: "3.0.5"
330+
version: "3.0.6"
339331
image_picker_ios:
340332
dependency: transitive
341333
description:
342334
name: image_picker_ios
343-
sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447"
335+
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
344336
url: "https://pub.dev"
345337
source: hosted
346-
version: "0.8.12"
338+
version: "0.8.12+2"
347339
image_picker_linux:
348340
dependency: transitive
349341
description:
@@ -364,10 +356,10 @@ packages:
364356
dependency: transitive
365357
description:
366358
name: image_picker_platform_interface
367-
sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
359+
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
368360
url: "https://pub.dev"
369361
source: hosted
370-
version: "2.10.0"
362+
version: "2.10.1"
371363
image_picker_windows:
372364
dependency: transitive
373365
description:
@@ -404,10 +396,10 @@ packages:
404396
dependency: transitive
405397
description:
406398
name: lints
407-
sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
399+
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
408400
url: "https://pub.dev"
409401
source: hosted
410-
version: "5.0.0"
402+
version: "5.1.1"
411403
matcher:
412404
dependency: transitive
413405
description:
@@ -436,10 +428,10 @@ packages:
436428
dependency: transitive
437429
description:
438430
name: mime
439-
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
431+
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
440432
url: "https://pub.dev"
441433
source: hosted
442-
version: "1.0.6"
434+
version: "2.0.0"
443435
path:
444436
dependency: "direct main"
445437
description:
@@ -452,26 +444,26 @@ packages:
452444
dependency: "direct main"
453445
description:
454446
name: path_provider
455-
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
447+
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
456448
url: "https://pub.dev"
457449
source: hosted
458-
version: "2.1.4"
450+
version: "2.1.5"
459451
path_provider_android:
460452
dependency: transitive
461453
description:
462454
name: path_provider_android
463-
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
455+
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
464456
url: "https://pub.dev"
465457
source: hosted
466-
version: "2.2.12"
458+
version: "2.2.15"
467459
path_provider_foundation:
468460
dependency: transitive
469461
description:
470462
name: path_provider_foundation
471-
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
463+
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
472464
url: "https://pub.dev"
473465
source: hosted
474-
version: "2.4.0"
466+
version: "2.4.1"
475467
path_provider_linux:
476468
dependency: transitive
477469
description:
@@ -500,10 +492,10 @@ packages:
500492
dependency: transitive
501493
description:
502494
name: platform
503-
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
495+
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
504496
url: "https://pub.dev"
505497
source: hosted
506-
version: "3.1.5"
498+
version: "3.1.6"
507499
plugin_platform_interface:
508500
dependency: transitive
509501
description:
@@ -545,10 +537,10 @@ packages:
545537
dependency: transitive
546538
description:
547539
name: stream_transform
548-
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
540+
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
549541
url: "https://pub.dev"
550542
source: hosted
551-
version: "2.1.0"
543+
version: "2.1.1"
552544
string_scanner:
553545
dependency: transitive
554546
description:
@@ -577,10 +569,10 @@ packages:
577569
dependency: transitive
578570
description:
579571
name: typed_data
580-
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
572+
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
581573
url: "https://pub.dev"
582574
source: hosted
583-
version: "1.3.2"
575+
version: "1.4.0"
584576
vector_math:
585577
dependency: transitive
586578
description:

packages/example/pubspec.yaml

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ dependencies:
1616
flutter_pdfview: ^1.3.3
1717
image_picker: ^1.1.2
1818
camera: ^0.11.0+2
19-
# The default Android implementation from camera_android_camerax doesn't support the required image format.
20-
camera_android: ^0.10.9+17
19+
camera_android_camerax: 0.6.11 # Due to https://github.com/flutter/flutter/issues/154241
2120
path: ^1.9.0
2221
path_provider: ^2.1.4
2322

packages/google_mlkit_barcode_scanning/android/src/main/java/com/google_mlkit_barcode_scanning/BarcodeScanner.java

+15-5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.flutter.plugin.common.MethodChannel;
2424

2525
public class BarcodeScanner implements MethodChannel.MethodCallHandler {
26+
2627
private static final String START = "vision#startBarcodeScanner";
2728
private static final String CLOSE = "vision#closeBarcodeScanner";
2829

@@ -67,8 +68,11 @@ private com.google.mlkit.vision.barcode.BarcodeScanner initialize(MethodCall cal
6768

6869
private void handleDetection(MethodCall call, final MethodChannel.Result result) {
6970
Map<String, Object> imageData = call.argument("imageData");
70-
InputImage inputImage = InputImageConverter.getInputImageFromData(imageData, context, result);
71-
if (inputImage == null) return;
71+
InputImageConverter converter = new InputImageConverter();
72+
InputImage inputImage = converter.getInputImageFromData(imageData, context, result);
73+
if (inputImage == null) {
74+
return;
75+
}
7276

7377
String id = call.argument("id");
7478
com.google.mlkit.vision.barcode.BarcodeScanner barcodeScanner = instances.get(id);
@@ -191,7 +195,9 @@ private void handleDetection(MethodCall call, final MethodChannel.Result result)
191195
}
192196
result.success(barcodeList);
193197
})
194-
.addOnFailureListener(e -> result.error("BarcodeDetectorError", e.toString(), null));
198+
.addOnFailureListener(e -> result.error("BarcodeDetectorError", e.toString(), e))
199+
// Closing is necessary for both success and failure.
200+
.addOnCompleteListener(r -> converter.close());
195201
}
196202

197203
private void addPoints(Point[] cornerPoints, List<Map<String, Integer>> points) {
@@ -205,7 +211,9 @@ private void addPoints(Point[] cornerPoints, List<Map<String, Integer>> points)
205211

206212
private Map<String, Integer> getBoundingPoints(@Nullable Rect rect) {
207213
Map<String, Integer> frame = new HashMap<>();
208-
if (rect == null) return frame;
214+
if (rect == null) {
215+
return frame;
216+
}
209217
frame.put("left", rect.left);
210218
frame.put("right", rect.right);
211219
frame.put("top", rect.top);
@@ -216,7 +224,9 @@ private Map<String, Integer> getBoundingPoints(@Nullable Rect rect) {
216224
private void closeDetector(MethodCall call) {
217225
String id = call.argument("id");
218226
com.google.mlkit.vision.barcode.BarcodeScanner barcodeScanner = instances.get(id);
219-
if (barcodeScanner == null) return;
227+
if (barcodeScanner == null) {
228+
return;
229+
}
220230
barcodeScanner.close();
221231
instances.remove(id);
222232
}

0 commit comments

Comments
 (0)