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 36 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
6976152
Identified deprecated usages and added initial fixes
camsim99 Oct 11, 2021
49722e4
Fix resolution feature tests
camsim99 Oct 11, 2021
f40abe2
fix mediarecorderbuilder test
camsim99 Oct 11, 2021
38d2310
suppress device orientation/unrelated errors
camsim99 Oct 11, 2021
09a2685
fix camera test
camsim99 Oct 11, 2021
accaed0
update android sdk versions in related build files
camsim99 Oct 12, 2021
a0441d9
add build checks for resolutionfeature:
camsim99 Oct 12, 2021
2383bf3
adjust mediarecorderbuilder and use robolectric
camsim99 Oct 13, 2021
09cbfa8
adjust camera and test
camsim99 Oct 13, 2021
252a6fe
format files
camsim99 Oct 13, 2021
f8fd254
restore device orientation (unrelated todo)
camsim99 Oct 13, 2021
f238289
changed error handling
camsim99 Oct 13, 2021
8532e97
add error check for media recorder
camsim99 Oct 13, 2021
724f891
add robolectric to resolutionfeature test
camsim99 Oct 15, 2021
b77b4fc
push latest changes to resolve camera test
camsim99 Oct 15, 2021
d6110ea
reformat and reorganize resolution feature test
camsim99 Oct 15, 2021
b7f7649
add missing imports
camsim99 Oct 15, 2021
c77ff4e
correct camera test
camsim99 Oct 16, 2021
162309b
removed print statement
camsim99 Oct 18, 2021
e74767a
change names
camsim99 Oct 18, 2021
3fd589c
Document gradle properties
camsim99 Oct 18, 2021
d3547d8
change bcprov jdk version
camsim99 Oct 18, 2021
c8194fc
modify enablejetifier
camsim99 Oct 19, 2021
84a243a
removie blacklist option
camsim99 Oct 19, 2021
95fac07
trying gradle modifications
camsim99 Oct 19, 2021
00a4e0d
upgrade gradle version:
camsim99 Oct 19, 2021
d59ff1f
upgrade gradle version and remove deprecations
camsim99 Oct 19, 2021
fb94483
downgrade robolectric version
camsim99 Oct 19, 2021
e0f71bc
remove robolectric req'd docker lines
camsim99 Oct 19, 2021
92e44c1
fix video_player and target api CI issues
camsim99 Oct 20, 2021
e54ec61
modify connectivity gradle verison
camsim99 Oct 20, 2021
bf9e4f7
circumvent android-lint problems for now
camsim99 Oct 20, 2021
e10031d
Bump plugin versions and update release notes
camsim99 Oct 20, 2021
ef23a8c
Revert mistake versionplugin bump
camsim99 Oct 20, 2021
f59241c
Merge branch 'master' into issue_89578_dev
camsim99 Oct 20, 2021
4fff3f3
Correct video_player version
camsim99 Oct 20, 2021
d2d58b3
Make first round of addressing Stuart's comments
camsim99 Oct 21, 2021
25550d0
rename on31, add back android-lint-artifcats
camsim99 Oct 21, 2021
4cbe753
Merge remote-tracking branch 'upstream/master' into issue_89578_dev
camsim99 Oct 22, 2021
7304068
Removed unneeded annotation
camsim99 Oct 25, 2021
3f50377
undo changes to other plugins
camsim99 Oct 25, 2021
5d6a93a
fix android platform test errors
camsim99 Oct 25, 2021
1e266e0
Remove undone changes from change logs
camsim99 Oct 25, 2021
9acf444
Merge remote-tracking branch 'upstream/master' into issue_89578_dev
camsim99 Oct 25, 2021
2cf0c68
undo changes to dart messenger
camsim99 Oct 25, 2021
e779b26
fix merge conflict
camsim99 Oct 28, 2021
e745349
undo accidental changelog modification
camsim99 Oct 28, 2021
d25fbac
Merge remote-tracking branch 'upstream/master' into issue_89578_dev
camsim99 Oct 29, 2021
979a37f
address nits
camsim99 Oct 29, 2021
11cc4e1
rename on31s
camsim99 Oct 29, 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
4 changes: 0 additions & 4 deletions .ci/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ FROM cirrusci/flutter:2.2.2

RUN apt-get update -y

# Required by Roboeletric and the Android SDK.
RUN apt-get install -y openjdk-8-jdk
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
Copy link
Contributor

