summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/MediaCodecInfo.java898
-rw-r--r--media/jni/Android.bp2
-rw-r--r--media/jni/android_media_CodecCapabilities.cpp117
-rw-r--r--media/jni/android_media_MediaPlayer.cpp6
4 files changed, 364 insertions, 659 deletions
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index dbcba57520e6..782db358bf9f 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1104,11 +1104,10 @@ public final class MediaCodecInfo {
// does not contain features and bitrate specific keys, keep only keys relevant for
// a level check.
Map<String, Object> levelCriticalFormatMap = new HashMap<>(map);
- final Set<String> criticalKeys = isVideo()
- ? VideoCapabilities.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS
- : isAudio()
- ? AudioCapabilities.AudioCapsLegacyImpl.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS
- : null;
+ final Set<String> criticalKeys =
+ isVideo() ? VideoCapabilities.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS :
+ isAudio() ? AudioCapabilities.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS :
+ null;
// critical keys will always contain KEY_MIME, but should also contain others to be
// meaningful
@@ -1411,597 +1410,434 @@ public final class MediaCodecInfo {
*/
public static final class AudioCapabilities {
private static final String TAG = "AudioCapabilities";
+ private CodecCapabilities mParent;
+ private Range<Integer> mBitrateRange;
- /* package private */ interface AudioCapsIntf {
- public Range<Integer> getBitrateRange();
-
- public int[] getSupportedSampleRates();
-
- public Range<Integer>[] getSupportedSampleRateRanges();
-
- public int getMaxInputChannelCount();
-
- public int getMinInputChannelCount();
-
- public Range<Integer>[] getInputChannelCountRanges();
-
- public boolean isSampleRateSupported(int sampleRate);
+ private int[] mSampleRates;
+ private Range<Integer>[] mSampleRateRanges;
+ private Range<Integer>[] mInputChannelRanges;
- public void getDefaultFormat(MediaFormat format);
+ private static final int MAX_INPUT_CHANNEL_COUNT = 30;
- public boolean supportsFormat(MediaFormat format);
+ /**
+ * Returns the range of supported bitrates in bits/second.
+ */
+ public Range<Integer> getBitrateRange() {
+ return mBitrateRange;
}
- /* package private */ static final class AudioCapsLegacyImpl implements AudioCapsIntf {
- private CodecCapabilities mParent;
- private Range<Integer> mBitrateRange;
-
- private int[] mSampleRates;
- private Range<Integer>[] mSampleRateRanges;
- private Range<Integer>[] mInputChannelRanges;
-
- private static final int MAX_INPUT_CHANNEL_COUNT = 30;
-
- public Range<Integer> getBitrateRange() {
- return mBitrateRange;
- }
-
- public int[] getSupportedSampleRates() {
- return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length)
- : null;
- }
+ /**
+ * Returns the array of supported sample rates if the codec
+ * supports only discrete values. Otherwise, it returns
+ * {@code null}. The array is sorted in ascending order.
+ */
+ public int[] getSupportedSampleRates() {
+ return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length) : null;
+ }
- public Range<Integer>[] getSupportedSampleRateRanges() {
- return Arrays.copyOf(mSampleRateRanges, mSampleRateRanges.length);
- }
+ /**
+ * Returns the array of supported sample rate ranges. The
+ * array is sorted in ascending order, and the ranges are
+ * distinct.
+ */
+ public Range<Integer>[] getSupportedSampleRateRanges() {
+ return Arrays.copyOf(mSampleRateRanges, mSampleRateRanges.length);
+ }
- public int getMaxInputChannelCount() {
- int overall_max = 0;
- for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
- int lmax = mInputChannelRanges[i].getUpper();
- if (lmax > overall_max) {
- overall_max = lmax;
- }
+ /**
+ * Returns the maximum number of input channels supported.
+ *
+ * Through {@link android.os.Build.VERSION_CODES#R}, this method indicated support
+ * for any number of input channels between 1 and this maximum value.
+ *
+ * As of {@link android.os.Build.VERSION_CODES#S},
+ * the implied lower limit of 1 channel is no longer valid.
+ * As of {@link android.os.Build.VERSION_CODES#S}, {@link #getMaxInputChannelCount} is
+ * superseded by {@link #getInputChannelCountRanges},
+ * which returns an array of ranges of channels.
+ * The {@link #getMaxInputChannelCount} method will return the highest value
+ * in the ranges returned by {@link #getInputChannelCountRanges}
+ *
+ */
+ @IntRange(from = 1, to = 255)
+ public int getMaxInputChannelCount() {
+ int overall_max = 0;
+ for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
+ int lmax = mInputChannelRanges[i].getUpper();
+ if (lmax > overall_max) {
+ overall_max = lmax;
}
- return overall_max;
}
+ return overall_max;
+ }
- public int getMinInputChannelCount() {
- int overall_min = MAX_INPUT_CHANNEL_COUNT;
- for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
- int lmin = mInputChannelRanges[i].getLower();
- if (lmin < overall_min) {
- overall_min = lmin;
- }
+ /**
+ * Returns the minimum number of input channels supported.
+ * This is often 1, but does vary for certain mime types.
+ *
+ * This returns the lowest channel count in the ranges returned by
+ * {@link #getInputChannelCountRanges}.
+ */
+ @IntRange(from = 1, to = 255)
+ public int getMinInputChannelCount() {
+ int overall_min = MAX_INPUT_CHANNEL_COUNT;
+ for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
+ int lmin = mInputChannelRanges[i].getLower();
+ if (lmin < overall_min) {
+ overall_min = lmin;
}
- return overall_min;
- }
-
- public Range<Integer>[] getInputChannelCountRanges() {
- return Arrays.copyOf(mInputChannelRanges, mInputChannelRanges.length);
}
+ return overall_min;
+ }
- /* no public constructor */
- private AudioCapsLegacyImpl() { }
-
- public static AudioCapsLegacyImpl create(
- MediaFormat info, CodecCapabilities parent) {
- if (GetFlag(() -> android.media.codec.Flags.nativeCapabilites())) {
- Log.d(TAG, "Legacy implementation is called while native flag is on.");
- }
-
- AudioCapsLegacyImpl caps = new AudioCapsLegacyImpl();
- caps.init(info, parent);
- return caps;
- }
+ /*
+ * Returns an array of ranges representing the number of input channels supported.
+ * The codec supports any number of input channels within this range.
+ *
+ * This supersedes the {@link #getMaxInputChannelCount} method.
+ *
+ * For many codecs, this will be a single range [1..N], for some N.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public Range<Integer>[] getInputChannelCountRanges() {
+ return Arrays.copyOf(mInputChannelRanges, mInputChannelRanges.length);
+ }
- private void init(MediaFormat info, CodecCapabilities parent) {
- mParent = parent;
- initWithPlatformLimits();
- applyLevelLimits();
- parseFromInfo(info);
- }
+ /* no public constructor */
+ private AudioCapabilities() { }
- private void initWithPlatformLimits() {
- mBitrateRange = Range.create(0, Integer.MAX_VALUE);
- mInputChannelRanges = new Range[] {Range.create(1, MAX_INPUT_CHANNEL_COUNT)};
- // mBitrateRange = Range.create(1, 320000);
- final int minSampleRate = SystemProperties.
- getInt("ro.mediacodec.min_sample_rate", 7350);
- final int maxSampleRate = SystemProperties.
- getInt("ro.mediacodec.max_sample_rate", 192000);
- mSampleRateRanges = new Range[] { Range.create(minSampleRate, maxSampleRate) };
- mSampleRates = null;
- }
+ /** @hide */
+ public static AudioCapabilities create(
+ MediaFormat info, CodecCapabilities parent) {
+ AudioCapabilities caps = new AudioCapabilities();
+ caps.init(info, parent);
+ return caps;
+ }
- private boolean supports(Integer sampleRate, Integer inputChannels) {
- // channels and sample rates are checked orthogonally
- if (inputChannels != null) {
- int ix = Utils.binarySearchDistinctRanges(
- mInputChannelRanges, inputChannels);
- if (ix < 0) {
- return false;
- }
- }
- if (sampleRate != null) {
- int ix = Utils.binarySearchDistinctRanges(
- mSampleRateRanges, sampleRate);
- if (ix < 0) {
- return false;
- }
- }
- return true;
- }
+ private void init(MediaFormat info, CodecCapabilities parent) {
+ mParent = parent;
+ initWithPlatformLimits();
+ applyLevelLimits();
+ parseFromInfo(info);
+ }
- public boolean isSampleRateSupported(int sampleRate) {
- return supports(sampleRate, null);
- }
+ private void initWithPlatformLimits() {
+ mBitrateRange = Range.create(0, Integer.MAX_VALUE);
+ mInputChannelRanges = new Range[] {Range.create(1, MAX_INPUT_CHANNEL_COUNT)};
+ // mBitrateRange = Range.create(1, 320000);
+ final int minSampleRate = SystemProperties.
+ getInt("ro.mediacodec.min_sample_rate", 7350);
+ final int maxSampleRate = SystemProperties.
+ getInt("ro.mediacodec.max_sample_rate", 192000);
+ mSampleRateRanges = new Range[] { Range.create(minSampleRate, maxSampleRate) };
+ mSampleRates = null;
+ }
- /** modifies rates */
- private void limitSampleRates(int[] rates) {
- Arrays.sort(rates);
- ArrayList<Range<Integer>> ranges = new ArrayList<Range<Integer>>();
- for (int rate: rates) {
- if (supports(rate, null /* channels */)) {
- ranges.add(Range.create(rate, rate));
- }
+ private boolean supports(Integer sampleRate, Integer inputChannels) {
+ // channels and sample rates are checked orthogonally
+ if (inputChannels != null) {
+ int ix = Utils.binarySearchDistinctRanges(
+ mInputChannelRanges, inputChannels);
+ if (ix < 0) {
+ return false;
}
- mSampleRateRanges = ranges.toArray(new Range[ranges.size()]);
- createDiscreteSampleRates();
}
-
- private void createDiscreteSampleRates() {
- mSampleRates = new int[mSampleRateRanges.length];
- for (int i = 0; i < mSampleRateRanges.length; i++) {
- mSampleRates[i] = mSampleRateRanges[i].getLower();
+ if (sampleRate != null) {
+ int ix = Utils.binarySearchDistinctRanges(
+ mSampleRateRanges, sampleRate);
+ if (ix < 0) {
+ return false;
}
}
+ return true;
+ }
- /** modifies rateRanges */
- private void limitSampleRates(Range<Integer>[] rateRanges) {
- sortDistinctRanges(rateRanges);
- mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, rateRanges);
+ /**
+ * Query whether the sample rate is supported by the codec.
+ */
+ public boolean isSampleRateSupported(int sampleRate) {
+ return supports(sampleRate, null);
+ }
- // check if all values are discrete
- for (Range<Integer> range: mSampleRateRanges) {
- if (!range.getLower().equals(range.getUpper())) {
- mSampleRates = null;
- return;
- }
+ /** modifies rates */
+ private void limitSampleRates(int[] rates) {
+ Arrays.sort(rates);
+ ArrayList<Range<Integer>> ranges = new ArrayList<Range<Integer>>();
+ for (int rate: rates) {
+ if (supports(rate, null /* channels */)) {
+ ranges.add(Range.create(rate, rate));
}
- createDiscreteSampleRates();
}
+ mSampleRateRanges = ranges.toArray(new Range[ranges.size()]);
+ createDiscreteSampleRates();
+ }
- private void applyLevelLimits() {
- int[] sampleRates = null;
- Range<Integer> sampleRateRange = null, bitRates = null;
- int maxChannels = MAX_INPUT_CHANNEL_COUNT;
- CodecProfileLevel[] profileLevels = mParent.profileLevels;
- String mime = mParent.getMimeType();
-
- if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG)) {
- sampleRates = new int[] {
- 8000, 11025, 12000,
- 16000, 22050, 24000,
- 32000, 44100, 48000 };
- bitRates = Range.create(8000, 320000);
- maxChannels = 2;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
- sampleRates = new int[] { 8000 };
- bitRates = Range.create(4750, 12200);
- maxChannels = 1;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB)) {
- sampleRates = new int[] { 16000 };
- bitRates = Range.create(6600, 23850);
- maxChannels = 1;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC)) {
- sampleRates = new int[] {
- 7350, 8000,
- 11025, 12000, 16000,
- 22050, 24000, 32000,
- 44100, 48000, 64000,
- 88200, 96000 };
- bitRates = Range.create(8000, 510000);
- maxChannels = 48;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS)) {
- bitRates = Range.create(32000, 500000);
- sampleRateRange = Range.create(8000, 192000);
- maxChannels = 255;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS)) {
- bitRates = Range.create(6000, 510000);
- sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000 };
- maxChannels = 255;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
- sampleRateRange = Range.create(1, 192000);
- bitRates = Range.create(1, 10000000);
- maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
- sampleRateRange = Range.create(1, 655350);
- // lossless codec, so bitrate is ignored
- maxChannels = 255;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW)
- || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW)) {
- sampleRates = new int[] { 8000 };
- bitRates = Range.create(64000, 64000);
- // platform allows multiple channels for this format
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM)) {
- sampleRates = new int[] { 8000 };
- bitRates = Range.create(13000, 13000);
- maxChannels = 1;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC3)) {
- maxChannels = 6;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
- maxChannels = 16;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3_JOC)) {
- sampleRates = new int[] { 48000 };
- bitRates = Range.create(32000, 6144000);
- maxChannels = 16;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
- sampleRates = new int[] { 44100, 48000, 96000, 192000 };
- bitRates = Range.create(16000, 2688000);
- maxChannels = 24;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS)) {
- sampleRates = new int[] { 44100, 48000 };
- bitRates = Range.create(96000, 1524000);
- maxChannels = 6;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_HD)) {
- for (CodecProfileLevel profileLevel: profileLevels) {
- switch (profileLevel.profile) {
- case CodecProfileLevel.DTS_HDProfileLBR:
- sampleRates = new int[]{ 22050, 24000, 44100, 48000 };
- bitRates = Range.create(32000, 768000);
- break;
- case CodecProfileLevel.DTS_HDProfileHRA:
- case CodecProfileLevel.DTS_HDProfileMA:
- sampleRates
- = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range.create(96000, 24500000);
- break;
- default:
- Log.w(TAG, "Unrecognized profile "
- + profileLevel.profile + " for " + mime);
- mParent.mError |= ERROR_UNRECOGNIZED;
- sampleRates
- = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range.create(96000, 24500000);
- }
- }
- maxChannels = 8;
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_UHD)) {
- for (CodecProfileLevel profileLevel: profileLevels) {
- switch (profileLevel.profile) {
- case CodecProfileLevel.DTS_UHDProfileP2:
- sampleRates = new int[]{ 48000 };
- bitRates = Range.create(96000, 768000);
- maxChannels = 10;
- break;
- case CodecProfileLevel.DTS_UHDProfileP1:
- sampleRates
- = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range.create(96000, 24500000);
- maxChannels = 32;
- break;
- default:
- Log.w(TAG, "Unrecognized profile "
- + profileLevel.profile + " for " + mime);
- mParent.mError |= ERROR_UNRECOGNIZED;
- sampleRates
- = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
- bitRates = Range.create(96000, 24500000);
- maxChannels = 32;
- }
- }
- } else {
- Log.w(TAG, "Unsupported mime " + mime);
- mParent.mError |= ERROR_UNSUPPORTED;
- }
-
- // restrict ranges
- if (sampleRates != null) {
- limitSampleRates(sampleRates);
- } else if (sampleRateRange != null) {
- limitSampleRates(new Range[] { sampleRateRange });
- }
-
- Range<Integer> channelRange = Range.create(1, maxChannels);
-
- applyLimits(new Range[] { channelRange }, bitRates);
+ private void createDiscreteSampleRates() {
+ mSampleRates = new int[mSampleRateRanges.length];
+ for (int i = 0; i < mSampleRateRanges.length; i++) {
+ mSampleRates[i] = mSampleRateRanges[i].getLower();
}
+ }
- private void applyLimits(Range<Integer>[] inputChannels, Range<Integer> bitRates) {
-
- // clamp & make a local copy
- Range<Integer>[] myInputChannels = new Range[inputChannels.length];
- for (int i = 0; i < inputChannels.length; i++) {
- int lower = inputChannels[i].clamp(1);
- int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
- myInputChannels[i] = Range.create(lower, upper);
- }
-
- // sort, intersect with existing, & save channel list
- sortDistinctRanges(myInputChannels);
- Range<Integer>[] joinedChannelList =
- intersectSortedDistinctRanges(myInputChannels, mInputChannelRanges);
- mInputChannelRanges = joinedChannelList;
+ /** modifies rateRanges */
+ private void limitSampleRates(Range<Integer>[] rateRanges) {
+ sortDistinctRanges(rateRanges);
+ mSampleRateRanges = intersectSortedDistinctRanges(mSampleRateRanges, rateRanges);
- if (bitRates != null) {
- mBitrateRange = mBitrateRange.intersect(bitRates);
+ // check if all values are discrete
+ for (Range<Integer> range: mSampleRateRanges) {
+ if (!range.getLower().equals(range.getUpper())) {
+ mSampleRates = null;
+ return;
}
}
+ createDiscreteSampleRates();
+ }
- private void parseFromInfo(MediaFormat info) {
- int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
- Range<Integer>[] channels = new Range[] { Range.create(1, maxInputChannels)};
- Range<Integer> bitRates = POSITIVE_INTEGERS;
+ private void applyLevelLimits() {
+ int[] sampleRates = null;
+ Range<Integer> sampleRateRange = null, bitRates = null;
+ int maxChannels = MAX_INPUT_CHANNEL_COUNT;
+ CodecProfileLevel[] profileLevels = mParent.profileLevels;
+ String mime = mParent.getMimeType();
- if (info.containsKey("sample-rate-ranges")) {
- String[] rateStrings = info.getString("sample-rate-ranges").split(",");
- Range<Integer>[] rateRanges = new Range[rateStrings.length];
- for (int i = 0; i < rateStrings.length; i++) {
- rateRanges[i] = Utils.parseIntRange(rateStrings[i], null);
+ if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG)) {
+ sampleRates = new int[] {
+ 8000, 11025, 12000,
+ 16000, 22050, 24000,
+ 32000, 44100, 48000 };
+ bitRates = Range.create(8000, 320000);
+ maxChannels = 2;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
+ sampleRates = new int[] { 8000 };
+ bitRates = Range.create(4750, 12200);
+ maxChannels = 1;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB)) {
+ sampleRates = new int[] { 16000 };
+ bitRates = Range.create(6600, 23850);
+ maxChannels = 1;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ sampleRates = new int[] {
+ 7350, 8000,
+ 11025, 12000, 16000,
+ 22050, 24000, 32000,
+ 44100, 48000, 64000,
+ 88200, 96000 };
+ bitRates = Range.create(8000, 510000);
+ maxChannels = 48;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS)) {
+ bitRates = Range.create(32000, 500000);
+ sampleRateRange = Range.create(8000, 192000);
+ maxChannels = 255;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS)) {
+ bitRates = Range.create(6000, 510000);
+ sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000 };
+ maxChannels = 255;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
+ sampleRateRange = Range.create(1, 192000);
+ bitRates = Range.create(1, 10000000);
+ maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
+ sampleRateRange = Range.create(1, 655350);
+ // lossless codec, so bitrate is ignored
+ maxChannels = 255;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW)
+ || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW)) {
+ sampleRates = new int[] { 8000 };
+ bitRates = Range.create(64000, 64000);
+ // platform allows multiple channels for this format
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM)) {
+ sampleRates = new int[] { 8000 };
+ bitRates = Range.create(13000, 13000);
+ maxChannels = 1;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC3)) {
+ maxChannels = 6;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
+ maxChannels = 16;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3_JOC)) {
+ sampleRates = new int[] { 48000 };
+ bitRates = Range.create(32000, 6144000);
+ maxChannels = 16;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
+ sampleRates = new int[] { 44100, 48000, 96000, 192000 };
+ bitRates = Range.create(16000, 2688000);
+ maxChannels = 24;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS)) {
+ sampleRates = new int[] { 44100, 48000 };
+ bitRates = Range.create(96000, 1524000);
+ maxChannels = 6;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_HD)) {
+ for (CodecProfileLevel profileLevel: profileLevels) {
+ switch (profileLevel.profile) {
+ case CodecProfileLevel.DTS_HDProfileLBR:
+ sampleRates = new int[]{ 22050, 24000, 44100, 48000 };
+ bitRates = Range.create(32000, 768000);
+ break;
+ case CodecProfileLevel.DTS_HDProfileHRA:
+ case CodecProfileLevel.DTS_HDProfileMA:
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
+ break;
+ default:
+ Log.w(TAG, "Unrecognized profile "
+ + profileLevel.profile + " for " + mime);
+ mParent.mError |= ERROR_UNRECOGNIZED;
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
}
- limitSampleRates(rateRanges);
}
-
- // we will prefer channel-ranges over max-channel-count
- if (info.containsKey("channel-ranges")) {
- String[] channelStrings = info.getString("channel-ranges").split(",");
- Range<Integer>[] channelRanges = new Range[channelStrings.length];
- for (int i = 0; i < channelStrings.length; i++) {
- channelRanges[i] = Utils.parseIntRange(channelStrings[i], null);
- }
- channels = channelRanges;
- } else if (info.containsKey("channel-range")) {
- Range<Integer> oneRange = Utils.parseIntRange(info.getString("channel-range"),
- null);
- channels = new Range[] { oneRange };
- } else if (info.containsKey("max-channel-count")) {
- maxInputChannels = Utils.parseIntSafely(
- info.getString("max-channel-count"), maxInputChannels);
- if (maxInputChannels == 0) {
- channels = new Range[] {Range.create(0, 0)};
- } else {
- channels = new Range[] {Range.create(1, maxInputChannels)};
+ maxChannels = 8;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_UHD)) {
+ for (CodecProfileLevel profileLevel: profileLevels) {
+ switch (profileLevel.profile) {
+ case CodecProfileLevel.DTS_UHDProfileP2:
+ sampleRates = new int[]{ 48000 };
+ bitRates = Range.create(96000, 768000);
+ maxChannels = 10;
+ break;
+ case CodecProfileLevel.DTS_UHDProfileP1:
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
+ maxChannels = 32;
+ break;
+ default:
+ Log.w(TAG, "Unrecognized profile "
+ + profileLevel.profile + " for " + mime);
+ mParent.mError |= ERROR_UNRECOGNIZED;
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
+ maxChannels = 32;
}
- } else if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
- maxInputChannels = 0;
- channels = new Range[] {Range.create(0, 0)};
}
-
- if (info.containsKey("bitrate-range")) {
- bitRates = bitRates.intersect(
- Utils.parseIntRange(info.getString("bitrate-range"), bitRates));
- }
-
- applyLimits(channels, bitRates);
+ } else {
+ Log.w(TAG, "Unsupported mime " + mime);
+ mParent.mError |= ERROR_UNSUPPORTED;
}
- /** @hide */
- public void getDefaultFormat(MediaFormat format) {
- // report settings that have only a single choice
- if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) {
- format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower());
- }
- if (getMaxInputChannelCount() == 1) {
- // mono-only format
- format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
- }
- if (mSampleRates != null && mSampleRates.length == 1) {
- format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRates[0]);
- }
+ // restrict ranges
+ if (sampleRates != null) {
+ limitSampleRates(sampleRates);
+ } else if (sampleRateRange != null) {
+ limitSampleRates(new Range[] { sampleRateRange });
}
- /* package private */
- // must not contain KEY_PROFILE
- static final Set<String> AUDIO_LEVEL_CRITICAL_FORMAT_KEYS = Set.of(
- // We don't set level-specific limits for audio codecs today. Key candidates
- // would be sample rate, bit rate or channel count.
- // MediaFormat.KEY_SAMPLE_RATE,
- // MediaFormat.KEY_CHANNEL_COUNT,
- // MediaFormat.KEY_BIT_RATE,
- MediaFormat.KEY_MIME);
-
- /** @hide */
- public boolean supportsFormat(MediaFormat format) {
- Map<String, Object> map = format.getMap();
- Integer sampleRate = (Integer)map.get(MediaFormat.KEY_SAMPLE_RATE);
- Integer channels = (Integer)map.get(MediaFormat.KEY_CHANNEL_COUNT);
-
- if (!supports(sampleRate, channels)) {
- return false;
- }
+ Range<Integer> channelRange = Range.create(1, maxChannels);
- if (!CodecCapabilities.supportsBitrate(mBitrateRange, format)) {
- return false;
- }
-
- // nothing to do for:
- // KEY_CHANNEL_MASK: codecs don't get this
- // KEY_IS_ADTS: required feature for all AAC decoders
- return true;
- }
+ applyLimits(new Range[] { channelRange }, bitRates);
}
- /* package private */ static final class AudioCapsNativeImpl implements AudioCapsIntf {
- private long mNativeContext; // accessed by native methods
-
- private Range<Integer> mBitrateRange;
- private int[] mSampleRates;
- private Range<Integer>[] mSampleRateRanges;
- private Range<Integer>[] mInputChannelRanges;
-
- /**
- * Constructor used by JNI.
- *
- * The Java AudioCapabilities object keeps these subobjects to avoid recontruction.
- */
- /* package private */ AudioCapsNativeImpl(Range<Integer> bitrateRange,
- int[] sampleRates, Range<Integer>[] sampleRateRanges,
- Range<Integer>[] inputChannelRanges) {
- mBitrateRange = bitrateRange;
- mSampleRates = sampleRates;
- mSampleRateRanges = sampleRateRanges;
- mInputChannelRanges = inputChannelRanges;
- }
-
- /* no public constructor */
- private AudioCapsNativeImpl() { }
+ private void applyLimits(Range<Integer>[] inputChannels, Range<Integer> bitRates) {
- public Range<Integer> getBitrateRange() {
- return mBitrateRange;
+ // clamp & make a local copy
+ Range<Integer>[] myInputChannels = new Range[inputChannels.length];
+ for (int i = 0; i < inputChannels.length; i++) {
+ int lower = inputChannels[i].clamp(1);
+ int upper = inputChannels[i].clamp(MAX_INPUT_CHANNEL_COUNT);
+ myInputChannels[i] = Range.create(lower, upper);
}
- public int[] getSupportedSampleRates() {
- return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length)
- : null;
- }
+ // sort, intersect with existing, & save channel list
+ sortDistinctRanges(myInputChannels);
+ Range<Integer>[] joinedChannelList =
+ intersectSortedDistinctRanges(myInputChannels, mInputChannelRanges);
+ mInputChannelRanges = joinedChannelList;
- public Range<Integer>[] getSupportedSampleRateRanges() {
- return Arrays.copyOf(mSampleRateRanges, mSampleRateRanges.length);
+ if (bitRates != null) {
+ mBitrateRange = mBitrateRange.intersect(bitRates);
}
+ }
- public Range<Integer>[] getInputChannelCountRanges() {
- return Arrays.copyOf(mInputChannelRanges, mInputChannelRanges.length);
+ private void parseFromInfo(MediaFormat info) {
+ int maxInputChannels = MAX_INPUT_CHANNEL_COUNT;
+ Range<Integer>[] channels = new Range[] { Range.create(1, maxInputChannels)};
+ Range<Integer> bitRates = POSITIVE_INTEGERS;
+
+ if (info.containsKey("sample-rate-ranges")) {
+ String[] rateStrings = info.getString("sample-rate-ranges").split(",");
+ Range<Integer>[] rateRanges = new Range[rateStrings.length];
+ for (int i = 0; i < rateStrings.length; i++) {
+ rateRanges[i] = Utils.parseIntRange(rateStrings[i], null);
+ }
+ limitSampleRates(rateRanges);
}
- public int getMaxInputChannelCount() {
- return native_getMaxInputChannelCount();
+ // we will prefer channel-ranges over max-channel-count
+ if (info.containsKey("channel-ranges")) {
+ String[] channelStrings = info.getString("channel-ranges").split(",");
+ Range<Integer>[] channelRanges = new Range[channelStrings.length];
+ for (int i = 0; i < channelStrings.length; i++) {
+ channelRanges[i] = Utils.parseIntRange(channelStrings[i], null);
+ }
+ channels = channelRanges;
+ } else if (info.containsKey("channel-range")) {
+ Range<Integer> oneRange = Utils.parseIntRange(info.getString("channel-range"),
+ null);
+ channels = new Range[] { oneRange };
+ } else if (info.containsKey("max-channel-count")) {
+ maxInputChannels = Utils.parseIntSafely(
+ info.getString("max-channel-count"), maxInputChannels);
+ if (maxInputChannels == 0) {
+ channels = new Range[] {Range.create(0, 0)};
+ } else {
+ channels = new Range[] {Range.create(1, maxInputChannels)};
+ }
+ } else if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
+ maxInputChannels = 0;
+ channels = new Range[] {Range.create(0, 0)};
}
- public int getMinInputChannelCount() {
- return native_getMinInputChannelCount();
+ if (info.containsKey("bitrate-range")) {
+ bitRates = bitRates.intersect(
+ Utils.parseIntRange(info.getString("bitrate-range"), bitRates));
}
- public boolean isSampleRateSupported(int sampleRate) {
- return native_isSampleRateSupported(sampleRate);
- }
+ applyLimits(channels, bitRates);
+ }
- // This API is for internal Java implementation only. Should not be called.
- public void getDefaultFormat(MediaFormat format) {
- throw new UnsupportedOperationException(
- "Java Implementation should not call native implemenatation");
+ /** @hide */
+ public void getDefaultFormat(MediaFormat format) {
+ // report settings that have only a single choice
+ if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) {
+ format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower());
}
-
- // This API is for internal Java implementation only. Should not be called.
- public boolean supportsFormat(MediaFormat format) {
- throw new UnsupportedOperationException(
- "Java Implementation should not call native implemenatation");
+ if (getMaxInputChannelCount() == 1) {
+ // mono-only format
+ format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
}
-
- private native int native_getMaxInputChannelCount();
- private native int native_getMinInputChannelCount();
- private native boolean native_isSampleRateSupported(int sampleRate);
- private static native void native_init();
-
- static {
- System.loadLibrary("media_jni");
- native_init();
+ if (mSampleRates != null && mSampleRates.length == 1) {
+ format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRates[0]);
}
}
- private AudioCapsIntf mImpl;
+ /* package private */
+ // must not contain KEY_PROFILE
+ static final Set<String> AUDIO_LEVEL_CRITICAL_FORMAT_KEYS = Set.of(
+ // We don't set level-specific limits for audio codecs today. Key candidates would
+ // be sample rate, bit rate or channel count.
+ // MediaFormat.KEY_SAMPLE_RATE,
+ // MediaFormat.KEY_CHANNEL_COUNT,
+ // MediaFormat.KEY_BIT_RATE,
+ MediaFormat.KEY_MIME);
/** @hide */
- public static AudioCapabilities create(
- MediaFormat info, CodecCapabilities parent) {
- AudioCapsLegacyImpl impl = AudioCapsLegacyImpl.create(info, parent);
- AudioCapabilities caps = new AudioCapabilities(impl);
- return caps;
- }
-
- /* package private */ AudioCapabilities(AudioCapsIntf impl) {
- mImpl = impl;
- }
-
- /* no public constructor */
- private AudioCapabilities() { }
-
- /**
- * Returns the range of supported bitrates in bits/second.
- */
- public Range<Integer> getBitrateRange() {
- return mImpl.getBitrateRange();
- }
-
- /**
- * Returns the array of supported sample rates if the codec
- * supports only discrete values. Otherwise, it returns
- * {@code null}. The array is sorted in ascending order.
- */
- public int[] getSupportedSampleRates() {
- return mImpl.getSupportedSampleRates();
- }
-
- /**
- * Returns the array of supported sample rate ranges. The
- * array is sorted in ascending order, and the ranges are
- * distinct.
- */
- public Range<Integer>[] getSupportedSampleRateRanges() {
- return mImpl.getSupportedSampleRateRanges();
- }
-
- /*
- * Returns an array of ranges representing the number of input channels supported.
- * The codec supports any number of input channels within this range.
- *
- * This supersedes the {@link #getMaxInputChannelCount} method.
- *
- * For many codecs, this will be a single range [1..N], for some N.
- */
- @SuppressLint("ArrayReturn")
- @NonNull
- public Range<Integer>[] getInputChannelCountRanges() {
- return mImpl.getInputChannelCountRanges();
- }
-
- /**
- * Returns the maximum number of input channels supported.
- *
- * Through {@link android.os.Build.VERSION_CODES#R}, this method indicated support
- * for any number of input channels between 1 and this maximum value.
- *
- * As of {@link android.os.Build.VERSION_CODES#S},
- * the implied lower limit of 1 channel is no longer valid.
- * As of {@link android.os.Build.VERSION_CODES#S}, {@link #getMaxInputChannelCount} is
- * superseded by {@link #getInputChannelCountRanges},
- * which returns an array of ranges of channels.
- * The {@link #getMaxInputChannelCount} method will return the highest value
- * in the ranges returned by {@link #getInputChannelCountRanges}
- *
- */
- @IntRange(from = 1, to = 255)
- public int getMaxInputChannelCount() {
- return mImpl.getMaxInputChannelCount();
- }
-
- /**
- * Returns the minimum number of input channels supported.
- * This is often 1, but does vary for certain mime types.
- *
- * This returns the lowest channel count in the ranges returned by
- * {@link #getInputChannelCountRanges}.
- */
- @IntRange(from = 1, to = 255)
- public int getMinInputChannelCount() {
- return mImpl.getMinInputChannelCount();
- }
+ public boolean supportsFormat(MediaFormat format) {
+ Map<String, Object> map = format.getMap();
+ Integer sampleRate = (Integer)map.get(MediaFormat.KEY_SAMPLE_RATE);
+ Integer channels = (Integer)map.get(MediaFormat.KEY_CHANNEL_COUNT);
- /**
- * Query whether the sample rate is supported by the codec.
- */
- public boolean isSampleRateSupported(int sampleRate) {
- return mImpl.isSampleRateSupported(sampleRate);
- }
+ if (!supports(sampleRate, channels)) {
+ return false;
+ }
- /** @hide */
- public void getDefaultFormat(MediaFormat format) {
- mImpl.getDefaultFormat(format);
- }
+ if (!CodecCapabilities.supportsBitrate(mBitrateRange, format)) {
+ return false;
+ }
- /** @hide */
- public boolean supportsFormat(MediaFormat format) {
- return mImpl.supportsFormat(format);
+ // nothing to do for:
+ // KEY_CHANNEL_MASK: codecs don't get this
+ // KEY_IS_ADTS: required feature for all AAC decoders
+ return true;
}
}
@@ -4990,10 +4826,4 @@ public final class MediaCodecInfo {
mName, mCanonicalName, mFlags,
caps.toArray(new CodecCapabilities[caps.size()]));
}
-
- /* package private */ class GenericHelper {
- private static Range<Integer> constructIntegerRange(int lower, int upper) {
- return Range.create(Integer.valueOf(lower), Integer.valueOf(upper));
- }
- }
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 126291ba4e3d..13dc748aa691 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -25,7 +25,6 @@ cc_library_shared {
min_sdk_version: "",
srcs: [
- "android_media_CodecCapabilities.cpp",
"android_media_ImageWriter.cpp",
"android_media_ImageReader.cpp",
"android_media_JetPlayer.cpp",
@@ -65,7 +64,6 @@ cc_library_shared {
"libbinder",
"libmedia",
"libmedia_codeclist",
- "libmedia_codeclist_capabilities",
"libmedia_jni_utils",
"libmedia_omx",
"libmediametrics",
diff --git a/media/jni/android_media_CodecCapabilities.cpp b/media/jni/android_media_CodecCapabilities.cpp
deleted file mode 100644
index d5e574764181..000000000000
--- a/media/jni/android_media_CodecCapabilities.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2024, 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaCodec-JNI"
-
-#include "android_runtime/AndroidRuntime.h"
-#include "jni.h"
-
-#include <media/AudioCapabilities.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/JNIHelp.h>
-
-namespace android {
-
-struct fields_t {
- jfieldID audioCapsContext;
-};
-static fields_t fields;
-
-// Getters
-
-static AudioCapabilities* getAudioCapabilities(JNIEnv *env, jobject thiz) {
- AudioCapabilities* const p = (AudioCapabilities*)env->GetLongField(
- thiz, fields.audioCapsContext);
- return p;
-}
-
-} // namespace android
-
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-// AudioCapabilities
-
-static void android_media_AudioCapabilities_native_init(JNIEnv *env, jobject /* thiz */) {
- jclass audioCapsImplClazz
- = env->FindClass("android/media/MediaCodecInfo$AudioCapabilities$AudioCapsNativeImpl");
- if (audioCapsImplClazz == NULL) {
- return;
- }
-
- fields.audioCapsContext = env->GetFieldID(audioCapsImplClazz, "mNativeContext", "J");
- if (fields.audioCapsContext == NULL) {
- return;
- }
-
- env->DeleteLocalRef(audioCapsImplClazz);
-}
-
-static jint android_media_AudioCapabilities_getMaxInputChannelCount(JNIEnv *env, jobject thiz) {
- AudioCapabilities* const audioCaps = getAudioCapabilities(env, thiz);
- if (audioCaps == nullptr) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
-
- int32_t maxInputChannelCount = audioCaps->getMaxInputChannelCount();
- return maxInputChannelCount;
-}
-
-static jint android_media_AudioCapabilities_getMinInputChannelCount(JNIEnv *env, jobject thiz) {
- AudioCapabilities* const audioCaps = getAudioCapabilities(env, thiz);
- if (audioCaps == nullptr) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
-
- int32_t minInputChannelCount = audioCaps->getMinInputChannelCount();
- return minInputChannelCount;
-}
-
-static jboolean android_media_AudioCapabilities_isSampleRateSupported(JNIEnv *env, jobject thiz,
- int sampleRate) {
- AudioCapabilities* const audioCaps = getAudioCapabilities(env, thiz);
- if (audioCaps == nullptr) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
-
- bool res = audioCaps->isSampleRateSupported(sampleRate);
- return res;
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gAudioCapsMethods[] = {
- {"native_init", "()V", (void *)android_media_AudioCapabilities_native_init},
- {"native_getMaxInputChannelCount", "()I", (void *)android_media_AudioCapabilities_getMaxInputChannelCount},
- {"native_getMinInputChannelCount", "()I", (void *)android_media_AudioCapabilities_getMinInputChannelCount},
- {"native_isSampleRateSupported", "(I)Z", (void *)android_media_AudioCapabilities_isSampleRateSupported}
-};
-
-int register_android_media_CodecCapabilities(JNIEnv *env) {
- int result = AndroidRuntime::registerNativeMethods(env,
- "android/media/MediaCodecInfo$AudioCapabilities$AudioCapsNativeImpl",
- gAudioCapsMethods, NELEM(gAudioCapsMethods));
- if (result != JNI_OK) {
- return result;
- }
-
- return result;
-} \ No newline at end of file
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index fcba150f0db5..d05ee551c172 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1465,7 +1465,6 @@ static int register_android_media_MediaPlayer(JNIEnv *env)
extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_ImageWriter(JNIEnv *env);
extern int register_android_media_JetPlayer(JNIEnv *env);
-extern int register_android_media_CodecCapabilities(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
extern int register_android_media_Descrambler(JNIEnv *env);
@@ -1580,11 +1579,6 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
goto bail;
}
- if (register_android_media_CodecCapabilities(env) < 0) {
- ALOGE("ERROR: CodecCapabilities native registration failed");
- goto bail;
- }
-
if (register_android_media_Crypto(env) < 0) {
ALOGE("ERROR: MediaCodec native registration failed");
goto bail;