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

[camera] Implemented ImageStream ImageFormat setting for Dart and Android #3359

Merged
merged 57 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
0a971a6
Implemented ImageStream ImageFormat setting for Dart and Android
danielroek Dec 21, 2020
9773fea
Fixed formatting and toString test
danielroek Dec 21, 2020
db6c88a
Apply suggestions from code review
danielroek Dec 21, 2020
fa71493
Removed imageStreamImageFormat from CameraValue
danielroek Dec 21, 2020
e94b474
Removed imageStreamImageFormat from CameraValue
danielroek Dec 21, 2020
ccd0225
Removed imageStreamImageFormat from CameraValue
danielroek Dec 21, 2020
fa0c685
fixed formatting
danielroek Dec 21, 2020
39bd9d1
fixed formatting
danielroek Dec 21, 2020
288684f
fixed formatting
danielroek Dec 21, 2020
b1ddd57
WIP: iOS implementation
danielroek Dec 21, 2020
332c2ba
Imaplemented suggested changes, added tests.
danielroek Dec 22, 2020
9188fab
iOS switch case videoFormat
anniek-valk Dec 22, 2020
a9ca829
Added imageFormatGroup to initialize
danielroek Dec 22, 2020
ec6aa45
Apply suggestions from code review
danielroek Dec 22, 2020
56f77ec
Added period to sentence
danielroek Dec 22, 2020
ecea347
Moved ImageFormatGroup to platform_interface; Added extension to conv…
danielroek Dec 23, 2020
ac23632
Fixed test
danielroek Dec 23, 2020
4eea926
Separated Android and iOS in name extension
danielroek Dec 23, 2020
9a86d26
Clarified returns on name extension
danielroek Dec 23, 2020
d5bc2a8
Merge branch 'image_stream_image_format_platform_interface' into imag…
danielroek Dec 23, 2020
f4e152f
updated Android implementation to support String output
danielroek Dec 23, 2020
7f06e79
removed getOrDefault
danielroek Dec 23, 2020
c887e9d
Updated camera implementation to use ImageFormatGroupName; Updated to…
danielroek Dec 23, 2020
18336f9
removed unused import
danielroek Dec 23, 2020
cf6345c
Export image_format_group.dart in types.dart
danielroek Dec 23, 2020
81799a7
Changed enum values to lowercase
danielroek Dec 23, 2020
6acd3ed
Merge branch 'image_stream_image_format_platform_interface' into imag…
danielroek Dec 23, 2020
ab15e89
Added ImageFormatGroup test
danielroek Dec 23, 2020
098fd76
Fixed formatting
danielroek Dec 23, 2020
6b65ed1
made enum strings lowercase
danielroek Dec 23, 2020
f8d9e4c
Removed target platform switch.
danielroek Dec 23, 2020
8936a7f
Fixed formatting
danielroek Dec 23, 2020
bece2b7
Merge branch 'image_stream_image_format_platform_interface' into imag…
danielroek Dec 23, 2020
71f77f4
Updated Android implementation
danielroek Dec 23, 2020
2b6751b
Updated iOS implementation
danielroek Dec 23, 2020
7e65ac1
updated log message for unsupported ImageFormatGroup
danielroek Dec 23, 2020
5f1a17c
Updated Android implementation
danielroek Dec 23, 2020
18efce6
fixed period in docs
danielroek Dec 23, 2020
06ec92d
Switch change to if-statement
anniek-valk Dec 23, 2020
be3bb3d
Moved switching videoFormat to method in iOS
Dec 24, 2020
b0a67fc
Implemented feedback
Dec 24, 2020
0e04029
fixed formatting
Dec 24, 2020
fb90463
Merge branch 'master' into image_stream_image_format
danielroek Dec 24, 2020
375396c
fixed mistakingly removed bracket
Dec 24, 2020
5ec0af9
fixed formatting
Dec 24, 2020
b0d603c
Merge remote-tracking branch 'origin/master' into image_stream_image_…
danielroek Jan 4, 2021
793e52d
Merge branch 'image_stream_image_format_platform_interface' into imag…
danielroek Jan 4, 2021
7953820
Updated version
danielroek Jan 4, 2021
8ea2454
Updated version
danielroek Jan 4, 2021
f904cbf
fixed formatting
danielroek Jan 4, 2021
90f816c
Merge remote-tracking branch 'origin/master' into image_stream_image_…
danielroek Jan 5, 2021
c6d2e78
Merge remote-tracking branch 'flutter/master' into image_stream_image…
danielroek Jan 5, 2021
5593116
Update version number
mvanbeusekom Jan 7, 2021
0a3e8d6
Update version number
mvanbeusekom Jan 7, 2021
ef1b551
Merge branch 'master' into image_stream_image_format
danielroek Jan 8, 2021
23e7753
Define TAG correctly
mvanbeusekom Jan 11, 2021
4de722d
Merge branch 'master' into image_stream_image_format
mvanbeusekom Jan 11, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## 0.6.5

