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:
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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},
|
||||
|
||||
Reference in New Issue
Block a user