| /* |
| * Copyright (C) 2021 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 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; |
| |
| /** |
| * Describes a set of encoding profiles for a given media (audio and/or video) profile. |
| * These settings are read-only. |
| * |
| * <p>Currently, this is used to describe camera recording profile with more detail than {@link |
| * CamcorderProfile}, by providing encoding parameters for more than just the default audio |
| * and/or video codec. |
| * |
| * <p>The compressed output from a camera recording session contains two tracks: |
| * one for audio and one for video. |
| * <p>In the future audio-only recording profiles may be defined. |
| * |
| * <p>Each media profile specifies a set of audio and a set of video specific settings. |
| * <ul> |
| * <li> The file output format |
| * <li> Default file duration |
| * <p>Video-specific settings are: |
| * <li> Video codec format |
| * <li> Video bit rate in bits per second |
| * <li> Video frame rate in frames per second |
| * <li> Video frame width and height, |
| * <li> Video encoder profile. |
| * <p>Audio-specific settings are: |
| * <li> Audio codec format |
| * <li> Audio bit rate in bits per second, |
| * <li> Audio sample rate |
| * <li> Number of audio channels for recording. |
| * </ul> |
| */ |
| public final class EncoderProfiles |
| { |
| /** |
| * Default recording duration in seconds before the session is terminated. |
| * This is useful for applications like MMS that have a limited file size requirement. |
| * This could be 0 if there is no default recording duration. |
| */ |
| public int getDefaultDurationSeconds() { |
| return durationSecs; |
| } |
| |
| /** |
| * Recommended output file format |
| * @see android.media.MediaRecorder.OutputFormat |
| */ |
| public @MediaRecorder.OutputFormatValues int getRecommendedFileFormat() { |
| return fileFormat; |
| } |
| |
| /** |
| * Configuration for a video encoder. |
| */ |
| public final static class VideoProfile { |
| /** |
| * The video encoder being used for the video track |
| * @see android.media.MediaRecorder.VideoEncoder |
| */ |
| public @MediaRecorder.VideoEncoderValues int getCodec() { |
| return codec; |
| } |
| |
| /** |
| * The media type of the video encoder being used for the video track |
| * @see android.media.MediaFormat#KEY_MIME |
| */ |
| public @NonNull String getMediaType() { |
| if (codec == MediaRecorder.VideoEncoder.H263) { |
| return MediaFormat.MIMETYPE_VIDEO_H263; |
| } else if (codec == MediaRecorder.VideoEncoder.H264) { |
| return MediaFormat.MIMETYPE_VIDEO_AVC; |
| } else if (codec == MediaRecorder.VideoEncoder.MPEG_4_SP) { |
| return MediaFormat.MIMETYPE_VIDEO_MPEG4; |
| } else if (codec == MediaRecorder.VideoEncoder.VP8) { |
| 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"); |
| } |
| |
| /** |
| * The target video output bitrate in bits per second |
| * <p> |
| * This is the target recorded video output bitrate if the application configures the video |
| * recording via {@link MediaRecorder#setProfile} without specifying any other |
| * {@link MediaRecorder} encoding parameters. For example, for high speed quality profiles |
| * (from {@link CamcorderProfile#QUALITY_HIGH_SPEED_LOW} to {@link |
| * CamcorderProfile#QUALITY_HIGH_SPEED_2160P}), this is the bitrate where the video is |
| * recorded with. If the application intends to record slow motion videos with the high |
| * speed quality profiles, it must set a different video bitrate that is corresponding to |
| * the desired recording output bit rate (i.e., the encoded video bitrate during normal |
| * playback) via {@link MediaRecorder#setVideoEncodingBitRate}. For example, if {@link |
| * CamcorderProfile#QUALITY_HIGH_SPEED_720P} advertises 240fps {@link #getFrameRate} and |
| * 64Mbps {@link #getBitrate} in the high speed VideoProfile, and the application |
| * intends to record 1/8 factor slow motion recording videos, the application must set 30fps |
| * via {@link MediaRecorder#setVideoFrameRate} and 8Mbps ( {@link #getBitrate} * slow motion |
| * factor) via {@link MediaRecorder#setVideoEncodingBitRate}. Failing to do so will result |
| * in videos with unexpected frame rate and bit rate, or {@link MediaRecorder} error if the |
| * output bit rate exceeds the encoder limit. If the application intends to do the video |
| * recording with {@link MediaCodec} encoder, it must set each individual field of {@link |
| * MediaFormat} similarly according to this VideoProfile. |
| * </p> |
| * |
| * @see #getFrameRate |
| * @see MediaRecorder |
| * @see MediaCodec |
| * @see MediaFormat |
| */ |
| public int getBitrate() { |
| return bitrate; |
| } |
| |
| /** |
| * The target video frame rate in frames per second. |
| * <p> |
| * This is the target recorded video output frame rate per second if the application |
| * configures the video recording via {@link MediaRecorder#setProfile} without specifying |
| * any other {@link MediaRecorder} encoding parameters. For example, for high speed quality |
| * profiles (from {@link CamcorderProfile#QUALITY_HIGH_SPEED_LOW} to {@link |
| * CamcorderProfile#QUALITY_HIGH_SPEED_2160P}), this is the frame rate where the video is |
| * recorded and played back with. If the application intends to create slow motion use case |
| * with the high speed quality profiles, it must set a different video frame rate that is |
| * corresponding to the desired output (playback) frame rate via {@link |
| * MediaRecorder#setVideoFrameRate}. For example, if {@link |
| * CamcorderProfile#QUALITY_HIGH_SPEED_720P} advertises 240fps {@link #getFrameRate} |
| * in the VideoProfile, and the application intends to create 1/8 factor slow motion |
| * recording videos, the application must set 30fps via {@link |
| * MediaRecorder#setVideoFrameRate}. Failing to do so will result in high speed videos with |
| * normal speed playback frame rate (240fps for above example). If the application intends |
| * to do the video recording with {@link MediaCodec} encoder, it must set each individual |
| * field of {@link MediaFormat} similarly according to this VideoProfile. |
| * </p> |
| * |
| * @see #getBitrate |
| * @see MediaRecorder |
| * @see MediaCodec |
| * @see MediaFormat |
| */ |
| public int getFrameRate() { |
| return frameRate; |
| } |
| |
| /** |
| * The target video frame width in pixels |
| */ |
| public int getWidth() { |
| return width; |
| } |
| |
| /** |
| * The target video frame height in pixels |
| */ |
| public int getHeight() { |
| return height; |
| } |
| |
| /** |
| * The video encoder profile being used for the video track. |
| * <p> |
| * This value is negative if there is no profile defined for the video codec. |
| * |
| * @see MediaRecorder#setVideoEncodingProfileLevel |
| * @see MediaFormat#KEY_PROFILE |
| */ |
| public int getProfile() { |
| 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 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; |
| private int width; |
| private int height; |
| 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; |
| } |
| |
| /** |
| * Returns the defined audio encoder profiles. |
| * <p> |
| * The list may be empty. This means there are no audio encoder |
| * profiles defined. Otherwise, the first profile is the default |
| * audio profile. |
| */ |
| public @NonNull List<AudioProfile> getAudioProfiles() { |
| return audioProfiles; |
| } |
| |
| /** |
| * Returns the defined video encoder profiles. |
| * <p> |
| * The list may be empty. This means there are no video encoder |
| * profiles defined. Otherwise, the first profile is the default |
| * video profile. |
| */ |
| public @NonNull List<VideoProfile> getVideoProfiles() { |
| return videoProfiles; |
| } |
| |
| /** |
| * Configuration for an audio encoder. |
| */ |
| public final static class AudioProfile { |
| /** |
| * The audio encoder being used for the audio track. |
| * @see android.media.MediaRecorder.AudioEncoder |
| */ |
| public @MediaRecorder.AudioEncoderValues int getCodec() { |
| return codec; |
| } |
| |
| /** |
| * The media type of the audio encoder being used for the video track |
| * @see android.media.MediaFormat#KEY_MIME |
| */ |
| public @NonNull String getMediaType() { |
| if (codec == MediaRecorder.AudioEncoder.AMR_NB) { |
| return MediaFormat.MIMETYPE_AUDIO_AMR_NB; |
| } else if (codec == MediaRecorder.AudioEncoder.AMR_WB) { |
| return MediaFormat.MIMETYPE_AUDIO_AMR_WB; |
| } else if (codec == MediaRecorder.AudioEncoder.AAC |
| || codec == MediaRecorder.AudioEncoder.HE_AAC |
| || codec == MediaRecorder.AudioEncoder.AAC_ELD) { |
| return MediaFormat.MIMETYPE_AUDIO_AAC; |
| } else if (codec == MediaRecorder.AudioEncoder.VORBIS) { |
| return MediaFormat.MIMETYPE_AUDIO_VORBIS; |
| } else if (codec == MediaRecorder.AudioEncoder.OPUS) { |
| return MediaFormat.MIMETYPE_AUDIO_OPUS; |
| } |
| // we should never be here |
| throw new RuntimeException("Unknown codec"); |
| } |
| |
| /** |
| * The target audio output bitrate in bits per second |
| */ |
| public int getBitrate() { |
| return bitrate; |
| } |
| |
| /** |
| * The audio sampling rate used for the audio track |
| */ |
| public int getSampleRate() { |
| return sampleRate; |
| } |
| |
| /** |
| * The number of audio channels used for the audio track |
| */ |
| public int getChannels() { |
| return channels; |
| } |
| |
| /** |
| * The audio encoder profile being used for the audio track |
| * <p> |
| * This value is negative if there is no profile defined for the audio codec. |
| * @see MediaFormat#KEY_PROFILE |
| */ |
| public int getProfile() { |
| if (codec == MediaRecorder.AudioEncoder.AAC) { |
| return MediaCodecInfo.CodecProfileLevel.AACObjectMain; |
| } else if (codec == MediaRecorder.AudioEncoder.HE_AAC) { |
| return MediaCodecInfo.CodecProfileLevel.AACObjectHE; |
| } else if (codec == MediaRecorder.AudioEncoder.AAC_ELD) { |
| return MediaCodecInfo.CodecProfileLevel.AACObjectELD; |
| } |
| return profile; |
| } |
| |
| |
| // Constructor called by JNI and CamcorderProfile |
| /* package private */ AudioProfile( |
| int codec, |
| int channels, |
| int sampleRate, |
| int bitrate, |
| int profile) { |
| this.codec = codec; |
| this.channels = channels; |
| this.sampleRate = sampleRate; |
| this.bitrate = bitrate; |
| this.profile = profile; |
| } |
| |
| private int codec; |
| private int channels; |
| private int sampleRate; |
| private int bitrate; |
| private int profile; // this contains the profile if codec itself does not |
| } |
| |
| private int durationSecs; |
| private int fileFormat; |
| // non-modifiable lists |
| private @NonNull List<AudioProfile> audioProfiles; |
| private @NonNull List<VideoProfile> videoProfiles; |
| |
| // Constructor called by JNI and CamcorderProfile |
| /* package private */ EncoderProfiles( |
| int duration, |
| int fileFormat, |
| VideoProfile[] videoProfiles, |
| AudioProfile[] audioProfiles) { |
| this.durationSecs = duration; |
| this.fileFormat = fileFormat; |
| this.videoProfiles = Collections.unmodifiableList(Arrays.asList(videoProfiles)); |
| this.audioProfiles = Collections.unmodifiableList(Arrays.asList(audioProfiles)); |
| } |
| } |