* Adds ImageFormat selection for ImageStream and Video(iOS only).

## 0.6.4+1

* Added closeCaptureSession() to stopVideoRecording in Camera.java to fix an Android 6 crash
* Added closeCaptureSession() to stopVideoRecording in Camera.java to fix an Android 6 crash.

## 0.6.4

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.flutter.plugins.camera;

import static android.content.ContentValues.TAG;
import static android.view.OrientationEventListener.ORIENTATION_UNKNOWN;
import static io.flutter.plugins.camera.CameraUtils.computeBestPreviewSize;

Expand Down Expand Up @@ -33,6 +34,7 @@
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.Range;
import android.util.Rational;
import android.util.Size;
Expand Down Expand Up @@ -88,6 +90,13 @@ public class Camera {
private PictureCaptureRequest pictureCaptureRequest;
private CameraRegions cameraRegions;
private int exposureOffset;
private static HashMap<String, Integer> supportedImageFormats;
danielroek marked this conversation as resolved.
Show resolved Hide resolved
// Current supported outputs
static {
supportedImageFormats = new HashMap<>();
supportedImageFormats.put("yuv420", 35);
supportedImageFormats.put("jpeg", 256);
};

public Camera(
final Activity activity,
Expand Down Expand Up @@ -150,15 +159,20 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException {
}

@SuppressLint("MissingPermission")
public void open() throws CameraAccessException {
public void open(String imageFormatGroup) throws CameraAccessException {
pictureImageReader =
ImageReader.newInstance(
captureSize.getWidth(), captureSize.getHeight(), ImageFormat.JPEG, 2);

Integer imageFormat = supportedImageFormats.get(imageFormatGroup);
if (imageFormat == null) {
Log.w(TAG, "The selected imageFormatGroup is not supported by Android. Defaulting to yuv420");
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused by this TAG value. I might be misunderstanding it, but classes typically have a value like this at the top

private static final String TAG = 'MyClassName`;

But, this class doesn't have that. It looks like your using the TAG value from the static import added above:

import static android.content.ContentValues.TAG;

Is this intentional?

Copy link
Contributor

Choose a reason for hiding this comment

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

No this is not on purpose and is a mistake, will correct it now.

imageFormat = ImageFormat.YUV_420_888;
}

// Used to steam image byte data to dart side.
imageStreamReader =
ImageReader.newInstance(
previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);
ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), imageFormat, 2);

cameraManager.openCamera(
cameraName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
{
if (camera != null) {
try {
camera.open();
camera.open(call.argument("imageFormatGroup"));
result.success(null);
} catch (Exception e) {
handleException(e, result);
Expand Down
1 change: 1 addition & 0 deletions packages/camera/camera/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
cameraDescription,
ResolutionPreset.medium,
enableAudio: enableAudio,
imageFormatGroup: ImageFormatGroup.jpeg,
);

// If the controller is updated then update the UI.
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ flutter:
uses-material-design: true

environment:
sdk: ">=2.0.0-dev.28.0 <3.0.0"
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.9.1+hotfix.4 <2.0.0"
16 changes: 15 additions & 1 deletion packages/camera/camera/ios/Classes/CameraPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,17 @@ static FlashMode getFlashModeForString(NSString *mode) {
}
}

static OSType getVideoFormatFromString(NSString *videoFormatString) {
if ([videoFormatString isEqualToString:@"bgra8888"]) {
return kCVPixelFormatType_32BGRA;
} else if ([videoFormatString isEqualToString:@"yuv420"]) {
return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
} else {
NSLog(@"The selected imageFormatGroup is not supported by iOS. Defaulting to brga8888");
return kCVPixelFormatType_32BGRA;
}
}

static AVCaptureFlashMode getAVCaptureFlashModeForFlashMode(FlashMode mode) {
switch (mode) {
case FlashModeOff:
Expand Down Expand Up @@ -296,7 +307,7 @@ @implementation FLTCam {
dispatch_queue_t _dispatchQueue;
}
// Format used for video and image streaming.
FourCharCode const videoFormat = kCVPixelFormatType_32BGRA;
FourCharCode videoFormat = kCVPixelFormatType_32BGRA;
NSString *const errorMethod = @"error";

- (instancetype)initWithCameraName:(NSString *)cameraName
Expand Down Expand Up @@ -1147,6 +1158,9 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re
NSDictionary *argsMap = call.arguments;
NSUInteger cameraId = ((NSNumber *)argsMap[@"cameraId"]).unsignedIntegerValue;
if ([@"initialize" isEqualToString:call.method]) {
NSString *videoFormatValue = ((NSString *)argsMap[@"imageFormatGroup"]);
videoFormat = getVideoFormatFromString(videoFormatValue);

__weak CameraPlugin *weakSelf = self;
_camera.onFrameAvailable = ^{
[weakSelf.registry textureFrameAvailable:cameraId];
Expand Down
3 changes: 2 additions & 1 deletion packages/camera/camera/lib/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ export 'package:camera_platform_interface/camera_platform_interface.dart'
FlashMode,
ExposureMode,
ResolutionPreset,
XFile;
XFile,
ImageFormatGroup;
11 changes: 10 additions & 1 deletion packages/camera/camera/lib/src/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class CameraController extends ValueNotifier<CameraValue> {
this.description,
this.resolutionPreset, {
this.enableAudio = true,
this.imageFormatGroup,
}) : super(const CameraValue.uninitialized());

/// The properties of the camera device controlled by this controller.
Expand All @@ -176,6 +177,11 @@ class CameraController extends ValueNotifier<CameraValue> {
/// Whether to include audio when recording a video.
final bool enableAudio;

/// The [ImageFormatGroup] describes the output of the raw image format.
///
/// When null the imageFormat will fallback to the platforms default.
final ImageFormatGroup imageFormatGroup;

int _cameraId;
bool _isDisposed = false;
StreamSubscription<dynamic> _imageStreamSubscription;
Expand Down Expand Up @@ -217,7 +223,10 @@ class CameraController extends ValueNotifier<CameraValue> {
_initializeCompleter.complete(event);
}));

await CameraPlatform.instance.initializeCamera(_cameraId);
await CameraPlatform.instance.initializeCamera(
_cameraId,
imageFormatGroup: imageFormatGroup,
);

value = value.copyWith(
isInitialized: true,
Expand Down
37 changes: 8 additions & 29 deletions packages/camera/camera/lib/src/camera_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:typed_data';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:camera_platform_interface/camera_platform_interface.dart';

/// A single color plane of image data.
///
Expand Down Expand Up @@ -41,32 +42,6 @@ class Plane {
final int width;
}

// TODO:(bmparr) Turn [ImageFormatGroup] to a class with int values.
/// Group of image formats that are comparable across Android and iOS platforms.
enum ImageFormatGroup {
/// The image format does not fit into any specific group.
unknown,

/// Multi-plane YUV 420 format.
///
/// This format is a generic YCbCr format, capable of describing any 4:2:0
/// chroma-subsampled planar or semiplanar buffer (but not fully interleaved),
/// with 8 bits per color sample.
///
/// On Android, this is `android.graphics.ImageFormat.YUV_420_888`. See
/// https://developer.android.com/reference/android/graphics/ImageFormat.html#YUV_420_888
///
/// On iOS, this is `kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange`. See
/// https://developer.apple.com/documentation/corevideo/1563591-pixel_format_identifiers/kcvpixelformattype_420ypcbcr8biplanarvideorange?language=objc
yuv420,

/// 32-bit BGRA.
///
/// On iOS, this is `kCVPixelFormatType_32BGRA`. See
/// https://developer.apple.com/documentation/corevideo/1563591-pixel_format_identifiers/kcvpixelformattype_32bgra?language=objc
bgra8888,
}

/// Describes how pixels are represented in an image.
class ImageFormat {
ImageFormat._fromPlatformData(this.raw) : group = _asImageFormatGroup(raw);
Expand All @@ -86,9 +61,13 @@ class ImageFormat {

ImageFormatGroup _asImageFormatGroup(dynamic rawFormat) {
if (defaultTargetPlatform == TargetPlatform.android) {
// android.graphics.ImageFormat.YUV_420_888
if (rawFormat == 35) {
return ImageFormatGroup.yuv420;
switch (rawFormat) {
// android.graphics.ImageFormat.YUV_420_888
case 35:
return ImageFormatGroup.yuv420;
// android.graphics.ImageFormat.JPEG
case 256:
return ImageFormatGroup.jpeg;
}
}

Expand Down
6 changes: 3 additions & 3 deletions packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ name: camera
description: A Flutter plugin for getting information about and controlling the
camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video,
and streaming image buffers to dart.
version: 0.6.4+1
version: 0.6.5
homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera

dependencies:
flutter:
sdk: flutter
camera_platform_interface: ^1.2.0
camera_platform_interface: ^1.3.0
pedantic: ^1.8.0

dev_dependencies:
Expand All @@ -31,5 +31,5 @@ flutter:
pluginClass: CameraPlugin

environment:
sdk: ">=2.1.0 <3.0.0"
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.12.13+hotfix.5"
1 change: 1 addition & 0 deletions packages/camera/camera/test/camera_image_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'dart:typed_data';

import 'package:camera/camera.dart';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down
21 changes: 21 additions & 0 deletions packages/camera/camera/test/camera_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:ui';

import 'package:camera/camera.dart';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -164,6 +165,22 @@ void main() {
mockPlatformException = false;
});

test('initialize() sets imageFormat', () async {
debugDefaultTargetPlatformOverride = TargetPlatform.android;
CameraController cameraController = CameraController(
CameraDescription(
name: 'cam',
lensDirection: CameraLensDirection.back,
sensorOrientation: 90),
ResolutionPreset.max,
imageFormatGroup: ImageFormatGroup.yuv420,
);
await cameraController.initialize();
verify(CameraPlatform.instance
.initializeCamera(13, imageFormatGroup: ImageFormatGroup.yuv420))
.called(1);
});

test('prepareForVideoRecording() calls $CameraPlatform ', () async {
CameraController cameraController = CameraController(
CameraDescription(
Expand Down Expand Up @@ -1004,6 +1021,10 @@ class MockCameraPlatform extends Mock
with MockPlatformInterfaceMixin
implements CameraPlatform {
@override
Future<void> initializeCamera(int cameraId,
{ImageFormatGroup imageFormatGroup});

@override
Future<List<CameraDescription>> availableCameras() =>
Future.value(mockAvailableCameras);

Expand Down