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 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 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 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 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 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 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 jobject res = env->CallObjectMethod( 140 hashMapObj, hashMapPutID, keyObj, valueObj); 141 142 env->DeleteLocalRef(valueObj); valueObj = NULL; 143 env->DeleteLocalRef(keyObj); keyObj = NULL; 144 } 145 146 status_t ConvertMessageToMap( 147 JNIEnv *env, const sp<AMessage> &msg, jobject *map) { 148 ScopedLocalRef<jclass> hashMapClazz( 149 env, env->FindClass("java/util/HashMap")); 150 151 if (hashMapClazz.get() == NULL) { 152 return -EINVAL; 153 } 154 155 jmethodID hashMapConstructID = 156 env->GetMethodID(hashMapClazz.get(), "<init>", "()V"); 157 158 if (hashMapConstructID == NULL) { 159 return -EINVAL; 160 } 161 162 jmethodID hashMapPutID = 163 env->GetMethodID( 164 hashMapClazz.get(), 165 "put", 166 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 167 168 if (hashMapPutID == NULL) { 169 return -EINVAL; 170 } 171 172 jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID); 173 174 for (size_t i = 0; i < msg->countEntries(); ++i) { 175 AMessage::Type valueType; 176 const char *key = msg->getEntryNameAt(i, &valueType); 177 178 jobject valueObj = NULL; 179 180 switch (valueType) { 181 case AMessage::kTypeInt32: 182 { 183 int32_t val; 184 CHECK(msg->findInt32(key, &val)); 185 186 valueObj = makeIntegerObject(env, val); 187 break; 188 } 189 190 case AMessage::kTypeInt64: 191 { 192 int64_t val; 193 CHECK(msg->findInt64(key, &val)); 194 195 valueObj = makeLongObject(env, val); 196 break; 197 } 198 199 case AMessage::kTypeFloat: 200 { 201 float val; 202 CHECK(msg->findFloat(key, &val)); 203 204 valueObj = makeFloatObject(env, val); 205 break; 206 } 207 208 case AMessage::kTypeString: 209 { 210 AString val; 211 CHECK(msg->findString(key, &val)); 212 213 valueObj = env->NewStringUTF(val.c_str()); 214 break; 215 } 216 217 case AMessage::kTypeBuffer: 218 { 219 sp<ABuffer> buffer; 220 CHECK(msg->findBuffer(key, &buffer)); 221 222 valueObj = makeByteBufferObject( 223 env, buffer->data(), buffer->size()); 224 break; 225 } 226 227 case AMessage::kTypeRect: 228 { 229 int32_t left, top, right, bottom; 230 CHECK(msg->findRect(key, &left, &top, &right, &bottom)); 231 232 SetMapInt32( 233 env, 234 hashMap, 235 hashMapPutID, 236 StringPrintf("%s-left", key).c_str(), 237 left); 238 239 SetMapInt32( 240 env, 241 hashMap, 242 hashMapPutID, 243 StringPrintf("%s-top", key).c_str(), 244 top); 245 246 SetMapInt32( 247 env, 248 hashMap, 249 hashMapPutID, 250 StringPrintf("%s-right", key).c_str(), 251 right); 252 253 SetMapInt32( 254 env, 255 hashMap, 256 hashMapPutID, 257 StringPrintf("%s-bottom", key).c_str(), 258 bottom); 259 break; 260 } 261 262 default: 263 break; 264 } 265 266 if (valueObj != NULL) { 267 jstring keyObj = env->NewStringUTF(key); 268 269 jobject res = env->CallObjectMethod( 270 hashMap, hashMapPutID, keyObj, valueObj); 271 272 env->DeleteLocalRef(keyObj); keyObj = NULL; 273 env->DeleteLocalRef(valueObj); valueObj = NULL; 274 } 275 } 276 277 *map = hashMap; 278 279 return OK; 280 } 281 282 status_t ConvertKeyValueArraysToMessage( 283 JNIEnv *env, jobjectArray keys, jobjectArray values, 284 sp<AMessage> *out) { 285 ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String")); 286 CHECK(stringClass.get() != NULL); 287 ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer")); 288 CHECK(integerClass.get() != NULL); 289 ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long")); 290 CHECK(longClass.get() != NULL); 291 ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float")); 292 CHECK(floatClass.get() != NULL); 293 ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer")); 294 CHECK(byteBufClass.get() != NULL); 295 296 sp<AMessage> msg = new AMessage; 297 298 jsize numEntries = 0; 299 300 if (keys != NULL) { 301 if (values == NULL) { 302 return -EINVAL; 303 } 304 305 numEntries = env->GetArrayLength(keys); 306 307 if (numEntries != env->GetArrayLength(values)) { 308 return -EINVAL; 309 } 310 } else if (values != NULL) { 311 return -EINVAL; 312 } 313 314 for (jsize i = 0; i < numEntries; ++i) { 315 jobject keyObj = env->GetObjectArrayElement(keys, i); 316 317 if (!env->IsInstanceOf(keyObj, stringClass.get())) { 318 return -EINVAL; 319 } 320 321 const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL); 322 323 if (tmp == NULL) { 324 return -ENOMEM; 325 } 326 327 AString key = tmp; 328 329 env->ReleaseStringUTFChars((jstring)keyObj, tmp); 330 tmp = NULL; 331 332 jobject valueObj = env->GetObjectArrayElement(values, i); 333 334 if (env->IsInstanceOf(valueObj, stringClass.get())) { 335 const char *value = env->GetStringUTFChars((jstring)valueObj, NULL); 336 337 if (value == NULL) { 338 return -ENOMEM; 339 } 340 341 msg->setString(key.c_str(), value); 342 343 env->ReleaseStringUTFChars((jstring)valueObj, value); 344 value = NULL; 345 } else if (env->IsInstanceOf(valueObj, integerClass.get())) { 346 jmethodID intValueID = 347 env->GetMethodID(integerClass.get(), "intValue", "()I"); 348 CHECK(intValueID != NULL); 349 350 jint value = env->CallIntMethod(valueObj, intValueID); 351 352 msg->setInt32(key.c_str(), value); 353 } else if (env->IsInstanceOf(valueObj, longClass.get())) { 354 jmethodID longValueID = 355 env->GetMethodID(longClass.get(), "longValue", "()J"); 356 CHECK(longValueID != NULL); 357 358 jlong value = env->CallLongMethod(valueObj, longValueID); 359 360 msg->setInt64(key.c_str(), value); 361 } else if (env->IsInstanceOf(valueObj, floatClass.get())) { 362 jmethodID floatValueID = 363 env->GetMethodID(floatClass.get(), "floatValue", "()F"); 364 CHECK(floatValueID != NULL); 365 366 jfloat value = env->CallFloatMethod(valueObj, floatValueID); 367 368 msg->setFloat(key.c_str(), value); 369 } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) { 370 jmethodID positionID = 371 env->GetMethodID(byteBufClass.get(), "position", "()I"); 372 CHECK(positionID != NULL); 373 374 jmethodID limitID = 375 env->GetMethodID(byteBufClass.get(), "limit", "()I"); 376 CHECK(limitID != NULL); 377 378 jint position = env->CallIntMethod(valueObj, positionID); 379 jint limit = env->CallIntMethod(valueObj, limitID); 380 381 sp<ABuffer> buffer = new ABuffer(limit - position); 382 383 void *data = env->GetDirectBufferAddress(valueObj); 384 385 if (data != NULL) { 386 memcpy(buffer->data(), 387 (const uint8_t *)data + position, 388 buffer->size()); 389 } else { 390 jmethodID arrayID = 391 env->GetMethodID(byteBufClass.get(), "array", "()[B"); 392 CHECK(arrayID != NULL); 393 394 jbyteArray byteArray = 395 (jbyteArray)env->CallObjectMethod(valueObj, arrayID); 396 CHECK(byteArray != NULL); 397 398 env->GetByteArrayRegion( 399 byteArray, 400 position, 401 buffer->size(), 402 (jbyte *)buffer->data()); 403 404 env->DeleteLocalRef(byteArray); byteArray = NULL; 405 } 406 407 msg->setBuffer(key.c_str(), buffer); 408 } 409 } 410 411 *out = msg; 412 413 return OK; 414 } 415 416 } // namespace android 417 418