blob: 52e99100e8e88b22f45967005a2217c16d09cf94 [file] [log] [blame]
Andreas Huber88572f72012-02-21 11:47:18 -08001/*
2 * Copyright 2012, 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 "MediaExtractor-JNI"
19#include <utils/Log.h>
20
21#include "android_media_MediaExtractor.h"
22
23#include "android_media_Utils.h"
24#include "android_runtime/AndroidRuntime.h"
Ruben Brunk87eac992013-09-09 17:44:59 -070025#include "android_runtime/Log.h"
Andreas Huber88572f72012-02-21 11:47:18 -080026#include "jni.h"
27#include "JNIHelp.h"
28
Andreas Huberd2506a52014-01-29 10:32:46 -080029#include <media/IMediaHTTPService.h>
Andreas Huberab57d032012-04-18 15:59:43 -070030#include <media/hardware/CryptoAPI.h>
Andreas Huber88572f72012-02-21 11:47:18 -080031#include <media/stagefright/foundation/ABuffer.h>
32#include <media/stagefright/foundation/ADebug.h>
33#include <media/stagefright/foundation/AMessage.h>
34#include <media/stagefright/DataSource.h>
35#include <media/stagefright/MediaErrors.h>
Andreas Huber91befdc2012-04-18 12:19:51 -070036#include <media/stagefright/MetaData.h>
Andreas Huber88572f72012-02-21 11:47:18 -080037#include <media/stagefright/NuMediaExtractor.h>
38
Lajos Molnar7be24522014-07-14 12:24:56 -070039#include <nativehelper/ScopedLocalRef.h>
40
Andreas Huberd2506a52014-01-29 10:32:46 -080041#include "android_util_Binder.h"
42
Andreas Huber88572f72012-02-21 11:47:18 -080043namespace android {
44
45struct fields_t {
46 jfieldID context;
Andreas Huber91befdc2012-04-18 12:19:51 -070047
48 jmethodID cryptoInfoSetID;
Andreas Huber88572f72012-02-21 11:47:18 -080049};
50
51static fields_t gFields;
52
Marco Nelissenc209a062012-08-24 09:55:44 -070053class JavaDataSourceBridge : public DataSource {
54 jmethodID mReadMethod;
55 jmethodID mGetSizeMethod;
56 jmethodID mCloseMethod;
57 jobject mDataSource;
58 public:
59 JavaDataSourceBridge(JNIEnv *env, jobject source) {
60 mDataSource = env->NewGlobalRef(source);
61
62 jclass datasourceclass = env->GetObjectClass(mDataSource);
63 CHECK(datasourceclass != NULL);
64
65 mReadMethod = env->GetMethodID(datasourceclass, "readAt", "(J[BI)I");
66 CHECK(mReadMethod != NULL);
67
68 mGetSizeMethod = env->GetMethodID(datasourceclass, "getSize", "()J");
69 CHECK(mGetSizeMethod != NULL);
70
71 mCloseMethod = env->GetMethodID(datasourceclass, "close", "()V");
72 CHECK(mCloseMethod != NULL);
73 }
74
75 ~JavaDataSourceBridge() {
76 JNIEnv *env = AndroidRuntime::getJNIEnv();
77 env->CallVoidMethod(mDataSource, mCloseMethod);
78 env->DeleteGlobalRef(mDataSource);
79 }
80
81 virtual status_t initCheck() const {
82 return OK;
83 }
84
85 virtual ssize_t readAt(off64_t offset, void* buffer, size_t size) {
86 JNIEnv *env = AndroidRuntime::getJNIEnv();
87
88 // XXX could optimize this by reusing the same array
89 jbyteArray byteArrayObj = env->NewByteArray(size);
90 env->DeleteLocalRef(env->GetObjectClass(mDataSource));
91 env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
Ashok Bhat136c08a2014-03-05 15:23:13 +000092 ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, (jint)size);
Marco Nelissenc209a062012-08-24 09:55:44 -070093 env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
94 env->DeleteLocalRef(byteArrayObj);
95 if (env->ExceptionCheck()) {
Ashok Bhat075e9a12014-01-06 13:45:09 +000096 ALOGW("Exception occurred while reading %zu at %lld", size, offset);
Marco Nelissenc209a062012-08-24 09:55:44 -070097 LOGW_EX(env);
98 env->ExceptionClear();
99 return -1;
100 }
101 return numread;
102 }
103
104 virtual status_t getSize(off64_t *size) {
105 JNIEnv *env = AndroidRuntime::getJNIEnv();
106
107 CHECK(size != NULL);
108
109 int64_t len = env->CallLongMethod(mDataSource, mGetSizeMethod);
110 if (len < 0) {
111 *size = ERROR_UNSUPPORTED;
112 } else {
113 *size = len;
114 }
115 return OK;
116 }
117};
118
Andreas Huber88572f72012-02-21 11:47:18 -0800119////////////////////////////////////////////////////////////////////////////////
120
121JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
122 : mClass(NULL),
123 mObject(NULL) {
124 jclass clazz = env->GetObjectClass(thiz);
125 CHECK(clazz != NULL);
126
127 mClass = (jclass)env->NewGlobalRef(clazz);
128 mObject = env->NewWeakGlobalRef(thiz);
129
130 mImpl = new NuMediaExtractor;
131}
132
133JMediaExtractor::~JMediaExtractor() {
134 JNIEnv *env = AndroidRuntime::getJNIEnv();
135
136 env->DeleteWeakGlobalRef(mObject);
137 mObject = NULL;
138 env->DeleteGlobalRef(mClass);
139 mClass = NULL;
140}
141
Andreas Huber07ea4262012-04-11 12:21:20 -0700142status_t JMediaExtractor::setDataSource(
Andreas Huberd2506a52014-01-29 10:32:46 -0800143 const sp<IMediaHTTPService> &httpService,
144 const char *path,
145 const KeyedVector<String8, String8> *headers) {
146 return mImpl->setDataSource(httpService, path, headers);
Andreas Huber07ea4262012-04-11 12:21:20 -0700147}
148
149status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
150 return mImpl->setDataSource(fd, offset, size);
Andreas Huber88572f72012-02-21 11:47:18 -0800151}
152
Marco Nelissenc209a062012-08-24 09:55:44 -0700153status_t JMediaExtractor::setDataSource(const sp<DataSource> &datasource) {
154 return mImpl->setDataSource(datasource);
155}
156
Andreas Huber88572f72012-02-21 11:47:18 -0800157size_t JMediaExtractor::countTracks() const {
158 return mImpl->countTracks();
159}
160
161status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
162 sp<AMessage> msg;
163 status_t err;
164 if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
165 return err;
166 }
167
168 JNIEnv *env = AndroidRuntime::getJNIEnv();
169
170 return ConvertMessageToMap(env, msg, format);
171}
172
Marco Nelissene20a6d52013-04-08 14:28:55 -0700173status_t JMediaExtractor::getFileFormat(jobject *format) const {
174 sp<AMessage> msg;
175 status_t err;
176 if ((err = mImpl->getFileFormat(&msg)) != OK) {
177 return err;
178 }
179
180 JNIEnv *env = AndroidRuntime::getJNIEnv();
181
182 return ConvertMessageToMap(env, msg, format);
183}
184
Andreas Huber88572f72012-02-21 11:47:18 -0800185status_t JMediaExtractor::selectTrack(size_t index) {
186 return mImpl->selectTrack(index);
187}
188
Andreas Huberf2855b32012-04-25 15:57:43 -0700189status_t JMediaExtractor::unselectTrack(size_t index) {
190 return mImpl->unselectTrack(index);
191}
192
193status_t JMediaExtractor::seekTo(
194 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
195 return mImpl->seekTo(timeUs, mode);
Andreas Huber88572f72012-02-21 11:47:18 -0800196}
197
198status_t JMediaExtractor::advance() {
199 return mImpl->advance();
200}
201
202status_t JMediaExtractor::readSampleData(
203 jobject byteBuf, size_t offset, size_t *sampleSize) {
204 JNIEnv *env = AndroidRuntime::getJNIEnv();
205
206 void *dst = env->GetDirectBufferAddress(byteBuf);
207
Ashok Bhat075e9a12014-01-06 13:45:09 +0000208 size_t dstSize;
Andreas Huberc52b9802012-03-12 14:04:01 -0700209 jbyteArray byteArray = NULL;
210
Lajos Molnar7be24522014-07-14 12:24:56 -0700211 ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
212 CHECK(byteBufClass.get() != NULL);
Andreas Huberc52b9802012-03-12 14:04:01 -0700213
Lajos Molnar7be24522014-07-14 12:24:56 -0700214 if (dst == NULL) {
Andreas Huberc52b9802012-03-12 14:04:01 -0700215 jmethodID arrayID =
Lajos Molnar7be24522014-07-14 12:24:56 -0700216 env->GetMethodID(byteBufClass.get(), "array", "()[B");
Andreas Huberc52b9802012-03-12 14:04:01 -0700217 CHECK(arrayID != NULL);
218
219 byteArray =
220 (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
221
222 if (byteArray == NULL) {
223 return INVALID_OPERATION;
224 }
225
226 jboolean isCopy;
227 dst = env->GetByteArrayElements(byteArray, &isCopy);
228
Ashok Bhat075e9a12014-01-06 13:45:09 +0000229 dstSize = (size_t) env->GetArrayLength(byteArray);
Andreas Huberc52b9802012-03-12 14:04:01 -0700230 } else {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000231 dstSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
Andreas Huber88572f72012-02-21 11:47:18 -0800232 }
233
Andreas Huber88572f72012-02-21 11:47:18 -0800234 if (dstSize < offset) {
Andreas Huberc52b9802012-03-12 14:04:01 -0700235 if (byteArray != NULL) {
236 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
237 }
238
Andreas Huber88572f72012-02-21 11:47:18 -0800239 return -ERANGE;
240 }
241
242 sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
243
244 status_t err = mImpl->readSampleData(buffer);
245
Andreas Huberc52b9802012-03-12 14:04:01 -0700246 if (byteArray != NULL) {
247 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
248 }
249
Andreas Huber88572f72012-02-21 11:47:18 -0800250 if (err != OK) {
251 return err;
252 }
253
254 *sampleSize = buffer->size();
255
Lajos Molnar7be24522014-07-14 12:24:56 -0700256 jmethodID positionID = env->GetMethodID(
257 byteBufClass.get(), "position", "(I)Ljava/nio/Buffer;");
258
259 CHECK(positionID != NULL);
260
261 jmethodID limitID = env->GetMethodID(
262 byteBufClass.get(), "limit", "(I)Ljava/nio/Buffer;");
263
264 CHECK(limitID != NULL);
265
266 jobject me = env->CallObjectMethod(
267 byteBuf, limitID, offset + *sampleSize);
268 env->DeleteLocalRef(me);
269 me = env->CallObjectMethod(
270 byteBuf, positionID, offset);
271 env->DeleteLocalRef(me);
272 me = NULL;
273
Andreas Huber88572f72012-02-21 11:47:18 -0800274 return OK;
275}
276
277status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
278 return mImpl->getSampleTrackIndex(trackIndex);
279}
280
281status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
282 return mImpl->getSampleTime(sampleTimeUs);
283}
284
Andreas Huber9b8e4962012-03-26 11:13:27 -0700285status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
Andreas Huber91befdc2012-04-18 12:19:51 -0700286 *sampleFlags = 0;
287
288 sp<MetaData> meta;
289 status_t err = mImpl->getSampleMeta(&meta);
290
291 if (err != OK) {
292 return err;
293 }
294
295 int32_t val;
296 if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
297 (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
298 }
299
300 uint32_t type;
301 const void *data;
302 size_t size;
303 if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
304 (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
305 }
306
307 return OK;
308}
309
310status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
311 return mImpl->getSampleMeta(sampleMeta);
Andreas Huber9b8e4962012-03-26 11:13:27 -0700312}
313
Andreas Huber74a78b02012-04-19 16:24:32 -0700314bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const {
315 return mImpl->getCachedDuration(durationUs, eos);
316}
317
Andreas Huber88572f72012-02-21 11:47:18 -0800318} // namespace android
319
320////////////////////////////////////////////////////////////////////////////////
321
322using namespace android;
323
324static sp<JMediaExtractor> setMediaExtractor(
325 JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
326 sp<JMediaExtractor> old =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000327 (JMediaExtractor *)env->GetLongField(thiz, gFields.context);
Andreas Huber88572f72012-02-21 11:47:18 -0800328
329 if (extractor != NULL) {
330 extractor->incStrong(thiz);
331 }
332 if (old != NULL) {
333 old->decStrong(thiz);
334 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000335 env->SetLongField(thiz, gFields.context, (jlong)extractor.get());
Andreas Huber88572f72012-02-21 11:47:18 -0800336
337 return old;
338}
339
340static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000341 return (JMediaExtractor *)env->GetLongField(thiz, gFields.context);
Andreas Huber88572f72012-02-21 11:47:18 -0800342}
343
344static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
345 setMediaExtractor(env, thiz, NULL);
346}
347
Andreas Huber60d610b2012-05-02 16:06:09 -0700348static jint android_media_MediaExtractor_getTrackCount(
Andreas Huber88572f72012-02-21 11:47:18 -0800349 JNIEnv *env, jobject thiz) {
350 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
351
352 if (extractor == NULL) {
353 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Andreas Huber07ea4262012-04-11 12:21:20 -0700354 return -1;
Andreas Huber88572f72012-02-21 11:47:18 -0800355 }
356
Ashok Bhat075e9a12014-01-06 13:45:09 +0000357 return (jint) extractor->countTracks();
Andreas Huber88572f72012-02-21 11:47:18 -0800358}
359
Andreas Huber60d610b2012-05-02 16:06:09 -0700360static jobject android_media_MediaExtractor_getTrackFormatNative(
Andreas Huber88572f72012-02-21 11:47:18 -0800361 JNIEnv *env, jobject thiz, jint index) {
362 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
363
364 if (extractor == NULL) {
365 jniThrowException(env, "java/lang/IllegalStateException", NULL);
366 return NULL;
367 }
368
369 jobject format;
370 status_t err = extractor->getTrackFormat(index, &format);
371
372 if (err != OK) {
373 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
374 return NULL;
375 }
376
377 return format;
378}
379
Marco Nelissene20a6d52013-04-08 14:28:55 -0700380static jobject android_media_MediaExtractor_getFileFormatNative(
381 JNIEnv *env, jobject thiz) {
382 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
383
384 if (extractor == NULL) {
385 jniThrowException(env, "java/lang/IllegalStateException", NULL);
386 return NULL;
387 }
388
389 jobject format;
390 status_t err = extractor->getFileFormat(&format);
391
392 if (err != OK) {
393 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
394 return NULL;
395 }
396
397 return format;
398}
399
Andreas Huber88572f72012-02-21 11:47:18 -0800400static void android_media_MediaExtractor_selectTrack(
401 JNIEnv *env, jobject thiz, jint index) {
402 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
403
404 if (extractor == NULL) {
405 jniThrowException(env, "java/lang/IllegalStateException", NULL);
406 return;
407 }
408
409 status_t err = extractor->selectTrack(index);
410
411 if (err != OK) {
412 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
413 return;
414 }
415}
416
Andreas Huberf2855b32012-04-25 15:57:43 -0700417static void android_media_MediaExtractor_unselectTrack(
418 JNIEnv *env, jobject thiz, jint index) {
Andreas Huber88572f72012-02-21 11:47:18 -0800419 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
420
421 if (extractor == NULL) {
422 jniThrowException(env, "java/lang/IllegalStateException", NULL);
423 return;
424 }
425
Andreas Huberf2855b32012-04-25 15:57:43 -0700426 status_t err = extractor->unselectTrack(index);
427
428 if (err != OK) {
429 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
430 return;
431 }
432}
433
434static void android_media_MediaExtractor_seekTo(
435 JNIEnv *env, jobject thiz, jlong timeUs, jint mode) {
436 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
437
438 if (extractor == NULL) {
439 jniThrowException(env, "java/lang/IllegalStateException", NULL);
440 return;
441 }
442
443 if (mode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC
Andreas Huberf86374a2012-05-09 11:25:29 -0700444 || mode >= MediaSource::ReadOptions::SEEK_CLOSEST) {
Andreas Huberf2855b32012-04-25 15:57:43 -0700445 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
446 return;
447 }
448
449 extractor->seekTo(timeUs, (MediaSource::ReadOptions::SeekMode)mode);
Andreas Huber88572f72012-02-21 11:47:18 -0800450}
451
452static jboolean android_media_MediaExtractor_advance(
453 JNIEnv *env, jobject thiz) {
454 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
455
456 if (extractor == NULL) {
457 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000458 return JNI_FALSE;
Andreas Huber88572f72012-02-21 11:47:18 -0800459 }
460
461 status_t err = extractor->advance();
462
463 if (err == ERROR_END_OF_STREAM) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000464 return JNI_FALSE;
Andreas Huber88572f72012-02-21 11:47:18 -0800465 } else if (err != OK) {
466 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000467 return JNI_FALSE;
Andreas Huber88572f72012-02-21 11:47:18 -0800468 }
469
Ashok Bhat075e9a12014-01-06 13:45:09 +0000470 return JNI_TRUE;
Andreas Huber88572f72012-02-21 11:47:18 -0800471}
472
473static jint android_media_MediaExtractor_readSampleData(
474 JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
475 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
476
477 if (extractor == NULL) {
478 jniThrowException(env, "java/lang/IllegalStateException", NULL);
479 return -1;
480 }
481
482 size_t sampleSize;
483 status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
484
485 if (err == ERROR_END_OF_STREAM) {
486 return -1;
487 } else if (err != OK) {
488 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000489 return -1;
Andreas Huber88572f72012-02-21 11:47:18 -0800490 }
491
Ashok Bhat075e9a12014-01-06 13:45:09 +0000492 return (jint) sampleSize;
Andreas Huber88572f72012-02-21 11:47:18 -0800493}
494
495static jint android_media_MediaExtractor_getSampleTrackIndex(
496 JNIEnv *env, jobject thiz) {
497 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
498
499 if (extractor == NULL) {
500 jniThrowException(env, "java/lang/IllegalStateException", NULL);
501 return -1;
502 }
503
504 size_t trackIndex;
505 status_t err = extractor->getSampleTrackIndex(&trackIndex);
506
507 if (err == ERROR_END_OF_STREAM) {
508 return -1;
509 } else if (err != OK) {
510 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000511 return -1;
Andreas Huber88572f72012-02-21 11:47:18 -0800512 }
513
Ashok Bhat075e9a12014-01-06 13:45:09 +0000514 return (jint) trackIndex;
Andreas Huber88572f72012-02-21 11:47:18 -0800515}
516
517static jlong android_media_MediaExtractor_getSampleTime(
518 JNIEnv *env, jobject thiz) {
519 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
520
521 if (extractor == NULL) {
522 jniThrowException(env, "java/lang/IllegalStateException", NULL);
523 return -1ll;
524 }
525
526 int64_t sampleTimeUs;
527 status_t err = extractor->getSampleTime(&sampleTimeUs);
528
529 if (err == ERROR_END_OF_STREAM) {
530 return -1ll;
531 } else if (err != OK) {
532 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000533 return -1ll;
Andreas Huber88572f72012-02-21 11:47:18 -0800534 }
535
Ashok Bhat075e9a12014-01-06 13:45:09 +0000536 return (jlong) sampleTimeUs;
Andreas Huber88572f72012-02-21 11:47:18 -0800537}
538
Andreas Huber9b8e4962012-03-26 11:13:27 -0700539static jint android_media_MediaExtractor_getSampleFlags(
540 JNIEnv *env, jobject thiz) {
541 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
542
543 if (extractor == NULL) {
544 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000545 return -1;
Andreas Huber9b8e4962012-03-26 11:13:27 -0700546 }
547
548 uint32_t sampleFlags;
549 status_t err = extractor->getSampleFlags(&sampleFlags);
550
551 if (err == ERROR_END_OF_STREAM) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000552 return -1;
Andreas Huber9b8e4962012-03-26 11:13:27 -0700553 } else if (err != OK) {
554 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000555 return -1;
Andreas Huber9b8e4962012-03-26 11:13:27 -0700556 }
557
Ashok Bhat075e9a12014-01-06 13:45:09 +0000558 return (jint) sampleFlags;
Andreas Huber9b8e4962012-03-26 11:13:27 -0700559}
560
Andreas Huber91befdc2012-04-18 12:19:51 -0700561static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
562 JNIEnv *env, jobject thiz, jobject cryptoInfoObj) {
563 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
564
565 if (extractor == NULL) {
566 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000567 return JNI_FALSE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700568 }
569
570 sp<MetaData> meta;
571 status_t err = extractor->getSampleMeta(&meta);
572
573 if (err != OK) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000574 return JNI_FALSE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700575 }
576
577 uint32_t type;
578 const void *data;
579 size_t size;
580 if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000581 return JNI_FALSE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700582 }
583
Ashok Bhate92416d2014-03-05 11:51:15 +0000584 size_t numSubSamples = size / sizeof(int32_t);
Andreas Huber91befdc2012-04-18 12:19:51 -0700585
586 if (numSubSamples == 0) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000587 return JNI_FALSE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700588 }
589
590 jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples);
591 jboolean isCopy;
592 jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
593 for (size_t i = 0; i < numSubSamples; ++i) {
Ashok Bhate92416d2014-03-05 11:51:15 +0000594 dst[i] = ((const int32_t *)data)[i];
Andreas Huber91befdc2012-04-18 12:19:51 -0700595 }
596 env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
597 dst = NULL;
598
599 size_t encSize = size;
600 jintArray numBytesOfPlainDataObj = NULL;
Andreas Huberab57d032012-04-18 15:59:43 -0700601 if (meta->findData(kKeyPlainSizes, &type, &data, &size)) {
Andreas Huber91befdc2012-04-18 12:19:51 -0700602 if (size != encSize) {
603 // The two must be of the same length.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000604 return JNI_FALSE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700605 }
606
607 numBytesOfPlainDataObj = env->NewIntArray(numSubSamples);
608 jboolean isCopy;
609 jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
610 for (size_t i = 0; i < numSubSamples; ++i) {
Ashok Bhate92416d2014-03-05 11:51:15 +0000611 dst[i] = ((const int32_t *)data)[i];
Andreas Huber91befdc2012-04-18 12:19:51 -0700612 }
613 env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
614 dst = NULL;
615 }
616
617 jbyteArray keyObj = NULL;
618 if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
619 if (size != 16) {
620 // Keys must be 16 bytes in length.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000621 return JNI_FALSE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700622 }
623
624 keyObj = env->NewByteArray(size);
625 jboolean isCopy;
626 jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy);
627 memcpy(dst, data, size);
628 env->ReleaseByteArrayElements(keyObj, dst, 0);
629 dst = NULL;
630 }
631
632 jbyteArray ivObj = NULL;
633 if (meta->findData(kKeyCryptoIV, &type, &data, &size)) {
634 if (size != 16) {
635 // IVs must be 16 bytes in length.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000636 return JNI_FALSE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700637 }
638
639 ivObj = env->NewByteArray(size);
640 jboolean isCopy;
641 jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy);
642 memcpy(dst, data, size);
643 env->ReleaseByteArrayElements(ivObj, dst, 0);
644 dst = NULL;
645 }
646
647 int32_t mode;
648 if (!meta->findInt32(kKeyCryptoMode, &mode)) {
Andreas Huberab57d032012-04-18 15:59:43 -0700649 mode = CryptoPlugin::kMode_AES_CTR;
Andreas Huber91befdc2012-04-18 12:19:51 -0700650 }
651
652 env->CallVoidMethod(
653 cryptoInfoObj,
654 gFields.cryptoInfoSetID,
Ashok Bhat136c08a2014-03-05 15:23:13 +0000655 (jint)numSubSamples,
Andreas Huber91befdc2012-04-18 12:19:51 -0700656 numBytesOfPlainDataObj,
657 numBytesOfEncryptedDataObj,
658 keyObj,
659 ivObj,
660 mode);
661
Ashok Bhat075e9a12014-01-06 13:45:09 +0000662 return JNI_TRUE;
Andreas Huber91befdc2012-04-18 12:19:51 -0700663}
664
Andreas Huber88572f72012-02-21 11:47:18 -0800665static void android_media_MediaExtractor_native_init(JNIEnv *env) {
666 jclass clazz = env->FindClass("android/media/MediaExtractor");
667 CHECK(clazz != NULL);
668
Ashok Bhat075e9a12014-01-06 13:45:09 +0000669 gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
Andreas Huber88572f72012-02-21 11:47:18 -0800670 CHECK(gFields.context != NULL);
671
Andreas Huber91befdc2012-04-18 12:19:51 -0700672 clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
673 CHECK(clazz != NULL);
674
675 gFields.cryptoInfoSetID =
676 env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V");
677
Andreas Huber88572f72012-02-21 11:47:18 -0800678 DataSource::RegisterDefaultSniffers();
679}
680
681static void android_media_MediaExtractor_native_setup(
Andreas Huber07ea4262012-04-11 12:21:20 -0700682 JNIEnv *env, jobject thiz) {
Andreas Huber88572f72012-02-21 11:47:18 -0800683 sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
Andreas Huber07ea4262012-04-11 12:21:20 -0700684 setMediaExtractor(env,thiz, extractor);
685}
Andreas Huber88572f72012-02-21 11:47:18 -0800686
Andreas Huber07ea4262012-04-11 12:21:20 -0700687static void android_media_MediaExtractor_setDataSource(
688 JNIEnv *env, jobject thiz,
Andreas Huberd2506a52014-01-29 10:32:46 -0800689 jobject httpServiceBinderObj,
690 jstring pathObj,
691 jobjectArray keysArray,
692 jobjectArray valuesArray) {
Andreas Huber07ea4262012-04-11 12:21:20 -0700693 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
694
695 if (extractor == NULL) {
696 jniThrowException(env, "java/lang/IllegalStateException", NULL);
697 return;
698 }
699
700 if (pathObj == NULL) {
Andreas Huber88572f72012-02-21 11:47:18 -0800701 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
702 return;
703 }
704
Andreas Huber07ea4262012-04-11 12:21:20 -0700705 KeyedVector<String8, String8> headers;
706 if (!ConvertKeyValueArraysToKeyedVector(
707 env, keysArray, valuesArray, &headers)) {
Andreas Huber88572f72012-02-21 11:47:18 -0800708 return;
709 }
710
Andreas Huber07ea4262012-04-11 12:21:20 -0700711 const char *path = env->GetStringUTFChars(pathObj, NULL);
Andreas Huber88572f72012-02-21 11:47:18 -0800712
Andreas Huber07ea4262012-04-11 12:21:20 -0700713 if (path == NULL) {
714 return;
715 }
716
Andreas Huberd2506a52014-01-29 10:32:46 -0800717 sp<IMediaHTTPService> httpService;
718 if (httpServiceBinderObj != NULL) {
719 sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
720 httpService = interface_cast<IMediaHTTPService>(binder);
721 }
722
723 status_t err = extractor->setDataSource(httpService, path, &headers);
Andreas Huber07ea4262012-04-11 12:21:20 -0700724
725 env->ReleaseStringUTFChars(pathObj, path);
726 path = NULL;
Andreas Huber88572f72012-02-21 11:47:18 -0800727
728 if (err != OK) {
729 jniThrowException(
730 env,
731 "java/io/IOException",
732 "Failed to instantiate extractor.");
733 return;
734 }
Andreas Huber07ea4262012-04-11 12:21:20 -0700735}
Andreas Huber88572f72012-02-21 11:47:18 -0800736
Andreas Huber07ea4262012-04-11 12:21:20 -0700737static void android_media_MediaExtractor_setDataSourceFd(
738 JNIEnv *env, jobject thiz,
739 jobject fileDescObj, jlong offset, jlong length) {
740 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
741
742 if (extractor == NULL) {
743 jniThrowException(env, "java/lang/IllegalStateException", NULL);
744 return;
745 }
746
747 if (fileDescObj == NULL) {
748 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
749 return;
750 }
751
752 int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
753
754 status_t err = extractor->setDataSource(fd, offset, length);
755
756 if (err != OK) {
757 jniThrowException(
758 env,
759 "java/io/IOException",
760 "Failed to instantiate extractor.");
761 return;
762 }
Andreas Huber88572f72012-02-21 11:47:18 -0800763}
764
Marco Nelissenc209a062012-08-24 09:55:44 -0700765static void android_media_MediaExtractor_setDataSourceCallback(
766 JNIEnv *env, jobject thiz,
767 jobject callbackObj) {
768 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
769
770 if (extractor == NULL) {
771 jniThrowException(env, "java/lang/IllegalStateException", NULL);
772 return;
773 }
774
775 if (callbackObj == NULL) {
776 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
777 return;
778 }
779
780 sp<JavaDataSourceBridge> bridge = new JavaDataSourceBridge(env, callbackObj);
781 status_t err = extractor->setDataSource(bridge);
782
783 if (err != OK) {
784 jniThrowException(
785 env,
786 "java/io/IOException",
787 "Failed to instantiate extractor.");
788 return;
789 }
790}
791
Andreas Huber74a78b02012-04-19 16:24:32 -0700792static jlong android_media_MediaExtractor_getCachedDurationUs(
793 JNIEnv *env, jobject thiz) {
794 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
795
796 if (extractor == NULL) {
797 jniThrowException(env, "java/lang/IllegalStateException", NULL);
798 return -1ll;
799 }
800
801 int64_t cachedDurationUs;
802 bool eos;
803 if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
804 return -1ll;
805 }
806
Ashok Bhat075e9a12014-01-06 13:45:09 +0000807 return (jlong) cachedDurationUs;
Andreas Huber74a78b02012-04-19 16:24:32 -0700808}
809
810static jboolean android_media_MediaExtractor_hasCacheReachedEOS(
811 JNIEnv *env, jobject thiz) {
812 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
813
814 if (extractor == NULL) {
815 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000816 return JNI_TRUE;
Andreas Huber74a78b02012-04-19 16:24:32 -0700817 }
818
819 int64_t cachedDurationUs;
820 bool eos;
821 if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000822 return JNI_TRUE;
Andreas Huber74a78b02012-04-19 16:24:32 -0700823 }
824
Ashok Bhat075e9a12014-01-06 13:45:09 +0000825 return eos ? JNI_TRUE : JNI_FALSE;
Andreas Huber74a78b02012-04-19 16:24:32 -0700826}
827
Andreas Huber88572f72012-02-21 11:47:18 -0800828static void android_media_MediaExtractor_native_finalize(
829 JNIEnv *env, jobject thiz) {
830 android_media_MediaExtractor_release(env, thiz);
831}
832
833static JNINativeMethod gMethods[] = {
834 { "release", "()V", (void *)android_media_MediaExtractor_release },
835
Andreas Huber60d610b2012-05-02 16:06:09 -0700836 { "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
Andreas Huber88572f72012-02-21 11:47:18 -0800837
Marco Nelissene20a6d52013-04-08 14:28:55 -0700838 { "getFileFormatNative", "()Ljava/util/Map;",
839 (void *)android_media_MediaExtractor_getFileFormatNative },
840
Andreas Huber60d610b2012-05-02 16:06:09 -0700841 { "getTrackFormatNative", "(I)Ljava/util/Map;",
842 (void *)android_media_MediaExtractor_getTrackFormatNative },
Andreas Huber88572f72012-02-21 11:47:18 -0800843
844 { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
845
Andreas Huberf2855b32012-04-25 15:57:43 -0700846 { "unselectTrack", "(I)V",
847 (void *)android_media_MediaExtractor_unselectTrack },
848
849 { "seekTo", "(JI)V", (void *)android_media_MediaExtractor_seekTo },
Andreas Huber88572f72012-02-21 11:47:18 -0800850
851 { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
852
853 { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
854 (void *)android_media_MediaExtractor_readSampleData },
855
856 { "getSampleTrackIndex", "()I",
857 (void *)android_media_MediaExtractor_getSampleTrackIndex },
858
859 { "getSampleTime", "()J",
860 (void *)android_media_MediaExtractor_getSampleTime },
861
Andreas Huber9b8e4962012-03-26 11:13:27 -0700862 { "getSampleFlags", "()I",
863 (void *)android_media_MediaExtractor_getSampleFlags },
864
Andreas Huber91befdc2012-04-18 12:19:51 -0700865 { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z",
866 (void *)android_media_MediaExtractor_getSampleCryptoInfo },
867
Andreas Huber88572f72012-02-21 11:47:18 -0800868 { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
869
Andreas Huber07ea4262012-04-11 12:21:20 -0700870 { "native_setup", "()V",
Andreas Huber88572f72012-02-21 11:47:18 -0800871 (void *)android_media_MediaExtractor_native_setup },
872
873 { "native_finalize", "()V",
874 (void *)android_media_MediaExtractor_native_finalize },
Andreas Huber07ea4262012-04-11 12:21:20 -0700875
Andreas Huberd2506a52014-01-29 10:32:46 -0800876 { "nativeSetDataSource",
877 "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
878 "[Ljava/lang/String;)V",
Andreas Huber07ea4262012-04-11 12:21:20 -0700879 (void *)android_media_MediaExtractor_setDataSource },
880
881 { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
882 (void *)android_media_MediaExtractor_setDataSourceFd },
Andreas Huber74a78b02012-04-19 16:24:32 -0700883
Marco Nelissenc209a062012-08-24 09:55:44 -0700884 { "setDataSource", "(Landroid/media/DataSource;)V",
885 (void *)android_media_MediaExtractor_setDataSourceCallback },
886
Andreas Huber74a78b02012-04-19 16:24:32 -0700887 { "getCachedDuration", "()J",
888 (void *)android_media_MediaExtractor_getCachedDurationUs },
889
890 { "hasCacheReachedEndOfStream", "()Z",
891 (void *)android_media_MediaExtractor_hasCacheReachedEOS },
Andreas Huber88572f72012-02-21 11:47:18 -0800892};
893
894int register_android_media_MediaExtractor(JNIEnv *env) {
895 return AndroidRuntime::registerNativeMethods(env,
896 "android/media/MediaExtractor", gMethods, NELEM(gMethods));
897}