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 29namespace android { 30 31bool 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 80static 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 91static 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 101static 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 112static 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 133static 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 145status_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 280status_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