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 <cutils/compiler.h>
31
32 #include <gui/Surface.h>
33
34 #include <media/ICrypto.h>
35 #include <media/stagefright/MediaCodec.h>
36 #include <media/stagefright/foundation/ABuffer.h>
37 #include <media/stagefright/foundation/ADebug.h>
38 #include <media/stagefright/foundation/ALooper.h>
39 #include <media/stagefright/foundation/AMessage.h>
40 #include <media/stagefright/foundation/AString.h>
41 #include <media/stagefright/MediaErrors.h>
42
43 #include <nativehelper/ScopedLocalRef.h>
44
45 #include <system/window.h>
46
47 namespace android {
48
49 // Keep these in sync with their equivalents in MediaCodec.java !!!
50 enum {
51 DEQUEUE_INFO_TRY_AGAIN_LATER = -1,
52 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2,
53 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3,
54 };
55
56 enum {
57 EVENT_CALLBACK = 1,
58 EVENT_SET_CALLBACK = 2,
59 };
60
61 static struct CryptoErrorCodes {
62 jint cryptoErrorNoKey;
63 jint cryptoErrorKeyExpired;
64 jint cryptoErrorResourceBusy;
65 jint cryptoErrorInsufficientOutputProtection;
66 } gCryptoErrorCodes;
67
68 static struct CodecActionCodes {
69 jint codecActionTransient;
70 jint codecActionRecoverable;
71 } gCodecActionCodes;
72
73 struct fields_t {
74 jfieldID context;
75 jmethodID postEventFromNativeID;
76 jfieldID cryptoInfoNumSubSamplesID;
77 jfieldID cryptoInfoNumBytesOfClearDataID;
78 jfieldID cryptoInfoNumBytesOfEncryptedDataID;
79 jfieldID cryptoInfoKeyID;
80 jfieldID cryptoInfoIVID;
81 jfieldID cryptoInfoModeID;
82 };
83
84 static fields_t gFields;
85
86 ////////////////////////////////////////////////////////////////////////////////
87
JMediaCodec(JNIEnv * env,jobject thiz,const char * name,bool nameIsType,bool encoder)88 JMediaCodec::JMediaCodec(
89 JNIEnv *env, jobject thiz,
90 const char *name, bool nameIsType, bool encoder)
91 : mClass(NULL),
92 mObject(NULL) {
93 jclass clazz = env->GetObjectClass(thiz);
94 CHECK(clazz != NULL);
95
96 mClass = (jclass)env->NewGlobalRef(clazz);
97 mObject = env->NewWeakGlobalRef(thiz);
98
99 cacheJavaObjects(env);
100
101 mLooper = new ALooper;
102 mLooper->setName("MediaCodec_looper");
103
104 mLooper->start(
105 false, // runOnCallingThread
106 true, // canCallJava
107 PRIORITY_FOREGROUND);
108
109 if (nameIsType) {
110 mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
111 } else {
112 mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
113 }
114 CHECK((mCodec != NULL) != (mInitStatus != OK));
115 }
116
cacheJavaObjects(JNIEnv * env)117 void JMediaCodec::cacheJavaObjects(JNIEnv *env) {
118 jclass clazz = (jclass)env->FindClass("java/nio/ByteBuffer");
119 mByteBufferClass = (jclass)env->NewGlobalRef(clazz);
120 CHECK(mByteBufferClass != NULL);
121
122 ScopedLocalRef<jclass> byteOrderClass(
123 env, env->FindClass("java/nio/ByteOrder"));
124 CHECK(byteOrderClass.get() != NULL);
125
126 jmethodID nativeOrderID = env->GetStaticMethodID(
127 byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
128 CHECK(nativeOrderID != NULL);
129
130 jobject nativeByteOrderObj =
131 env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
132 mNativeByteOrderObj = env->NewGlobalRef(nativeByteOrderObj);
133 CHECK(mNativeByteOrderObj != NULL);
134 env->DeleteLocalRef(nativeByteOrderObj);
135 nativeByteOrderObj = NULL;
136
137 mByteBufferOrderMethodID = env->GetMethodID(
138 mByteBufferClass,
139 "order",
140 "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
141 CHECK(mByteBufferOrderMethodID != NULL);
142
143 mByteBufferAsReadOnlyBufferMethodID = env->GetMethodID(
144 mByteBufferClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
145 CHECK(mByteBufferAsReadOnlyBufferMethodID != NULL);
146
147 mByteBufferPositionMethodID = env->GetMethodID(
148 mByteBufferClass, "position", "(I)Ljava/nio/Buffer;");
149 CHECK(mByteBufferPositionMethodID != NULL);
150
151 mByteBufferLimitMethodID = env->GetMethodID(
152 mByteBufferClass, "limit", "(I)Ljava/nio/Buffer;");
153 CHECK(mByteBufferLimitMethodID != NULL);
154 }
155
initCheck() const156 status_t JMediaCodec::initCheck() const {
157 return mInitStatus;
158 }
159
registerSelf()160 void JMediaCodec::registerSelf() {
161 mLooper->registerHandler(this);
162 }
163
release()164 void JMediaCodec::release() {
165 if (mCodec != NULL) {
166 mCodec->release();
167 mCodec.clear();
168 mInitStatus = NO_INIT;
169 }
170
171 if (mLooper != NULL) {
172 mLooper->unregisterHandler(id());
173 mLooper->stop();
174 mLooper.clear();
175 }
176 }
177
~JMediaCodec()178 JMediaCodec::~JMediaCodec() {
179 if (mCodec != NULL || mLooper != NULL) {
180 /* MediaCodec and looper should have been released explicitly already
181 * in setMediaCodec() (see comments in setMediaCodec()).
182 *
183 * Otherwise JMediaCodec::~JMediaCodec() might be called from within the
184 * message handler, doing release() there risks deadlock as MediaCodec::
185 * release() post synchronous message to the same looper.
186 *
187 * Print a warning and try to proceed with releasing.
188 */
189 ALOGW("try to release MediaCodec from JMediaCodec::~JMediaCodec()...");
190 release();
191 ALOGW("done releasing MediaCodec from JMediaCodec::~JMediaCodec().");
192 }
193
194 JNIEnv *env = AndroidRuntime::getJNIEnv();
195
196 env->DeleteWeakGlobalRef(mObject);
197 mObject = NULL;
198 env->DeleteGlobalRef(mClass);
199 mClass = NULL;
200 deleteJavaObjects(env);
201 }
202
deleteJavaObjects(JNIEnv * env)203 void JMediaCodec::deleteJavaObjects(JNIEnv *env) {
204 env->DeleteGlobalRef(mByteBufferClass);
205 mByteBufferClass = NULL;
206 env->DeleteGlobalRef(mNativeByteOrderObj);
207 mNativeByteOrderObj = NULL;
208
209 mByteBufferOrderMethodID = NULL;
210 mByteBufferAsReadOnlyBufferMethodID = NULL;
211 mByteBufferPositionMethodID = NULL;
212 mByteBufferLimitMethodID = NULL;
213 }
214
setCallback(jobject cb)215 status_t JMediaCodec::setCallback(jobject cb) {
216 if (cb != NULL) {
217 if (mCallbackNotification == NULL) {
218 mCallbackNotification = new AMessage(kWhatCallbackNotify, id());
219 }
220 } else {
221 mCallbackNotification.clear();
222 }
223
224 return mCodec->setCallback(mCallbackNotification);
225 }
226
configure(const sp<AMessage> & format,const sp<IGraphicBufferProducer> & bufferProducer,const sp<ICrypto> & crypto,int flags)227 status_t JMediaCodec::configure(
228 const sp<AMessage> &format,
229 const sp<IGraphicBufferProducer> &bufferProducer,
230 const sp<ICrypto> &crypto,
231 int flags) {
232 sp<Surface> client;
233 if (bufferProducer != NULL) {
234 mSurfaceTextureClient =
235 new Surface(bufferProducer, true /* controlledByApp */);
236 } else {
237 mSurfaceTextureClient.clear();
238 }
239
240 return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
241 }
242
createInputSurface(sp<IGraphicBufferProducer> * bufferProducer)243 status_t JMediaCodec::createInputSurface(
244 sp<IGraphicBufferProducer>* bufferProducer) {
245 return mCodec->createInputSurface(bufferProducer);
246 }
247
start()248 status_t JMediaCodec::start() {
249 return mCodec->start();
250 }
251
stop()252 status_t JMediaCodec::stop() {
253 mSurfaceTextureClient.clear();
254
255 return mCodec->stop();
256 }
257
flush()258 status_t JMediaCodec::flush() {
259 return mCodec->flush();
260 }
261
reset()262 status_t JMediaCodec::reset() {
263 return mCodec->reset();
264 }
265
queueInputBuffer(size_t index,size_t offset,size_t size,int64_t timeUs,uint32_t flags,AString * errorDetailMsg)266 status_t JMediaCodec::queueInputBuffer(
267 size_t index,
268 size_t offset, size_t size, int64_t timeUs, uint32_t flags,
269 AString *errorDetailMsg) {
270 return mCodec->queueInputBuffer(
271 index, offset, size, timeUs, flags, errorDetailMsg);
272 }
273
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)274 status_t JMediaCodec::queueSecureInputBuffer(
275 size_t index,
276 size_t offset,
277 const CryptoPlugin::SubSample *subSamples,
278 size_t numSubSamples,
279 const uint8_t key[16],
280 const uint8_t iv[16],
281 CryptoPlugin::Mode mode,
282 int64_t presentationTimeUs,
283 uint32_t flags,
284 AString *errorDetailMsg) {
285 return mCodec->queueSecureInputBuffer(
286 index, offset, subSamples, numSubSamples, key, iv, mode,
287 presentationTimeUs, flags, errorDetailMsg);
288 }
289
dequeueInputBuffer(size_t * index,int64_t timeoutUs)290 status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
291 return mCodec->dequeueInputBuffer(index, timeoutUs);
292 }
293
dequeueOutputBuffer(JNIEnv * env,jobject bufferInfo,size_t * index,int64_t timeoutUs)294 status_t JMediaCodec::dequeueOutputBuffer(
295 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
296 size_t size, offset;
297 int64_t timeUs;
298 uint32_t flags;
299 status_t err = mCodec->dequeueOutputBuffer(
300 index, &offset, &size, &timeUs, &flags, timeoutUs);
301
302 if (err != OK) {
303 return err;
304 }
305
306 ScopedLocalRef<jclass> clazz(
307 env, env->FindClass("android/media/MediaCodec$BufferInfo"));
308
309 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
310 env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags);
311
312 return OK;
313 }
314
releaseOutputBuffer(size_t index,bool render,bool updatePTS,int64_t timestampNs)315 status_t JMediaCodec::releaseOutputBuffer(
316 size_t index, bool render, bool updatePTS, int64_t timestampNs) {
317 if (updatePTS) {
318 return mCodec->renderOutputBufferAndRelease(index, timestampNs);
319 }
320 return render
321 ? mCodec->renderOutputBufferAndRelease(index)
322 : mCodec->releaseOutputBuffer(index);
323 }
324
signalEndOfInputStream()325 status_t JMediaCodec::signalEndOfInputStream() {
326 return mCodec->signalEndOfInputStream();
327 }
328
getFormat(JNIEnv * env,bool input,jobject * format) const329 status_t JMediaCodec::getFormat(JNIEnv *env, bool input, jobject *format) const {
330 sp<AMessage> msg;
331 status_t err;
332 err = input ? mCodec->getInputFormat(&msg) : mCodec->getOutputFormat(&msg);
333 if (err != OK) {
334 return err;
335 }
336
337 return ConvertMessageToMap(env, msg, format);
338 }
339
getOutputFormat(JNIEnv * env,size_t index,jobject * format) const340 status_t JMediaCodec::getOutputFormat(JNIEnv *env, size_t index, jobject *format) const {
341 sp<AMessage> msg;
342 status_t err;
343 if ((err = mCodec->getOutputFormat(index, &msg)) != OK) {
344 return err;
345 }
346
347 return ConvertMessageToMap(env, msg, format);
348 }
349
getBuffers(JNIEnv * env,bool input,jobjectArray * bufArray) const350 status_t JMediaCodec::getBuffers(
351 JNIEnv *env, bool input, jobjectArray *bufArray) const {
352 Vector<sp<ABuffer> > buffers;
353
354 status_t err =
355 input
356 ? mCodec->getInputBuffers(&buffers)
357 : mCodec->getOutputBuffers(&buffers);
358
359 if (err != OK) {
360 return err;
361 }
362
363 *bufArray = (jobjectArray)env->NewObjectArray(
364 buffers.size(), mByteBufferClass, NULL);
365 if (*bufArray == NULL) {
366 return NO_MEMORY;
367 }
368
369 for (size_t i = 0; i < buffers.size(); ++i) {
370 const sp<ABuffer> &buffer = buffers.itemAt(i);
371
372 jobject byteBuffer = NULL;
373 err = createByteBufferFromABuffer(
374 env, !input /* readOnly */, true /* clearBuffer */, buffer, &byteBuffer);
375 if (err != OK) {
376 return err;
377 }
378 if (byteBuffer != NULL) {
379 env->SetObjectArrayElement(
380 *bufArray, i, byteBuffer);
381
382 env->DeleteLocalRef(byteBuffer);
383 byteBuffer = NULL;
384 }
385 }
386
387 return OK;
388 }
389
390 // static
createByteBufferFromABuffer(JNIEnv * env,bool readOnly,bool clearBuffer,const sp<ABuffer> & buffer,jobject * buf) const391 status_t JMediaCodec::createByteBufferFromABuffer(
392 JNIEnv *env, bool readOnly, bool clearBuffer, const sp<ABuffer> &buffer,
393 jobject *buf) const {
394 // if this is an ABuffer that doesn't actually hold any accessible memory,
395 // use a null ByteBuffer
396 *buf = NULL;
397 if (buffer->base() == NULL) {
398 return OK;
399 }
400
401 jobject byteBuffer =
402 env->NewDirectByteBuffer(buffer->base(), buffer->capacity());
403 if (readOnly && byteBuffer != NULL) {
404 jobject readOnlyBuffer = env->CallObjectMethod(
405 byteBuffer, mByteBufferAsReadOnlyBufferMethodID);
406 env->DeleteLocalRef(byteBuffer);
407 byteBuffer = readOnlyBuffer;
408 }
409 if (byteBuffer == NULL) {
410 return NO_MEMORY;
411 }
412 jobject me = env->CallObjectMethod(
413 byteBuffer, mByteBufferOrderMethodID, mNativeByteOrderObj);
414 env->DeleteLocalRef(me);
415 me = env->CallObjectMethod(
416 byteBuffer, mByteBufferLimitMethodID,
417 clearBuffer ? buffer->capacity() : (buffer->offset() + buffer->size()));
418 env->DeleteLocalRef(me);
419 me = env->CallObjectMethod(
420 byteBuffer, mByteBufferPositionMethodID,
421 clearBuffer ? 0 : buffer->offset());
422 env->DeleteLocalRef(me);
423 me = NULL;
424
425 *buf = byteBuffer;
426 return OK;
427 }
428
getBuffer(JNIEnv * env,bool input,size_t index,jobject * buf) const429 status_t JMediaCodec::getBuffer(
430 JNIEnv *env, bool input, size_t index, jobject *buf) const {
431 sp<ABuffer> buffer;
432
433 status_t err =
434 input
435 ? mCodec->getInputBuffer(index, &buffer)
436 : mCodec->getOutputBuffer(index, &buffer);
437
438 if (err != OK) {
439 return err;
440 }
441
442 return createByteBufferFromABuffer(
443 env, !input /* readOnly */, input /* clearBuffer */, buffer, buf);
444 }
445
getImage(JNIEnv * env,bool input,size_t index,jobject * buf) const446 status_t JMediaCodec::getImage(
447 JNIEnv *env, bool input, size_t index, jobject *buf) const {
448 sp<ABuffer> buffer;
449
450 status_t err =
451 input
452 ? mCodec->getInputBuffer(index, &buffer)
453 : mCodec->getOutputBuffer(index, &buffer);
454
455 if (err != OK) {
456 return err;
457 }
458
459 // if this is an ABuffer that doesn't actually hold any accessible memory,
460 // use a null ByteBuffer
461 *buf = NULL;
462 if (buffer->base() == NULL) {
463 return OK;
464 }
465
466 // check if buffer is an image
467 sp<ABuffer> imageData;
468 if (!buffer->meta()->findBuffer("image-data", &imageData)) {
469 return OK;
470 }
471
472 int64_t timestamp = 0;
473 if (!input && buffer->meta()->findInt64("timeUs", ×tamp)) {
474 timestamp *= 1000; // adjust to ns
475 }
476
477 jobject byteBuffer = NULL;
478 err = createByteBufferFromABuffer(
479 env, !input /* readOnly */, input /* clearBuffer */, buffer, &byteBuffer);
480 if (err != OK) {
481 return OK;
482 }
483
484 jobject infoBuffer = NULL;
485 err = createByteBufferFromABuffer(
486 env, true /* readOnly */, true /* clearBuffer */, imageData, &infoBuffer);
487 if (err != OK) {
488 env->DeleteLocalRef(byteBuffer);
489 byteBuffer = NULL;
490 return OK;
491 }
492
493 jobject cropRect = NULL;
494 int32_t left, top, right, bottom;
495 if (buffer->meta()->findRect("crop-rect", &left, &top, &right, &bottom)) {
496 ScopedLocalRef<jclass> rectClazz(
497 env, env->FindClass("android/graphics/Rect"));
498 CHECK(rectClazz.get() != NULL);
499
500 jmethodID rectConstructID = env->GetMethodID(
501 rectClazz.get(), "<init>", "(IIII)V");
502
503 cropRect = env->NewObject(
504 rectClazz.get(), rectConstructID, left, top, right + 1, bottom + 1);
505 }
506
507 ScopedLocalRef<jclass> imageClazz(
508 env, env->FindClass("android/media/MediaCodec$MediaImage"));
509 CHECK(imageClazz.get() != NULL);
510
511 jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>",
512 "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;ZJIILandroid/graphics/Rect;)V");
513
514 *buf = env->NewObject(imageClazz.get(), imageConstructID,
515 byteBuffer, infoBuffer,
516 (jboolean)!input /* readOnly */,
517 (jlong)timestamp,
518 (jint)0 /* xOffset */, (jint)0 /* yOffset */, cropRect);
519
520 // if MediaImage creation fails, return null
521 if (env->ExceptionCheck()) {
522 env->ExceptionDescribe();
523 env->ExceptionClear();
524 *buf = NULL;
525 }
526
527 if (cropRect != NULL) {
528 env->DeleteLocalRef(cropRect);
529 cropRect = NULL;
530 }
531
532 env->DeleteLocalRef(byteBuffer);
533 byteBuffer = NULL;
534
535 env->DeleteLocalRef(infoBuffer);
536 infoBuffer = NULL;
537
538 return OK;
539 }
540
getName(JNIEnv * env,jstring * nameStr) const541 status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
542 AString name;
543
544 status_t err = mCodec->getName(&name);
545
546 if (err != OK) {
547 return err;
548 }
549
550 *nameStr = env->NewStringUTF(name.c_str());
551
552 return OK;
553 }
554
setParameters(const sp<AMessage> & msg)555 status_t JMediaCodec::setParameters(const sp<AMessage> &msg) {
556 return mCodec->setParameters(msg);
557 }
558
setVideoScalingMode(int mode)559 void JMediaCodec::setVideoScalingMode(int mode) {
560 if (mSurfaceTextureClient != NULL) {
561 native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
562 }
563 }
564
createCodecException(JNIEnv * env,status_t err,int32_t actionCode,const char * msg=NULL)565 static jthrowable createCodecException(
566 JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) {
567 ScopedLocalRef<jclass> clazz(
568 env, env->FindClass("android/media/MediaCodec$CodecException"));
569 CHECK(clazz.get() != NULL);
570
571 const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V");
572 CHECK(ctor != NULL);
573
574 ScopedLocalRef<jstring> msgObj(
575 env, env->NewStringUTF(msg != NULL ? msg : String8::format("Error %#x", err)));
576
577 // translate action code to Java equivalent
578 switch (actionCode) {
579 case ACTION_CODE_TRANSIENT:
580 actionCode = gCodecActionCodes.codecActionTransient;
581 break;
582 case ACTION_CODE_RECOVERABLE:
583 actionCode = gCodecActionCodes.codecActionRecoverable;
584 break;
585 default:
586 actionCode = 0; // everything else is fatal
587 break;
588 }
589
590 return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get());
591 }
592
handleCallback(const sp<AMessage> & msg)593 void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
594 int32_t arg1, arg2 = 0;
595 jobject obj = NULL;
596 CHECK(msg->findInt32("callbackID", &arg1));
597 JNIEnv *env = AndroidRuntime::getJNIEnv();
598
599 switch (arg1) {
600 case MediaCodec::CB_INPUT_AVAILABLE:
601 {
602 CHECK(msg->findInt32("index", &arg2));
603 break;
604 }
605
606 case MediaCodec::CB_OUTPUT_AVAILABLE:
607 {
608 CHECK(msg->findInt32("index", &arg2));
609
610 size_t size, offset;
611 int64_t timeUs;
612 uint32_t flags;
613 CHECK(msg->findSize("size", &size));
614 CHECK(msg->findSize("offset", &offset));
615 CHECK(msg->findInt64("timeUs", &timeUs));
616 CHECK(msg->findInt32("flags", (int32_t *)&flags));
617
618 ScopedLocalRef<jclass> clazz(
619 env, env->FindClass("android/media/MediaCodec$BufferInfo"));
620 jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "()V");
621 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
622
623 obj = env->NewObject(clazz.get(), ctor);
624
625 if (obj == NULL) {
626 if (env->ExceptionCheck()) {
627 ALOGE("Could not create MediaCodec.BufferInfo.");
628 env->ExceptionClear();
629 }
630 jniThrowException(env, "java/lang/IllegalStateException", NULL);
631 return;
632 }
633
634 env->CallVoidMethod(obj, method, (jint)offset, (jint)size, timeUs, flags);
635 break;
636 }
637
638 case MediaCodec::CB_ERROR:
639 {
640 int32_t err, actionCode;
641 CHECK(msg->findInt32("err", &err));
642 CHECK(msg->findInt32("actionCode", &actionCode));
643
644 // note that DRM errors could conceivably alias into a CodecException
645 obj = (jobject)createCodecException(env, err, actionCode);
646
647 if (obj == NULL) {
648 if (env->ExceptionCheck()) {
649 ALOGE("Could not create CodecException object.");
650 env->ExceptionClear();
651 }
652 jniThrowException(env, "java/lang/IllegalStateException", NULL);
653 return;
654 }
655
656 break;
657 }
658
659 case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
660 {
661 sp<AMessage> format;
662 CHECK(msg->findMessage("format", &format));
663
664 if (OK != ConvertMessageToMap(env, format, &obj)) {
665 jniThrowException(env, "java/lang/IllegalStateException", NULL);
666 return;
667 }
668
669 break;
670 }
671
672 default:
673 TRESPASS();
674 }
675
676 env->CallVoidMethod(
677 mObject,
678 gFields.postEventFromNativeID,
679 EVENT_CALLBACK,
680 arg1,
681 arg2,
682 obj);
683
684 env->DeleteLocalRef(obj);
685 }
686
onMessageReceived(const sp<AMessage> & msg)687 void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
688 switch (msg->what()) {
689 case kWhatCallbackNotify:
690 {
691 handleCallback(msg);
692 break;
693 }
694 default:
695 TRESPASS();
696 }
697 }
698
699 } // namespace android
700
701 ////////////////////////////////////////////////////////////////////////////////
702
703 using namespace android;
704
setMediaCodec(JNIEnv * env,jobject thiz,const sp<JMediaCodec> & codec)705 static sp<JMediaCodec> setMediaCodec(
706 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
707 sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
708 if (codec != NULL) {
709 codec->incStrong(thiz);
710 }
711 if (old != NULL) {
712 /* release MediaCodec and stop the looper now before decStrong.
713 * otherwise JMediaCodec::~JMediaCodec() could be called from within
714 * its message handler, doing release() from there will deadlock
715 * (as MediaCodec::release() post synchronous message to the same looper)
716 */
717 old->release();
718 old->decStrong(thiz);
719 }
720 env->SetLongField(thiz, gFields.context, (jlong)codec.get());
721
722 return old;
723 }
724
getMediaCodec(JNIEnv * env,jobject thiz)725 static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
726 return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
727 }
728
android_media_MediaCodec_release(JNIEnv * env,jobject thiz)729 static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
730 setMediaCodec(env, thiz, NULL);
731 }
732
throwCodecException(JNIEnv * env,status_t err,int32_t actionCode,const char * msg)733 static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) {
734 jthrowable exception = createCodecException(env, err, actionCode, msg);
735 env->Throw(exception);
736 }
737
throwCryptoException(JNIEnv * env,status_t err,const char * msg)738 static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
739 ScopedLocalRef<jclass> clazz(
740 env, env->FindClass("android/media/MediaCodec$CryptoException"));
741 CHECK(clazz.get() != NULL);
742
743 jmethodID constructID =
744 env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
745 CHECK(constructID != NULL);
746
747 jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
748
749 /* translate OS errors to Java API CryptoException errorCodes (which are positive) */
750 switch (err) {
751 case ERROR_DRM_NO_LICENSE:
752 err = gCryptoErrorCodes.cryptoErrorNoKey;
753 break;
754 case ERROR_DRM_LICENSE_EXPIRED:
755 err = gCryptoErrorCodes.cryptoErrorKeyExpired;
756 break;
757 case ERROR_DRM_RESOURCE_BUSY:
758 err = gCryptoErrorCodes.cryptoErrorResourceBusy;
759 break;
760 case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
761 err = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
762 break;
763 default: /* Other negative DRM error codes go out as is. */
764 break;
765 }
766
767 jthrowable exception =
768 (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
769
770 env->Throw(exception);
771 }
772
throwExceptionAsNecessary(JNIEnv * env,status_t err,int32_t actionCode=ACTION_CODE_FATAL,const char * msg=NULL)773 static jint throwExceptionAsNecessary(
774 JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL,
775 const char *msg = NULL) {
776 switch (err) {
777 case OK:
778 return 0;
779
780 case -EAGAIN:
781 return DEQUEUE_INFO_TRY_AGAIN_LATER;
782
783 case INFO_FORMAT_CHANGED:
784 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
785
786 case INFO_OUTPUT_BUFFERS_CHANGED:
787 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
788
789 case INVALID_OPERATION:
790 jniThrowException(env, "java/lang/IllegalStateException", msg);
791 return 0;
792
793 default:
794 if (isCryptoError(err)) {
795 throwCryptoException(env, err, msg);
796 return 0;
797 }
798 throwCodecException(env, err, actionCode, msg);
799 return 0;
800 }
801 }
802
android_media_MediaCodec_native_setCallback(JNIEnv * env,jobject thiz,jobject cb)803 static void android_media_MediaCodec_native_setCallback(
804 JNIEnv *env,
805 jobject thiz,
806 jobject cb) {
807 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
808
809 if (codec == NULL) {
810 throwExceptionAsNecessary(env, INVALID_OPERATION);
811 return;
812 }
813
814 status_t err = codec->setCallback(cb);
815
816 throwExceptionAsNecessary(env, err);
817 }
818
android_media_MediaCodec_native_configure(JNIEnv * env,jobject thiz,jobjectArray keys,jobjectArray values,jobject jsurface,jobject jcrypto,jint flags)819 static void android_media_MediaCodec_native_configure(
820 JNIEnv *env,
821 jobject thiz,
822 jobjectArray keys, jobjectArray values,
823 jobject jsurface,
824 jobject jcrypto,
825 jint flags) {
826 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
827
828 if (codec == NULL) {
829 throwExceptionAsNecessary(env, INVALID_OPERATION);
830 return;
831 }
832
833 sp<AMessage> format;
834 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
835
836 if (err != OK) {
837 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
838 return;
839 }
840
841 sp<IGraphicBufferProducer> bufferProducer;
842 if (jsurface != NULL) {
843 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
844 if (surface != NULL) {
845 bufferProducer = surface->getIGraphicBufferProducer();
846 } else {
847 jniThrowException(
848 env,
849 "java/lang/IllegalArgumentException",
850 "The surface has been released");
851 return;
852 }
853 }
854
855 sp<ICrypto> crypto;
856 if (jcrypto != NULL) {
857 crypto = JCrypto::GetCrypto(env, jcrypto);
858 }
859
860 err = codec->configure(format, bufferProducer, crypto, flags);
861
862 throwExceptionAsNecessary(env, err);
863 }
864
android_media_MediaCodec_createInputSurface(JNIEnv * env,jobject thiz)865 static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
866 jobject thiz) {
867 ALOGV("android_media_MediaCodec_createInputSurface");
868
869 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
870 if (codec == NULL) {
871 throwExceptionAsNecessary(env, INVALID_OPERATION);
872 return NULL;
873 }
874
875 // Tell the MediaCodec that we want to use a Surface as input.
876 sp<IGraphicBufferProducer> bufferProducer;
877 status_t err = codec->createInputSurface(&bufferProducer);
878 if (err != NO_ERROR) {
879 throwExceptionAsNecessary(env, err);
880 return NULL;
881 }
882
883 // Wrap the IGBP in a Java-language Surface.
884 return android_view_Surface_createFromIGraphicBufferProducer(env,
885 bufferProducer);
886 }
887
android_media_MediaCodec_start(JNIEnv * env,jobject thiz)888 static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
889 ALOGV("android_media_MediaCodec_start");
890
891 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
892
893 if (codec == NULL) {
894 throwExceptionAsNecessary(env, INVALID_OPERATION);
895 return;
896 }
897
898 status_t err = codec->start();
899
900 throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed");
901 }
902
android_media_MediaCodec_stop(JNIEnv * env,jobject thiz)903 static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
904 ALOGV("android_media_MediaCodec_stop");
905
906 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
907
908 if (codec == NULL) {
909 throwExceptionAsNecessary(env, INVALID_OPERATION);
910 return;
911 }
912
913 status_t err = codec->stop();
914
915 throwExceptionAsNecessary(env, err);
916 }
917
android_media_MediaCodec_reset(JNIEnv * env,jobject thiz)918 static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
919 ALOGV("android_media_MediaCodec_reset");
920
921 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
922
923 if (codec == NULL) {
924 throwExceptionAsNecessary(env, INVALID_OPERATION);
925 return;
926 }
927
928 status_t err = codec->reset();
929 if (err != OK) {
930 // treat all errors as fatal for now, though resource not available
931 // errors could be treated as transient.
932 // we also should avoid sending INVALID_OPERATION here due to
933 // the transitory nature of reset(), it should not inadvertently
934 // trigger an IllegalStateException.
935 err = UNKNOWN_ERROR;
936 }
937 throwExceptionAsNecessary(env, err);
938 }
939
android_media_MediaCodec_flush(JNIEnv * env,jobject thiz)940 static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
941 ALOGV("android_media_MediaCodec_flush");
942
943 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
944
945 if (codec == NULL) {
946 throwExceptionAsNecessary(env, INVALID_OPERATION);
947 return;
948 }
949
950 status_t err = codec->flush();
951
952 throwExceptionAsNecessary(env, err);
953 }
954
android_media_MediaCodec_queueInputBuffer(JNIEnv * env,jobject thiz,jint index,jint offset,jint size,jlong timestampUs,jint flags)955 static void android_media_MediaCodec_queueInputBuffer(
956 JNIEnv *env,
957 jobject thiz,
958 jint index,
959 jint offset,
960 jint size,
961 jlong timestampUs,
962 jint flags) {
963 ALOGV("android_media_MediaCodec_queueInputBuffer");
964
965 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
966
967 if (codec == NULL) {
968 throwExceptionAsNecessary(env, INVALID_OPERATION);
969 return;
970 }
971
972 AString errorDetailMsg;
973
974 status_t err = codec->queueInputBuffer(
975 index, offset, size, timestampUs, flags, &errorDetailMsg);
976
977 throwExceptionAsNecessary(
978 env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
979 }
980
android_media_MediaCodec_queueSecureInputBuffer(JNIEnv * env,jobject thiz,jint index,jint offset,jobject cryptoInfoObj,jlong timestampUs,jint flags)981 static void android_media_MediaCodec_queueSecureInputBuffer(
982 JNIEnv *env,
983 jobject thiz,
984 jint index,
985 jint offset,
986 jobject cryptoInfoObj,
987 jlong timestampUs,
988 jint flags) {
989 ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
990
991 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
992
993 if (codec == NULL) {
994 throwExceptionAsNecessary(env, INVALID_OPERATION);
995 return;
996 }
997
998 jint numSubSamples =
999 env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
1000
1001 jintArray numBytesOfClearDataObj =
1002 (jintArray)env->GetObjectField(
1003 cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
1004
1005 jintArray numBytesOfEncryptedDataObj =
1006 (jintArray)env->GetObjectField(
1007 cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
1008
1009 jbyteArray keyObj =
1010 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
1011
1012 jbyteArray ivObj =
1013 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
1014
1015 jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
1016
1017 status_t err = OK;
1018
1019 CryptoPlugin::SubSample *subSamples = NULL;
1020 jbyte *key = NULL;
1021 jbyte *iv = NULL;
1022
1023 if (numSubSamples <= 0) {
1024 err = -EINVAL;
1025 } else if (numBytesOfClearDataObj == NULL
1026 && numBytesOfEncryptedDataObj == NULL) {
1027 err = -EINVAL;
1028 } else if (numBytesOfEncryptedDataObj != NULL
1029 && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
1030 err = -ERANGE;
1031 } else if (numBytesOfClearDataObj != NULL
1032 && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
1033 err = -ERANGE;
1034 // subSamples array may silently overflow if number of samples are too large. Use
1035 // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
1036 } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) {
1037 err = -EINVAL;
1038 } else {
1039 jboolean isCopy;
1040
1041 jint *numBytesOfClearData =
1042 (numBytesOfClearDataObj == NULL)
1043 ? NULL
1044 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
1045
1046 jint *numBytesOfEncryptedData =
1047 (numBytesOfEncryptedDataObj == NULL)
1048 ? NULL
1049 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
1050
1051 subSamples = new CryptoPlugin::SubSample[numSubSamples];
1052
1053 for (jint i = 0; i < numSubSamples; ++i) {
1054 subSamples[i].mNumBytesOfClearData =
1055 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
1056
1057 subSamples[i].mNumBytesOfEncryptedData =
1058 (numBytesOfEncryptedData == NULL)
1059 ? 0 : numBytesOfEncryptedData[i];
1060 }
1061
1062 if (numBytesOfEncryptedData != NULL) {
1063 env->ReleaseIntArrayElements(
1064 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
1065 numBytesOfEncryptedData = NULL;
1066 }
1067
1068 if (numBytesOfClearData != NULL) {
1069 env->ReleaseIntArrayElements(
1070 numBytesOfClearDataObj, numBytesOfClearData, 0);
1071 numBytesOfClearData = NULL;
1072 }
1073 }
1074
1075 if (err == OK && keyObj != NULL) {
1076 if (env->GetArrayLength(keyObj) != 16) {
1077 err = -EINVAL;
1078 } else {
1079 jboolean isCopy;
1080 key = env->GetByteArrayElements(keyObj, &isCopy);
1081 }
1082 }
1083
1084 if (err == OK && ivObj != NULL) {
1085 if (env->GetArrayLength(ivObj) != 16) {
1086 err = -EINVAL;
1087 } else {
1088 jboolean isCopy;
1089 iv = env->GetByteArrayElements(ivObj, &isCopy);
1090 }
1091 }
1092
1093 AString errorDetailMsg;
1094
1095 if (err == OK) {
1096 err = codec->queueSecureInputBuffer(
1097 index, offset,
1098 subSamples, numSubSamples,
1099 (const uint8_t *)key, (const uint8_t *)iv,
1100 (CryptoPlugin::Mode)mode,
1101 timestampUs,
1102 flags,
1103 &errorDetailMsg);
1104 }
1105
1106 if (iv != NULL) {
1107 env->ReleaseByteArrayElements(ivObj, iv, 0);
1108 iv = NULL;
1109 }
1110
1111 if (key != NULL) {
1112 env->ReleaseByteArrayElements(keyObj, key, 0);
1113 key = NULL;
1114 }
1115
1116 delete[] subSamples;
1117 subSamples = NULL;
1118
1119 throwExceptionAsNecessary(
1120 env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
1121 }
1122
android_media_MediaCodec_dequeueInputBuffer(JNIEnv * env,jobject thiz,jlong timeoutUs)1123 static jint android_media_MediaCodec_dequeueInputBuffer(
1124 JNIEnv *env, jobject thiz, jlong timeoutUs) {
1125 ALOGV("android_media_MediaCodec_dequeueInputBuffer");
1126
1127 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1128
1129 if (codec == NULL) {
1130 throwExceptionAsNecessary(env, INVALID_OPERATION);
1131 return -1;
1132 }
1133
1134 size_t index;
1135 status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
1136
1137 if (err == OK) {
1138 return (jint) index;
1139 }
1140
1141 return throwExceptionAsNecessary(env, err);
1142 }
1143
android_media_MediaCodec_dequeueOutputBuffer(JNIEnv * env,jobject thiz,jobject bufferInfo,jlong timeoutUs)1144 static jint android_media_MediaCodec_dequeueOutputBuffer(
1145 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
1146 ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
1147
1148 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1149
1150 if (codec == NULL) {
1151 throwExceptionAsNecessary(env, INVALID_OPERATION);
1152 return 0;
1153 }
1154
1155 size_t index;
1156 status_t err = codec->dequeueOutputBuffer(
1157 env, bufferInfo, &index, timeoutUs);
1158
1159 if (err == OK) {
1160 return (jint) index;
1161 }
1162
1163 return throwExceptionAsNecessary(env, err);
1164 }
1165
android_media_MediaCodec_releaseOutputBuffer(JNIEnv * env,jobject thiz,jint index,jboolean render,jboolean updatePTS,jlong timestampNs)1166 static void android_media_MediaCodec_releaseOutputBuffer(
1167 JNIEnv *env, jobject thiz,
1168 jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
1169 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
1170
1171 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1172
1173 if (codec == NULL) {
1174 throwExceptionAsNecessary(env, INVALID_OPERATION);
1175 return;
1176 }
1177
1178 status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
1179
1180 throwExceptionAsNecessary(env, err);
1181 }
1182
android_media_MediaCodec_signalEndOfInputStream(JNIEnv * env,jobject thiz)1183 static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
1184 jobject thiz) {
1185 ALOGV("android_media_MediaCodec_signalEndOfInputStream");
1186
1187 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1188 if (codec == NULL) {
1189 throwExceptionAsNecessary(env, INVALID_OPERATION);
1190 return;
1191 }
1192
1193 status_t err = codec->signalEndOfInputStream();
1194
1195 throwExceptionAsNecessary(env, err);
1196 }
1197
android_media_MediaCodec_getFormatNative(JNIEnv * env,jobject thiz,jboolean input)1198 static jobject android_media_MediaCodec_getFormatNative(
1199 JNIEnv *env, jobject thiz, jboolean input) {
1200 ALOGV("android_media_MediaCodec_getFormatNative");
1201
1202 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1203
1204 if (codec == NULL) {
1205 throwExceptionAsNecessary(env, INVALID_OPERATION);
1206 return NULL;
1207 }
1208
1209 jobject format;
1210 status_t err = codec->getFormat(env, input, &format);
1211
1212 if (err == OK) {
1213 return format;
1214 }
1215
1216 throwExceptionAsNecessary(env, err);
1217
1218 return NULL;
1219 }
1220
android_media_MediaCodec_getOutputFormatForIndexNative(JNIEnv * env,jobject thiz,jint index)1221 static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
1222 JNIEnv *env, jobject thiz, jint index) {
1223 ALOGV("android_media_MediaCodec_getOutputFormatForIndexNative");
1224
1225 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1226
1227 if (codec == NULL) {
1228 throwExceptionAsNecessary(env, INVALID_OPERATION);
1229 return NULL;
1230 }
1231
1232 jobject format;
1233 status_t err = codec->getOutputFormat(env, index, &format);
1234
1235 if (err == OK) {
1236 return format;
1237 }
1238
1239 throwExceptionAsNecessary(env, err);
1240
1241 return NULL;
1242 }
1243
android_media_MediaCodec_getBuffers(JNIEnv * env,jobject thiz,jboolean input)1244 static jobjectArray android_media_MediaCodec_getBuffers(
1245 JNIEnv *env, jobject thiz, jboolean input) {
1246 ALOGV("android_media_MediaCodec_getBuffers");
1247
1248 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1249
1250 if (codec == NULL) {
1251 throwExceptionAsNecessary(env, INVALID_OPERATION);
1252 return NULL;
1253 }
1254
1255 jobjectArray buffers;
1256 status_t err = codec->getBuffers(env, input, &buffers);
1257
1258 if (err == OK) {
1259 return buffers;
1260 }
1261
1262 // if we're out of memory, an exception was already thrown
1263 if (err != NO_MEMORY) {
1264 throwExceptionAsNecessary(env, err);
1265 }
1266
1267 return NULL;
1268 }
1269
android_media_MediaCodec_getBuffer(JNIEnv * env,jobject thiz,jboolean input,jint index)1270 static jobject android_media_MediaCodec_getBuffer(
1271 JNIEnv *env, jobject thiz, jboolean input, jint index) {
1272 ALOGV("android_media_MediaCodec_getBuffer");
1273
1274 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1275
1276 if (codec == NULL) {
1277 throwExceptionAsNecessary(env, INVALID_OPERATION);
1278 return NULL;
1279 }
1280
1281 jobject buffer;
1282 status_t err = codec->getBuffer(env, input, index, &buffer);
1283
1284 if (err == OK) {
1285 return buffer;
1286 }
1287
1288 // if we're out of memory, an exception was already thrown
1289 if (err != NO_MEMORY) {
1290 throwExceptionAsNecessary(env, err);
1291 }
1292
1293 return NULL;
1294 }
1295
android_media_MediaCodec_getImage(JNIEnv * env,jobject thiz,jboolean input,jint index)1296 static jobject android_media_MediaCodec_getImage(
1297 JNIEnv *env, jobject thiz, jboolean input, jint index) {
1298 ALOGV("android_media_MediaCodec_getImage");
1299
1300 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1301
1302 if (codec == NULL) {
1303 throwExceptionAsNecessary(env, INVALID_OPERATION);
1304 return NULL;
1305 }
1306
1307 jobject image;
1308 status_t err = codec->getImage(env, input, index, &image);
1309
1310 if (err == OK) {
1311 return image;
1312 }
1313
1314 // if we're out of memory, an exception was already thrown
1315 if (err != NO_MEMORY) {
1316 throwExceptionAsNecessary(env, err);
1317 }
1318
1319 return NULL;
1320 }
1321
android_media_MediaCodec_getName(JNIEnv * env,jobject thiz)1322 static jobject android_media_MediaCodec_getName(
1323 JNIEnv *env, jobject thiz) {
1324 ALOGV("android_media_MediaCodec_getName");
1325
1326 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1327
1328 if (codec == NULL) {
1329 throwExceptionAsNecessary(env, INVALID_OPERATION);
1330 return NULL;
1331 }
1332
1333 jstring name;
1334 status_t err = codec->getName(env, &name);
1335
1336 if (err == OK) {
1337 return name;
1338 }
1339
1340 throwExceptionAsNecessary(env, err);
1341
1342 return NULL;
1343 }
1344
android_media_MediaCodec_setParameters(JNIEnv * env,jobject thiz,jobjectArray keys,jobjectArray vals)1345 static void android_media_MediaCodec_setParameters(
1346 JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) {
1347 ALOGV("android_media_MediaCodec_setParameters");
1348
1349 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1350
1351 if (codec == NULL) {
1352 throwExceptionAsNecessary(env, INVALID_OPERATION);
1353 return;
1354 }
1355
1356 sp<AMessage> params;
1357 status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, ¶ms);
1358
1359 if (err == OK) {
1360 err = codec->setParameters(params);
1361 }
1362
1363 throwExceptionAsNecessary(env, err);
1364 }
1365
android_media_MediaCodec_setVideoScalingMode(JNIEnv * env,jobject thiz,jint mode)1366 static void android_media_MediaCodec_setVideoScalingMode(
1367 JNIEnv *env, jobject thiz, jint mode) {
1368 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1369
1370 if (codec == NULL) {
1371 throwExceptionAsNecessary(env, INVALID_OPERATION);
1372 return;
1373 }
1374
1375 if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
1376 && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
1377 jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
1378 return;
1379 }
1380
1381 codec->setVideoScalingMode(mode);
1382 }
1383
android_media_MediaCodec_native_init(JNIEnv * env)1384 static void android_media_MediaCodec_native_init(JNIEnv *env) {
1385 ScopedLocalRef<jclass> clazz(
1386 env, env->FindClass("android/media/MediaCodec"));
1387 CHECK(clazz.get() != NULL);
1388
1389 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
1390 CHECK(gFields.context != NULL);
1391
1392 gFields.postEventFromNativeID =
1393 env->GetMethodID(
1394 clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
1395
1396 CHECK(gFields.postEventFromNativeID != NULL);
1397
1398 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
1399 CHECK(clazz.get() != NULL);
1400
1401 gFields.cryptoInfoNumSubSamplesID =
1402 env->GetFieldID(clazz.get(), "numSubSamples", "I");
1403 CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
1404
1405 gFields.cryptoInfoNumBytesOfClearDataID =
1406 env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
1407 CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
1408
1409 gFields.cryptoInfoNumBytesOfEncryptedDataID =
1410 env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
1411 CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
1412
1413 gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
1414 CHECK(gFields.cryptoInfoKeyID != NULL);
1415
1416 gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
1417 CHECK(gFields.cryptoInfoIVID != NULL);
1418
1419 gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
1420 CHECK(gFields.cryptoInfoModeID != NULL);
1421
1422 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
1423 CHECK(clazz.get() != NULL);
1424
1425 jfieldID field;
1426 field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
1427 CHECK(field != NULL);
1428 gCryptoErrorCodes.cryptoErrorNoKey =
1429 env->GetStaticIntField(clazz.get(), field);
1430
1431 field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
1432 CHECK(field != NULL);
1433 gCryptoErrorCodes.cryptoErrorKeyExpired =
1434 env->GetStaticIntField(clazz.get(), field);
1435
1436 field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
1437 CHECK(field != NULL);
1438 gCryptoErrorCodes.cryptoErrorResourceBusy =
1439 env->GetStaticIntField(clazz.get(), field);
1440
1441 field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_OUTPUT_PROTECTION", "I");
1442 CHECK(field != NULL);
1443 gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection =
1444 env->GetStaticIntField(clazz.get(), field);
1445
1446 clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
1447 CHECK(clazz.get() != NULL);
1448 field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
1449 CHECK(field != NULL);
1450 gCodecActionCodes.codecActionTransient =
1451 env->GetStaticIntField(clazz.get(), field);
1452
1453 field = env->GetStaticFieldID(clazz.get(), "ACTION_RECOVERABLE", "I");
1454 CHECK(field != NULL);
1455 gCodecActionCodes.codecActionRecoverable =
1456 env->GetStaticIntField(clazz.get(), field);
1457 }
1458
android_media_MediaCodec_native_setup(JNIEnv * env,jobject thiz,jstring name,jboolean nameIsType,jboolean encoder)1459 static void android_media_MediaCodec_native_setup(
1460 JNIEnv *env, jobject thiz,
1461 jstring name, jboolean nameIsType, jboolean encoder) {
1462 if (name == NULL) {
1463 jniThrowException(env, "java/lang/NullPointerException", NULL);
1464 return;
1465 }
1466
1467 const char *tmp = env->GetStringUTFChars(name, NULL);
1468
1469 if (tmp == NULL) {
1470 return;
1471 }
1472
1473 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
1474
1475 const status_t err = codec->initCheck();
1476 if (err == NAME_NOT_FOUND) {
1477 // fail and do not try again.
1478 jniThrowException(env, "java/lang/IllegalArgumentException",
1479 String8::format("Failed to initialize %s, error %#x", tmp, err));
1480 env->ReleaseStringUTFChars(name, tmp);
1481 return;
1482 } else if (err != OK) {
1483 // believed possible to try again
1484 jniThrowException(env, "java/io/IOException",
1485 String8::format("Failed to find matching codec %s, error %#x", tmp, err));
1486 env->ReleaseStringUTFChars(name, tmp);
1487 return;
1488 }
1489
1490 env->ReleaseStringUTFChars(name, tmp);
1491
1492 codec->registerSelf();
1493
1494 setMediaCodec(env,thiz, codec);
1495 }
1496
android_media_MediaCodec_native_finalize(JNIEnv * env,jobject thiz)1497 static void android_media_MediaCodec_native_finalize(
1498 JNIEnv *env, jobject thiz) {
1499 android_media_MediaCodec_release(env, thiz);
1500 }
1501
1502 static JNINativeMethod gMethods[] = {
1503 { "native_release", "()V", (void *)android_media_MediaCodec_release },
1504
1505 { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
1506
1507 { "native_setCallback",
1508 "(Landroid/media/MediaCodec$Callback;)V",
1509 (void *)android_media_MediaCodec_native_setCallback },
1510
1511 { "native_configure",
1512 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
1513 "Landroid/media/MediaCrypto;I)V",
1514 (void *)android_media_MediaCodec_native_configure },
1515
1516 { "createInputSurface", "()Landroid/view/Surface;",
1517 (void *)android_media_MediaCodec_createInputSurface },
1518
1519 { "native_start", "()V", (void *)android_media_MediaCodec_start },
1520 { "native_stop", "()V", (void *)android_media_MediaCodec_stop },
1521 { "native_flush", "()V", (void *)android_media_MediaCodec_flush },
1522
1523 { "native_queueInputBuffer", "(IIIJI)V",
1524 (void *)android_media_MediaCodec_queueInputBuffer },
1525
1526 { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
1527 (void *)android_media_MediaCodec_queueSecureInputBuffer },
1528
1529 { "native_dequeueInputBuffer", "(J)I",
1530 (void *)android_media_MediaCodec_dequeueInputBuffer },
1531
1532 { "native_dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
1533 (void *)android_media_MediaCodec_dequeueOutputBuffer },
1534
1535 { "releaseOutputBuffer", "(IZZJ)V",
1536 (void *)android_media_MediaCodec_releaseOutputBuffer },
1537
1538 { "signalEndOfInputStream", "()V",
1539 (void *)android_media_MediaCodec_signalEndOfInputStream },
1540
1541 { "getFormatNative", "(Z)Ljava/util/Map;",
1542 (void *)android_media_MediaCodec_getFormatNative },
1543
1544 { "getOutputFormatNative", "(I)Ljava/util/Map;",
1545 (void *)android_media_MediaCodec_getOutputFormatForIndexNative },
1546
1547 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1548 (void *)android_media_MediaCodec_getBuffers },
1549
1550 { "getBuffer", "(ZI)Ljava/nio/ByteBuffer;",
1551 (void *)android_media_MediaCodec_getBuffer },
1552
1553 { "getImage", "(ZI)Landroid/media/Image;",
1554 (void *)android_media_MediaCodec_getImage },
1555
1556 { "getName", "()Ljava/lang/String;",
1557 (void *)android_media_MediaCodec_getName },
1558
1559 { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1560 (void *)android_media_MediaCodec_setParameters },
1561
1562 { "setVideoScalingMode", "(I)V",
1563 (void *)android_media_MediaCodec_setVideoScalingMode },
1564
1565 { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
1566
1567 { "native_setup", "(Ljava/lang/String;ZZ)V",
1568 (void *)android_media_MediaCodec_native_setup },
1569
1570 { "native_finalize", "()V",
1571 (void *)android_media_MediaCodec_native_finalize },
1572 };
1573
register_android_media_MediaCodec(JNIEnv * env)1574 int register_android_media_MediaCodec(JNIEnv *env) {
1575 return AndroidRuntime::registerNativeMethods(env,
1576 "android/media/MediaCodec", gMethods, NELEM(gMethods));
1577 }
1578