Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2b7aa9b
Base classes to support Android camera features
mvanbeusekom Apr 8, 2021
f780742
Fixed formatting
mvanbeusekom Apr 8, 2021
76bc5bd
Applied feedback from PR
mvanbeusekom Apr 20, 2021
0bbed99
Added Android FPS range, resolution and sensor orientation features
mvanbeusekom Apr 8, 2021
1ba738d
Use mockito-inline
mvanbeusekom Apr 9, 2021
de4e70f
Merge remote-tracking branch 'upstream/master' into camera-android/fp…
mvanbeusekom Apr 21, 2021
728346a
Fix issue Pixel 4A
mvanbeusekom May 26, 2021
c014fe3
Merge remote-tracking branch 'upstream/master' into camera-android/fp…
mvanbeusekom May 31, 2021
84f5e73
Added API documentation
mvanbeusekom May 31, 2021
f763f77
Processed feedback on PR
mvanbeusekom May 31, 2021
4a7c73a
Fix formatting
mvanbeusekom May 31, 2021
a890919
Fix formatting
mvanbeusekom May 31, 2021
55a6702
Only exclude 60 FPS limit for Pixel 4a
mvanbeusekom Jun 8, 2021
cd53321
Removed redundant empty line
mvanbeusekom Jun 8, 2021
35831d3
Fixed comment
mvanbeusekom Jun 8, 2021
a9f3142
Test Pixel 4a workaround
mvanbeusekom Jun 8, 2021
551800e
Add tests for orientation updates
mvanbeusekom Jun 10, 2021
68cbc56
Fix formatting
mvanbeusekom Jun 10, 2021
1b137c2
Fix formatting
mvanbeusekom Jun 10, 2021
6514a00
Added missing license header
mvanbeusekom Jun 10, 2021
7f0180e
Accept cameraName as String
mvanbeusekom Jun 16, 2021
24af367
Format
mvanbeusekom Jun 16, 2021
8313dd0
Removed obsolete comment
mvanbeusekom Jun 16, 2021
a39c2e1
update method structure in class to a more logical order
mvanbeusekom Jun 16, 2021
3eecfe9
Merge remote-tracking branch 'origin/master' into camera-android/fps_…
mvanbeusekom Jun 22, 2021
7299b1d
Fix formatting
mvanbeusekom Jun 22, 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
2 changes: 1 addition & 1 deletion packages/camera/camera/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ android {
dependencies {
compileOnly 'androidx.annotation:annotation:1.1.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:3.5.13'
testImplementation 'org.mockito:mockito-inline:3.5.13'
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'org.robolectric:robolectric:4.3'
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import java.util.HashMap;
import java.util.Map;

class DartMessenger {
public class DartMessenger {
@NonNull private final Handler handler;
@Nullable private MethodChannel cameraChannel;
@Nullable private MethodChannel deviceChannel;
Expand Down Expand Up @@ -48,7 +48,7 @@ enum CameraEventType {
this.handler = handler;
}

void sendDeviceOrientationChangeEvent(PlatformChannel.DeviceOrientation orientation) {
public void sendDeviceOrientationChangeEvent(PlatformChannel.DeviceOrientation orientation) {
assert (orientation != null);
this.send(
DeviceEventType.ORIENTATION_CHANGED,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features.fpsrange;

import android.hardware.camera2.CaptureRequest;
import android.util.Log;
import android.util.Range;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;

public class FpsRangeFeature extends CameraFeature<Range<Integer>> {
private Range<Integer> currentSetting;

public FpsRangeFeature(CameraProperties cameraProperties) {
super(cameraProperties);

Log.i("Camera", "getAvailableFpsRange");

try {
Range<Integer>[] ranges = cameraProperties.getControlAutoExposureAvailableTargetFpsRanges();

if (ranges != null) {
for (Range<Integer> range : ranges) {
int upper = range.getUpper();
if (upper >= 10) {
if (currentSetting == null || upper > currentSetting.getUpper()) {
currentSetting = range;
}
}
}
}
} catch (Exception e) {
// TODO: maybe just send a dart error back
// pictureCaptureRequest.error("cameraAccess", e.getMessage(), null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this a TODO? It seems like having some kind of error handling is important here, whether that's reporting it back, or setting a default if that would make more sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After some refactoring the need for error handling is removed. I have removed the obsolete try...catch statement.

}
}

@Override
public String getDebugName() {
return "FpsRangeFeature";
}

@Override
public Range<Integer> getValue() {
return currentSetting;
}

@Override
public void setValue(Range<Integer> value) {
this.currentSetting = value;
}

// Always supported
@Override
public boolean checkIsSupported() {
return true;
}

@Override
public void updateBuilder(CaptureRequest.Builder requestBuilder) {
if (!checkIsSupported()) {
return;
}

requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, currentSetting);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features.resolution;

import android.hardware.camera2.CaptureRequest;
import android.media.CamcorderProfile;
import android.util.Size;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;

public class ResolutionFeature extends CameraFeature<ResolutionPreset> {
private final Size captureSize;
private final Size previewSize;
private final CamcorderProfile recordingProfile;
private ResolutionPreset currentSetting;

public ResolutionFeature(
CameraProperties cameraProperties, ResolutionPreset initialSetting, String cameraName) {
super(cameraProperties);
setValue(initialSetting);

// Resolution configuration
recordingProfile =
getBestAvailableCamcorderProfileForResolutionPreset(cameraName, initialSetting);
captureSize = new Size(recordingProfile.videoFrameWidth, recordingProfile.videoFrameHeight);

previewSize = computeBestPreviewSize(cameraName, initialSetting);
}

public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPreset(
String cameraName, ResolutionPreset preset) {
int cameraId = Integer.parseInt(cameraName);
Copy link
Contributor

Choose a reason for hiding this comment

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

If what's actually needed is an integer ID, why does the method take a string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored the code to only accept an integer.

switch (preset) {
// All of these cases deliberately fall through to get the best available profile.
case max:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH)) {
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);
}
case ultraHigh:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_2160P)) {
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_2160P);
}
case veryHigh:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_1080P)) {
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_1080P);
}
case high:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P)) {
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_720P);
}
case medium:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P)) {
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_480P);
}
case low:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QVGA)) {
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_QVGA);
}
default:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_LOW)) {
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW);
} else {
throw new IllegalArgumentException(
"No capture session available for current capture session.");
}
}
}

static Size computeBestPreviewSize(String cameraName, ResolutionPreset preset) {
if (preset.ordinal() > ResolutionPreset.high.ordinal()) {
preset = ResolutionPreset.high;
}

CamcorderProfile profile =
getBestAvailableCamcorderProfileForResolutionPreset(cameraName, preset);
return new Size(profile.videoFrameWidth, profile.videoFrameHeight);
}

@Override
public String getDebugName() {
return "ResolutionFeature";
}

@Override
public ResolutionPreset getValue() {
return currentSetting;
}

@Override
public void setValue(ResolutionPreset value) {
this.currentSetting = value;
}

// Always supported
@Override
public boolean checkIsSupported() {
return true;
}

@Override
public void updateBuilder(CaptureRequest.Builder requestBuilder) {
// No-op: when setting a resolution there is no need to update the request builder.
}

public CamcorderProfile getRecordingProfile() {
return this.recordingProfile;
}

public Size getPreviewSize() {
return this.previewSize;
}

public Size getCaptureSize() {
return this.captureSize;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features.resolution;

// Mirrors camera.dart
public enum ResolutionPreset {
low,
medium,
high,
veryHigh,
ultraHigh,
max,
}
Loading