• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params);
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