Choose a reason for hiding this comment

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

This change, and the related changes to other plugins, shouldn't be part of this PR. We should land @blasten's PR first, and rebase this PR onto it so that it only contains camera changes.


RUN apt-get install -y --no-install-recommends gnupg

# Add repo for gcloud sdk and install it
Expand Down
11 changes: 6 additions & 5 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,12 @@ task:
- echo "This user does not have permission to run Firebase Test Lab tests."
- fi
# Upload the full lint results to Cirrus to display in the results UI.
always:
android-lint_artifacts:
path: "**/reports/lint-results-debug.xml"
type: text/xml
format: android-lint
# TODO(camsim99): See https://github.com/flutter/flutter/issues/92201
# always:
# android-lint_artifacts:
# path: "**/reports/lint-results-debug.xml"
# type: text/xml
# format: android-lint
### Web tasks ###
- name: web-platform_tests
env:
Expand Down
3 changes: 3 additions & 0 deletions packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 0.9.4+3

* Change compileSdkVersion to 31.
* Remove deprecated `CamcorderProfile`.
* Update gradle version to 7.0.2.
* Fix registerTexture and result being called on background thread on iOS.

## 0.9.4+2
Expand Down
9 changes: 5 additions & 4 deletions packages/camera/camera/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:7.0.2'
}
}

Expand All @@ -27,9 +27,10 @@ project.getTasks().withType(JavaCompile){
apply plugin: 'com.android.library'

android {
compileSdkVersion 29
compileSdkVersion 31

defaultConfig {
targetSdkVersion 31
minSdkVersion 21
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -60,7 +61,7 @@ android {
dependencies {
compileOnly 'androidx.annotation:annotation:1.1.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-inline:3.12.4'
testImplementation 'org.mockito:mockito-inline:4.0.0'
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'org.robolectric:robolectric:4.3'
testImplementation 'org.robolectric:robolectric:4.5'
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.media.CamcorderProfile;
import android.media.EncoderProfiles;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
Expand Down Expand Up @@ -199,8 +200,16 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException {
((SensorOrientationFeature) cameraFeatures.getSensorOrientation())
.getLockedCaptureOrientation();

MediaRecorderBuilder mediaRecorderBuilder;

if (Build.VERSION.SDK_INT >= 31) {
mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfileOn31(), outputFilePath);
} else {
mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfile(), outputFilePath);
}

mediaRecorder =
new MediaRecorderBuilder(getRecordingProfile(), outputFilePath)
mediaRecorderBuilder
.setEnableAudio(enableAudio)
.setMediaOrientation(
lockedOrientation == null
Expand Down Expand Up @@ -923,6 +932,10 @@ CamcorderProfile getRecordingProfile() {
return cameraFeatures.getResolution().getRecordingProfile();
}

EncoderProfiles getRecordingProfileOn31() {
return cameraFeatures.getResolution().getRecordingProfileOn31();
}

/** Shortut to get deviceOrientationListener. */
DeviceOrientationManager getDeviceOrientationManager() {
return cameraFeatures.getSensorOrientation().getDeviceOrientationManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

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

import android.annotation.TargetApi;
import android.hardware.camera2.CaptureRequest;
import android.media.CamcorderProfile;
import android.media.EncoderProfiles;
import android.os.Build;
import android.util.Size;
import androidx.annotation.VisibleForTesting;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;
import java.util.List;

/**
* Controls the resolutions configuration on the {@link android.hardware.camera2} API.
Expand All @@ -22,6 +26,7 @@ public class ResolutionFeature extends CameraFeature<ResolutionPreset> {
private Size captureSize;
private Size previewSize;
private CamcorderProfile recordingProfile;
private EncoderProfiles recordingProfileOn31;
private ResolutionPreset currentSetting;
private int cameraId;

Expand Down Expand Up @@ -55,6 +60,10 @@ public CamcorderProfile getRecordingProfile() {
return this.recordingProfile;
}

public EncoderProfiles getRecordingProfileOn31() {
return this.recordingProfileOn31;
}

/**
* Gets the optimal preview size based on the configured resolution.
*
Expand Down Expand Up @@ -99,15 +108,25 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
// No-op: when setting a resolution there is no need to update the request builder.
}

@SuppressWarnings("deprecation")
@VisibleForTesting
static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset) {
static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset)
throws IndexOutOfBoundsException {
if (preset.ordinal() > ResolutionPreset.high.ordinal()) {
preset = ResolutionPreset.high;
}

CamcorderProfile profile =
getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset);
return new Size(profile.videoFrameWidth, profile.videoFrameHeight);
if (Build.VERSION.SDK_INT >= 31) {
EncoderProfiles profile =
getBestAvailableCamcorderProfileForResolutionPresetOn31(cameraId, preset);
List<EncoderProfiles.VideoProfile> videoProfiles = profile.getVideoProfiles();
EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0);

return new Size(defaultVideoProfile.getWidth(), defaultVideoProfile.getHeight());
} else {
CamcorderProfile profile =
getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset);
return new Size(profile.videoFrameWidth, profile.videoFrameHeight);
}
}

/**
Expand All @@ -121,6 +140,7 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset) {
* @return The best possible {@link android.media.CamcorderProfile} that matches the supplied
* {@link ResolutionPreset}.
*/
@SuppressWarnings("deprecation")
public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPreset(
int cameraId, ResolutionPreset preset) {
if (cameraId < 0) {
Expand Down Expand Up @@ -164,13 +184,72 @@ public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPres
}
}

private void configureResolution(ResolutionPreset resolutionPreset, int cameraId) {
@TargetApi(Build.VERSION_CODES.S)
public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPresetOn31(
int cameraId, ResolutionPreset preset) {
if (cameraId < 0) {
throw new AssertionError(
"getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers.");
}

String cameraIdString = Integer.toString(cameraId);

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.getAll(cameraIdString, CamcorderProfile.QUALITY_HIGH);
}
case ultraHigh:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_2160P)) {
return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_2160P);
}
case veryHigh:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_1080P)) {
return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_1080P);
}
case high:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P)) {
return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_720P);
}
case medium:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P)) {
return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_480P);
}
case low:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QVGA)) {
return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_QVGA);
}
default:
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_LOW)) {
return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_LOW);
}

