1 /*
2  * Copyright 2018, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
18 #define _ANDROID_MEDIA_AUDIOPRESENTATION_H_
19 
20 #include "jni.h"
21 
22 #include <aidl/android/hardware/tv/tuner/AudioPresentation.h>
23 #include <media/stagefright/foundation/ADebug.h>  // CHECK
24 #include <media/stagefright/foundation/AudioPresentationInfo.h>
25 #include <nativehelper/ScopedLocalRef.h>
26 
27 using ::aidl::android::hardware::tv::tuner::AudioPreselectionRenderingIndicationType;
28 using TunerAudioPresentation = ::aidl::android::hardware::tv::tuner::AudioPresentation;
29 
30 namespace android {
31 
32 struct JAudioPresentationInfo {
33     struct fields_t {
34         jclass      clazz = NULL;
35         jmethodID   constructID;
36 
37         // list parameters
38         jclass listClazz = NULL;
39         jmethodID listConstructId;
40         jmethodID listAddId;
41 
42         // hashmap parameters
43         jclass hashMapClazz = NULL;
44         jmethodID hashMapConstructID;
45         jmethodID hashMapPutID;
46 
47         // ulocale parameters
48         jclass ulocaleClazz = NULL;
49         jmethodID ulocaleConstructID;
50 
initJAudioPresentationInfo::fields_t51         void init(JNIEnv *env) {
52             jclass lclazz = env->FindClass("android/media/AudioPresentation");
53             CHECK(lclazz != NULL);
54             clazz = (jclass)env->NewGlobalRef(lclazz);
55             CHECK(clazz != NULL);
56             constructID = env->GetMethodID(clazz, "<init>",
57                     "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
58             CHECK(constructID != NULL);
59 
60             // list objects
61             jclass llistClazz = env->FindClass("java/util/ArrayList");
62             CHECK(llistClazz != NULL);
63             listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
64             CHECK(listClazz != NULL);
65             listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
66             CHECK(listConstructId != NULL);
67             listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
68             CHECK(listAddId != NULL);
69 
70             // hashmap objects
71             jclass lhashMapClazz = env->FindClass("java/util/HashMap");
72             CHECK(lhashMapClazz != NULL);
73             hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
74             CHECK(hashMapClazz != NULL);
75             hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
76             CHECK(hashMapConstructID != NULL);
77             hashMapPutID = env->GetMethodID(
78                     hashMapClazz,
79                     "put",
80                     "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
81             CHECK(hashMapPutID != NULL);
82 
83             jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
84             CHECK(lulocaleClazz != NULL);
85             ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
86             CHECK(ulocaleClazz != NULL);
87             ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
88             CHECK(ulocaleConstructID != NULL);
89         }
90 
exitJAudioPresentationInfo::fields_t91         void exit(JNIEnv *env) {
92             env->DeleteGlobalRef(clazz); clazz = NULL;
93             env->DeleteGlobalRef(listClazz); listClazz = NULL;
94             env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
95             env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
96         }
97     };
98 
asJobjectJAudioPresentationInfo99     static jobject asJobject(JNIEnv *env, const fields_t& fields) {
100         return env->NewObject(fields.listClazz, fields.listConstructId);
101     }
102 
addPresentationsJAudioPresentationInfo103     static void addPresentations(JNIEnv *env, const fields_t& fields,
104             const std::vector<TunerAudioPresentation>& tunerAudioPresentations,
105             jobject presentationsJObj) {
106         AudioPresentationCollection apc = {};
107         static const std::map<AudioPreselectionRenderingIndicationType, MasteringIndication> mMap {
108             { AudioPreselectionRenderingIndicationType::NOT_INDICATED, MASTERING_NOT_INDICATED },
109             { AudioPreselectionRenderingIndicationType::STEREO, MASTERED_FOR_STEREO },
110             { AudioPreselectionRenderingIndicationType::TWO_DIMENSIONAL, MASTERED_FOR_SURROUND },
111             { AudioPreselectionRenderingIndicationType::THREE_DIMENSIONAL, MASTERED_FOR_3D },
112             { AudioPreselectionRenderingIndicationType::HEADPHONE, MASTERED_FOR_HEADPHONE },
113         };
114         for (const auto& tap : tunerAudioPresentations) {
115             AudioPresentationV1 ap;
116             ap.mPresentationId = tap.preselection.preselectionId;
117             ap.mProgramId = tap.ac4ShortProgramId;
118             for (const auto& md : tap.preselection.labels) {
119                 ap.mLabels.insert(std::pair(md.language, md.text));
120             }
121             ap.mLanguage = tap.preselection.language;
122             ap.mMasteringIndication = MASTERING_NOT_INDICATED;
123             auto masteringSearch = mMap.find(tap.preselection.renderingIndication);
124             if (masteringSearch != mMap.end()) {
125                 ap.mMasteringIndication = masteringSearch->second;
126             }
127             ap.mAudioDescriptionAvailable = tap.preselection.hasAudioDescription;
128             ap.mSpokenSubtitlesAvailable = tap.preselection.hasSpokenSubtitles;
129             ap.mDialogueEnhancementAvailable = tap.preselection.hasDialogueEnhancement;
130             apc.push_back(ap);
131         }
132         addPresentations(env, fields, apc, presentationsJObj);
133     }
134 
addPresentationsJAudioPresentationInfo135     static void addPresentations(JNIEnv *env, const fields_t& fields,
136                     const AudioPresentationCollection& presentations, jobject presentationsJObj) {
137         for (const auto& ap : presentations) {
138             ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
139             if (jLabelObject == nullptr) return;
140             ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
141             if (jLanguage == nullptr) return;
142             ScopedLocalRef<jobject> jLocale(env, env->NewObject(
143                             fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
144             ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
145                             static_cast<jint>(ap.mPresentationId),
146                             static_cast<jint>(ap.mProgramId),
147                             jLocale.get(),
148                             static_cast<jint>(ap.mMasteringIndication),
149                             static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
150                             static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
151                             static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
152                             jLabelObject.get()));
153             if (jValueObj != nullptr) {
154                 env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
155             }
156         }
157     }
158 
159   private:
convertLabelsToMapJAudioPresentationInfo160     static ScopedLocalRef<jobject> convertLabelsToMap(
161             JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
162         ScopedLocalRef<jobject> nullMap(env, nullptr);
163         ScopedLocalRef<jobject> hashMap(env, env->NewObject(
164                         fields.hashMapClazz, fields.hashMapConstructID));
165         if (hashMap == nullptr) {
166             return nullMap;
167         }
168 
169         for (const auto& label : labels) {
170             ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
171             if (jLanguage == nullptr) return nullMap;
172             ScopedLocalRef<jobject> jLocale(env, env->NewObject(
173                             fields.ulocaleClazz,
174                             fields.ulocaleConstructID,
175                             jLanguage.get()));
176             if (jLocale == nullptr) return nullMap;
177             ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
178             if (jValue == nullptr) return nullMap;
179             env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
180         }
181         return hashMap;
182     }
183 };
184 }  // namespace android
185 
186 #endif  // _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
187