media: added advanced HDR-ready CamcorderProfiles

Added support for CamcorderProfile.getAll() method to retrieve advanced
camcorder profiles. These are profiles that do not capture in 8-bit
YUV 4:2:0 SDR mode.

To ensure app compatibility, whether to return advanced profiles is
gated by target SDK version.

Exteded EncoderProfiles.VideoProfile to surface these additional
parameters: chroma subsampling, HDR format and bit-depth.

(Also added some missing MediaRecorder video codec enumerations
that may be used for HDR capture.)

Bug: 197651595
Test: atest android.media.cts.CamcorderProfileTest
Change-Id: I743e510d425df5bc1ea017710aeff277b8d1c31d
This commit is contained in:
Lajos Molnar
2022-01-10 15:44:48 -08:00
parent eb59ea4c11
commit ed4b94728b
5 changed files with 204 additions and 13 deletions

View File

@@ -20559,13 +20559,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 {
@@ -22669,12 +22680,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 {

View File

@@ -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.
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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.
* <p>
* <b>Note:</b> as of {@link android.os.Build.VERSION_CODES#TIRAMISU Android TIRAMISU},
* this method can return advanced encoder profiles.
* <p>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);
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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).
* <p>
* An HDR format specifying SDR (Standard Dynamic
* Range) recording. */
public static final int HDR_NONE = 0;
/** HLG (Hybrid-Log Gamma).
* <p>
* An HDR format specifying HLG. */
public static final int HDR_HLG = 1;
/** HDR10.
* <p>
* An HDR format specifying HDR10. */
public static final int HDR_HDR10 = 2;
/** HDR10+.
* <p>
* An HDR format specifying HDR10+. */
public static final int HDR_HDR10PLUS = 3;
/**
* Dolby Vision
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* A chroma subsampling where the U and V planes are not
* subsampled. */
public static final int YUV_444 = 2;
}
/**

View File

@@ -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 {}

View File

@@ -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, "<init>", "(IIIIII)V");
env->GetMethodID(videoProfileClazz, "<init>", "(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<bool>(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<int>(cs),
bitDepth,
static_cast<int>(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},