Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08e7706d authored by Sampath Shetty's avatar Sampath Shetty Committed by Mikhail Naganov
Browse files

Hook up MediaExtractor getAudioPresentations to native impl

Adds interface to MediaExtractor JNI to retrieve AudioPresentations

Test: Manual test
Bug: 119312182
Change-Id: I5802ff20b8221aeede25148cb3213b0e9e74f270
parent 9d7964b9
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -434,9 +434,12 @@ final public class MediaExtractor {
     */
    @NonNull
    public List<AudioPresentation> getAudioPresentations(int trackIndex) {
        return new ArrayList<AudioPresentation>();
        return native_getAudioPresentations(trackIndex);
    }

    @NonNull
    private native List<AudioPresentation> native_getAudioPresentations(int trackIndex);

    /**
     * Get the PSSH info if present.
     * @return a map of uuid-to-bytes, with the uuid specifying
+93 −131
Original line number Diff line number Diff line
@@ -14,173 +14,135 @@
 * limitations under the License.
 */

#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_

#include "jni.h"

#include <media/AudioPresentationInfo.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>

#include <media/stagefright/foundation/ADebug.h>  // CHECK
#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <nativehelper/ScopedLocalRef.h>

namespace android {

struct JAudioPresentationInfo {
    struct fields_t {
        jclass      clazz;
        jclass      clazz = NULL;
        jmethodID   constructID;

        // list parameters
        jclass listclazz;
        jclass listClazz = NULL;
        jmethodID listConstructId;
        jmethodID listAddId;

        // hashmap parameters
        jclass hashMapClazz = NULL;
        jmethodID hashMapConstructID;
        jmethodID hashMapPutID;

        // ulocale parameters
        jclass ulocaleClazz = NULL;
        jmethodID ulocaleConstructID;

        void init(JNIEnv *env) {
            jclass lclazz = env->FindClass("android/media/AudioPresentation");
            if (lclazz == NULL) {
                return;
            }

            CHECK(lclazz != NULL);
            clazz = (jclass)env->NewGlobalRef(lclazz);
            if (clazz == NULL) {
                return;
            }

            CHECK(clazz != NULL);
            constructID = env->GetMethodID(clazz, "<init>",
                    "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
            env->DeleteLocalRef(lclazz);
            CHECK(constructID != NULL);

            // list objects
            jclass llistclazz = env->FindClass("java/util/ArrayList");
            CHECK(llistclazz != NULL);
            listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
            CHECK(listclazz != NULL);
            listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
            jclass llistClazz = env->FindClass("java/util/ArrayList");
            CHECK(llistClazz != NULL);
            listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
            CHECK(listClazz != NULL);
            listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
            CHECK(listConstructId != NULL);
            listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
            listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
            CHECK(listAddId != NULL);
            env->DeleteLocalRef(llistclazz);
        }

        void exit(JNIEnv *env) {
            env->DeleteGlobalRef(clazz);
            clazz = NULL;
            env->DeleteGlobalRef(listclazz);
            listclazz = NULL;
        }
    };

    static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
        ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));

        if (hashMapClazz.get() == NULL) {
            return -EINVAL;
        }
        jmethodID hashMapConstructID =
            env->GetMethodID(hashMapClazz.get(), "<init>", "()V");

        if (hashMapConstructID == NULL) {
            return -EINVAL;
        }
        jmethodID hashMapPutID =
            env->GetMethodID(
                    hashMapClazz.get(),
            // hashmap objects
            jclass lhashMapClazz = env->FindClass("java/util/HashMap");
            CHECK(lhashMapClazz != NULL);
            hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
            CHECK(hashMapClazz != NULL);
            hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
            CHECK(hashMapConstructID != NULL);
            hashMapPutID = env->GetMethodID(
                    hashMapClazz,
                    "put",
                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
            CHECK(hashMapPutID != NULL);

        if (hashMapPutID == NULL) {
            return -EINVAL;
        }

        jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);

        for (size_t i = 0; i < msg->countEntries(); ++i) {
            AMessage::Type valueType;
            const char *key = msg->getEntryNameAt(i, &valueType);

            if (!strncmp(key, "android._", 9)) {
                // don't expose private keys (starting with android._)
                continue;
            }
            jobject valueObj = NULL;
            AString val;
            CHECK(msg->findString(key, &val));
            valueObj = env->NewStringUTF(val.c_str());
            if (valueObj != NULL) {
                ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
                if (localeClazz.get() == NULL) {
                    return -EINVAL;
                }
                jmethodID localeConstructID =
                        env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
                if (localeConstructID == NULL) {
                    return -EINVAL;
                }
                jstring jLanguage = env->NewStringUTF(key);
                jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
                env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj);
                env->DeleteLocalRef(jLocale); jLocale = NULL;
                env->DeleteLocalRef(valueObj); valueObj = NULL;
                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
            }
        }

        *map = hashMap;

        return OK;
            jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
            CHECK(lulocaleClazz != NULL);
            ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
            CHECK(ulocaleClazz != NULL);
            ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
            CHECK(ulocaleConstructID != NULL);
        }

    jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
        jobject list = env->NewObject(fields.listclazz, fields.listConstructId);

        for (size_t i = 0; i < info.countPresentations(); ++i) {
            const sp<AudioPresentation> &ap = info.getPresentation(i);
            jobject jLabelObject;

            sp<AMessage> labelMessage = new AMessage();
            for (size_t i = 0; i < ap->mLabels.size(); ++i) {
                labelMessage->setString(ap->mLabels.keyAt(i).string(),
                                        ap->mLabels.valueAt(i).string());
            }
            if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
                return NULL;
            }
            ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
            if (localeClazz.get() == NULL) {
                return NULL;
            }
            jmethodID localeConstructID =
                    env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
            if (localeConstructID == NULL) {
                return NULL;
            }
            jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str());
            jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
            jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
                                static_cast<jint>(ap->mPresentationId),
                                static_cast<jint>(ap->mProgramId),
                                jLocale,
                                static_cast<jint>(ap->mMasteringIndication),
                                static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
                                    1 : 0),
                                static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
                                    1 : 0),
                                static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
                                    1 : 0),
                                jLabelObject);
            if (jValueObj == NULL) {
                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
                return NULL;
        void exit(JNIEnv *env) {
            env->DeleteGlobalRef(clazz); clazz = NULL;
            env->DeleteGlobalRef(listClazz); listClazz = NULL;
            env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
            env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
        }
    };

            env->CallBooleanMethod(list, fields.listAddId, jValueObj);
            env->DeleteLocalRef(jLocale); jLocale = NULL;
            env->DeleteLocalRef(jValueObj); jValueObj = NULL;
            env->DeleteLocalRef(jLanguage); jLanguage = NULL;
        }
        return list;
    static jobject asJobject(JNIEnv *env, const fields_t& fields) {
        return env->NewObject(fields.listClazz, fields.listConstructId);
    }

    static void addPresentations(JNIEnv *env, const fields_t& fields,
                    const AudioPresentationCollection& presentations, jobject presentationsJObj) {
        for (const auto& ap : presentations) {
            ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
            if (jLabelObject == nullptr) return;
            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
            if (jLanguage == nullptr) return;
            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
                            fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
            ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
                            static_cast<jint>(ap.mPresentationId),
                            static_cast<jint>(ap.mProgramId),
                            jLocale.get(),
                            static_cast<jint>(ap.mMasteringIndication),
                            static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
                            static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
                            static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
                            jLabelObject.get()));
            if (jValueObj != nullptr) {
                env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
            }
        }
    }

  private:
    static ScopedLocalRef<jobject> convertLabelsToMap(
            JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
        ScopedLocalRef<jobject> nullMap(env, nullptr);
        ScopedLocalRef<jobject> hashMap(env, env->NewObject(
                        fields.hashMapClazz, fields.hashMapConstructID));
        if (hashMap == nullptr) {
            return nullMap;
        }

        for (const auto& label : labels) {
            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
            if (jLanguage == nullptr) return nullMap;
            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
                            fields.ulocaleClazz,
                            fields.ulocaleConstructID,
                            jLanguage.get()));
            if (jLocale == nullptr) return nullMap;
            ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
            if (jValue == nullptr) return nullMap;
            env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
        }
        return hashMap;
    }
};
}  // namespace android
+33 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define LOG_TAG "MediaExtractor-JNI"
#include <utils/Log.h>

