1 /*
2 * Copyright 2011, 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 // #define LOG_NDEBUG 0
18 #define LOG_TAG "AndroidMediaUtils"
19
20 #include <utils/Log.h>
21 #include "android_media_Utils.h"
22
23 #include <media/stagefright/foundation/ADebug.h>
24 #include <media/stagefright/foundation/ABuffer.h>
25 #include <media/stagefright/foundation/AMessage.h>
26
27 #include <nativehelper/ScopedLocalRef.h>
28
29 namespace android {
30
ConvertKeyValueArraysToKeyedVector(JNIEnv * env,jobjectArray keys,jobjectArray values,KeyedVector<String8,String8> * keyedVector)31 bool ConvertKeyValueArraysToKeyedVector(
32 JNIEnv *env, jobjectArray keys, jobjectArray values,
33 KeyedVector<String8, String8>* keyedVector) {
34
35 int nKeyValuePairs = 0;
36 bool failed = false;
37 if (keys != NULL && values != NULL) {
38 nKeyValuePairs = env->GetArrayLength(keys);
39 failed = (nKeyValuePairs != env->GetArrayLength(values));
40 }
41
42 if (!failed) {
43 failed = ((keys != NULL && values == NULL) ||
44 (keys == NULL && values != NULL));
45 }
46
47 if (failed) {
48 ALOGE("keys and values arrays have different length");
49 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
50 return false;
51 }
52
53 for (int i = 0; i < nKeyValuePairs; ++i) {
54 // No need to check on the ArrayIndexOutOfBoundsException, since
55 // it won't happen here.
56 jstring key = (jstring) env->GetObjectArrayElement(keys, i);
57 jstring value = (jstring) env->GetObjectArrayElement(values, i);
58
59 const char* keyStr = env->GetStringUTFChars(key, NULL);
60 if (!keyStr) { // OutOfMemoryError
61 return false;
62 }
63
64 const char* valueStr = env->GetStringUTFChars(value, NULL);
65 if (!valueStr) { // OutOfMemoryError
66 env->ReleaseStringUTFChars(key, keyStr);
67 return false;
68 }
69
70 keyedVector->add(String8(keyStr), String8(valueStr));
71
72 env->ReleaseStringUTFChars(key, keyStr);
73 env->ReleaseStringUTFChars(value, valueStr);
74 env->DeleteLocalRef(key);
75 env->DeleteLocalRef(value);
76 }
77 return true;
78 }
79
makeIntegerObject(JNIEnv * env,int32_t value)80 static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
81 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
82 CHECK(clazz.get() != NULL);
83
84 jmethodID integerConstructID =
85 env->GetMethodID(clazz.get(), "<init>", "(I)V");
86 CHECK(integerConstructID != NULL);
87
88 return env->NewObject(clazz.get(), integerConstructID, value);
89 }
90
makeLongObject(JNIEnv * env,int64_t value)91 static jobject makeLongObject(JNIEnv *env, int64_t value) {
92 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
93 CHECK(clazz.get() != NULL);
94
95 jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
96 CHECK(longConstructID != NULL);
97
98 return env->NewObject(clazz.get(), longConstructID, value);
99 }
100
makeFloatObject(JNIEnv * env,float value)101 static jobject makeFloatObject(JNIEnv *env, float value) {
102 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
103 CHECK(clazz.get() != NULL);
104
105 jmethodID floatConstructID =
106 env->GetMethodID(clazz.get(), "<init>", "(F)V");
107 CHECK(floatConstructID != NULL);
108
109 return env->NewObject(clazz.get(), floatConstructID, value);
110 }
111
makeByteBufferObject(JNIEnv * env,const void * data,size_t size)112 static jobject makeByteBufferObject(
113 JNIEnv *env, const void *data, size_t size) {
114 jbyteArray byteArrayObj = env->NewByteArray(size);
115 env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
116
117 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
118 CHECK(clazz.get() != NULL);
119
120 jmethodID byteBufWrapID =
121 env->GetStaticMethodID(
122 clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
123 CHECK(byteBufWrapID != NULL);
124
125 jobject byteBufObj = env->CallStaticObjectMethod(
126 clazz.get(), byteBufWrapID, byteArrayObj);
127
128 env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
129
130 return byteBufObj;
131 }
132
SetMapInt32(JNIEnv * env,jobject hashMapObj,jmethodID hashMapPutID,const char * key,int32_t value)133 static void SetMapInt32(
134 JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
135 const char *key, int32_t value) {
136 jstring keyObj = env->NewStringUTF(key);
137 jobject valueObj = makeIntegerObject(env, value);
138
139 env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
140
141 env->DeleteLocalRef(valueObj); valueObj = NULL;
142 env->DeleteLocalRef(keyObj); keyObj = NULL;
143 }
144
ConvertMessageToMap(JNIEnv * env,const sp<AMessage> & msg,jobject * map)145 status_t ConvertMessageToMap(
146 JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
147 ScopedLocalRef<jclass> hashMapClazz(
148 env, env->FindClass("java/util/HashMap"));
149
150 if (hashMapClazz.get() == NULL) {
151 return -EINVAL;
152 }
153
154 jmethodID hashMapConstructID =
155 env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
156
157 if (hashMapConstructID == NULL) {
158 return -EINVAL;
159 }
160
161 jmethodID hashMapPutID =
162 env->GetMethodID(
163 hashMapClazz.get(),
164 "put",
165 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
166
167 if (hashMapPutID == NULL) {
168 return -EINVAL;
169 }
170
171 jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
172
173 for (size_t i = 0; i < msg->countEntries(); ++i) {
174 AMessage::Type valueType;
175 const char *key = msg->getEntryNameAt(i, &valueType);
176
177 jobject valueObj = NULL;
178
179 switch (valueType) {
180 case AMessage::kTypeInt32:
181 {
182 int32_t val;
183 CHECK(msg->findInt32(key, &val));
184
185 valueObj = makeIntegerObject(env, val);
186 break;
187 }
188
189 case AMessage::kTypeInt64:
190 {
191 int64_t val;
192 CHECK(msg->findInt64(key, &val));
193
194 valueObj = makeLongObject(env, val);
195 break;
196 }
197
198 case AMessage::kTypeFloat:
199 {
200 float val;
201 CHECK(msg->findFloat(key, &val));
202
203 valueObj = makeFloatObject(env, val);
204 break;
205 }
206
207 case AMessage::kTypeString:
208 {
209 AString val;
210 CHECK(msg->findString(key, &val));
211
212 valueObj = env->NewStringUTF(val.c_str());
213 break;
214 }
215
216 case AMessage::kTypeBuffer:
217 {
218 sp<ABuffer> buffer;
219 CHECK(msg->findBuffer(key, &buffer));
220
221 valueObj = makeByteBufferObject(
222 env, buffer->data(), buffer->size());
223 break;
224 }
225
226 case AMessage::kTypeRect:
227 {
228 int32_t left, top, right, bottom;
229 CHECK(msg->findRect(key, &left, &top, &right, &bottom));
230
231 SetMapInt32(
232 env,
233 hashMap,
234 hashMapPutID,
235 AStringPrintf("%s-left", key).c_str(),
236 left);
237
238 SetMapInt32(
239 env,
240 hashMap,
241 hashMapPutID,
242 AStringPrintf("%s-top", key).c_str(),
243 top);
244
245 SetMapInt32(
246 env,
247 hashMap,
248 hashMapPutID,
249 AStringPrintf("%s-right", key).c_str(),
250 right);
251
252 SetMapInt32(
253 env,
254 hashMap,
255 hashMapPutID,
256 AStringPrintf("%s-bottom", key).c_str(),
257 bottom);
258 break;
259 }
260
261 default:
262 break;
263 }
264
265 if (valueObj != NULL) {
266 jstring keyObj = env->NewStringUTF(key);
267
268 env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
269
270 env->DeleteLocalRef(keyObj); keyObj = NULL;
271 env->DeleteLocalRef(valueObj); valueObj = NULL;
272 }
273 }
274
275 *map = hashMap;
276
277 return OK;
278 }
279
ConvertKeyValueArraysToMessage(JNIEnv * env,jobjectArray keys,jobjectArray values,sp<AMessage> * out)280 status_t ConvertKeyValueArraysToMessage(
281 JNIEnv *env, jobjectArray keys, jobjectArray values,
282 sp<AMessage> *out) {
283 ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
284 CHECK(stringClass.get() != NULL);
285 ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
286 CHECK(integerClass.get() != NULL);
287 ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
288 CHECK(longClass.get() != NULL);
289 ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
290 CHECK(floatClass.get() != NULL);
291 ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
292 CHECK(byteBufClass.get() != NULL);
293
294 sp<AMessage> msg = new AMessage;
295
296 jsize numEntries = 0;
297
298 if (keys != NULL) {
299 if (values == NULL) {
300 return -EINVAL;
301 }
302
303 numEntries = env->GetArrayLength(keys);
304
305 if (numEntries != env->GetArrayLength(values)) {
306 return -EINVAL;
307 }
308 } else if (values != NULL) {
309 return -EINVAL;
310 }
311
312 for (jsize i = 0; i < numEntries; ++i) {
313 jobject keyObj = env->GetObjectArrayElement(keys, i);
314
315 if (!env->IsInstanceOf(keyObj, stringClass.get())) {
316 return -EINVAL;
317 }
318
319 const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
320
321 if (tmp == NULL) {
322 return -ENOMEM;
323 }
324
325 AString key = tmp;
326
327 env->ReleaseStringUTFChars((jstring)keyObj, tmp);
328 tmp = NULL;
329
330 jobject valueObj = env->GetObjectArrayElement(values, i);
331
332 if (env->IsInstanceOf(valueObj, stringClass.get())) {
333 const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
334
335 if (value == NULL) {
336 return -ENOMEM;
337 }
338
339 msg->setString(key.c_str(), value);
340
341 env->ReleaseStringUTFChars((jstring)valueObj, value);
342 value = NULL;
343 } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
344 jmethodID intValueID =
345 env->GetMethodID(integerClass.get(), "intValue", "()I");
346 CHECK(intValueID != NULL);
347
348 jint value = env->CallIntMethod(valueObj, intValueID);
349
350 msg->setInt32(key.c_str(), value);
351 } else if (env->IsInstanceOf(valueObj, longClass.get())) {
352 jmethodID longValueID =
353 env->GetMethodID(longClass.get(), "longValue", "()J");
354 CHECK(longValueID != NULL);
355
356 jlong value = env->CallLongMethod(valueObj, longValueID);
357
358 msg->setInt64(key.c_str(), value);
359 } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
360 jmethodID floatValueID =
361 env->GetMethodID(floatClass.get(), "floatValue", "()F");
362 CHECK(floatValueID != NULL);
363
364 jfloat value = env->CallFloatMethod(valueObj, floatValueID);
365
366 msg->setFloat(key.c_str(), value);
367 } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
368 jmethodID positionID =
369 env->GetMethodID(byteBufClass.get(), "position", "()I");
370 CHECK(positionID != NULL);
371
372 jmethodID limitID =
373 env->GetMethodID(byteBufClass.get(), "limit", "()I");
374 CHECK(limitID != NULL);
375
376 jint position = env->CallIntMethod(valueObj, positionID);
377 jint limit = env->CallIntMethod(valueObj, limitID);
378
379 sp<ABuffer> buffer = new ABuffer(limit - position);
380
381 void *data = env->GetDirectBufferAddress(valueObj);
382
383 if (data != NULL) {
384 memcpy(buffer->data(),
385 (const uint8_t *)data + position,
386 buffer->size());
387 } else {
388 jmethodID arrayID =
389 env->GetMethodID(byteBufClass.get(), "array", "()[B");
390 CHECK(arrayID != NULL);
391
392 jbyteArray byteArray =
393 (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
394 CHECK(byteArray != NULL);
395
396 env->GetByteArrayRegion(
397 byteArray,
398 position,
399 buffer->size(),
400 (jbyte *)buffer->data());
401
402 env->DeleteLocalRef(byteArray); byteArray = NULL;
403 }
404
405 msg->setBuffer(key.c_str(), buffer);
406 }
407 }
408
409 *out = msg;
410
411 return OK;
412 }
413
414 } // namespace android
415
416