throw new IllegalArgumentException(
"No capture session available for current capture session.");
}
}

@SuppressWarnings("deprecation")
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we put this on the specific lines we are trying to suppress, rather than the whole function, so that we're not suppressing future deprecation warnings for the currently-new codepath?

Copy link
Contributor

Choose a reason for hiding this comment

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

Same question here; can't we remove this now?

Copy link
Contributor

Choose a reason for hiding this comment

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

This was marked as resolved, but is still present; is that a mistake, or is there a reason we need this? If the latter, please explain in a reply so that we have a record of it for people trying to understand this code later.

private void configureResolution(ResolutionPreset resolutionPreset, int cameraId)
throws IndexOutOfBoundsException {
if (!checkIsSupported()) {
return;
}
recordingProfile =
getBestAvailableCamcorderProfileForResolutionPreset(cameraId, resolutionPreset);
captureSize = new Size(recordingProfile.videoFrameWidth, recordingProfile.videoFrameHeight);

if (Build.VERSION.SDK_INT >= 31) {
recordingProfileOn31 =
getBestAvailableCamcorderProfileForResolutionPresetOn31(cameraId, resolutionPreset);
List<EncoderProfiles.VideoProfile> videoProfiles = recordingProfileOn31.getVideoProfiles();

EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0);
captureSize = new Size(defaultVideoProfile.getWidth(), defaultVideoProfile.getHeight());
} else {
recordingProfile =
getBestAvailableCamcorderProfileForResolutionPreset(cameraId, resolutionPreset);
captureSize = new Size(recordingProfile.videoFrameWidth, recordingProfile.videoFrameHeight);
}

previewSize = computeBestPreviewSize(cameraId, resolutionPreset);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,55 @@
package io.flutter.plugins.camera.media;

import android.media.CamcorderProfile;
import android.media.EncoderProfiles;
import android.media.MediaRecorder;
import android.os.Build;
import androidx.annotation.NonNull;
import java.io.IOException;

