Create VideoValidatedEncoderProfilesProxy
Create an implementation of EncoderProfilesProxy in camera-video
that guarantees to provide video information. It is expected to be
used to replace the using of CamcorderProfileProxy in camera-video.
This CL also modify EncoderProfilesProxy into an interface and add
an implementation for compat.
Bug: 264514713
Test: manual test and ./gradlew bOS
Change-Id: Id89e9bc2d7d50f8d8515112a747a52b96b4b4562
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java
index 963e3d6..f37cb38 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java
@@ -53,49 +53,31 @@
* {@link EncoderProfiles}.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-@AutoValue
-public abstract class EncoderProfilesProxy {
+public interface EncoderProfilesProxy {
/** Constant representing no codec profile. */
- public static final int CODEC_PROFILE_NONE = -1;
-
- /** Creates an EncoderProfilesProxy instance. */
- @NonNull
- public static EncoderProfilesProxy create(
- int defaultDurationSeconds,
- int recommendedFileFormat,
- @NonNull List<AudioProfileProxy> audioProfiles,
- @NonNull List<VideoProfileProxy> videoProfiles) {
- return new AutoValue_EncoderProfilesProxy(
- defaultDurationSeconds,
- recommendedFileFormat,
- unmodifiableList(new ArrayList<>(audioProfiles)),
- unmodifiableList(new ArrayList<>(videoProfiles))
- );
- }
+ int CODEC_PROFILE_NONE = -1;
/** @see EncoderProfiles#getDefaultDurationSeconds() */
- public abstract int getDefaultDurationSeconds();
+ int getDefaultDurationSeconds();
/** @see EncoderProfiles#getRecommendedFileFormat() */
- public abstract int getRecommendedFileFormat();
+ int getRecommendedFileFormat();
/** @see EncoderProfiles#getAudioProfiles() */
- @SuppressWarnings("AutoValueImmutableFields")
@NonNull
- public abstract List<AudioProfileProxy> getAudioProfiles();
+ List<AudioProfileProxy> getAudioProfiles();
/** @see EncoderProfiles#getVideoProfiles() */
- @SuppressWarnings("AutoValueImmutableFields")
@NonNull
- public abstract List<VideoProfileProxy> getVideoProfiles();
+ List<VideoProfileProxy> getVideoProfiles();
/**
* VideoProfileProxy defines the get methods that is mapping to the fields of
* {@link EncoderProfiles.VideoProfile}.
*/
@AutoValue
- public abstract static class VideoProfileProxy {
+ abstract class VideoProfileProxy {
/** Constant representing no media type. */
public static final String MEDIA_TYPE_NONE = "video/none";
@@ -174,7 +156,7 @@
* {@link EncoderProfiles.AudioProfile}.
*/
@AutoValue
- public abstract static class AudioProfileProxy {
+ abstract class AudioProfileProxy {
/** Constant representing no media type. */
public static final String MEDIA_TYPE_NONE = "audio/none";
@@ -224,4 +206,26 @@
/** @see EncoderProfiles.AudioProfile#getProfile() */
public abstract int getProfile();
}
+
+ /**
+ * An implementation of {@link EncoderProfilesProxy} that is immutable.
+ */
+ @AutoValue
+ abstract class ImmutableEncoderProfilesProxy implements EncoderProfilesProxy {
+
+ /** Creates an EncoderProfilesProxy instance. */
+ @NonNull
+ public static ImmutableEncoderProfilesProxy create(
+ int defaultDurationSeconds,
+ int recommendedFileFormat,
+ @NonNull List<AudioProfileProxy> audioProfiles,
+ @NonNull List<VideoProfileProxy> videoProfiles) {
+ return new AutoValue_EncoderProfilesProxy_ImmutableEncoderProfilesProxy(
+ defaultDurationSeconds,
+ recommendedFileFormat,
+ unmodifiableList(new ArrayList<>(audioProfiles)),
+ unmodifiableList(new ArrayList<>(videoProfiles))
+ );
+ }
+ }
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesResolutionValidator.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesResolutionValidator.java
index 0d84cd4..1c54463 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesResolutionValidator.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesResolutionValidator.java
@@ -22,6 +22,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy;
import androidx.camera.core.impl.quirk.ProfileResolutionQuirk;
@@ -114,7 +115,7 @@
}
}
- return validVideoProfiles.isEmpty() ? null : EncoderProfilesProxy.create(
+ return validVideoProfiles.isEmpty() ? null : ImmutableEncoderProfilesProxy.create(
profiles.getDefaultDurationSeconds(),
profiles.getRecommendedFileFormat(),
profiles.getAudioProfiles(),
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi31Impl.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi31Impl.java
index 6e63f4b..57a5476 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi31Impl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi31Impl.java
@@ -24,6 +24,7 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.impl.EncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.AudioProfileProxy;
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy;
import java.util.ArrayList;
@@ -36,7 +37,7 @@
@NonNull
public static EncoderProfilesProxy from(
@NonNull EncoderProfiles encoderProfiles) {
- return EncoderProfilesProxy.create(
+ return ImmutableEncoderProfilesProxy.create(
encoderProfiles.getDefaultDurationSeconds(),
encoderProfiles.getRecommendedFileFormat(),
fromAudioProfiles(encoderProfiles.getAudioProfiles()),
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi33Impl.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi33Impl.java
index 9dd5e2d..e69305b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi33Impl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatApi33Impl.java
@@ -24,6 +24,7 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.impl.EncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.AudioProfileProxy;
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy;
import java.util.ArrayList;
@@ -36,7 +37,7 @@
@NonNull
public static EncoderProfilesProxy from(
@NonNull EncoderProfiles encoderProfiles) {
- return EncoderProfilesProxy.create(
+ return ImmutableEncoderProfilesProxy.create(
encoderProfiles.getDefaultDurationSeconds(),
encoderProfiles.getRecommendedFileFormat(),
fromAudioProfiles(encoderProfiles.getAudioProfiles()),
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatBaseImpl.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatBaseImpl.java
index cacc8d6..7e4b830 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatBaseImpl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatBaseImpl.java
@@ -42,6 +42,7 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.impl.EncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.AudioProfileProxy;
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy;
import java.util.ArrayList;
@@ -54,7 +55,7 @@
@NonNull
public static EncoderProfilesProxy from(
@NonNull CamcorderProfile camcorderProfile) {
- return EncoderProfilesProxy.create(
+ return ImmutableEncoderProfilesProxy.create(
camcorderProfile.duration,
camcorderProfile.fileFormat,
toAudioProfiles(camcorderProfile),
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java
index f3d56b2..4c84b93 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java
@@ -25,6 +25,7 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.impl.EncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.AudioProfileProxy;
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy;
import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy;
import java.util.Collections;
@@ -175,7 +176,7 @@
DEFAULT_AUDIO_PROFILE
);
- return EncoderProfilesProxy.create(
+ return ImmutableEncoderProfilesProxy.create(
DEFAULT_DURATION,
DEFAULT_OUTPUT_FORMAT,
Collections.singletonList(audioProfile),
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/VideoValidatedEncoderProfilesProxy.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/VideoValidatedEncoderProfilesProxy.java
new file mode 100644
index 0000000..025c0c2
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/VideoValidatedEncoderProfilesProxy.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal;
+
+import static java.util.Collections.unmodifiableList;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.EncoderProfilesProxy;
+import androidx.core.util.Preconditions;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * VideoValidatedEncoderProfilesProxy is an implementation of {@link EncoderProfilesProxy} that
+ * guarantees to provide video information.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@AutoValue
+public abstract class VideoValidatedEncoderProfilesProxy implements EncoderProfilesProxy {
+
+ /** Creates a VideoValidatedEncoderProfilesProxy instance from {@link EncoderProfilesProxy}. */
+ @NonNull
+ public static VideoValidatedEncoderProfilesProxy from(@NonNull EncoderProfilesProxy profiles) {
+ return create(
+ profiles.getDefaultDurationSeconds(),
+ profiles.getRecommendedFileFormat(),
+ profiles.getAudioProfiles(),
+ profiles.getVideoProfiles()
+ );
+ }
+
+ /** Creates a VideoValidatedEncoderProfilesProxy instance. */
+ @NonNull
+ public static VideoValidatedEncoderProfilesProxy create(
+ int defaultDurationSeconds,
+ int recommendedFileFormat,
+ @NonNull List<AudioProfileProxy> audioProfiles,
+ @NonNull List<VideoProfileProxy> videoProfiles) {
+ Preconditions.checkArgument(!videoProfiles.isEmpty(),
+ "Should contain at least one VideoProfile.");
+ VideoProfileProxy defaultVideoProfile = videoProfiles.get(0);
+
+ AudioProfileProxy defaultAudioProfile = null;
+ if (!audioProfiles.isEmpty()) {
+ defaultAudioProfile = audioProfiles.get(0);
+ }
+
+ return new AutoValue_VideoValidatedEncoderProfilesProxy(
+ defaultDurationSeconds,
+ recommendedFileFormat,
+ unmodifiableList(new ArrayList<>(audioProfiles)),
+ unmodifiableList(new ArrayList<>(videoProfiles)),
+ defaultAudioProfile,
+ defaultVideoProfile
+ );
+ }
+
+ /** Returns the default {@link AudioProfileProxy} or null if not existed. */
+ @Nullable
+ public abstract AudioProfileProxy getDefaultAudioProfile();
+
+ /** Returns the default {@link VideoProfileProxy}. */
+ @NonNull
+ public abstract VideoProfileProxy getDefaultVideoProfile();
+}
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/internal/VideoValidatedEncoderProfilesProxyTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/internal/VideoValidatedEncoderProfilesProxyTest.kt
new file mode 100644
index 0000000..b853ba7
--- /dev/null
+++ b/camera/camera-video/src/test/java/androidx/camera/video/internal/VideoValidatedEncoderProfilesProxyTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal
+
+import android.os.Build
+import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy
+import androidx.camera.testing.EncoderProfilesUtil
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+private const val DEFAULT_WIDTH = 1920
+private const val DEFAULT_HEIGHT = 1080
+private val DEFAULT_VIDEO_PROFILE = VideoProfileProxy.create(
+ EncoderProfilesUtil.DEFAULT_VIDEO_CODEC,
+ EncoderProfilesUtil.DEFAULT_VIDEO_MEDIA_TYPE,
+ EncoderProfilesUtil.DEFAULT_VIDEO_BITRATE,
+ EncoderProfilesUtil.DEFAULT_VIDEO_FRAME_RATE,
+ DEFAULT_WIDTH,
+ DEFAULT_HEIGHT,
+ EncoderProfilesUtil.DEFAULT_VIDEO_PROFILE,
+ EncoderProfilesUtil.DEFAULT_VIDEO_BIT_DEPTH,
+ EncoderProfilesUtil.DEFAULT_VIDEO_CHROMA_SUBSAMPLING,
+ EncoderProfilesUtil.DEFAULT_VIDEO_HDR_FORMAT
+)
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class VideoValidatedEncoderProfilesProxyTest {
+
+ @Test
+ fun createFromEncoderProfilesProxy() {
+ val profiles = EncoderProfilesUtil.PROFILES_1080P
+ val validatedProfiles = VideoValidatedEncoderProfilesProxy.from(profiles)
+
+ assertThat(validatedProfiles.recommendedFileFormat)
+ .isEqualTo(profiles.recommendedFileFormat)
+ assertThat(validatedProfiles.defaultDurationSeconds)
+ .isEqualTo(profiles.defaultDurationSeconds)
+ assertThat(validatedProfiles.audioProfiles.size).isEqualTo(profiles.audioProfiles.size)
+ assertThat(validatedProfiles.videoProfiles.size).isEqualTo(profiles.videoProfiles.size)
+ assertThat(validatedProfiles.defaultAudioProfile).isNotNull()
+ assertThat(validatedProfiles.defaultVideoProfile).isNotNull()
+ assertThat(validatedProfiles.audioProfiles[0].codec)
+ .isEqualTo(profiles.audioProfiles[0].codec)
+ assertThat(validatedProfiles.audioProfiles[0].mediaType)
+ .isEqualTo(profiles.audioProfiles[0].mediaType)
+ assertThat(validatedProfiles.audioProfiles[0].bitrate)
+ .isEqualTo(profiles.audioProfiles[0].bitrate)
+ assertThat(validatedProfiles.audioProfiles[0].sampleRate)
+ .isEqualTo(profiles.audioProfiles[0].sampleRate)
+ assertThat(validatedProfiles.audioProfiles[0].channels)
+ .isEqualTo(profiles.audioProfiles[0].channels)
+ assertThat(validatedProfiles.audioProfiles[0].profile)
+ .isEqualTo(profiles.audioProfiles[0].profile)
+ assertThat(validatedProfiles.videoProfiles[0].codec)
+ .isEqualTo(profiles.videoProfiles[0].codec)
+ assertThat(validatedProfiles.videoProfiles[0].mediaType)
+ .isEqualTo(profiles.videoProfiles[0].mediaType)
+ assertThat(validatedProfiles.videoProfiles[0].bitrate)
+ .isEqualTo(profiles.videoProfiles[0].bitrate)
+ assertThat(validatedProfiles.videoProfiles[0].frameRate)
+ .isEqualTo(profiles.videoProfiles[0].frameRate)
+ assertThat(validatedProfiles.videoProfiles[0].width)
+ .isEqualTo(profiles.videoProfiles[0].width)
+ assertThat(validatedProfiles.videoProfiles[0].height)
+ .isEqualTo(profiles.videoProfiles[0].height)
+ assertThat(validatedProfiles.videoProfiles[0].profile)
+ .isEqualTo(profiles.videoProfiles[0].profile)
+ assertThat(validatedProfiles.videoProfiles[0].bitDepth)
+ .isEqualTo(profiles.videoProfiles[0].bitDepth)
+ assertThat(validatedProfiles.videoProfiles[0].chromaSubsampling)
+ .isEqualTo(profiles.videoProfiles[0].chromaSubsampling)
+ assertThat(validatedProfiles.videoProfiles[0].hdrFormat)
+ .isEqualTo(profiles.videoProfiles[0].hdrFormat)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun create_throwsException_whenVideoProfilesIsEmpty() {
+ VideoValidatedEncoderProfilesProxy.create(
+ EncoderProfilesUtil.DEFAULT_DURATION,
+ EncoderProfilesUtil.DEFAULT_OUTPUT_FORMAT,
+ emptyList(),
+ emptyList()
+ )
+ }
+
+ @Test
+ fun create_withEmptyAudioProfiles() {
+ val validatedProfiles = VideoValidatedEncoderProfilesProxy.create(
+ EncoderProfilesUtil.DEFAULT_DURATION,
+ EncoderProfilesUtil.DEFAULT_OUTPUT_FORMAT,
+ emptyList(),
+ listOf(DEFAULT_VIDEO_PROFILE)
+ )
+ assertThat(validatedProfiles.defaultAudioProfile).isNull()
+ }
+}
\ No newline at end of file