1 /*
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 "MediaCodec-JNI"
19 #include <utils/Log.h>
20
21 #include "android_media_MediaCodec.h"
22
23 #include "android_media_MediaCrypto.h"
24 #include "android_media_Utils.h"
25 #include "android_runtime/AndroidRuntime.h"
26 #include "android_runtime/android_view_Surface.h"
27 #include "jni.h"
28 #include "JNIHelp.h"
29
30 #include <gui/Surface.h>
31
32 #include <media/ICrypto.h>
33 #include <media/stagefright/MediaCodec.h>
34 #include <media/stagefright/foundation/ABuffer.h>
35 #include <media/stagefright/foundation/ADebug.h>
36 #include <media/stagefright/foundation/ALooper.h>
37 #include <media/stagefright/foundation/AMessage.h>
38 #include <media/stagefright/foundation/AString.h>
39 #include <media/stagefright/MediaErrors.h>
40
41 #include <nativehelper/ScopedLocalRef.h>
42
43 #include <system/window.h>
44
45 namespace android {
46
47 // Keep these in sync with their equivalents in MediaCodec.java !!!
48 enum {
49 DEQUEUE_INFO_TRY_AGAIN_LATER = -1,
50 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2,
51 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3,
52 };
53
54 struct CryptoErrorCodes {
55 jint cryptoErrorNoKey;
56 jint cryptoErrorKeyExpired;
57 jint cryptoErrorResourceBusy;
58 } gCryptoErrorCodes;
59
60 struct fields_t {
61 jfieldID context;
62 jfieldID cryptoInfoNumSubSamplesID;
63 jfieldID cryptoInfoNumBytesOfClearDataID;
64 jfieldID cryptoInfoNumBytesOfEncryptedDataID;
65 jfieldID cryptoInfoKeyID;
66 jfieldID cryptoInfoIVID;
67 jfieldID cryptoInfoModeID;
68 };
69
70 static fields_t gFields;
71
72 ////////////////////////////////////////////////////////////////////////////////
73
JMediaCodec(JNIEnv * env,jobject thiz,const char * name,bool nameIsType,bool encoder)74 JMediaCodec::JMediaCodec(
75 JNIEnv *env, jobject thiz,
76 const char *name, bool nameIsType, bool encoder)
77 : mClass(NULL),
78 mObject(NULL) {
79 jclass clazz = env->GetObjectClass(thiz);
80 CHECK(clazz != NULL);
81
82 mClass = (jclass)env->NewGlobalRef(clazz);
83 mObject = env->NewWeakGlobalRef(thiz);
84
85 mLooper = new ALooper;
86 mLooper->setName("MediaCodec_looper");
87
88 mLooper->start(
89 false, // runOnCallingThread
90 false, // canCallJava
91 PRIORITY_FOREGROUND);
92
93 if (nameIsType) {
94 mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
95 } else {
96 mCodec = MediaCodec::CreateByComponentName(mLooper, name);
97 }
98 }
99
initCheck() const100 status_t JMediaCodec::initCheck() const {
101 return mCodec != NULL ? OK : NO_INIT;
102 }
103
~JMediaCodec()104 JMediaCodec::~JMediaCodec() {
105 if (mCodec != NULL) {
106 mCodec->release();
107 mCodec.clear();
108 }
109
110 JNIEnv *env = AndroidRuntime::getJNIEnv();
111
112 env->DeleteWeakGlobalRef(mObject);
113 mObject = NULL;
114 env->DeleteGlobalRef(mClass);
115 mClass = NULL;
116 }
117
configure(const sp<AMessage> & format,const sp<IGraphicBufferProducer> & bufferProducer,const sp<ICrypto> & crypto,int flags)118 status_t JMediaCodec::configure(
119 const sp<AMessage> &format,
120 const sp<IGraphicBufferProducer> &bufferProducer,
121 const sp<ICrypto> &crypto,
122 int flags) {
123 sp<Surface> client;
124 if (bufferProducer != NULL) {
125 mSurfaceTextureClient = new Surface(bufferProducer, true /* controlledByApp */);
126 } else {
127 mSurfaceTextureClient.clear();
128 }
129
130 return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
131 }
132
createInputSurface(sp<IGraphicBufferProducer> * bufferProducer)133 status_t JMediaCodec::createInputSurface(
134 sp<IGraphicBufferProducer>* bufferProducer) {
135 return mCodec->createInputSurface(bufferProducer);
136 }
137
start()138 status_t JMediaCodec::start() {
139 return mCodec->start();
140 }
141
stop()142 status_t JMediaCodec::stop() {
143 mSurfaceTextureClient.clear();
144
145 return mCodec->stop();
146 }
147
flush()148 status_t JMediaCodec::flush() {
149 return mCodec->flush();
150 }
151
queueInputBuffer(size_t index,size_t offset,size_t size,int64_t timeUs,uint32_t flags,AString * errorDetailMsg)152 status_t JMediaCodec::queueInputBuffer(
153 size_t index,
154 size_t offset, size_t size, int64_t timeUs, uint32_t flags,
155 AString *errorDetailMsg) {
156 return mCodec->queueInputBuffer(
157 index, offset, size, timeUs, flags, errorDetailMsg);
158 }
159
queueSecureInputBuffer(size_t index,size_t offset,const CryptoPlugin::SubSample * subSamples,size_t numSubSamples,const uint8_t key[16],const uint8_t iv[16],CryptoPlugin::Mode mode,int64_t presentationTimeUs,uint32_t flags,AString * errorDetailMsg)160 status_t JMediaCodec::queueSecureInputBuffer(
161 size_t index,
162 size_t offset,
163 const CryptoPlugin::SubSample *subSamples,
164 size_t numSubSamples,
165 const uint8_t key[16],
166 const uint8_t iv[16],
167 CryptoPlugin::Mode mode,
168 int64_t presentationTimeUs,
169 uint32_t flags,
170 AString *errorDetailMsg) {
171 return mCodec->queueSecureInputBuffer(
172 index, offset, subSamples, numSubSamples, key, iv, mode,
173 presentationTimeUs, flags, errorDetailMsg);
174 }
175
dequeueInputBuffer(size_t * index,int64_t timeoutUs)176 status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
177 return mCodec->dequeueInputBuffer(index, timeoutUs);
178 }
179
dequeueOutputBuffer(JNIEnv * env,jobject bufferInfo,size_t * index,int64_t timeoutUs)180 status_t JMediaCodec::dequeueOutputBuffer(
181 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
182 size_t size, offset;
183 int64_t timeUs;
184 uint32_t flags;
185 status_t err;
186 if ((err = mCodec->dequeueOutputBuffer(
187 index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
188 return err;
189 }
190
191 ScopedLocalRef<jclass> clazz(
192 env, env->FindClass("android/media/MediaCodec$BufferInfo"));
193
194 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
195 env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
196
197 return OK;
198 }
199
releaseOutputBuffer(size_t index,bool render)200 status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
201 return render
202 ? mCodec->renderOutputBufferAndRelease(index)
203 : mCodec->releaseOutputBuffer(index);
204 }
205
signalEndOfInputStream()206 status_t JMediaCodec::signalEndOfInputStream() {
207 return mCodec->signalEndOfInputStream();
208 }
209
getOutputFormat(JNIEnv * env,jobject * format) const210 status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
211 sp<AMessage> msg;
212 status_t err;
213 if ((err = mCodec->getOutputFormat(&msg)) != OK) {
214 return err;
215 }
216
217 return ConvertMessageToMap(env, msg, format);
218 }
219
getBuffers(JNIEnv * env,bool input,jobjectArray * bufArray) const220 status_t JMediaCodec::getBuffers(
221 JNIEnv *env, bool input, jobjectArray *bufArray) const {
222 Vector<sp<ABuffer> > buffers;
223
224 status_t err =
225 input
226 ? mCodec->getInputBuffers(&buffers)
227 : mCodec->getOutputBuffers(&buffers);
228
229 if (err != OK) {
230 return err;
231 }
232
233 ScopedLocalRef<jclass> byteBufferClass(
234 env, env->FindClass("java/nio/ByteBuffer"));
235
236 CHECK(byteBufferClass.get() != NULL);
237
238 jmethodID orderID = env->GetMethodID(
239 byteBufferClass.get(),
240 "order",
241 "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
242
243 CHECK(orderID != NULL);
244
245 ScopedLocalRef<jclass> byteOrderClass(
246 env, env->FindClass("java/nio/ByteOrder"));
247
248 CHECK(byteOrderClass.get() != NULL);
249
250 jmethodID nativeOrderID = env->GetStaticMethodID(
251 byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
252 CHECK(nativeOrderID != NULL);
253
254 jobject nativeByteOrderObj =
255 env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
256 CHECK(nativeByteOrderObj != NULL);
257
258 *bufArray = (jobjectArray)env->NewObjectArray(
259 buffers.size(), byteBufferClass.get(), NULL);
260 if (*bufArray == NULL) {
261 env->DeleteLocalRef(nativeByteOrderObj);
262 return NO_MEMORY;
263 }
264
265 for (size_t i = 0; i < buffers.size(); ++i) {
266 const sp<ABuffer> &buffer = buffers.itemAt(i);
267
268 // if this is an ABuffer that doesn't actually hold any accessible memory,
269 // use a null ByteBuffer
270 if (buffer->base() == NULL) {
271 continue;
272 }
273 jobject byteBuffer =
274 env->NewDirectByteBuffer(
275 buffer->base(),
276 buffer->capacity());
277 if (byteBuffer == NULL) {
278 env->DeleteLocalRef(nativeByteOrderObj);
279 return NO_MEMORY;
280 }
281 jobject me = env->CallObjectMethod(
282 byteBuffer, orderID, nativeByteOrderObj);
283 env->DeleteLocalRef(me);
284 me = NULL;
285
286 env->SetObjectArrayElement(
287 *bufArray, i, byteBuffer);
288
289 env->DeleteLocalRef(byteBuffer);
290 byteBuffer = NULL;
291 }
292
293 env->DeleteLocalRef(nativeByteOrderObj);
294 nativeByteOrderObj = NULL;
295
296 return OK;
297 }
298
getName(JNIEnv * env,jstring * nameStr) const299 status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
300 AString name;
301
302 status_t err = mCodec->getName(&name);
303
304 if (err != OK) {
305 return err;
306 }
307
308 *nameStr = env->NewStringUTF(name.c_str());
309
310 return OK;
311 }
312
setParameters(const sp<AMessage> & msg)313 status_t JMediaCodec::setParameters(const sp<AMessage> &msg) {
314 return mCodec->setParameters(msg);
315 }
316
setVideoScalingMode(int mode)317 void JMediaCodec::setVideoScalingMode(int mode) {
318 if (mSurfaceTextureClient != NULL) {
319 native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
320 }
321 }
322
323 } // namespace android
324
325 ////////////////////////////////////////////////////////////////////////////////
326
327 using namespace android;
328
setMediaCodec(JNIEnv * env,jobject thiz,const sp<JMediaCodec> & codec)329 static sp<JMediaCodec> setMediaCodec(
330 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
331 sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
332 if (codec != NULL) {
333 codec->incStrong(thiz);
334 }
335 if (old != NULL) {
336 old->decStrong(thiz);
337 }
338 env->SetIntField(thiz, gFields.context, (int)codec.get());
339
340 return old;
341 }
342
getMediaCodec(JNIEnv * env,jobject thiz)343 static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
344 return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
345 }
346
android_media_MediaCodec_release(JNIEnv * env,jobject thiz)347 static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
348 setMediaCodec(env, thiz, NULL);
349 }
350
throwCryptoException(JNIEnv * env,status_t err,const char * msg)351 static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
352 ScopedLocalRef<jclass> clazz(
353 env, env->FindClass("android/media/MediaCodec$CryptoException"));
354 CHECK(clazz.get() != NULL);
355
356 jmethodID constructID =
357 env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
358 CHECK(constructID != NULL);
359
360 jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
361
362 /* translate OS errors to Java API CryptoException errorCodes */
363 switch (err) {
364 case ERROR_DRM_NO_LICENSE:
365 err = gCryptoErrorCodes.cryptoErrorNoKey;
366 break;
367 case ERROR_DRM_LICENSE_EXPIRED:
368 err = gCryptoErrorCodes.cryptoErrorKeyExpired;
369 break;
370 case ERROR_DRM_RESOURCE_BUSY:
371 err = gCryptoErrorCodes.cryptoErrorResourceBusy;
372 break;
373 default:
374 break;
375 }
376
377 jthrowable exception =
378 (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
379
380 env->Throw(exception);
381 }
382
throwExceptionAsNecessary(JNIEnv * env,status_t err,const char * msg=NULL)383 static jint throwExceptionAsNecessary(
384 JNIEnv *env, status_t err, const char *msg = NULL) {
385 if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
386 // We'll throw our custom MediaCodec.CryptoException
387 throwCryptoException(env, err, msg);
388 return 0;
389 }
390
391 switch (err) {
392 case OK:
393 return 0;
394
395 case -EAGAIN:
396 return DEQUEUE_INFO_TRY_AGAIN_LATER;
397
398 case INFO_FORMAT_CHANGED:
399 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
400
401 case INFO_OUTPUT_BUFFERS_CHANGED:
402 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
403
404 case ERROR_DRM_NO_LICENSE:
405 case ERROR_DRM_LICENSE_EXPIRED:
406 case ERROR_DRM_RESOURCE_BUSY:
407 throwCryptoException(env, err, msg);
408 break;
409
410 default:
411 {
412 jniThrowException(env, "java/lang/IllegalStateException", msg);
413 break;
414 }
415 }
416
417 return 0;
418 }
419
android_media_MediaCodec_native_configure(JNIEnv * env,jobject thiz,jobjectArray keys,jobjectArray values,jobject jsurface,jobject jcrypto,jint flags)420 static void android_media_MediaCodec_native_configure(
421 JNIEnv *env,
422 jobject thiz,
423 jobjectArray keys, jobjectArray values,
424 jobject jsurface,
425 jobject jcrypto,
426 jint flags) {
427 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
428
429 if (codec == NULL) {
430 jniThrowException(env, "java/lang/IllegalStateException", NULL);
431 return;
432 }
433
434 sp<AMessage> format;
435 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
436
437 if (err != OK) {
438 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
439 return;
440 }
441
442 sp<IGraphicBufferProducer> bufferProducer;
443 if (jsurface != NULL) {
444 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
445 if (surface != NULL) {
446 bufferProducer = surface->getIGraphicBufferProducer();
447 } else {
448 jniThrowException(
449 env,
450 "java/lang/IllegalArgumentException",
451 "The surface has been released");
452 return;
453 }
454 }
455
456 sp<ICrypto> crypto;
457 if (jcrypto != NULL) {
458 crypto = JCrypto::GetCrypto(env, jcrypto);
459 }
460
461 err = codec->configure(format, bufferProducer, crypto, flags);
462
463 throwExceptionAsNecessary(env, err);
464 }
465
android_media_MediaCodec_createInputSurface(JNIEnv * env,jobject thiz)466 static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
467 jobject thiz) {
468 ALOGV("android_media_MediaCodec_createInputSurface");
469
470 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
471 if (codec == NULL) {
472 jniThrowException(env, "java/lang/IllegalStateException", NULL);
473 return NULL;
474 }
475
476 // Tell the MediaCodec that we want to use a Surface as input.
477 sp<IGraphicBufferProducer> bufferProducer;
478 status_t err = codec->createInputSurface(&bufferProducer);
479 if (err != NO_ERROR) {
480 throwExceptionAsNecessary(env, err);
481 return NULL;
482 }
483
484 // Wrap the IGBP in a Java-language Surface.
485 return android_view_Surface_createFromIGraphicBufferProducer(env,
486 bufferProducer);
487 }
488
android_media_MediaCodec_start(JNIEnv * env,jobject thiz)489 static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
490 ALOGV("android_media_MediaCodec_start");
491
492 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
493
494 if (codec == NULL) {
495 jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
496 return;
497 }
498
499 status_t err = codec->start();
500
501 throwExceptionAsNecessary(env, err, "start failed");
502 }
503
android_media_MediaCodec_stop(JNIEnv * env,jobject thiz)504 static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
505 ALOGV("android_media_MediaCodec_stop");
506
507 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
508
509 if (codec == NULL) {
510 jniThrowException(env, "java/lang/IllegalStateException", NULL);
511 return;
512 }
513
514 status_t err = codec->stop();
515
516 throwExceptionAsNecessary(env, err);
517 }
518
android_media_MediaCodec_flush(JNIEnv * env,jobject thiz)519 static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
520 ALOGV("android_media_MediaCodec_flush");
521
522 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
523
524 if (codec == NULL) {
525 jniThrowException(env, "java/lang/IllegalStateException", NULL);
526 return;
527 }
528
529 status_t err = codec->flush();
530
531 throwExceptionAsNecessary(env, err);
532 }
533
android_media_MediaCodec_queueInputBuffer(JNIEnv * env,jobject thiz,jint index,jint offset,jint size,jlong timestampUs,jint flags)534 static void android_media_MediaCodec_queueInputBuffer(
535 JNIEnv *env,
536 jobject thiz,
537 jint index,
538 jint offset,
539 jint size,
540 jlong timestampUs,
541 jint flags) {
542 ALOGV("android_media_MediaCodec_queueInputBuffer");
543
544 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
545
546 if (codec == NULL) {
547 jniThrowException(env, "java/lang/IllegalStateException", NULL);
548 return;
549 }
550
551 AString errorDetailMsg;
552
553 status_t err = codec->queueInputBuffer(
554 index, offset, size, timestampUs, flags, &errorDetailMsg);
555
556 throwExceptionAsNecessary(
557 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
558 }
559
android_media_MediaCodec_queueSecureInputBuffer(JNIEnv * env,jobject thiz,jint index,jint offset,jobject cryptoInfoObj,jlong timestampUs,jint flags)560 static void android_media_MediaCodec_queueSecureInputBuffer(
561 JNIEnv *env,
562 jobject thiz,
563 jint index,
564 jint offset,
565 jobject cryptoInfoObj,
566 jlong timestampUs,
567 jint flags) {
568 ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
569
570 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
571
572 if (codec == NULL) {
573 jniThrowException(env, "java/lang/IllegalStateException", NULL);
574 return;
575 }
576
577 jint numSubSamples =
578 env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
579
580 jintArray numBytesOfClearDataObj =
581 (jintArray)env->GetObjectField(
582 cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
583
584 jintArray numBytesOfEncryptedDataObj =
585 (jintArray)env->GetObjectField(
586 cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
587
588 jbyteArray keyObj =
589 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
590
591 jbyteArray ivObj =
592 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
593
594 jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
595
596 status_t err = OK;
597
598 CryptoPlugin::SubSample *subSamples = NULL;
599 jbyte *key = NULL;
600 jbyte *iv = NULL;
601
602 if (numSubSamples <= 0) {
603 err = -EINVAL;
604 } else if (numBytesOfClearDataObj == NULL
605 && numBytesOfEncryptedDataObj == NULL) {
606 err = -EINVAL;
607 } else if (numBytesOfEncryptedDataObj != NULL
608 && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
609 err = -ERANGE;
610 } else if (numBytesOfClearDataObj != NULL
611 && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
612 err = -ERANGE;
613 } else {
614 jboolean isCopy;
615
616 jint *numBytesOfClearData =
617 (numBytesOfClearDataObj == NULL)
618 ? NULL
619 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
620
621 jint *numBytesOfEncryptedData =
622 (numBytesOfEncryptedDataObj == NULL)
623 ? NULL
624 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
625
626 subSamples = new CryptoPlugin::SubSample[numSubSamples];
627
628 for (jint i = 0; i < numSubSamples; ++i) {
629 subSamples[i].mNumBytesOfClearData =
630 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
631
632 subSamples[i].mNumBytesOfEncryptedData =
633 (numBytesOfEncryptedData == NULL)
634 ? 0 : numBytesOfEncryptedData[i];
635 }
636
637 if (numBytesOfEncryptedData != NULL) {
638 env->ReleaseIntArrayElements(
639 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
640 numBytesOfEncryptedData = NULL;
641 }
642
643 if (numBytesOfClearData != NULL) {
644 env->ReleaseIntArrayElements(
645 numBytesOfClearDataObj, numBytesOfClearData, 0);
646 numBytesOfClearData = NULL;
647 }
648 }
649
650 if (err == OK && keyObj != NULL) {
651 if (env->GetArrayLength(keyObj) != 16) {
652 err = -EINVAL;
653 } else {
654 jboolean isCopy;
655 key = env->GetByteArrayElements(keyObj, &isCopy);
656 }
657 }
658
659 if (err == OK && ivObj != NULL) {
660 if (env->GetArrayLength(ivObj) != 16) {
661 err = -EINVAL;
662 } else {
663 jboolean isCopy;
664 iv = env->GetByteArrayElements(ivObj, &isCopy);
665 }
666 }
667
668 AString errorDetailMsg;
669
670 if (err == OK) {
671 err = codec->queueSecureInputBuffer(
672 index, offset,
673 subSamples, numSubSamples,
674 (const uint8_t *)key, (const uint8_t *)iv,
675 (CryptoPlugin::Mode)mode,
676 timestampUs,
677 flags,
678 &errorDetailMsg);
679 }
680
681 if (iv != NULL) {
682 env->ReleaseByteArrayElements(ivObj, iv, 0);
683 iv = NULL;
684 }
685
686 if (key != NULL) {
687 env->ReleaseByteArrayElements(keyObj, key, 0);
688 key = NULL;
689 }
690
691 delete[] subSamples;
692 subSamples = NULL;
693
694 throwExceptionAsNecessary(
695 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
696 }
697
android_media_MediaCodec_dequeueInputBuffer(JNIEnv * env,jobject thiz,jlong timeoutUs)698 static jint android_media_MediaCodec_dequeueInputBuffer(
699 JNIEnv *env, jobject thiz, jlong timeoutUs) {
700 ALOGV("android_media_MediaCodec_dequeueInputBuffer");
701
702 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
703
704 if (codec == NULL) {
705 jniThrowException(env, "java/lang/IllegalStateException", NULL);
706 return -1;
707 }
708
709 size_t index;
710 status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
711
712 if (err == OK) {
713 return index;
714 }
715
716 return throwExceptionAsNecessary(env, err);
717 }
718
android_media_MediaCodec_dequeueOutputBuffer(JNIEnv * env,jobject thiz,jobject bufferInfo,jlong timeoutUs)719 static jint android_media_MediaCodec_dequeueOutputBuffer(
720 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
721 ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
722
723 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
724
725 if (codec == NULL) {
726 jniThrowException(env, "java/lang/IllegalStateException", NULL);
727 return 0;
728 }
729
730 size_t index;
731 status_t err = codec->dequeueOutputBuffer(
732 env, bufferInfo, &index, timeoutUs);
733
734 if (err == OK) {
735 return index;
736 }
737
738 return throwExceptionAsNecessary(env, err);
739 }
740
android_media_MediaCodec_releaseOutputBuffer(JNIEnv * env,jobject thiz,jint index,jboolean render)741 static void android_media_MediaCodec_releaseOutputBuffer(
742 JNIEnv *env, jobject thiz, jint index, jboolean render) {
743 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
744
745 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
746
747 if (codec == NULL) {
748 jniThrowException(env, "java/lang/IllegalStateException", NULL);
749 return;
750 }
751
752 status_t err = codec->releaseOutputBuffer(index, render);
753
754 throwExceptionAsNecessary(env, err);
755 }
756
android_media_MediaCodec_signalEndOfInputStream(JNIEnv * env,jobject thiz)757 static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
758 jobject thiz) {
759 ALOGV("android_media_MediaCodec_signalEndOfInputStream");
760
761 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
762 if (codec == NULL) {
763 jniThrowException(env, "java/lang/IllegalStateException", NULL);
764 return;
765 }
766
767 status_t err = codec->signalEndOfInputStream();
768
769 throwExceptionAsNecessary(env, err);
770 }
771
android_media_MediaCodec_getOutputFormatNative(JNIEnv * env,jobject thiz)772 static jobject android_media_MediaCodec_getOutputFormatNative(
773 JNIEnv *env, jobject thiz) {
774 ALOGV("android_media_MediaCodec_getOutputFormatNative");
775
776 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
777
778 if (codec == NULL) {
779 jniThrowException(env, "java/lang/IllegalStateException", NULL);
780 return NULL;
781 }
782
783 jobject format;
784 status_t err = codec->getOutputFormat(env, &format);
785
786 if (err == OK) {
787 return format;
788 }
789
790 throwExceptionAsNecessary(env, err);
791
792 return NULL;
793 }
794
android_media_MediaCodec_getBuffers(JNIEnv * env,jobject thiz,jboolean input)795 static jobjectArray android_media_MediaCodec_getBuffers(
796 JNIEnv *env, jobject thiz, jboolean input) {
797 ALOGV("android_media_MediaCodec_getBuffers");
798
799 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
800
801 if (codec == NULL) {
802 jniThrowException(env, "java/lang/IllegalStateException", NULL);
803 return NULL;
804 }
805
806 jobjectArray buffers;
807 status_t err = codec->getBuffers(env, input, &buffers);
808
809 if (err == OK) {
810 return buffers;
811 }
812
813 // if we're out of memory, an exception was already thrown
814 if (err != NO_MEMORY) {
815 throwExceptionAsNecessary(env, err);
816 }
817
818 return NULL;
819 }
820
android_media_MediaCodec_getName(JNIEnv * env,jobject thiz)821 static jobject android_media_MediaCodec_getName(
822 JNIEnv *env, jobject thiz) {
823 ALOGV("android_media_MediaCodec_getName");
824
825 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
826
827 if (codec == NULL) {
828 jniThrowException(env, "java/lang/IllegalStateException", NULL);
829 return NULL;
830 }
831
832 jstring name;
833 status_t err = codec->getName(env, &name);
834
835 if (err == OK) {
836 return name;
837 }
838
839 throwExceptionAsNecessary(env, err);
840
841 return NULL;
842 }
843
android_media_MediaCodec_setParameters(JNIEnv * env,jobject thiz,jobjectArray keys,jobjectArray vals)844 static void android_media_MediaCodec_setParameters(
845 JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) {
846 ALOGV("android_media_MediaCodec_setParameters");
847
848 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
849
850 if (codec == NULL) {
851 jniThrowException(env, "java/lang/IllegalStateException", NULL);
852 return;
853 }
854
855 sp<AMessage> params;
856 status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, ¶ms);
857
858 if (err == OK) {
859 err = codec->setParameters(params);
860 }
861
862 throwExceptionAsNecessary(env, err);
863 }
864
android_media_MediaCodec_setVideoScalingMode(JNIEnv * env,jobject thiz,jint mode)865 static void android_media_MediaCodec_setVideoScalingMode(
866 JNIEnv *env, jobject thiz, jint mode) {
867 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
868
869 if (codec == NULL) {
870 jniThrowException(env, "java/lang/IllegalStateException", NULL);
871 return;
872 }
873
874 if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
875 && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
876 jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
877 return;
878 }
879
880 codec->setVideoScalingMode(mode);
881 }
882
android_media_MediaCodec_native_init(JNIEnv * env)883 static void android_media_MediaCodec_native_init(JNIEnv *env) {
884 ScopedLocalRef<jclass> clazz(
885 env, env->FindClass("android/media/MediaCodec"));
886 CHECK(clazz.get() != NULL);
887
888 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I");
889 CHECK(gFields.context != NULL);
890
891 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
892 CHECK(clazz.get() != NULL);
893
894 gFields.cryptoInfoNumSubSamplesID =
895 env->GetFieldID(clazz.get(), "numSubSamples", "I");
896 CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
897
898 gFields.cryptoInfoNumBytesOfClearDataID =
899 env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
900 CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
901
902 gFields.cryptoInfoNumBytesOfEncryptedDataID =
903 env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
904 CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
905
906 gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
907 CHECK(gFields.cryptoInfoKeyID != NULL);
908
909 gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
910 CHECK(gFields.cryptoInfoIVID != NULL);
911
912 gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
913 CHECK(gFields.cryptoInfoModeID != NULL);
914
915 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
916 CHECK(clazz.get() != NULL);
917
918 jfieldID field;
919 field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
920 CHECK(field != NULL);
921 gCryptoErrorCodes.cryptoErrorNoKey =
922 env->GetStaticIntField(clazz.get(), field);
923
924 field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
925 CHECK(field != NULL);
926 gCryptoErrorCodes.cryptoErrorKeyExpired =
927 env->GetStaticIntField(clazz.get(), field);
928
929 field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
930 CHECK(field != NULL);
931 gCryptoErrorCodes.cryptoErrorResourceBusy =
932 env->GetStaticIntField(clazz.get(), field);
933 }
934
android_media_MediaCodec_native_setup(JNIEnv * env,jobject thiz,jstring name,jboolean nameIsType,jboolean encoder)935 static void android_media_MediaCodec_native_setup(
936 JNIEnv *env, jobject thiz,
937 jstring name, jboolean nameIsType, jboolean encoder) {
938 if (name == NULL) {
939 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
940 return;
941 }
942
943 const char *tmp = env->GetStringUTFChars(name, NULL);
944
945 if (tmp == NULL) {
946 return;
947 }
948
949 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
950
951 status_t err = codec->initCheck();
952
953 env->ReleaseStringUTFChars(name, tmp);
954 tmp = NULL;
955
956 if (err != OK) {
957 jniThrowException(
958 env,
959 "java/io/IOException",
960 "Failed to allocate component instance");
961 return;
962 }
963
964 setMediaCodec(env,thiz, codec);
965 }
966
android_media_MediaCodec_native_finalize(JNIEnv * env,jobject thiz)967 static void android_media_MediaCodec_native_finalize(
968 JNIEnv *env, jobject thiz) {
969 android_media_MediaCodec_release(env, thiz);
970 }
971
972 static JNINativeMethod gMethods[] = {
973 { "release", "()V", (void *)android_media_MediaCodec_release },
974
975 { "native_configure",
976 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
977 "Landroid/media/MediaCrypto;I)V",
978 (void *)android_media_MediaCodec_native_configure },
979
980 { "createInputSurface", "()Landroid/view/Surface;",
981 (void *)android_media_MediaCodec_createInputSurface },
982
983 { "start", "()V", (void *)android_media_MediaCodec_start },
984 { "stop", "()V", (void *)android_media_MediaCodec_stop },
985 { "flush", "()V", (void *)android_media_MediaCodec_flush },
986
987 { "queueInputBuffer", "(IIIJI)V",
988 (void *)android_media_MediaCodec_queueInputBuffer },
989
990 { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
991 (void *)android_media_MediaCodec_queueSecureInputBuffer },
992
993 { "dequeueInputBuffer", "(J)I",
994 (void *)android_media_MediaCodec_dequeueInputBuffer },
995
996 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
997 (void *)android_media_MediaCodec_dequeueOutputBuffer },
998
999 { "releaseOutputBuffer", "(IZ)V",
1000 (void *)android_media_MediaCodec_releaseOutputBuffer },
1001
1002 { "signalEndOfInputStream", "()V",
1003 (void *)android_media_MediaCodec_signalEndOfInputStream },
1004
1005 { "getOutputFormatNative", "()Ljava/util/Map;",
1006 (void *)android_media_MediaCodec_getOutputFormatNative },
1007
1008 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1009 (void *)android_media_MediaCodec_getBuffers },
1010
1011 { "getName", "()Ljava/lang/String;",
1012 (void *)android_media_MediaCodec_getName },
1013
1014 { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1015 (void *)android_media_MediaCodec_setParameters },
1016
1017 { "setVideoScalingMode", "(I)V",
1018 (void *)android_media_MediaCodec_setVideoScalingMode },
1019
1020 { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
1021
1022 { "native_setup", "(Ljava/lang/String;ZZ)V",
1023 (void *)android_media_MediaCodec_native_setup },
1024
1025 { "native_finalize", "()V",
1026 (void *)android_media_MediaCodec_native_finalize },
1027 };
1028
register_android_media_MediaCodec(JNIEnv * env)1029 int register_android_media_MediaCodec(JNIEnv *env) {
1030 return AndroidRuntime::registerNativeMethods(env,
1031 "android/media/MediaCodec", gMethods, NELEM(gMethods));
1032 }
1033