diff --git a/core/api/current.txt b/core/api/current.txt index 25bf224eab4a1..e1d05be57847a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -20571,13 +20571,24 @@ package android.media { } public static final class EncoderProfiles.VideoProfile { + method public int getBitDepth(); method public int getBitrate(); + method public int getChromaSubsampling(); method public int getCodec(); method public int getFrameRate(); + method public int getHdrFormat(); method public int getHeight(); method @NonNull public String getMediaType(); method public int getProfile(); method public int getWidth(); + field public static final int HDR_DOLBY_VISION = 4; // 0x4 + field public static final int HDR_HDR10 = 2; // 0x2 + field public static final int HDR_HDR10PLUS = 3; // 0x3 + field public static final int HDR_HLG = 1; // 0x1 + field public static final int HDR_NONE = 0; // 0x0 + field public static final int YUV_420 = 0; // 0x0 + field public static final int YUV_422 = 1; // 0x1 + field public static final int YUV_444 = 2; // 0x2 } public class ExifInterface { @@ -22686,12 +22697,15 @@ package android.media { } public final class MediaRecorder.VideoEncoder { + field public static final int AV1 = 8; // 0x8 field public static final int DEFAULT = 0; // 0x0 + field public static final int DOLBY_VISION = 7; // 0x7 field public static final int H263 = 1; // 0x1 field public static final int H264 = 2; // 0x2 field public static final int HEVC = 5; // 0x5 field public static final int MPEG_4_SP = 3; // 0x3 field public static final int VP8 = 4; // 0x4 + field public static final int VP9 = 6; // 0x6 } public final class MediaRecorder.VideoSource { diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index b4fdcb9ba2cf0..3dbb8e0986f0c 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -19,6 +19,9 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.Camera; import android.hardware.Camera.CameraInfo; @@ -526,9 +529,17 @@ public class CamcorderProfile } /** - * Returns all encoder profiles of a camcorder profile for the given camera at - * the given quality level. - * + * This change id controls the kind of video profiles returned by {@link #getAll}. + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long RETURN_ADVANCED_VIDEO_PROFILES = 206033068L; // buganizer id + + /** + * Returns all basic encoder profiles of a camcorder profile for + * the given camera at the given quality level. + *

* Quality levels QUALITY_LOW, QUALITY_HIGH are guaranteed to be supported, while * other levels may or may not be supported. The supported levels can be checked using * {@link #hasProfile(int, int)}. @@ -537,19 +548,26 @@ public class CamcorderProfile * QUALITY_LOW/QUALITY_HIGH have to match one of qcif, cif, 480p, 720p, 1080p or 2160p. * E.g. if the device supports 480p, 720p, 1080p and 2160p, then low is 480p and high is * 2160p. - * + *

* The same is true for time lapse quality levels, i.e. QUALITY_TIME_LAPSE_LOW, * QUALITY_TIME_LAPSE_HIGH are guaranteed to be supported and have to match one of * qcif, cif, 480p, 720p, 1080p, or 2160p. - * + *

* For high speed quality levels, they may or may not be supported. If a subset of the levels * are supported, QUALITY_HIGH_SPEED_LOW and QUALITY_HIGH_SPEED_HIGH are guaranteed to be * supported and have to match one of 480p, 720p, or 1080p. - * + *

* A camcorder recording session with higher quality level usually has higher output * bit rate, better video and/or audio recording quality, larger video frame * resolution and higher audio sampling rate, etc, than those with lower quality * level. + *

+ * Note: as of {@link android.os.Build.VERSION_CODES#TIRAMISU Android TIRAMISU}, + * this method can return advanced encoder profiles. + *

Apps targeting {@link Build.VERSION_CODES#S_V2} or before will only receive basic + * video encoder profiles that use output YUV 4:2:0 8-bit content. + * Apps targeting {@link Build.VERSION_CODES#TIRAMISU} or above will also receive advanced + * video encoder profiles that may output 10-bit, YUV 4:2:2/4:4:4 or HDR content. * * @param cameraId the id for the camera. Numeric camera ids from the list received by invoking * {@link CameraManager#getCameraIdList} can be used as long as they are @@ -602,7 +620,9 @@ public class CamcorderProfile } catch (NumberFormatException e) { return null; } - return native_get_camcorder_profiles(id, quality); + return native_get_camcorder_profiles( + id, quality, + CompatChanges.isChangeEnabled(RETURN_ADVANCED_VIDEO_PROFILES)); } /** @@ -712,7 +732,7 @@ public class CamcorderProfile private static native final CamcorderProfile native_get_camcorder_profile( int cameraId, int quality); private static native final EncoderProfiles native_get_camcorder_profiles( - int cameraId, int quality); + int cameraId, int quality, boolean advanced); private static native final boolean native_has_camcorder_profile( int cameraId, int quality); } diff --git a/media/java/android/media/EncoderProfiles.java b/media/java/android/media/EncoderProfiles.java index ac8c65e69e725..3e26fc6fb9697 100644 --- a/media/java/android/media/EncoderProfiles.java +++ b/media/java/android/media/EncoderProfiles.java @@ -16,8 +16,11 @@ package android.media; +import android.annotation.IntDef; import android.annotation.NonNull; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -97,6 +100,12 @@ public final class EncoderProfiles return MediaFormat.MIMETYPE_VIDEO_VP8; } else if (codec == MediaRecorder.VideoEncoder.HEVC) { return MediaFormat.MIMETYPE_VIDEO_HEVC; + } else if (codec == MediaRecorder.VideoEncoder.VP9) { + return MediaFormat.MIMETYPE_VIDEO_VP9; + } else if (codec == MediaRecorder.VideoEncoder.DOLBY_VISION) { + return MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION; + } else if (codec == MediaRecorder.VideoEncoder.AV1) { + return MediaFormat.MIMETYPE_VIDEO_AV1; } // we should never be here throw new RuntimeException("Unknown codec"); @@ -190,19 +199,73 @@ public final class EncoderProfiles return profile; } + /** + * The bit depth of the encoded video. + *

+ * This value is effectively 8 or 10, but some devices may + * support additional values. + */ + public int getBitDepth() { + return bitDepth; + } + + /** + * The chroma subsampling of the encoded video. + *

+ * For most devices this is always YUV_420 but some devices may + * support additional values. + * + * @see #YUV_420 + * @see #YUV_422 + * @see #YUV_444 + */ + public @ChromaSubsampling int getChromaSubsampling() { + return chromaSubsampling; + } + + /** + * The HDR format of the encoded video. + *

+ * This is one of the HDR_ values. + * @see #HDR_NONE + * @see #HDR_HLG + * @see #HDR_HDR10 + * @see #HDR_HDR10PLUS + * @see #HDR_DOLBY_VISION + */ + public @HdrFormat int getHdrFormat() { + return hdrFormat; + } + // Constructor called by JNI and CamcorderProfile /* package private */ VideoProfile(int codec, int width, int height, int frameRate, int bitrate, - int profile) { + int profile, + int chromaSubsampling, + int bitDepth, + int hdrFormat) { this.codec = codec; this.width = width; this.height = height; this.frameRate = frameRate; this.bitrate = bitrate; this.profile = profile; + this.chromaSubsampling = chromaSubsampling; + this.bitDepth = bitDepth; + this.hdrFormat = hdrFormat; + } + + /* package private */ VideoProfile(int codec, + int width, + int height, + int frameRate, + int bitrate, + int profile) { + this(codec, width, height, frameRate, bitrate, profile, + YUV_420, 8 /* bitDepth */, HDR_NONE); } private int codec; @@ -211,6 +274,81 @@ public final class EncoderProfiles private int frameRate; private int bitrate; private int profile; + private int chromaSubsampling; + private int bitDepth; + private int hdrFormat; + + /** @hide */ + @IntDef({ + HDR_NONE, + HDR_HLG, + HDR_HDR10, + HDR_HDR10PLUS, + HDR_DOLBY_VISION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface HdrFormat {} + + /** Not HDR (SDR). + *

+ * An HDR format specifying SDR (Standard Dynamic + * Range) recording. */ + public static final int HDR_NONE = 0; + + /** HLG (Hybrid-Log Gamma). + *

+ * An HDR format specifying HLG. */ + public static final int HDR_HLG = 1; + + /** HDR10. + *

+ * An HDR format specifying HDR10. */ + public static final int HDR_HDR10 = 2; + + /** HDR10+. + *

+ * An HDR format specifying HDR10+. */ + public static final int HDR_HDR10PLUS = 3; + + /** + * Dolby Vision + *

+ * An HDR format specifying Dolby Vision. For this format + * the codec is always a Dolby Vision encoder. The encoder + * profile specifies which Dolby Vision version is being + * used. + * + * @see #getProfile + */ + public static final int HDR_DOLBY_VISION = 4; + + /** @hide */ + @IntDef({ + YUV_420, + YUV_422, + YUV_444, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ChromaSubsampling {} + + + /** YUV 4:2:0. + *

+ * A chroma subsampling where the U and V planes are subsampled + * by 2 both horizontally and vertically. */ + public static final int YUV_420 = 0; + + /** YUV 4:2:2. + *

+ * A chroma subsampling where the U and V planes are subsampled + * by 2 horizontally alone. */ + public static final int YUV_422 = 1; + + /** YUV 4:4:4. + *

+ * A chroma subsampling where the U and V planes are not + * subsampled. */ + public static final int YUV_444 = 2; } /** diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index d7857a01f7eaf..f1e60387340e6 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -705,6 +705,9 @@ public class MediaRecorder implements AudioRouting, public static final int MPEG_4_SP = 3; public static final int VP8 = 4; public static final int HEVC = 5; + public static final int VP9 = 6; + public static final int DOLBY_VISION = 7; + public static final int AV1 = 8; } /** @@ -717,6 +720,9 @@ public class MediaRecorder implements AudioRouting, VideoEncoder.MPEG_4_SP, VideoEncoder.VP8, VideoEncoder.HEVC, + VideoEncoder.VP9, + VideoEncoder.DOLBY_VISION, + VideoEncoder.AV1, }) @Retention(RetentionPolicy.SOURCE) public @interface VideoEncoderValues {} diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp index 90325e74b8abc..f2c48a04935fd 100644 --- a/media/jni/android_media_MediaProfiles.cpp +++ b/media/jni/android_media_MediaProfiles.cpp @@ -225,7 +225,7 @@ android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject /* static jobject android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject /* thiz */, jint id, - jint quality) + jint quality, jboolean advanced) { ALOGV("native_get_camcorder_profiles: %d %d", id, quality); if (!isCamcorderQualityKnown(quality)) { @@ -251,7 +251,7 @@ android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject / jclass videoProfileClazz = env->FindClass("android/media/EncoderProfiles$VideoProfile"); jmethodID videoProfileConstructorMethodID = - env->GetMethodID(videoProfileClazz, "", "(IIIIII)V"); + env->GetMethodID(videoProfileClazz, "", "(IIIIIIIII)V"); jclass audioProfileClazz = env->FindClass("android/media/EncoderProfiles$AudioProfile"); jmethodID audioProfileConstructorMethodID = @@ -262,6 +262,16 @@ android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject / { int i = 0; for (const MediaProfiles::VideoCodec *vc : cp->getVideoCodecs()) { + chroma_subsampling cs = vc->getChromaSubsampling(); + int bitDepth = vc->getBitDepth(); + hdr_format hdr = vc->getHdrFormat(); + + bool isAdvanced = + (bitDepth != 8 || cs != CHROMA_SUBSAMPLING_YUV_420 || hdr != HDR_FORMAT_NONE); + if (static_cast(advanced) && !isAdvanced) { + continue; + } + jobject videoCodec = env->NewObject(videoProfileClazz, videoProfileConstructorMethodID, vc->getCodec(), @@ -269,7 +279,10 @@ android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject / vc->getFrameHeight(), vc->getFrameRate(), vc->getBitrate(), - vc->getProfile()); + vc->getProfile(), + static_cast(cs), + bitDepth, + static_cast(hdr)); env->SetObjectArrayElement(videoCodecs, i++, videoCodec); } } @@ -400,7 +413,7 @@ static const JNINativeMethod gMethodsForCamcorderProfileClass[] = { {"native_init", "()V", (void *)android_media_MediaProfiles_native_init}, {"native_get_camcorder_profile", "(II)Landroid/media/CamcorderProfile;", (void *)android_media_MediaProfiles_native_get_camcorder_profile}, - {"native_get_camcorder_profiles", "(II)Landroid/media/EncoderProfiles;", + {"native_get_camcorder_profiles", "(IIZ)Landroid/media/EncoderProfiles;", (void *)android_media_MediaProfiles_native_get_camcorder_profiles}, {"native_has_camcorder_profile", "(II)Z", (void *)android_media_MediaProfiles_native_has_camcorder_profile},