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