android_media_Utils.cpp revision 8d5f3e31c914e29129f50fe9830d71adf52ab5cf
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    jobject res = env->CallObjectMethod(
140            hashMapObj, hashMapPutID, keyObj, valueObj);
141
142    env->DeleteLocalRef(valueObj); valueObj = NULL;
143    env->DeleteLocalRef(keyObj); keyObj = NULL;
144}
145
146status_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
282status_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