public class MediaRecorderBuilder {
@SuppressWarnings("deprecation")
static class MediaRecorderFactory {
MediaRecorder makeMediaRecorder() {
return new MediaRecorder();
}
}

private final String outputFilePath;
private final CamcorderProfile recordingProfile;
private final CamcorderProfile camcorderProfile;
private final EncoderProfiles encoderProfiles;
private final MediaRecorderFactory recorderFactory;

private boolean enableAudio;
private int mediaOrientation;

public MediaRecorderBuilder(
@NonNull CamcorderProfile recordingProfile, @NonNull String outputFilePath) {
this(recordingProfile, outputFilePath, new MediaRecorderFactory());
@NonNull CamcorderProfile camcorderProfile, @NonNull String outputFilePath) {
this(camcorderProfile, outputFilePath, new MediaRecorderFactory());
}

public MediaRecorderBuilder(
@NonNull EncoderProfiles encoderProfiles, @NonNull String outputFilePath) {
this(encoderProfiles, outputFilePath, new MediaRecorderFactory());
}

MediaRecorderBuilder(
@NonNull CamcorderProfile recordingProfile,
@NonNull CamcorderProfile camcorderProfile,
@NonNull String outputFilePath,
MediaRecorderFactory helper) {
this.outputFilePath = outputFilePath;
this.recordingProfile = recordingProfile;
this.camcorderProfile = camcorderProfile;
this.encoderProfiles = null;
this.recorderFactory = helper;
}

MediaRecorderBuilder(
@NonNull EncoderProfiles encoderProfiles,
@NonNull String outputFilePath,
MediaRecorderFactory helper) {
this.outputFilePath = outputFilePath;
this.encoderProfiles = encoderProfiles;
this.camcorderProfile = null;
this.recorderFactory = helper;
}

Expand All @@ -47,23 +67,43 @@ public MediaRecorderBuilder setMediaOrientation(int orientation) {
return this;
}

public MediaRecorder build() throws IOException {
public MediaRecorder build() throws IOException, NullPointerException, IndexOutOfBoundsException {
MediaRecorder mediaRecorder = recorderFactory.makeMediaRecorder();

// There's a fixed order that mediaRecorder expects. Only change these functions accordingly.
// You can find the specifics here: https://developer.android.com/reference/android/media/MediaRecorder.
if (enableAudio) mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(recordingProfile.fileFormat);
if (enableAudio) {
mediaRecorder.setAudioEncoder(recordingProfile.audioCodec);
mediaRecorder.setAudioEncodingBitRate(recordingProfile.audioBitRate);
mediaRecorder.setAudioSamplingRate(recordingProfile.audioSampleRate);

if (Build.VERSION.SDK_INT >= 31) {
EncoderProfiles.VideoProfile videoProfile = encoderProfiles.getVideoProfiles().get(0);
EncoderProfiles.AudioProfile audioProfile = encoderProfiles.getAudioProfiles().get(0);

mediaRecorder.setOutputFormat(encoderProfiles.getRecommendedFileFormat());
if (enableAudio) {
mediaRecorder.setAudioEncoder(audioProfile.getCodec());
mediaRecorder.setAudioEncodingBitRate(audioProfile.getBitrate());
mediaRecorder.setAudioSamplingRate(audioProfile.getSampleRate());
}
mediaRecorder.setVideoEncoder(videoProfile.getCodec());
mediaRecorder.setVideoEncodingBitRate(videoProfile.getBitrate());
mediaRecorder.setVideoFrameRate(videoProfile.getFrameRate());
mediaRecorder.setVideoSize(videoProfile.getWidth(), videoProfile.getHeight());
mediaRecorder.setVideoSize(videoProfile.getWidth(), videoProfile.getHeight());
} else {
mediaRecorder.setOutputFormat(camcorderProfile.fileFormat);
if (enableAudio) {
mediaRecorder.setAudioEncoder(camcorderProfile.audioCodec);
mediaRecorder.setAudioEncodingBitRate(camcorderProfile.audioBitRate);
mediaRecorder.setAudioSamplingRate(camcorderProfile.audioSampleRate);
}
mediaRecorder.setVideoEncoder(camcorderProfile.videoCodec);
mediaRecorder.setVideoEncodingBitRate(camcorderProfile.videoBitRate);
mediaRecorder.setVideoFrameRate(camcorderProfile.videoFrameRate);
mediaRecorder.setVideoSize(
camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
}
mediaRecorder.setVideoEncoder(recordingProfile.videoCodec);
mediaRecorder.setVideoEncodingBitRate(recordingProfile.videoBitRate);
mediaRecorder.setVideoFrameRate(recordingProfile.videoFrameRate);
mediaRecorder.setVideoSize(recordingProfile.videoFrameWidth, recordingProfile.videoFrameHeight);

mediaRecorder.setOutputFile(outputFilePath);
mediaRecorder.setOrientationHint(this.mediaOrientation);

Expand Down
Loading