Home | History | Annotate | Download | only in jni
      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