#include "android_media_AudioPresentation.h"
#include "android_media_MediaDataSource.h"
#include "android_media_MediaExtractor.h"
#include "android_media_MediaMetricsJNI.h"
@@ -56,6 +57,7 @@ struct fields_t {
};

static fields_t gFields;
static JAudioPresentationInfo::fields_t gAudioPresentationFields;

JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
    : mClass(NULL),
@@ -289,6 +291,10 @@ bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const {
    return mImpl->getCachedDuration(durationUs, eos);
}

status_t JMediaExtractor::getAudioPresentations(size_t trackIdx,
        AudioPresentationCollection *presentations) const {
    return mImpl->getAudioPresentations(trackIdx, presentations);
}
}  // namespace android

////////////////////////////////////////////////////////////////////////////////
@@ -668,6 +674,28 @@ static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
    return JNI_TRUE;
}

static jobject android_media_MediaExtractor_getAudioPresentations(
        JNIEnv *env, jobject thiz, jint trackIdx) {
    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
    if (extractor == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return presentationsJObj;
    }
    AudioPresentationCollection presentations;
    status_t err = extractor->getAudioPresentations(trackIdx, &presentations);
    if (err == ERROR_END_OF_STREAM || err == ERROR_UNSUPPORTED) {
        return presentationsJObj;
    } else if (err != OK) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return presentationsJObj;
    }

    JAudioPresentationInfo::addPresentations(
            env, gAudioPresentationFields, presentations, presentationsJObj);
    return presentationsJObj;
}

static void android_media_MediaExtractor_native_init(JNIEnv *env) {
    jclass clazz = env->FindClass("android/media/MediaExtractor");
    CHECK(clazz != NULL);
@@ -683,6 +711,8 @@ static void android_media_MediaExtractor_native_init(JNIEnv *env) {

    gFields.cryptoInfoSetPatternID =
        env->GetMethodID(clazz, "setPattern", "(II)V");

    gAudioPresentationFields.init(env);
}

static void android_media_MediaExtractor_native_setup(
@@ -963,6 +993,9 @@ static const JNINativeMethod gMethods[] = {

    {"native_getMetrics",          "()Landroid/os/PersistableBundle;",
      (void *)android_media_MediaExtractor_native_getMetrics},

    { "native_getAudioPresentations", "(I)Ljava/util/List;",
      (void *)android_media_MediaExtractor_getAudioPresentations },
};

int register_android_media_MediaExtractor(JNIEnv *env) {
+3 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_

#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <media/MediaSource.h>
#include <media/DataSource.h>
#include <utils/Errors.h>
@@ -66,6 +67,8 @@ struct JMediaExtractor : public RefBase {
    status_t getMetrics(Parcel *reply) const;

    bool getCachedDuration(int64_t *durationUs, bool *eos) const;
    status_t getAudioPresentations(size_t trackIdx,
            AudioPresentationCollection *presentations) const;

protected:
    virtual ~JMediaExtractor();