Loading media/java/android/media/MediaCodecInfo.java +534 −364 Original line number Diff line number Diff line Loading @@ -1104,10 +1104,11 @@ 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.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS : null; final Set<String> criticalKeys = isVideo() ? VideoCapabilities.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS : isAudio() ? AudioCapabilities.AudioCapsLegacyImpl.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS : null; // critical keys will always contain KEY_MIME, but should also contain others to be // meaningful Loading Loading @@ -1410,6 +1411,28 @@ public final class MediaCodecInfo { */ public static final class AudioCapabilities { private static final String TAG = "AudioCapabilities"; /* 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); public void getDefaultFormat(MediaFormat format); public boolean supportsFormat(MediaFormat format); } /* package private */ static final class AudioCapsLegacyImpl implements AudioCapsIntf { private CodecCapabilities mParent; private Range<Integer> mBitrateRange; Loading @@ -1419,47 +1442,19 @@ public final class MediaCodecInfo { private static final int MAX_INPUT_CHANNEL_COUNT = 30; /** * Returns the range of supported bitrates in bits/second. */ public Range<Integer> getBitrateRange() { return mBitrateRange; } /** * 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; return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length) : null; } /** * 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); } /** * 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--) { Loading @@ -1471,14 +1466,6 @@ public final class MediaCodecInfo { return overall_max; } /** * 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--) { Loading @@ -1490,27 +1477,20 @@ public final class MediaCodecInfo { return overall_min; } /* * 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); } /* no public constructor */ private AudioCapabilities() { } private AudioCapsLegacyImpl() { } /** @hide */ public static AudioCapabilities create( public static AudioCapsLegacyImpl create( MediaFormat info, CodecCapabilities parent) { AudioCapabilities caps = new AudioCapabilities(); 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; } Loading Loading @@ -1553,9 +1533,6 @@ public final class MediaCodecInfo { return true; } /** * Query whether the sample rate is supported by the codec. */ public boolean isSampleRateSupported(int sampleRate) { return supports(sampleRate, null); } Loading Loading @@ -1676,14 +1653,16 @@ public final class MediaCodecInfo { break; case CodecProfileLevel.DTS_HDProfileHRA: case CodecProfileLevel.DTS_HDProfileMA: sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; 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 }; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; bitRates = Range.create(96000, 24500000); } } Loading @@ -1697,7 +1676,8 @@ public final class MediaCodecInfo { maxChannels = 10; break; case CodecProfileLevel.DTS_UHDProfileP1: sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; bitRates = Range.create(96000, 24500000); maxChannels = 32; break; Loading @@ -1705,7 +1685,8 @@ public final class MediaCodecInfo { Log.w(TAG, "Unrecognized profile " + profileLevel.profile + " for " + mime); mParent.mError |= ERROR_UNRECOGNIZED; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; bitRates = Range.create(96000, 24500000); maxChannels = 32; } Loading Loading @@ -1813,8 +1794,8 @@ public final class MediaCodecInfo { /* 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. // 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, Loading @@ -1841,6 +1822,189 @@ public final class MediaCodecInfo { } } /* 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() { } public Range<Integer> getBitrateRange() { return mBitrateRange; } public int[] getSupportedSampleRates() { return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length) : null; } public Range<Integer>[] getSupportedSampleRateRanges() { return Arrays.copyOf(mSampleRateRanges, mSampleRateRanges.length); } public Range<Integer>[] getInputChannelCountRanges() { return Arrays.copyOf(mInputChannelRanges, mInputChannelRanges.length); } public int getMaxInputChannelCount() { return native_getMaxInputChannelCount(); } public int getMinInputChannelCount() { return native_getMinInputChannelCount(); } public boolean isSampleRateSupported(int sampleRate) { return native_isSampleRateSupported(sampleRate); } // 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"); } // 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"); } 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(); } } private AudioCapsIntf mImpl; /** @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(); } /** * Query whether the sample rate is supported by the codec. */ public boolean isSampleRateSupported(int sampleRate) { return mImpl.isSampleRateSupported(sampleRate); } /** @hide */ public void getDefaultFormat(MediaFormat format) { mImpl.getDefaultFormat(format); } /** @hide */ public boolean supportsFormat(MediaFormat format) { return mImpl.supportsFormat(format); } } /** @hide */ @IntDef(prefix = {"SECURITY_MODEL_"}, value = { SECURITY_MODEL_SANDBOXED, Loading Loading @@ -4826,4 +4990,10 @@ 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)); } } } media/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ cc_library_shared { min_sdk_version: "", srcs: [ "android_media_CodecCapabilities.cpp", "android_media_ImageWriter.cpp", "android_media_ImageReader.cpp", "android_media_JetPlayer.cpp", Loading Loading @@ -64,6 +65,7 @@ cc_library_shared { "libbinder", "libmedia", "libmedia_codeclist", "libmedia_codeclist_capabilities", "libmedia_jni_utils", "libmedia_omx", "libmediametrics", Loading media/jni/android_media_CodecCapabilities.cpp 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * 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 media/jni/android_media_MediaPlayer.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -1465,6 +1465,7 @@ 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); Loading Loading @@ -1579,6 +1580,11 @@ 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; Loading Loading
media/java/android/media/MediaCodecInfo.java +534 −364 Original line number Diff line number Diff line Loading @@ -1104,10 +1104,11 @@ 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.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS : null; final Set<String> criticalKeys = isVideo() ? VideoCapabilities.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS : isAudio() ? AudioCapabilities.AudioCapsLegacyImpl.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS : null; // critical keys will always contain KEY_MIME, but should also contain others to be // meaningful Loading Loading @@ -1410,6 +1411,28 @@ public final class MediaCodecInfo { */ public static final class AudioCapabilities { private static final String TAG = "AudioCapabilities"; /* 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); public void getDefaultFormat(MediaFormat format); public boolean supportsFormat(MediaFormat format); } /* package private */ static final class AudioCapsLegacyImpl implements AudioCapsIntf { private CodecCapabilities mParent; private Range<Integer> mBitrateRange; Loading @@ -1419,47 +1442,19 @@ public final class MediaCodecInfo { private static final int MAX_INPUT_CHANNEL_COUNT = 30; /** * Returns the range of supported bitrates in bits/second. */ public Range<Integer> getBitrateRange() { return mBitrateRange; } /** * 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; return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length) : null; } /** * 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); } /** * 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--) { Loading @@ -1471,14 +1466,6 @@ public final class MediaCodecInfo { return overall_max; } /** * 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--) { Loading @@ -1490,27 +1477,20 @@ public final class MediaCodecInfo { return overall_min; } /* * 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); } /* no public constructor */ private AudioCapabilities() { } private AudioCapsLegacyImpl() { } /** @hide */ public static AudioCapabilities create( public static AudioCapsLegacyImpl create( MediaFormat info, CodecCapabilities parent) { AudioCapabilities caps = new AudioCapabilities(); 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; } Loading Loading @@ -1553,9 +1533,6 @@ public final class MediaCodecInfo { return true; } /** * Query whether the sample rate is supported by the codec. */ public boolean isSampleRateSupported(int sampleRate) { return supports(sampleRate, null); } Loading Loading @@ -1676,14 +1653,16 @@ public final class MediaCodecInfo { break; case CodecProfileLevel.DTS_HDProfileHRA: case CodecProfileLevel.DTS_HDProfileMA: sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; 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 }; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; bitRates = Range.create(96000, 24500000); } } Loading @@ -1697,7 +1676,8 @@ public final class MediaCodecInfo { maxChannels = 10; break; case CodecProfileLevel.DTS_UHDProfileP1: sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; bitRates = Range.create(96000, 24500000); maxChannels = 32; break; Loading @@ -1705,7 +1685,8 @@ public final class MediaCodecInfo { Log.w(TAG, "Unrecognized profile " + profileLevel.profile + " for " + mime); mParent.mError |= ERROR_UNRECOGNIZED; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 }; bitRates = Range.create(96000, 24500000); maxChannels = 32; } Loading Loading @@ -1813,8 +1794,8 @@ public final class MediaCodecInfo { /* 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. // 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, Loading @@ -1841,6 +1822,189 @@ public final class MediaCodecInfo { } } /* 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() { } public Range<Integer> getBitrateRange() { return mBitrateRange; } public int[] getSupportedSampleRates() { return mSampleRates != null ? Arrays.copyOf(mSampleRates, mSampleRates.length) : null; } public Range<Integer>[] getSupportedSampleRateRanges() { return Arrays.copyOf(mSampleRateRanges, mSampleRateRanges.length); } public Range<Integer>[] getInputChannelCountRanges() { return Arrays.copyOf(mInputChannelRanges, mInputChannelRanges.length); } public int getMaxInputChannelCount() { return native_getMaxInputChannelCount(); } public int getMinInputChannelCount() { return native_getMinInputChannelCount(); } public boolean isSampleRateSupported(int sampleRate) { return native_isSampleRateSupported(sampleRate); } // 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"); } // 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"); } 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(); } } private AudioCapsIntf mImpl; /** @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(); } /** * Query whether the sample rate is supported by the codec. */ public boolean isSampleRateSupported(int sampleRate) { return mImpl.isSampleRateSupported(sampleRate); } /** @hide */ public void getDefaultFormat(MediaFormat format) { mImpl.getDefaultFormat(format); } /** @hide */ public boolean supportsFormat(MediaFormat format) { return mImpl.supportsFormat(format); } } /** @hide */ @IntDef(prefix = {"SECURITY_MODEL_"}, value = { SECURITY_MODEL_SANDBOXED, Loading Loading @@ -4826,4 +4990,10 @@ 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)); } } }
media/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ cc_library_shared { min_sdk_version: "", srcs: [ "android_media_CodecCapabilities.cpp", "android_media_ImageWriter.cpp", "android_media_ImageReader.cpp", "android_media_JetPlayer.cpp", Loading Loading @@ -64,6 +65,7 @@ cc_library_shared { "libbinder", "libmedia", "libmedia_codeclist", "libmedia_codeclist_capabilities", "libmedia_jni_utils", "libmedia_omx", "libmediametrics", Loading
media/jni/android_media_CodecCapabilities.cpp 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * 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
media/jni/android_media_MediaPlayer.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -1465,6 +1465,7 @@ 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); Loading Loading @@ -1579,6 +1580,11 @@ 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; Loading