Files
android_frameworks_base/media/jni/android_media_CodecCapabilities.cpp
Songyue Han c123ba0baa CodecCapabilities: Allow returning NULL for getAchievableFrameRatesFor() if no measurement data is published.
Bug: 422728243
Test: atest NativeAMediaCodecInfoTest
Flag: EXEMPT bugfix
Change-Id: I4a131360e921696215cb5d038fd72a17bb76fd4d
2025-06-24 14:43:14 -07:00

1058 lines
40 KiB
C++

/*
* 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_media_CodecCapabilities.h"
#include "android_media_Streams.h"
#include "android_runtime/AndroidRuntime.h"
#include "jni.h"
#include <media/AudioCapabilities.h>
#include <media/CodecCapabilities.h>
#include <media/EncoderCapabilities.h>
#include <media/VideoCapabilities.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaErrors.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <utils/Log.h>
namespace android {
struct fields_t {
jfieldID audioCapsContext;
jfieldID videoCapsContext;
jfieldID encoderCapsContext;
jfieldID codecCapsContext;
};
static fields_t fields;
// JCodecCapabilities
JCodecCapabilities::JCodecCapabilities(std::shared_ptr<CodecCapabilities> codecCaps)
: mCodecCaps(codecCaps) {}
std::shared_ptr<CodecCapabilities> JCodecCapabilities::getCodecCaps() const {
return mCodecCaps;
}
int32_t JCodecCapabilities::getMaxSupportedInstances() const {
return mCodecCaps->getMaxSupportedInstances();
}
std::string JCodecCapabilities::getMediaType() const {
return mCodecCaps->getMediaType();
}
bool JCodecCapabilities::isFeatureRequired(const std::string& name) const {
return mCodecCaps->isFeatureRequired(name);
}
bool JCodecCapabilities::isFeatureSupported(const std::string& name) const {
return mCodecCaps->isFeatureSupported(name);
}
bool JCodecCapabilities::isFormatSupported(const sp<AMessage> &format) const {
return mCodecCaps->isFormatSupported(format);
}
bool JCodecCapabilities::isRegular() const {
return mCodecCaps->isRegular();
}
std::vector<std::string> JCodecCapabilities::validFeatures() const {
return mCodecCaps->validFeatures();
}
// Setter
static sp<JCodecCapabilities> setCodecCapabilities(JNIEnv *env, jobject thiz,
const sp<JCodecCapabilities>& jCodecCaps) {
sp<JCodecCapabilities> old
= (JCodecCapabilities*)env->GetLongField(thiz, fields.codecCapsContext);
if (jCodecCaps != NULL) {
jCodecCaps->incStrong(thiz);
}
if (old != NULL) {
old->decStrong(thiz);
}
env->SetLongField(thiz, fields.codecCapsContext, (jlong)jCodecCaps.get());
return old;
}
// Getters
static AudioCapabilities* getAudioCapabilities(JNIEnv *env, jobject thiz) {
AudioCapabilities* const p = (AudioCapabilities*)env->GetLongField(
thiz, fields.audioCapsContext);
return p;
}
static VideoCapabilities* getVideoCapabilities(JNIEnv *env, jobject thiz) {
VideoCapabilities* const p = (VideoCapabilities*)env->GetLongField(
thiz, fields.videoCapsContext);
return p;
}
static EncoderCapabilities* getEncoderCapabilities(JNIEnv *env, jobject thiz) {
EncoderCapabilities* const p = (EncoderCapabilities*)env->GetLongField(
thiz, fields.encoderCapsContext);
return p;
}
static sp<JCodecCapabilities> getCodecCapabilities(JNIEnv *env, jobject thiz) {
JCodecCapabilities* const p = (JCodecCapabilities*)env->GetLongField(
thiz, fields.codecCapsContext);
return sp<JCodecCapabilities>(p);
}
// Utils
static jobject convertToJavaIntRange(JNIEnv *env, const Range<int32_t>& range) {
jclass helperClazz = env->FindClass("android/media/MediaCodecInfo$GenericHelper");
jmethodID constructIntegerRangeID = env->GetStaticMethodID(helperClazz, "constructIntegerRange",
"(II)Landroid/util/Range;");
jobject jRange = env->CallStaticObjectMethod(helperClazz, constructIntegerRangeID,
range.lower(), range.upper());
return jRange;
}
static jobject convertToJavaDoubleRange(JNIEnv *env, const Range<double>& range) {
jclass helperClazz = env->FindClass("android/media/MediaCodecInfo$GenericHelper");
jmethodID constructDoubleRangeID = env->GetStaticMethodID(helperClazz, "constructDoubleRange",
"(DD)Landroid/util/Range;");
jobject jRange = env->CallStaticObjectMethod(helperClazz, constructDoubleRangeID,
range.lower(), range.upper());
return jRange;
}
static jobjectArray convertToJavaIntRangeArray(JNIEnv *env,
const std::vector<Range<int32_t>>& ranges) {
jclass rangeClazz = env->FindClass("android/util/Range");
CHECK(rangeClazz != NULL);
jobjectArray jRanges = env->NewObjectArray(ranges.size(), rangeClazz, NULL);
for (int i = 0; i < ranges.size(); i++) {
Range<int32_t> range = ranges.at(i);
jobject jRange = convertToJavaIntRange(env, range);
env->SetObjectArrayElement(jRanges, i, jRange);
env->DeleteLocalRef(jRange);
jRange = NULL;
}
return jRanges;
}
// Converters between Java objects and native instances
// The Java AudioCapabilities object keep bitrateRange, sampleRates, sampleRateRanges
// and inputChannelRanges in it to prevent reconstruction when called the getters functions.
static jobject convertToJavaAudioCapabilities(
JNIEnv *env, std::shared_ptr<AudioCapabilities> audioCaps) {
if (audioCaps == nullptr) {
return NULL;
}
// construct Java bitrateRange
const Range<int32_t>& bitrateRange = audioCaps->getBitrateRange();
jobject jBitrateRange = convertToJavaIntRange(env, bitrateRange);
// construct Java sampleRates array
const std::vector<int32_t>& sampleRates = audioCaps->getSupportedSampleRates();
jintArray jSampleRates;
if (sampleRates.size() > 0) {
jSampleRates = env->NewIntArray(sampleRates.size());
for (size_t i = 0; i < sampleRates.size(); ++i) {
jint val = sampleRates.at(i);
env->SetIntArrayRegion(jSampleRates, i, 1, &val);
}
}
// construct Java sampleRateRanges
const std::vector<Range<int32_t>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges();
jobjectArray jSampleRateRanges = convertToJavaIntRangeArray(env, sampleRateRanges);
// construct Java inputChannelRanges
const std::vector<Range<int32_t>>& inputChannelRanges = audioCaps->getInputChannelCountRanges();
jobjectArray jInputChannelRanges = convertToJavaIntRangeArray(env, inputChannelRanges);
// construct Java AudioCapsNativeImpl
jclass audioCapsImplClazz
= env->FindClass("android/media/MediaCodecInfo$AudioCapabilities$AudioCapsNativeImpl");
CHECK(audioCapsImplClazz != NULL);
jmethodID audioCapsImplConstructID = env->GetMethodID(audioCapsImplClazz, "<init>",
"(Landroid/util/Range;"
"[I"
"[Landroid/util/Range;"
"[Landroid/util/Range;)V");
jobject jAudioCapsImpl = env->NewObject(audioCapsImplClazz, audioCapsImplConstructID,
jBitrateRange, jSampleRates, jSampleRateRanges, jInputChannelRanges);
// The native AudioCapabilities won't be destructed until process ends.
env->SetLongField(jAudioCapsImpl, fields.audioCapsContext, (jlong)audioCaps.get());
// construct Java AudioCapabilities
jclass audioCapsClazz
= env->FindClass("android/media/MediaCodecInfo$AudioCapabilities");
CHECK(audioCapsClazz != NULL);
jmethodID audioCapsConstructID = env->GetMethodID(audioCapsClazz, "<init>",
"(Landroid/media/MediaCodecInfo$AudioCapabilities$AudioCapsIntf;)V");
jobject jAudioCaps = env->NewObject(audioCapsClazz, audioCapsConstructID, jAudioCapsImpl);
env->DeleteLocalRef(jBitrateRange);
jBitrateRange = NULL;
env->DeleteLocalRef(jSampleRates);
jSampleRates = NULL;
env->DeleteLocalRef(jSampleRateRanges);
jSampleRateRanges = NULL;
env->DeleteLocalRef(jInputChannelRanges);
jInputChannelRanges = NULL;
env->DeleteLocalRef(jAudioCapsImpl);
jAudioCapsImpl = NULL;
return jAudioCaps;
}
// convert native PerformancePoints to Java objects
static jobject convertToJavaPerformancePoints(JNIEnv *env,
const std::vector<VideoCapabilities::PerformancePoint>& performancePoints) {
jclass performancePointClazz = env->FindClass(
"android/media/MediaCodecInfo$VideoCapabilities$PerformancePoint");
CHECK(performancePointClazz != NULL);
jmethodID performancePointConstructID = env->GetMethodID(performancePointClazz, "<init>",
"(IIIJII)V");
jobjectArray jPerformancePoints = env->NewObjectArray(performancePoints.size(),
performancePointClazz, NULL);
int i = 0;
for (auto it = performancePoints.begin(); it != performancePoints.end(); ++it, ++i) {
jobject jPerformancePoint = env->NewObject(performancePointClazz,
performancePointConstructID, it->getWidth(),
it->getHeight(), it->getMaxFrameRate(),
it->getMaxMacroBlockRate(), it->getBlockSize().getWidth(),
it->getBlockSize().getHeight());
env->SetObjectArrayElement(jPerformancePoints, i, jPerformancePoint);
env->DeleteLocalRef(jPerformancePoint);
}
jclass helperClazz = env->FindClass("android/media/MediaCodecInfo$GenericHelper");
CHECK(helperClazz != NULL);
jmethodID asListID = env->GetStaticMethodID(helperClazz, "constructPerformancePointList",
"([Landroid/media/MediaCodecInfo$VideoCapabilities$PerformancePoint;)Ljava/util/List;");
CHECK(asListID != NULL);
jobject jList = env->CallStaticObjectMethod(helperClazz, asListID, jPerformancePoints);
return jList;
}
static VideoCapabilities::PerformancePoint convertToNativePerformancePoint(
JNIEnv *env, jobject pp) {
if (pp == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
}
jclass clazz = env->FindClass(
"android/media/MediaCodecInfo$VideoCapabilities$PerformancePoint");
CHECK(clazz != NULL);
CHECK(env->IsInstanceOf(pp, clazz));
jmethodID getWidthID = env->GetMethodID(clazz, "getWidth", "()I");
CHECK(getWidthID != NULL);
jint width = env->CallIntMethod(pp, getWidthID);
jmethodID getHeightID = env->GetMethodID(clazz, "getHeight", "()I");
CHECK(getHeightID != NULL);
jint height = env->CallIntMethod(pp, getHeightID);
jmethodID getMaxFrameRateID = env->GetMethodID(clazz, "getMaxFrameRate", "()I");
CHECK(getMaxFrameRateID != NULL);
jint maxFrameRate = env->CallIntMethod(pp, getMaxFrameRateID);
jmethodID getMaxMacroBlockRateID = env->GetMethodID(clazz, "getMaxMacroBlockRate", "()J");
CHECK(getMaxMacroBlockRateID != NULL);
jlong maxMacroBlockRate = env->CallLongMethod(pp, getMaxMacroBlockRateID);
jmethodID getBlockWidthID = env->GetMethodID(clazz, "getBlockWidth", "()I");
CHECK(getBlockWidthID != NULL);
jint blockWidth = env->CallIntMethod(pp, getBlockWidthID);
jmethodID getBlockHeightID = env->GetMethodID(clazz, "getBlockHeight", "()I");
CHECK(getBlockHeightID != NULL);
jint blockHeight = env->CallIntMethod(pp, getBlockHeightID);
return VideoCapabilities::PerformancePoint(VideoSize(blockWidth, blockHeight),
width, height, maxFrameRate, maxMacroBlockRate);
}
static jobject convertToJavaVideoCapabilities(JNIEnv *env,
std::shared_ptr<VideoCapabilities> videoCaps) {
if (videoCaps == nullptr) {
return NULL;
}
// get Java bitrateRange
const Range<int32_t>& bitrateRange = videoCaps->getBitrateRange();
jobject jBitrateRange = convertToJavaIntRange(env, bitrateRange);
// get Java widthRange
const Range<int32_t>& widthRange = videoCaps->getSupportedWidths();
jobject jWidthRange = convertToJavaIntRange(env, widthRange);
// get Java heightRange
const Range<int32_t>& heightRange = videoCaps->getSupportedHeights();
jobject jHeightRange = convertToJavaIntRange(env, heightRange);
// get Java frameRateRange
const Range<int32_t>& frameRateRange = videoCaps->getSupportedFrameRates();
jobject jFrameRateRange = convertToJavaIntRange(env, frameRateRange);
// get Java performancePoints
const std::vector<VideoCapabilities::PerformancePoint>& performancePoints
= videoCaps->getSupportedPerformancePoints();
jobject jPerformancePoints = convertToJavaPerformancePoints(env, performancePoints);
// get width alignment
int32_t widthAlignment = videoCaps->getWidthAlignment();
// get height alignment
int32_t heightAlignment = videoCaps->getHeightAlignment();
// get Java VideoCapsNativeImpl
jclass videoCapsImplClazz = env->FindClass(
"android/media/MediaCodecInfo$VideoCapabilities$VideoCapsNativeImpl");
CHECK(videoCapsImplClazz != NULL);
jmethodID videoCapsImplConstructID = env->GetMethodID(videoCapsImplClazz, "<init>",
"(Landroid/util/Range;"
"Landroid/util/Range;"
"Landroid/util/Range;"
"Landroid/util/Range;"
"Ljava/util/List;II)V");
jobject jVideoCapsImpl = env->NewObject(videoCapsImplClazz, videoCapsImplConstructID,
jBitrateRange, jWidthRange, jHeightRange, jFrameRateRange, jPerformancePoints,
widthAlignment, heightAlignment);
// The native VideoCapabilities won't be destructed until process ends.
env->SetLongField(jVideoCapsImpl, fields.videoCapsContext, (jlong)videoCaps.get());
// get Java VideoCapabilities
jclass videoCapsClazz
= env->FindClass("android/media/MediaCodecInfo$VideoCapabilities");
CHECK(videoCapsClazz != NULL);
jmethodID videoCapsConstructID = env->GetMethodID(videoCapsClazz, "<init>",
"(Landroid/media/MediaCodecInfo$VideoCapabilities$VideoCapsIntf;)V");
jobject jVideoCaps = env->NewObject(videoCapsClazz, videoCapsConstructID, jVideoCapsImpl);
env->DeleteLocalRef(jBitrateRange);
jBitrateRange = NULL;
env->DeleteLocalRef(jWidthRange);
jWidthRange = NULL;
env->DeleteLocalRef(jHeightRange);
jHeightRange = NULL;
env->DeleteLocalRef(jFrameRateRange);
jFrameRateRange = NULL;
env->DeleteLocalRef(jPerformancePoints);
jPerformancePoints = NULL;
env->DeleteLocalRef(jVideoCapsImpl);
jVideoCapsImpl = NULL;
return jVideoCaps;
}
static jobject convertToJavaEncoderCapabilities(JNIEnv *env,
std::shared_ptr<EncoderCapabilities> encoderCaps) {
if (encoderCaps == nullptr) {
return NULL;
}
// get quality range
const Range<int>& qualityRange = encoderCaps->getQualityRange();
jobject jQualityRange = convertToJavaIntRange(env, qualityRange);
// get complexity range
const Range<int>& complexityRange = encoderCaps->getComplexityRange();
jobject jComplexityRange = convertToJavaIntRange(env, complexityRange);
// construct java EncoderCapsNativeImpl
jclass encoderCapsImplClazz = env->FindClass(
"android/media/MediaCodecInfo$EncoderCapabilities$EncoderCapsNativeImpl");
CHECK(encoderCapsImplClazz != NULL);
jmethodID encoderCapsImplConstructID = env->GetMethodID(encoderCapsImplClazz, "<init>",
"(Landroid/util/Range;Landroid/util/Range;)V");
jobject jEncoderCapsImpl = env->NewObject(encoderCapsImplClazz, encoderCapsImplConstructID,
jQualityRange, jComplexityRange);
// The native EncoderCapabilities won't be destructed until process ends.
env->SetLongField(jEncoderCapsImpl, fields.encoderCapsContext, (jlong)encoderCaps.get());
// construct java EncoderCapabilities object
jclass encoderCapsClazz
= env->FindClass("android/media/MediaCodecInfo$EncoderCapabilities");
CHECK(encoderCapsClazz != NULL);
jmethodID encoderCapsConstructID = env->GetMethodID(encoderCapsClazz, "<init>",
"(Landroid/media/MediaCodecInfo$EncoderCapabilities$EncoderCapsIntf;)V");
jobject jEncoderCaps = env->NewObject(encoderCapsClazz, encoderCapsConstructID,
jEncoderCapsImpl);
env->DeleteLocalRef(jQualityRange);
jQualityRange = NULL;
env->DeleteLocalRef(jComplexityRange);
jComplexityRange = NULL;
env->DeleteLocalRef(jEncoderCapsImpl);
jEncoderCapsImpl = NULL;
return jEncoderCaps;
}
// Java CodecCapsNativeImpl keeps the defaultFormat, profileLevels, colorFormats, audioCapabilities,
// videoCapabilities and encoderCapabilities in it to prevent reconsturction when called by getter.
static jobject convertToJavaCodecCapsNativeImpl(
JNIEnv *env, std::shared_ptr<CodecCapabilities> codecCaps) {
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
// Construct defaultFormat
sp<AMessage> defaultFormat = codecCaps->getDefaultFormat();
jobject formatMap = NULL;
if (OK != ConvertMessageToMap(env, defaultFormat, &formatMap)) {
jniThrowException(env, "java/lang/IllegalStateException",
"Fatal error: failed to convert format from native to Java object");
return NULL;
}
ScopedLocalRef<jclass> mediaFormatClass{env, env->FindClass("android/media/MediaFormat")};
ScopedLocalRef<jobject> jDefaultFormat{env, env->NewObject(
mediaFormatClass.get(),
env->GetMethodID(mediaFormatClass.get(), "<init>", "(Ljava/util/Map;)V"),
formatMap)};
env->DeleteLocalRef(formatMap);
formatMap = NULL;
// Construct Java ProfileLevelArray
std::vector<ProfileLevel> profileLevels = codecCaps->getProfileLevels();
jclass profileLevelClazz =
env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel");
CHECK(profileLevelClazz != NULL);
jobjectArray profileLevelArray =
env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL);
jfieldID profileField =
env->GetFieldID(profileLevelClazz, "profile", "I");
jfieldID levelField =
env->GetFieldID(profileLevelClazz, "level", "I");
for (size_t i = 0; i < profileLevels.size(); ++i) {
const ProfileLevel &src = profileLevels.at(i);
jobject profileLevelObj = env->AllocObject(profileLevelClazz);
env->SetIntField(profileLevelObj, profileField, src.mProfile);
env->SetIntField(profileLevelObj, levelField, src.mLevel);
env->SetObjectArrayElement(profileLevelArray, i, profileLevelObj);
env->DeleteLocalRef(profileLevelObj);
profileLevelObj = NULL;
}
// Construct ColorFormatArray
std::vector<uint32_t> colorFormats = codecCaps->getColorFormats();
jintArray colorFormatsArray = env->NewIntArray(colorFormats.size());
env->SetIntArrayRegion(colorFormatsArray, 0, colorFormats.size(),
reinterpret_cast<jint*>(colorFormats.data()));
// Construct and set AudioCapabilities
std::shared_ptr<AudioCapabilities> audioCaps = codecCaps->getAudioCapabilities();
jobject jAudioCaps = convertToJavaAudioCapabilities(env, audioCaps);
// Set VideoCapabilities
std::shared_ptr<VideoCapabilities> videoCaps = codecCaps->getVideoCapabilities();
jobject jVideoCaps = convertToJavaVideoCapabilities(env, videoCaps);
// Set EncoderCapabilities
std::shared_ptr<EncoderCapabilities> encoderCaps = codecCaps->getEncoderCapabilities();
jobject jEncoderCaps = convertToJavaEncoderCapabilities(env, encoderCaps);
// Construct CodecCapsNativeImpl
jclass codecCapsImplClazz =
env->FindClass("android/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl");
CHECK(codecCapsImplClazz != NULL);
jmethodID codecCapsImplConstructID = env->GetMethodID(codecCapsImplClazz, "<init>",
"([Landroid/media/MediaCodecInfo$CodecProfileLevel;[I"
"Landroid/media/MediaFormat;"
"Landroid/media/MediaCodecInfo$AudioCapabilities;"
"Landroid/media/MediaCodecInfo$VideoCapabilities;"
"Landroid/media/MediaCodecInfo$EncoderCapabilities;)V");
jobject javaCodecCapsImpl = env->NewObject(codecCapsImplClazz, codecCapsImplConstructID,
profileLevelArray, colorFormatsArray, jDefaultFormat.get(),
jAudioCaps, jVideoCaps, jEncoderCaps);
// Construct JCodecCapabilities and hold the codecCaps in it
sp<JCodecCapabilities> jCodecCaps = sp<JCodecCapabilities>::make(codecCaps);
setCodecCapabilities(env, javaCodecCapsImpl, jCodecCaps);
env->DeleteLocalRef(profileLevelArray);
profileLevelArray = NULL;
env->DeleteLocalRef(colorFormatsArray);
colorFormatsArray = NULL;
env->DeleteLocalRef(jAudioCaps);
jAudioCaps = NULL;
env->DeleteLocalRef(jVideoCaps);
jVideoCaps = NULL;
env->DeleteLocalRef(jEncoderCaps);
jEncoderCaps = NULL;
return javaCodecCapsImpl;
}
jobject convertToJavaCodecCapabiliites(
JNIEnv *env, std::shared_ptr<CodecCapabilities> codecCaps) {
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
jobject javaCodecCapsImpl = convertToJavaCodecCapsNativeImpl(env, codecCaps);
// Construct CodecCapabilities
jclass codecCapsClazz = env->FindClass("android/media/MediaCodecInfo$CodecCapabilities");
CHECK(codecCapsClazz != NULL);
jmethodID codecCapsConstructID = env->GetMethodID(codecCapsClazz, "<init>",
"(Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsIntf;)V");
jobject javaCodecCaps = env->NewObject(codecCapsClazz, codecCapsConstructID, javaCodecCapsImpl);
return javaCodecCaps;
}
} // 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;
}
// PerformancePoint
static jboolean android_media_VideoCapabilities_PerformancePoint_covers(JNIEnv *env, jobject thiz,
jobject other) {
VideoCapabilities::PerformancePoint pp0 = convertToNativePerformancePoint(env, thiz);
VideoCapabilities::PerformancePoint pp1 = convertToNativePerformancePoint(env, other);
bool res = pp0.covers(pp1);
return res;
}
static jboolean android_media_VideoCapabilities_PerformancePoint_equals(JNIEnv *env, jobject thiz,
jobject other) {
VideoCapabilities::PerformancePoint pp0 = convertToNativePerformancePoint(env, thiz);
VideoCapabilities::PerformancePoint pp1 = convertToNativePerformancePoint(env, other);
bool res = pp0.equals(pp1);
return res;
}
// VideoCapabilities
static void android_media_VideoCapabilities_native_init(JNIEnv *env, jobject /* thiz */) {
jclass clazz
= env->FindClass("android/media/MediaCodecInfo$VideoCapabilities$VideoCapsNativeImpl");
if (clazz == NULL) {
return;
}
fields.videoCapsContext = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.videoCapsContext == NULL) {
return;
}
env->DeleteLocalRef(clazz);
}
static jboolean android_media_VideoCapabilities_areSizeAndRateSupported(JNIEnv *env, jobject thiz,
int32_t width, int32_t height, double frameRate) {
VideoCapabilities* const videoCaps = getVideoCapabilities(env, thiz);
if (videoCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
bool res = videoCaps->areSizeAndRateSupported(width, height, frameRate);
return res;
}
static jboolean android_media_VideoCapabilities_isSizeSupported(JNIEnv *env, jobject thiz,
int32_t width, int32_t height) {
VideoCapabilities* const videoCaps = getVideoCapabilities(env, thiz);
if (videoCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
bool res = videoCaps->isSizeSupported(width, height);
return res;
}
static jobject android_media_VideoCapabilities_getAchievableFrameRatesFor(JNIEnv *env, jobject thiz,
int32_t width, int32_t height) {
VideoCapabilities* const videoCaps = getVideoCapabilities(env, thiz);
if (videoCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
}
if (!videoCaps->supports(width, height, std::nullopt)) {
jniThrowException(env, "java/lang/IllegalArgumentException", "unsupported size");
return NULL;
}
std::optional<Range<double>> frameRates = videoCaps->getAchievableFrameRatesFor(width, height);
if (!frameRates) {
// Return NULL if the device doesn't publish data for that resolution
return NULL;
}
jobject jFrameRates = convertToJavaDoubleRange(env, frameRates.value());
return jFrameRates;
}
static jobject android_media_VideoCapabilities_getSupportedFrameRatesFor(JNIEnv *env, jobject thiz,
int32_t width, int32_t height) {
VideoCapabilities* const videoCaps = getVideoCapabilities(env, thiz);
if (videoCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
}
std::optional<Range<double>> frameRates = videoCaps->getSupportedFrameRatesFor(width, height);
if (!frameRates) {
jniThrowException(env, "java/lang/IllegalArgumentException", "unsupported size");
return NULL;
}
jobject jFrameRates = convertToJavaDoubleRange(env, frameRates.value());
return jFrameRates;
}
static jobject android_media_VideoCapabilities_getSupportedWidthsFor(JNIEnv *env, jobject thiz,
int32_t height) {
VideoCapabilities* const videoCaps = getVideoCapabilities(env, thiz);
if (videoCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
}
std::optional<Range<int32_t>> supportedWidths = videoCaps->getSupportedWidthsFor(height);
if (!supportedWidths) {
jniThrowException(env, "java/lang/IllegalArgumentException", "unsupported height");
return NULL;
}
jobject jSupportedWidths = convertToJavaIntRange(env, supportedWidths.value());
return jSupportedWidths;
}
static jobject android_media_VideoCapabilities_getSupportedHeightsFor(JNIEnv *env, jobject thiz,
int32_t width) {
VideoCapabilities* const videoCaps = getVideoCapabilities(env, thiz);
if (videoCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
}
std::optional<Range<int32_t>> supportedHeights = videoCaps->getSupportedHeightsFor(width);
if (!supportedHeights) {
jniThrowException(env, "java/lang/IllegalArgumentException", "unsupported width");
return NULL;
}
jobject jSupportedHeights = convertToJavaIntRange(env, supportedHeights.value());
return jSupportedHeights;
}
static jint android_media_VideoCapabilities_getSmallerDimensionUpperLimit(JNIEnv *env,
jobject thiz) {
VideoCapabilities* const videoCaps = getVideoCapabilities(env, thiz);
if (videoCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
int smallerDimensionUpperLimit = videoCaps->getSmallerDimensionUpperLimit();
return smallerDimensionUpperLimit;
}
// EncoderCapabilities
static void android_media_EncoderCapabilities_native_init(JNIEnv *env, jobject /* thiz */) {
jclass clazz = env->FindClass(
"android/media/MediaCodecInfo$EncoderCapabilities$EncoderCapsNativeImpl");
if (clazz == NULL) {
return;
}
fields.encoderCapsContext = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.encoderCapsContext == NULL) {
return;
}
env->DeleteLocalRef(clazz);
}
static jboolean android_media_EncoderCapabilities_isBitrateModeSupported(JNIEnv *env, jobject thiz,
int mode) {
EncoderCapabilities* const encoderCaps = getEncoderCapabilities(env, thiz);
if (encoderCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
bool res = encoderCaps->isBitrateModeSupported(mode);
return res;
}
// CodecCapabilities
static void android_media_CodecCapabilities_native_init(JNIEnv *env, jobject /* thiz */) {
jclass codecCapsClazz
= env->FindClass("android/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl");
if (codecCapsClazz == NULL) {
return;
}
fields.codecCapsContext = env->GetFieldID(codecCapsClazz, "mNativeContext", "J");
if (fields.codecCapsContext == NULL) {
return;
}
env->DeleteLocalRef(codecCapsClazz);
}
static jobject android_media_CodecCapabilities_createFromProfileLevel(JNIEnv *env,
jobject /* thiz */, jstring mediaType, jint profile, jint level) {
if (mediaType == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
const char *mediaTypeStr = env->GetStringUTFChars(mediaType, nullptr);
if (mediaTypeStr == nullptr) {
// Out of memory exception already pending.
return NULL;
}
std::shared_ptr<CodecCapabilities> codecCaps = CodecCapabilities::CreateFromProfileLevel(
mediaTypeStr, profile, level);
jobject javaCodecCapsImpl = convertToJavaCodecCapsNativeImpl(env, codecCaps);
env->ReleaseStringUTFChars(mediaType, mediaTypeStr);
return javaCodecCapsImpl;
}
static jobject android_media_CodecCapabilities_native_dup(JNIEnv *env, jobject thiz) {
sp<JCodecCapabilities> jCodecCaps = getCodecCapabilities(env, thiz);
// As the CodecCaps objects are ready ony, it is ok to use the default copy constructor.
// The duplicate CodecCaps will share the same subobjects with the existing one.
// The lifetime of subobjects are managed by the shared pointer and sp.
std::shared_ptr<CodecCapabilities> duplicate
= std::make_shared<CodecCapabilities>(*(jCodecCaps->getCodecCaps()));
jobject javaCodecCapsImpl = convertToJavaCodecCapsNativeImpl(env, duplicate);
return javaCodecCapsImpl;
}
static void android_media_CodecCapabilities_native_finalize(JNIEnv *env, jobject thiz) {
ALOGV("native_finalize");
setCodecCapabilities(env, thiz, NULL);
}
static jint android_media_CodecCapabilities_getMaxSupportedInstances(JNIEnv *env, jobject thiz) {
sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz);
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
int maxSupportedInstances = codecCaps->getMaxSupportedInstances();
return maxSupportedInstances;
}
static jstring android_media_CodecCapabilities_getMimeType(JNIEnv *env, jobject thiz) {
sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz);
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
}
std::string mediaType = codecCaps->getMediaType();
return env->NewStringUTF(mediaType.c_str());
}
static jboolean android_media_CodecCapabilities_isFeatureRequired(
JNIEnv *env, jobject thiz, jstring name) {
sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz);
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
if (name == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return false;
}
const char *nameStr = env->GetStringUTFChars(name, NULL);
if (nameStr == NULL) {
// Out of memory exception already pending.
return false;
}
bool isFeatureRequired = codecCaps->isFeatureRequired(nameStr);
env->ReleaseStringUTFChars(name, nameStr);
return isFeatureRequired;
}
static jboolean android_media_CodecCapabilities_isFeatureSupported(
JNIEnv *env, jobject thiz, jstring name) {
sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz);
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
if (name == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return false;
}
const char *nameStr = env->GetStringUTFChars(name, NULL);
if (nameStr == NULL) {
// Out of memory exception already pending.
return false;
}
bool isFeatureSupported = codecCaps->isFeatureSupported(nameStr);
env->ReleaseStringUTFChars(name, nameStr);
return isFeatureSupported;
}
static jboolean android_media_CodecCapabilities_isFormatSupported(JNIEnv *env, jobject thiz,
jobjectArray keys, jobjectArray values) {
sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz);
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
sp<AMessage> format;
status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
if (err != OK) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return -ENOENT;;
}
return codecCaps->isFormatSupported(format);
}
static jboolean android_media_CodecCapabilities_isRegular(JNIEnv *env, jobject thiz) {
sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz);
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
bool res = codecCaps->isRegular();
return res;
}
static jobjectArray android_media_CodecCapabilities_validFeatures(JNIEnv *env, jobject thiz) {
sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz);
if (codecCaps == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
}
std::vector<std::string> features = codecCaps->validFeatures();
jclass stringClazz = env->FindClass("java/lang/String");
CHECK(stringClazz != NULL);
jobjectArray jFeatures = env->NewObjectArray(features.size(), stringClazz, NULL);
for (int i = 0; i < features.size(); i++) {
jstring jFeature = env->NewStringUTF(features.at(i).c_str());
env->SetObjectArrayElement(jFeatures, i, jFeature);
env->DeleteLocalRef(jFeature);
jFeature = NULL;
}
return jFeatures;
}
// ----------------------------------------------------------------------------
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}
};
static const JNINativeMethod gPerformancePointMethods[] = {
{"native_covers", "(Landroid/media/MediaCodecInfo$VideoCapabilities$PerformancePoint;)Z", (void *)android_media_VideoCapabilities_PerformancePoint_covers},
{"native_equals", "(Landroid/media/MediaCodecInfo$VideoCapabilities$PerformancePoint;)Z", (void *)android_media_VideoCapabilities_PerformancePoint_equals},
};
static const JNINativeMethod gVideoCapsMethods[] = {
{"native_init", "()V", (void *)android_media_VideoCapabilities_native_init},
{"native_areSizeAndRateSupported", "(IID)Z", (void *)android_media_VideoCapabilities_areSizeAndRateSupported},
{"native_isSizeSupported", "(II)Z", (void *)android_media_VideoCapabilities_isSizeSupported},
{"native_getAchievableFrameRatesFor", "(II)Landroid/util/Range;", (void *)android_media_VideoCapabilities_getAchievableFrameRatesFor},
{"native_getSupportedFrameRatesFor", "(II)Landroid/util/Range;", (void *)android_media_VideoCapabilities_getSupportedFrameRatesFor},
{"native_getSupportedWidthsFor", "(I)Landroid/util/Range;", (void *)android_media_VideoCapabilities_getSupportedWidthsFor},
{"native_getSupportedHeightsFor", "(I)Landroid/util/Range;", (void *)android_media_VideoCapabilities_getSupportedHeightsFor},
{"native_getSmallerDimensionUpperLimit", "()I", (void *)android_media_VideoCapabilities_getSmallerDimensionUpperLimit}
};
static const JNINativeMethod gEncoderCapsMethods[] = {
{"native_init", "()V", (void *)android_media_EncoderCapabilities_native_init},
{"native_isBitrateModeSupported", "(I)Z", (void *)android_media_EncoderCapabilities_isBitrateModeSupported}
};
static const JNINativeMethod gCodecCapsMethods[] = {
{ "native_init", "()V", (void *)android_media_CodecCapabilities_native_init },
{ "native_createFromProfileLevel", "(Ljava/lang/String;II)Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl;", (void *)android_media_CodecCapabilities_createFromProfileLevel },
{ "native_dup", "()Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl;", (void *)android_media_CodecCapabilities_native_dup },
{ "native_finalize", "()V", (void *)android_media_CodecCapabilities_native_finalize },
{ "native_getMaxSupportedInstances", "()I", (void *)android_media_CodecCapabilities_getMaxSupportedInstances },
{ "native_getMimeType", "()Ljava/lang/String;", (void *)android_media_CodecCapabilities_getMimeType },
{ "native_isFeatureRequired", "(Ljava/lang/String;)Z", (void *)android_media_CodecCapabilities_isFeatureRequired },
{ "native_isFeatureSupported", "(Ljava/lang/String;)Z", (void *)android_media_CodecCapabilities_isFeatureSupported },
{ "native_isFormatSupported", "([Ljava/lang/String;[Ljava/lang/Object;)Z", (void *)android_media_CodecCapabilities_isFormatSupported },
{ "native_isRegular", "()Z", (void *)android_media_CodecCapabilities_isRegular },
{ "native_validFeatures", "()[Ljava/lang/String;", (void *)android_media_CodecCapabilities_validFeatures },
};
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;
}
result = AndroidRuntime::registerNativeMethods(env,
"android/media/MediaCodecInfo$VideoCapabilities$PerformancePoint",
gPerformancePointMethods, NELEM(gPerformancePointMethods));
if (result != JNI_OK) {
return result;
}
result = AndroidRuntime::registerNativeMethods(env,
"android/media/MediaCodecInfo$VideoCapabilities$VideoCapsNativeImpl",
gVideoCapsMethods, NELEM(gVideoCapsMethods));
if (result != JNI_OK) {
return result;
}
result = AndroidRuntime::registerNativeMethods(env,
"android/media/MediaCodecInfo$EncoderCapabilities$EncoderCapsNativeImpl",
gEncoderCapsMethods, NELEM(gEncoderCapsMethods));
if (result != JNI_OK) {
return result;
}
result = AndroidRuntime::registerNativeMethods(env,
"android/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl",
gCodecCapsMethods, NELEM(gCodecCapsMethods));
return result;
}