Bug: 422728243 Test: atest NativeAMediaCodecInfoTest Flag: EXEMPT bugfix Change-Id: I4a131360e921696215cb5d038fd72a17bb76fd4d
1058 lines
40 KiB
C++
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;
|
|
} |