blob: 221ea57f8ed8c1c2914056ec54887a7f9df6630f [file] [log] [blame]
Andreas Huber88572f72012-02-21 11:47:18 -08001/*
2 * Copyright 2012, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "MediaCodec-JNI"
19#include <utils/Log.h>
20
21#include "android_media_MediaCodec.h"
22
Andreas Huber07ea4262012-04-11 12:21:20 -070023#include "android_media_MediaCrypto.h"
Andreas Huber88572f72012-02-21 11:47:18 -080024#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
Mathias Agopian8335f1c2012-02-25 18:48:35 -080030#include <gui/Surface.h>
Mathias Agopian8335f1c2012-02-25 18:48:35 -080031
Andreas Huber0e97fc22012-04-03 13:32:16 -070032#include <media/ICrypto.h>
Andreas Huber88572f72012-02-21 11:47:18 -080033#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>
Andreas Huberbfc56f42012-04-19 12:47:07 -070038#include <media/stagefright/foundation/AString.h>
Andreas Huber88572f72012-02-21 11:47:18 -080039#include <media/stagefright/MediaErrors.h>
Andreas Huber88572f72012-02-21 11:47:18 -080040
Andreas Huber8d5f3e32013-08-12 09:19:45 -070041#include <nativehelper/ScopedLocalRef.h>
42
Andreas Huberb12a5392012-04-30 14:18:33 -070043#include <system/window.h>
44
Andreas Huber88572f72012-02-21 11:47:18 -080045namespace android {
46
47// Keep these in sync with their equivalents in MediaCodec.java !!!
48enum {
49 DEQUEUE_INFO_TRY_AGAIN_LATER = -1,
50 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2,
51 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3,
52};
53
Jeff Tinker3ed38262013-08-02 23:24:51 -070054struct CryptoErrorCodes {
55 jint cryptoErrorNoKey;
56 jint cryptoErrorKeyExpired;
57 jint cryptoErrorResourceBusy;
58} gCryptoErrorCodes;
59
Andreas Huber88572f72012-02-21 11:47:18 -080060struct fields_t {
61 jfieldID context;
Andreas Huber91befdc2012-04-18 12:19:51 -070062 jfieldID cryptoInfoNumSubSamplesID;
63 jfieldID cryptoInfoNumBytesOfClearDataID;
64 jfieldID cryptoInfoNumBytesOfEncryptedDataID;
65 jfieldID cryptoInfoKeyID;
66 jfieldID cryptoInfoIVID;
67 jfieldID cryptoInfoModeID;
Andreas Huber88572f72012-02-21 11:47:18 -080068};
69
70static fields_t gFields;
71
72////////////////////////////////////////////////////////////////////////////////
73
74JMediaCodec::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
Andreas Huber8d5f3e32013-08-12 09:19:45 -070091 PRIORITY_FOREGROUND);
Andreas Huber88572f72012-02-21 11:47:18 -080092
93 if (nameIsType) {
94 mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
95 } else {
96 mCodec = MediaCodec::CreateByComponentName(mLooper, name);
97 }
98}
99
100status_t JMediaCodec::initCheck() const {
101 return mCodec != NULL ? OK : NO_INIT;
102}
103
104JMediaCodec::~JMediaCodec() {
Martin Storsjod932de92012-07-13 13:01:06 +0300105 if (mCodec != NULL) {
106 mCodec->release();
107 mCodec.clear();
108 }
Andreas Huber88572f72012-02-21 11:47:18 -0800109
110 JNIEnv *env = AndroidRuntime::getJNIEnv();
111
112 env->DeleteWeakGlobalRef(mObject);
113 mObject = NULL;
114 env->DeleteGlobalRef(mClass);
115 mClass = NULL;
116}
117
118status_t JMediaCodec::configure(
119 const sp<AMessage> &format,
Andy McFaddend47f7d82012-12-18 09:48:38 -0800120 const sp<IGraphicBufferProducer> &bufferProducer,
Andreas Huber8240d922012-04-04 14:06:32 -0700121 const sp<ICrypto> &crypto,
Andreas Huber88572f72012-02-21 11:47:18 -0800122 int flags) {
Mathias Agopian52800612013-02-14 17:11:20 -0800123 sp<Surface> client;
Andy McFaddend47f7d82012-12-18 09:48:38 -0800124 if (bufferProducer != NULL) {
Marco Nelissenfd2e5002013-09-25 13:45:41 -0700125 mSurfaceTextureClient = new Surface(bufferProducer, true /* controlledByApp */);
Andreas Huberb12a5392012-04-30 14:18:33 -0700126 } else {
127 mSurfaceTextureClient.clear();
Andreas Huber88572f72012-02-21 11:47:18 -0800128 }
Andreas Huberb12a5392012-04-30 14:18:33 -0700129
130 return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
Andreas Huber88572f72012-02-21 11:47:18 -0800131}
132
Andy McFadden2621e402013-02-19 07:29:21 -0800133status_t JMediaCodec::createInputSurface(
134 sp<IGraphicBufferProducer>* bufferProducer) {
135 return mCodec->createInputSurface(bufferProducer);
136}
137
Andreas Huber88572f72012-02-21 11:47:18 -0800138status_t JMediaCodec::start() {
139 return mCodec->start();
140}
141
142status_t JMediaCodec::stop() {
Andreas Huberb12a5392012-04-30 14:18:33 -0700143 mSurfaceTextureClient.clear();
144
Andreas Huber88572f72012-02-21 11:47:18 -0800145 return mCodec->stop();
146}
147
148status_t JMediaCodec::flush() {
149 return mCodec->flush();
150}
151
152status_t JMediaCodec::queueInputBuffer(
153 size_t index,
Andreas Huberbfc56f42012-04-19 12:47:07 -0700154 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);
Andreas Huber88572f72012-02-21 11:47:18 -0800158}
159
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700160status_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,
Andreas Huberbfc56f42012-04-19 12:47:07 -0700169 uint32_t flags,
170 AString *errorDetailMsg) {
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700171 return mCodec->queueSecureInputBuffer(
172 index, offset, subSamples, numSubSamples, key, iv, mode,
Andreas Huberbfc56f42012-04-19 12:47:07 -0700173 presentationTimeUs, flags, errorDetailMsg);
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700174}
175
Andreas Huber88572f72012-02-21 11:47:18 -0800176status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
177 return mCodec->dequeueInputBuffer(index, timeoutUs);
178}
179
180status_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(
Andreas Huberc52b9802012-03-12 14:04:01 -0700187 index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
Andreas Huber88572f72012-02-21 11:47:18 -0800188 return err;
189 }
190
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700191 ScopedLocalRef<jclass> clazz(
192 env, env->FindClass("android/media/MediaCodec$BufferInfo"));
Andreas Huber88572f72012-02-21 11:47:18 -0800193
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700194 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
Andreas Huber88572f72012-02-21 11:47:18 -0800195 env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
196
197 return OK;
198}
199
200status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
201 return render
202 ? mCodec->renderOutputBufferAndRelease(index)
203 : mCodec->releaseOutputBuffer(index);
204}
205
Andy McFadden2621e402013-02-19 07:29:21 -0800206status_t JMediaCodec::signalEndOfInputStream() {
207 return mCodec->signalEndOfInputStream();
208}
209
Andreas Huber88572f72012-02-21 11:47:18 -0800210status_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
220status_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
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700233 ScopedLocalRef<jclass> byteBufferClass(
234 env, env->FindClass("java/nio/ByteBuffer"));
235
236 CHECK(byteBufferClass.get() != NULL);
Andreas Huber3dd7fd02012-05-08 13:50:45 -0700237
238 jmethodID orderID = env->GetMethodID(
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700239 byteBufferClass.get(),
Andreas Huber3dd7fd02012-05-08 13:50:45 -0700240 "order",
241 "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
242
243 CHECK(orderID != NULL);
244
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700245 ScopedLocalRef<jclass> byteOrderClass(
246 env, env->FindClass("java/nio/ByteOrder"));
247
248 CHECK(byteOrderClass.get() != NULL);
Andreas Huber3dd7fd02012-05-08 13:50:45 -0700249
250 jmethodID nativeOrderID = env->GetStaticMethodID(
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700251 byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
Andreas Huber3dd7fd02012-05-08 13:50:45 -0700252 CHECK(nativeOrderID != NULL);
253
254 jobject nativeByteOrderObj =
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700255 env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
Andreas Huber3dd7fd02012-05-08 13:50:45 -0700256 CHECK(nativeByteOrderObj != NULL);
Andreas Huber88572f72012-02-21 11:47:18 -0800257
258 *bufArray = (jobjectArray)env->NewObjectArray(
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700259 buffers.size(), byteBufferClass.get(), NULL);
Marco Nelissencbbea8e2012-12-19 11:42:55 -0800260 if (*bufArray == NULL) {
261 env->DeleteLocalRef(nativeByteOrderObj);
262 return NO_MEMORY;
263 }
Andreas Huber88572f72012-02-21 11:47:18 -0800264
265 for (size_t i = 0; i < buffers.size(); ++i) {
266 const sp<ABuffer> &buffer = buffers.itemAt(i);
267
Marco Nelissencbbea8e2012-12-19 11:42:55 -0800268 // 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 }
Andreas Huber88572f72012-02-21 11:47:18 -0800273 jobject byteBuffer =
274 env->NewDirectByteBuffer(
275 buffer->base(),
276 buffer->capacity());
Marco Nelissencbbea8e2012-12-19 11:42:55 -0800277 if (byteBuffer == NULL) {
278 env->DeleteLocalRef(nativeByteOrderObj);
279 return NO_MEMORY;
280 }
Andreas Huber3dd7fd02012-05-08 13:50:45 -0700281 jobject me = env->CallObjectMethod(
282 byteBuffer, orderID, nativeByteOrderObj);
283 env->DeleteLocalRef(me);
284 me = NULL;
285
Andreas Huber88572f72012-02-21 11:47:18 -0800286 env->SetObjectArrayElement(
287 *bufArray, i, byteBuffer);
288
289 env->DeleteLocalRef(byteBuffer);
290 byteBuffer = NULL;
291 }
292
Andreas Huber3dd7fd02012-05-08 13:50:45 -0700293 env->DeleteLocalRef(nativeByteOrderObj);
294 nativeByteOrderObj = NULL;
295
Andreas Huber88572f72012-02-21 11:47:18 -0800296 return OK;
297}
298
Martin Storsjo056ef2e2012-09-25 11:53:04 +0300299status_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
Andreas Huber226065b2013-08-12 10:14:11 -0700313status_t JMediaCodec::setParameters(const sp<AMessage> &msg) {
314 return mCodec->setParameters(msg);
315}
316
Andreas Huberb12a5392012-04-30 14:18:33 -0700317void JMediaCodec::setVideoScalingMode(int mode) {
318 if (mSurfaceTextureClient != NULL) {
319 native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
320 }
321}
322
Andreas Huber88572f72012-02-21 11:47:18 -0800323} // namespace android
324
325////////////////////////////////////////////////////////////////////////////////
326
327using namespace android;
328
329static sp<JMediaCodec> setMediaCodec(
330 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000331 sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
Andreas Huber88572f72012-02-21 11:47:18 -0800332 if (codec != NULL) {
333 codec->incStrong(thiz);
334 }
335 if (old != NULL) {
336 old->decStrong(thiz);
337 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000338 env->SetLongField(thiz, gFields.context, (jlong)codec.get());
Andreas Huber88572f72012-02-21 11:47:18 -0800339
340 return old;
341}
342
343static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000344 return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
Andreas Huber88572f72012-02-21 11:47:18 -0800345}
346
347static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
348 setMediaCodec(env, thiz, NULL);
349}
350
Andreas Huberbfc56f42012-04-19 12:47:07 -0700351static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700352 ScopedLocalRef<jclass> clazz(
353 env, env->FindClass("android/media/MediaCodec$CryptoException"));
354 CHECK(clazz.get() != NULL);
Andreas Huberbfc56f42012-04-19 12:47:07 -0700355
356 jmethodID constructID =
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700357 env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
Andreas Huberbfc56f42012-04-19 12:47:07 -0700358 CHECK(constructID != NULL);
359
360 jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
361
Jeff Tinker3ed38262013-08-02 23:24:51 -0700362 /* 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
Andreas Huberbfc56f42012-04-19 12:47:07 -0700377 jthrowable exception =
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700378 (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
Andreas Huberbfc56f42012-04-19 12:47:07 -0700379
380 env->Throw(exception);
381}
382
383static jint throwExceptionAsNecessary(
384 JNIEnv *env, status_t err, const char *msg = NULL) {
Jeff Tinker3ed38262013-08-02 23:24:51 -0700385 if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
Andreas Huberbfc56f42012-04-19 12:47:07 -0700386 // We'll throw our custom MediaCodec.CryptoException
Andreas Huberbfc56f42012-04-19 12:47:07 -0700387 throwCryptoException(env, err, msg);
388 return 0;
389 }
390
Andreas Huber88572f72012-02-21 11:47:18 -0800391 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
Jeff Tinker3ed38262013-08-02 23:24:51 -0700404 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
Andreas Huber88572f72012-02-21 11:47:18 -0800410 default:
411 {
Marco Nelissene20a6d52013-04-08 14:28:55 -0700412 jniThrowException(env, "java/lang/IllegalStateException", msg);
Andreas Huber88572f72012-02-21 11:47:18 -0800413 break;
414 }
415 }
416
417 return 0;
418}
419
420static void android_media_MediaCodec_native_configure(
421 JNIEnv *env,
422 jobject thiz,
423 jobjectArray keys, jobjectArray values,
424 jobject jsurface,
Andreas Huber8240d922012-04-04 14:06:32 -0700425 jobject jcrypto,
Andreas Huber88572f72012-02-21 11:47:18 -0800426 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
Andy McFaddend47f7d82012-12-18 09:48:38 -0800442 sp<IGraphicBufferProducer> bufferProducer;
Andreas Huber88572f72012-02-21 11:47:18 -0800443 if (jsurface != NULL) {
Jeff Brown64a55af2012-08-26 02:47:39 -0700444 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
Andreas Huber88572f72012-02-21 11:47:18 -0800445 if (surface != NULL) {
Mathias Agopian52800612013-02-14 17:11:20 -0800446 bufferProducer = surface->getIGraphicBufferProducer();
Andreas Huber88572f72012-02-21 11:47:18 -0800447 } else {
448 jniThrowException(
449 env,
450 "java/lang/IllegalArgumentException",
451 "The surface has been released");
452 return;
453 }
454 }
455
Andreas Huber8240d922012-04-04 14:06:32 -0700456 sp<ICrypto> crypto;
457 if (jcrypto != NULL) {
458 crypto = JCrypto::GetCrypto(env, jcrypto);
459 }
460
Andy McFaddend47f7d82012-12-18 09:48:38 -0800461 err = codec->configure(format, bufferProducer, crypto, flags);
Andreas Huber88572f72012-02-21 11:47:18 -0800462
463 throwExceptionAsNecessary(env, err);
464}
465
Andy McFadden2621e402013-02-19 07:29:21 -0800466static 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
Andreas Huber88572f72012-02-21 11:47:18 -0800489static 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) {
Marco Nelissene20a6d52013-04-08 14:28:55 -0700495 jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
Andreas Huber88572f72012-02-21 11:47:18 -0800496 return;
497 }
498
499 status_t err = codec->start();
500
Marco Nelissene20a6d52013-04-08 14:28:55 -0700501 throwExceptionAsNecessary(env, err, "start failed");
Andreas Huber88572f72012-02-21 11:47:18 -0800502}
503
504static 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
519static 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
534static 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
Andreas Huberbfc56f42012-04-19 12:47:07 -0700551 AString errorDetailMsg;
Andreas Huber88572f72012-02-21 11:47:18 -0800552
Andreas Huberbfc56f42012-04-19 12:47:07 -0700553 status_t err = codec->queueInputBuffer(
554 index, offset, size, timestampUs, flags, &errorDetailMsg);
555
556 throwExceptionAsNecessary(
557 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
Andreas Huber88572f72012-02-21 11:47:18 -0800558}
559
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700560static void android_media_MediaCodec_queueSecureInputBuffer(
561 JNIEnv *env,
562 jobject thiz,
563 jint index,
564 jint offset,
Andreas Huber91befdc2012-04-18 12:19:51 -0700565 jobject cryptoInfoObj,
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700566 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
Andreas Huber91befdc2012-04-18 12:19:51 -0700577 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
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700596 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
Andreas Huberbfc56f42012-04-19 12:47:07 -0700668 AString errorDetailMsg;
669
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700670 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,
Andreas Huberbfc56f42012-04-19 12:47:07 -0700676 timestampUs,
677 flags,
678 &errorDetailMsg);
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700679 }
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
Andreas Huberbfc56f42012-04-19 12:47:07 -0700694 throwExceptionAsNecessary(
695 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700696}
697
Andreas Huber88572f72012-02-21 11:47:18 -0800698static 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) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000713 return (jint) index;
Andreas Huber88572f72012-02-21 11:47:18 -0800714 }
715
716 return throwExceptionAsNecessary(env, err);
717}
718
719static 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);
Andreas Huber0e97fc22012-04-03 13:32:16 -0700727 return 0;
Andreas Huber88572f72012-02-21 11:47:18 -0800728 }
729
730 size_t index;
731 status_t err = codec->dequeueOutputBuffer(
732 env, bufferInfo, &index, timeoutUs);
733
734 if (err == OK) {
Ashok Bhat075e9a12014-01-06 13:45:09 +0000735 return (jint) index;
Andreas Huber88572f72012-02-21 11:47:18 -0800736 }
737
738 return throwExceptionAsNecessary(env, err);
739}
740
741static 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
Andy McFadden2621e402013-02-19 07:29:21 -0800757static 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
Andreas Huber60d610b2012-05-02 16:06:09 -0700772static jobject android_media_MediaCodec_getOutputFormatNative(
Andreas Huber88572f72012-02-21 11:47:18 -0800773 JNIEnv *env, jobject thiz) {
Andreas Huber60d610b2012-05-02 16:06:09 -0700774 ALOGV("android_media_MediaCodec_getOutputFormatNative");
Andreas Huber88572f72012-02-21 11:47:18 -0800775
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
795static 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
Marco Nelissencbbea8e2012-12-19 11:42:55 -0800813 // if we're out of memory, an exception was already thrown
814 if (err != NO_MEMORY) {
815 throwExceptionAsNecessary(env, err);
816 }
Andreas Huber88572f72012-02-21 11:47:18 -0800817
818 return NULL;
819}
820
Martin Storsjo056ef2e2012-09-25 11:53:04 +0300821static 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
Andreas Huber226065b2013-08-12 10:14:11 -0700844static 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
Andreas Huberb12a5392012-04-30 14:18:33 -0700865static 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
Andreas Huber88572f72012-02-21 11:47:18 -0800883static void android_media_MediaCodec_native_init(JNIEnv *env) {
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700884 ScopedLocalRef<jclass> clazz(
885 env, env->FindClass("android/media/MediaCodec"));
886 CHECK(clazz.get() != NULL);
Andreas Huber88572f72012-02-21 11:47:18 -0800887
Ashok Bhat075e9a12014-01-06 13:45:09 +0000888 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
Andreas Huber88572f72012-02-21 11:47:18 -0800889 CHECK(gFields.context != NULL);
Andreas Huber91befdc2012-04-18 12:19:51 -0700890
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700891 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
892 CHECK(clazz.get() != NULL);
Andreas Huber91befdc2012-04-18 12:19:51 -0700893
894 gFields.cryptoInfoNumSubSamplesID =
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700895 env->GetFieldID(clazz.get(), "numSubSamples", "I");
Andreas Huber91befdc2012-04-18 12:19:51 -0700896 CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
897
898 gFields.cryptoInfoNumBytesOfClearDataID =
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700899 env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
Andreas Huber91befdc2012-04-18 12:19:51 -0700900 CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
901
902 gFields.cryptoInfoNumBytesOfEncryptedDataID =
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700903 env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
Andreas Huber91befdc2012-04-18 12:19:51 -0700904 CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
905
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700906 gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
Andreas Huber91befdc2012-04-18 12:19:51 -0700907 CHECK(gFields.cryptoInfoKeyID != NULL);
908
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700909 gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
Andreas Huber91befdc2012-04-18 12:19:51 -0700910 CHECK(gFields.cryptoInfoIVID != NULL);
911
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700912 gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
Andreas Huber91befdc2012-04-18 12:19:51 -0700913 CHECK(gFields.cryptoInfoModeID != NULL);
Jeff Tinker3ed38262013-08-02 23:24:51 -0700914
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700915 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
916 CHECK(clazz.get() != NULL);
Jeff Tinker3ed38262013-08-02 23:24:51 -0700917
918 jfieldID field;
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700919 field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
Jeff Tinker3ed38262013-08-02 23:24:51 -0700920 CHECK(field != NULL);
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700921 gCryptoErrorCodes.cryptoErrorNoKey =
922 env->GetStaticIntField(clazz.get(), field);
Jeff Tinker3ed38262013-08-02 23:24:51 -0700923
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700924 field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
Jeff Tinker3ed38262013-08-02 23:24:51 -0700925 CHECK(field != NULL);
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700926 gCryptoErrorCodes.cryptoErrorKeyExpired =
927 env->GetStaticIntField(clazz.get(), field);
Jeff Tinker3ed38262013-08-02 23:24:51 -0700928
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700929 field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
Jeff Tinker3ed38262013-08-02 23:24:51 -0700930 CHECK(field != NULL);
Andreas Huber8d5f3e32013-08-12 09:19:45 -0700931 gCryptoErrorCodes.cryptoErrorResourceBusy =
932 env->GetStaticIntField(clazz.get(), field);
Andreas Huber88572f72012-02-21 11:47:18 -0800933}
934
935static 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
967static void android_media_MediaCodec_native_finalize(
968 JNIEnv *env, jobject thiz) {
969 android_media_MediaCodec_release(env, thiz);
970}
971
972static JNINativeMethod gMethods[] = {
973 { "release", "()V", (void *)android_media_MediaCodec_release },
974
975 { "native_configure",
Andreas Huber8240d922012-04-04 14:06:32 -0700976 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
Andreas Huber07ea4262012-04-11 12:21:20 -0700977 "Landroid/media/MediaCrypto;I)V",
Andreas Huber88572f72012-02-21 11:47:18 -0800978 (void *)android_media_MediaCodec_native_configure },
979
Andy McFadden2621e402013-02-19 07:29:21 -0800980 { "createInputSurface", "()Landroid/view/Surface;",
981 (void *)android_media_MediaCodec_createInputSurface },
982
Andreas Huber88572f72012-02-21 11:47:18 -0800983 { "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
Andreas Huber91befdc2012-04-18 12:19:51 -0700990 { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700991 (void *)android_media_MediaCodec_queueSecureInputBuffer },
992
Andreas Huber88572f72012-02-21 11:47:18 -0800993 { "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
Andy McFadden2621e402013-02-19 07:29:21 -08001002 { "signalEndOfInputStream", "()V",
1003 (void *)android_media_MediaCodec_signalEndOfInputStream },
1004
Andreas Huber60d610b2012-05-02 16:06:09 -07001005 { "getOutputFormatNative", "()Ljava/util/Map;",
1006 (void *)android_media_MediaCodec_getOutputFormatNative },
Andreas Huber88572f72012-02-21 11:47:18 -08001007
1008 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1009 (void *)android_media_MediaCodec_getBuffers },
1010
Martin Storsjo056ef2e2012-09-25 11:53:04 +03001011 { "getName", "()Ljava/lang/String;",
1012 (void *)android_media_MediaCodec_getName },
1013
Andreas Huber226065b2013-08-12 10:14:11 -07001014 { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1015 (void *)android_media_MediaCodec_setParameters },
1016
Andreas Huberb12a5392012-04-30 14:18:33 -07001017 { "setVideoScalingMode", "(I)V",
1018 (void *)android_media_MediaCodec_setVideoScalingMode },
1019
Andreas Huber88572f72012-02-21 11:47:18 -08001020 { "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
1029int register_android_media_MediaCodec(JNIEnv *env) {
1030 return AndroidRuntime::registerNativeMethods(env,
1031 "android/media/MediaCodec", gMethods, NELEM(gMethods));
1032}