New JAVA API to support submitting encrypted buffers of input data.
Change-Id: Ib0df9a9427b4580946179860495b26f743558597
related-to-bug: 6275919
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 66cea9d4..410383d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -45,10 +45,16 @@
public int mFlags;
};
+ // The follow flag constants MUST stay in sync with their equivalents
+ // in MediaCodec.h !
public static int FLAG_SYNCFRAME = 1;
public static int FLAG_CODECCONFIG = 2;
public static int FLAG_EOS = 4;
- public static int FLAG_ENCRYPTED = 8;
+
+ // The following mode constants MUST stay in sync with their equivalents
+ // in media/hardware/CryptoAPI.h !
+ public static int MODE_UNENCRYPTED = 0;
+ public static int MODE_AES_CTR = 1;
/** Instantiate a codec component by mime type. For decoder components
this is the mime type of media that this decoder should be able to
@@ -176,6 +182,36 @@
int index,
int offset, int size, long presentationTimeUs, int flags);
+ /** Similar to {@link queueInputBuffer} but submits a buffer that is
+ * potentially encrypted. The buffer's data is considered to be
+ * partitioned into "subSamples", each subSample starts with a
+ * (potentially empty) run of plain, unencrypted bytes followed
+ * by a (also potentially empty) run of encrypted bytes.
+ * @param numBytesOfClearData The number of leading unencrypted bytes in
+ * each subSample.
+ * @param numBytesOfEncryptedData The number of trailing encrypted bytes
+ * in each subSample.
+ * @param numSubSamples The number of subSamples that make up the
+ * buffer's contents.
+ * @param key A 16-byte opaque key
+ * @param iv A 16-byte initialization vector
+ * @param mode The type of encryption that has been applied
+ *
+ * Either numBytesOfClearData or numBytesOfEncryptedData (but not both)
+ * can be null to indicate that all respective sizes are 0.
+ */
+ public native final void queueSecureInputBuffer(
+ int index,
+ int offset,
+ int[] numBytesOfClearData,
+ int[] numBytesOfEncryptedData,
+ int numSubSamples,
+ byte[] key,
+ byte[] iv,
+ int mode,
+ long presentationTimeUs,
+ int flags);
+
// Returns the index of an input buffer to be filled with valid data
// or -1 if no such buffer is currently available.
// This method will return immediately if timeoutUs == 0, wait indefinitely
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 217216a..01d3833 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -126,6 +126,21 @@
return mCodec->queueInputBuffer(index, offset, size, timeUs, flags);
}
+status_t JMediaCodec::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) {
+ return mCodec->queueSecureInputBuffer(
+ index, offset, subSamples, numSubSamples, key, iv, mode,
+ presentationTimeUs, flags);
+}
+
status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
return mCodec->dequeueInputBuffer(index, timeoutUs);
}
@@ -367,6 +382,125 @@
throwExceptionAsNecessary(env, err);
}
+static void android_media_MediaCodec_queueSecureInputBuffer(
+ JNIEnv *env,
+ jobject thiz,
+ jint index,
+ jint offset,
+ jintArray numBytesOfClearDataObj,
+ jintArray numBytesOfEncryptedDataObj,
+ jint numSubSamples,
+ jbyteArray keyObj,
+ jbyteArray ivObj,
+ jint mode,
+ jlong timestampUs,
+ jint flags) {
+ ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = OK;
+
+ CryptoPlugin::SubSample *subSamples = NULL;
+ jbyte *key = NULL;
+ jbyte *iv = NULL;
+
+ if (numSubSamples <= 0) {
+ err = -EINVAL;
+ } else if (numBytesOfClearDataObj == NULL
+ && numBytesOfEncryptedDataObj == NULL) {
+ err = -EINVAL;
+ } else if (numBytesOfEncryptedDataObj != NULL
+ && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
+ err = -ERANGE;
+ } else if (numBytesOfClearDataObj != NULL
+ && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
+ err = -ERANGE;
+ } else {
+ jboolean isCopy;
+
+ jint *numBytesOfClearData =
+ (numBytesOfClearDataObj == NULL)
+ ? NULL
+ : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
+
+ jint *numBytesOfEncryptedData =
+ (numBytesOfEncryptedDataObj == NULL)
+ ? NULL
+ : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
+
+ subSamples = new CryptoPlugin::SubSample[numSubSamples];
+
+ for (jint i = 0; i < numSubSamples; ++i) {
+ subSamples[i].mNumBytesOfClearData =
+ (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
+
+ subSamples[i].mNumBytesOfEncryptedData =
+ (numBytesOfEncryptedData == NULL)
+ ? 0 : numBytesOfEncryptedData[i];
+ }
+
+ if (numBytesOfEncryptedData != NULL) {
+ env->ReleaseIntArrayElements(
+ numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
+ numBytesOfEncryptedData = NULL;
+ }
+
+ if (numBytesOfClearData != NULL) {
+ env->ReleaseIntArrayElements(
+ numBytesOfClearDataObj, numBytesOfClearData, 0);
+ numBytesOfClearData = NULL;
+ }
+ }
+
+ if (err == OK && keyObj != NULL) {
+ if (env->GetArrayLength(keyObj) != 16) {
+ err = -EINVAL;
+ } else {
+ jboolean isCopy;
+ key = env->GetByteArrayElements(keyObj, &isCopy);
+ }
+ }
+
+ if (err == OK && ivObj != NULL) {
+ if (env->GetArrayLength(ivObj) != 16) {
+ err = -EINVAL;
+ } else {
+ jboolean isCopy;
+ iv = env->GetByteArrayElements(ivObj, &isCopy);
+ }
+ }
+
+ if (err == OK) {
+ err = codec->queueSecureInputBuffer(
+ index, offset,
+ subSamples, numSubSamples,
+ (const uint8_t *)key, (const uint8_t *)iv,
+ (CryptoPlugin::Mode)mode,
+ timestampUs, flags);
+ }
+
+ if (iv != NULL) {
+ env->ReleaseByteArrayElements(ivObj, iv, 0);
+ iv = NULL;
+ }
+
+ if (key != NULL) {
+ env->ReleaseByteArrayElements(keyObj, key, 0);
+ key = NULL;
+ }
+
+ delete[] subSamples;
+ subSamples = NULL;
+
+ throwExceptionAsNecessary(env, err);
+}
+
static jint android_media_MediaCodec_dequeueInputBuffer(
JNIEnv *env, jobject thiz, jlong timeoutUs) {
ALOGV("android_media_MediaCodec_dequeueInputBuffer");
@@ -532,6 +666,9 @@
{ "queueInputBuffer", "(IIIJI)V",
(void *)android_media_MediaCodec_queueInputBuffer },
+ { "queueSecureInputBuffer", "(II[I[II[B[BIJI)V",
+ (void *)android_media_MediaCodec_queueSecureInputBuffer },
+
{ "dequeueInputBuffer", "(J)I",
(void *)android_media_MediaCodec_dequeueInputBuffer },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6bb4071..570c33b 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -19,6 +19,7 @@
#include "jni.h"
+#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -53,6 +54,17 @@
size_t index,
size_t offset, size_t size, int64_t timeUs, uint32_t flags);
+ status_t 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);
+
status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs);
status_t dequeueOutputBuffer(