1 /*
2 * Copyright 2019, 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 "AndroidMediaStreams"
19
20 #include <utils/Log.h>
21 #include "android_media_Streams.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
AssetStream(SkStream * stream)31 AssetStream::AssetStream(SkStream* stream)
32 : mStream(stream), mPosition(0) {
33 }
34
~AssetStream()35 AssetStream::~AssetStream() {
36 }
37
GetData(const size_t offset,const size_t length,std::uint8_t * data)38 piex::Error AssetStream::GetData(
39 const size_t offset, const size_t length, std::uint8_t* data) {
40 // Seek first.
41 if (mPosition != offset) {
42 if (!mStream->seek(offset)) {
43 return piex::Error::kFail;
44 }
45 }
46
47 // Read bytes.
48 size_t size = mStream->read((void*)data, length);
49 mPosition = offset + size;
50
51 return size == length ? piex::Error::kOk : piex::Error::kFail;
52 }
53
BufferedStream(SkStream * stream)54 BufferedStream::BufferedStream(SkStream* stream)
55 : mStream(stream) {
56 }
57
~BufferedStream()58 BufferedStream::~BufferedStream() {
59 }
60
GetData(const size_t offset,const size_t length,std::uint8_t * data)61 piex::Error BufferedStream::GetData(
62 const size_t offset, const size_t length, std::uint8_t* data) {
63 // Seek first.
64 if (offset + length > mStreamBuffer.bytesWritten()) {
65 size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
66 if (sizeToRead <= kMinSizeToRead) {
67 sizeToRead = kMinSizeToRead;
68 }
69
70 void* tempBuffer = malloc(sizeToRead);
71 if (tempBuffer == NULL) {
72 return piex::Error::kFail;
73 }
74
75 size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
76 if (bytesRead != sizeToRead) {
77 free(tempBuffer);
78 return piex::Error::kFail;
79 }
80 mStreamBuffer.write(tempBuffer, bytesRead);
81 free(tempBuffer);
82 }
83
84 // Read bytes.
85 if (mStreamBuffer.read((void*)data, offset, length)) {
86 return piex::Error::kOk;
87 } else {
88 return piex::Error::kFail;
89 }
90 }
91
FileStream(const int fd)92 FileStream::FileStream(const int fd)
93 : mPosition(0) {
94 mFile = fdopen(fd, "r");
95 if (mFile == NULL) {
96 return;
97 }
98 }
99
FileStream(const String8 filename)100 FileStream::FileStream(const String8 filename)
101 : mPosition(0) {
102 mFile = fopen(filename.string(), "r");
103 if (mFile == NULL) {
104 return;
105 }
106 }
107
~FileStream()108 FileStream::~FileStream() {
109 if (mFile != NULL) {
110 fclose(mFile);
111 mFile = NULL;
112 }
113 }
114
GetData(const size_t offset,const size_t length,std::uint8_t * data)115 piex::Error FileStream::GetData(
116 const size_t offset, const size_t length, std::uint8_t* data) {
117 if (mFile == NULL) {
118 return piex::Error::kFail;
119 }
120
121 // Seek first.
122 if (mPosition != offset) {
123 fseek(mFile, offset, SEEK_SET);
124 }
125
126 // Read bytes.
127 size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
128 mPosition += size;
129
130 // Handle errors and verify the size.
131 if (ferror(mFile) || size != length) {
132 ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
133 return piex::Error::kFail;
134 }
135 return piex::Error::kOk;
136 }
137
exists() const138 bool FileStream::exists() const {
139 return mFile != NULL;
140 }
141
GetExifFromRawImage(piex::StreamInterface * stream,const String8 & filename,piex::PreviewImageData & image_data)142 bool GetExifFromRawImage(
143 piex::StreamInterface* stream, const String8& filename,
144 piex::PreviewImageData& image_data) {
145 // Reset the PreviewImageData to its default.
146 image_data = piex::PreviewImageData();
147
148 if (!piex::IsRaw(stream)) {
149 // Format not supported.
150 ALOGV("Format not supported: %s", filename.string());
151 return false;
152 }
153
154 piex::Error err = piex::GetPreviewImageData(stream, &image_data);
155
156 if (err != piex::Error::kOk) {
157 // The input data seems to be broken.
158 ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
159 return false;
160 }
161
162 return true;
163 }
164
ConvertKeyValueArraysToKeyedVector(JNIEnv * env,jobjectArray keys,jobjectArray values,KeyedVector<String8,String8> * keyedVector)165 bool ConvertKeyValueArraysToKeyedVector(
166 JNIEnv *env, jobjectArray keys, jobjectArray values,
167 KeyedVector<String8, String8>* keyedVector) {
168
169 int nKeyValuePairs = 0;
170 bool failed = false;
171 if (keys != NULL && values != NULL) {
172 nKeyValuePairs = env->GetArrayLength(keys);
173 failed = (nKeyValuePairs != env->GetArrayLength(values));
174 }
175
176 if (!failed) {
177 failed = ((keys != NULL && values == NULL) ||
178 (keys == NULL && values != NULL));
179 }
180
181 if (failed) {
182 ALOGE("keys and values arrays have different length");
183 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
184 return false;
185 }
186
187 for (int i = 0; i < nKeyValuePairs; ++i) {
188 // No need to check on the ArrayIndexOutOfBoundsException, since
189 // it won't happen here.
190 jstring key = (jstring) env->GetObjectArrayElement(keys, i);
191 jstring value = (jstring) env->GetObjectArrayElement(values, i);
192
193 const char* keyStr = env->GetStringUTFChars(key, NULL);
194 if (!keyStr) { // OutOfMemoryError
195 return false;
196 }
197
198 const char* valueStr = env->GetStringUTFChars(value, NULL);
199 if (!valueStr) { // OutOfMemoryError
200 env->ReleaseStringUTFChars(key, keyStr);
201 return false;
202 }
203
204 keyedVector->add(String8(keyStr), String8(valueStr));
205
206 env->ReleaseStringUTFChars(key, keyStr);
207 env->ReleaseStringUTFChars(value, valueStr);
208 env->DeleteLocalRef(key);
209 env->DeleteLocalRef(value);
210 }
211 return true;
212 }
213
makeIntegerObject(JNIEnv * env,int32_t value)214 static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
215 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
216 CHECK(clazz.get() != NULL);
217
218 jmethodID integerConstructID =
219 env->GetMethodID(clazz.get(), "<init>", "(I)V");
220 CHECK(integerConstructID != NULL);
221
222 return env->NewObject(clazz.get(), integerConstructID, value);
223 }
224
makeLongObject(JNIEnv * env,int64_t value)225 static jobject makeLongObject(JNIEnv *env, int64_t value) {
226 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
227 CHECK(clazz.get() != NULL);
228
229 jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
230 CHECK(longConstructID != NULL);
231
232 return env->NewObject(clazz.get(), longConstructID, value);
233 }
234
makeFloatObject(JNIEnv * env,float value)235 static jobject makeFloatObject(JNIEnv *env, float value) {
236 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
237 CHECK(clazz.get() != NULL);
238
239 jmethodID floatConstructID =
240 env->GetMethodID(clazz.get(), "<init>", "(F)V");
241 CHECK(floatConstructID != NULL);
242
243 return env->NewObject(clazz.get(), floatConstructID, value);
244 }
245
makeByteBufferObject(JNIEnv * env,const void * data,size_t size)246 static jobject makeByteBufferObject(
247 JNIEnv *env, const void *data, size_t size) {
248 jbyteArray byteArrayObj = env->NewByteArray(size);
249 env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
250
251 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
252 CHECK(clazz.get() != NULL);
253
254 jmethodID byteBufWrapID =
255 env->GetStaticMethodID(
256 clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
257 CHECK(byteBufWrapID != NULL);
258
259 jobject byteBufObj = env->CallStaticObjectMethod(
260 clazz.get(), byteBufWrapID, byteArrayObj);
261
262 env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
263
264 return byteBufObj;
265 }
266
SetMapInt32(JNIEnv * env,jobject hashMapObj,jmethodID hashMapPutID,const char * key,int32_t value)267 static void SetMapInt32(
268 JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
269 const char *key, int32_t value) {
270 jstring keyObj = env->NewStringUTF(key);
271 jobject valueObj = makeIntegerObject(env, value);
272
273 env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
274
275 env->DeleteLocalRef(valueObj); valueObj = NULL;
276 env->DeleteLocalRef(keyObj); keyObj = NULL;
277 }
278
ConvertMessageToMap(JNIEnv * env,const sp<AMessage> & msg,jobject * map)279 status_t ConvertMessageToMap(
280 JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
281 ScopedLocalRef<jclass> hashMapClazz(
282 env, env->FindClass("java/util/HashMap"));
283
284 if (hashMapClazz.get() == NULL) {
285 return -EINVAL;
286 }
287
288 jmethodID hashMapConstructID =
289 env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
290
291 if (hashMapConstructID == NULL) {
292 return -EINVAL;
293 }
294
295 jmethodID hashMapPutID =
296 env->GetMethodID(
297 hashMapClazz.get(),
298 "put",
299 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
300
301 if (hashMapPutID == NULL) {
302 return -EINVAL;
303 }
304
305 jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
306
307 for (size_t i = 0; i < msg->countEntries(); ++i) {
308 AMessage::Type valueType;
309 const char *key = msg->getEntryNameAt(i, &valueType);
310
311 if (!strncmp(key, "android._", 9)) {
312 // don't expose private keys (starting with android._)
313 continue;
314 }
315
316 jobject valueObj = NULL;
317
318 switch (valueType) {
319 case AMessage::kTypeInt32:
320 {
321 int32_t val;
322 CHECK(msg->findInt32(key, &val));
323
324 valueObj = makeIntegerObject(env, val);
325 break;
326 }
327
328 case AMessage::kTypeInt64:
329 {
330 int64_t val;
331 CHECK(msg->findInt64(key, &val));
332
333 valueObj = makeLongObject(env, val);
334 break;
335 }
336
337 case AMessage::kTypeFloat:
338 {
339 float val;
340 CHECK(msg->findFloat(key, &val));
341
342 valueObj = makeFloatObject(env, val);
343 break;
344 }
345
346 case AMessage::kTypeString:
347 {
348 AString val;
349 CHECK(msg->findString(key, &val));
350
351 valueObj = env->NewStringUTF(val.c_str());
352 break;
353 }
354
355 case AMessage::kTypeBuffer:
356 {
357 sp<ABuffer> buffer;
358 CHECK(msg->findBuffer(key, &buffer));
359
360 valueObj = makeByteBufferObject(
361 env, buffer->data(), buffer->size());
362 break;
363 }
364
365 case AMessage::kTypeRect:
366 {
367 int32_t left, top, right, bottom;
368 CHECK(msg->findRect(key, &left, &top, &right, &bottom));
369
370 SetMapInt32(
371 env,
372 hashMap,
373 hashMapPutID,
374 AStringPrintf("%s-left", key).c_str(),
375 left);
376
377 SetMapInt32(
378 env,
379 hashMap,
380 hashMapPutID,
381 AStringPrintf("%s-top", key).c_str(),
382 top);
383
384 SetMapInt32(
385 env,
386 hashMap,
387 hashMapPutID,
388 AStringPrintf("%s-right", key).c_str(),
389 right);
390
391 SetMapInt32(
392 env,
393 hashMap,
394 hashMapPutID,
395 AStringPrintf("%s-bottom", key).c_str(),
396 bottom);
397 break;
398 }
399
400 default:
401 break;
402 }
403
404 if (valueObj != NULL) {
405 jstring keyObj = env->NewStringUTF(key);
406
407 env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
408
409 env->DeleteLocalRef(keyObj); keyObj = NULL;
410 env->DeleteLocalRef(valueObj); valueObj = NULL;
411 }
412 }
413
414 *map = hashMap;
415
416 return OK;
417 }
418
ConvertKeyValueArraysToMessage(JNIEnv * env,jobjectArray keys,jobjectArray values,sp<AMessage> * out)419 status_t ConvertKeyValueArraysToMessage(
420 JNIEnv *env, jobjectArray keys, jobjectArray values,
421 sp<AMessage> *out) {
422 ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
423 CHECK(stringClass.get() != NULL);
424 ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
425 CHECK(integerClass.get() != NULL);
426 ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
427 CHECK(longClass.get() != NULL);
428 ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
429 CHECK(floatClass.get() != NULL);
430 ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
431 CHECK(byteBufClass.get() != NULL);
432
433 sp<AMessage> msg = new AMessage;
434
435 jsize numEntries = 0;
436
437 if (keys != NULL) {
438 if (values == NULL) {
439 return -EINVAL;
440 }
441
442 numEntries = env->GetArrayLength(keys);
443
444 if (numEntries != env->GetArrayLength(values)) {
445 return -EINVAL;
446 }
447 } else if (values != NULL) {
448 return -EINVAL;
449 }
450
451 for (jsize i = 0; i < numEntries; ++i) {
452 jobject keyObj = env->GetObjectArrayElement(keys, i);
453
454 if (!env->IsInstanceOf(keyObj, stringClass.get())) {
455 return -EINVAL;
456 }
457
458 const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
459
460 if (tmp == NULL) {
461 return -ENOMEM;
462 }
463
464 AString key = tmp;
465
466 env->ReleaseStringUTFChars((jstring)keyObj, tmp);
467 tmp = NULL;
468
469 if (key.startsWith("android._")) {
470 // don't propagate private keys (starting with android._)
471 continue;
472 }
473
474 jobject valueObj = env->GetObjectArrayElement(values, i);
475
476 if (env->IsInstanceOf(valueObj, stringClass.get())) {
477 const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
478
479 if (value == NULL) {
480 return -ENOMEM;
481 }
482
483 msg->setString(key.c_str(), value);
484
485 env->ReleaseStringUTFChars((jstring)valueObj, value);
486 value = NULL;
487 } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
488 jmethodID intValueID =
489 env->GetMethodID(integerClass.get(), "intValue", "()I");
490 CHECK(intValueID != NULL);
491
492 jint value = env->CallIntMethod(valueObj, intValueID);
493
494 msg->setInt32(key.c_str(), value);
495 } else if (env->IsInstanceOf(valueObj, longClass.get())) {
496 jmethodID longValueID =
497 env->GetMethodID(longClass.get(), "longValue", "()J");
498 CHECK(longValueID != NULL);
499
500 jlong value = env->CallLongMethod(valueObj, longValueID);
501
502 msg->setInt64(key.c_str(), value);
503 } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
504 jmethodID floatValueID =
505 env->GetMethodID(floatClass.get(), "floatValue", "()F");
506 CHECK(floatValueID != NULL);
507
508 jfloat value = env->CallFloatMethod(valueObj, floatValueID);
509
510 msg->setFloat(key.c_str(), value);
511 } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
512 jmethodID positionID =
513 env->GetMethodID(byteBufClass.get(), "position", "()I");
514 CHECK(positionID != NULL);
515
516 jmethodID limitID =
517 env->GetMethodID(byteBufClass.get(), "limit", "()I");
518 CHECK(limitID != NULL);
519
520 jint position = env->CallIntMethod(valueObj, positionID);
521 jint limit = env->CallIntMethod(valueObj, limitID);
522
523 sp<ABuffer> buffer = new ABuffer(limit - position);
524
525 void *data = env->GetDirectBufferAddress(valueObj);
526
527 if (data != NULL) {
528 memcpy(buffer->data(),
529 (const uint8_t *)data + position,
530 buffer->size());
531 } else {
532 jmethodID arrayID =
533 env->GetMethodID(byteBufClass.get(), "array", "()[B");
534 CHECK(arrayID != NULL);
535
536 jbyteArray byteArray =
537 (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
538 CHECK(byteArray != NULL);
539
540 env->GetByteArrayRegion(
541 byteArray,
542 position,
543 buffer->size(),
544 (jbyte *)buffer->data());
545
546 env->DeleteLocalRef(byteArray); byteArray = NULL;
547 }
548
549 msg->setBuffer(key.c_str(), buffer);
550 }
551 }
552
553 *out = msg;
554
555 return OK;
556 }
557
558 } // namespace android
559
560