Merge "Information required to decrypt buffers is now packaged into MediaCodec.CryptoInfo"
diff --git a/api/current.txt b/api/current.txt
index 8d12a56..fef1c83 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10960,7 +10960,7 @@
method public java.nio.ByteBuffer[] getOutputBuffers();
method public final java.util.Map<java.lang.String, java.lang.Object> getOutputFormat();
method public final void queueInputBuffer(int, int, int, long, int);
- method public final void queueSecureInputBuffer(int, int, int[], int[], int, byte[], byte[], int, long, int);
+ method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int);
method public final void release();
method public final void releaseOutputBuffer(int, boolean);
method public final void start();
@@ -10985,6 +10985,17 @@
field public int size;
}
+ public static final class MediaCodec.CryptoInfo {
+ ctor public MediaCodec.CryptoInfo();
+ method public void set(int, int[], int[], byte[], byte[], int);
+ field public byte[] iv;
+ field public byte[] key;
+ field public int mode;
+ field public int[] numBytesOfClearData;
+ field public int[] numBytesOfEncryptedData;
+ field public int numSubSamples;
+ }
+
public final class MediaCodecList {
method public static final int countCodecs();
method public static final android.media.MediaCodecList.CodecCapabilities getCodecCapabilities(int, java.lang.String);
@@ -11016,6 +11027,7 @@
ctor public MediaExtractor();
method public boolean advance();
method public int countTracks();
+ method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
method public int getSampleFlags();
method public long getSampleTime();
method public int getSampleTrackIndex();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index a65c2aa..2efacd8 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -309,36 +309,57 @@
int index,
int offset, int size, long presentationTimeUs, int flags);
+ /** Metadata describing the structure of a (at least partially) encrypted
+ * input sample.
+ * A 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.
+ * numBytesOfClearData can be null to indicate that all data is encrypted.
+ */
+ public final static class CryptoInfo {
+ public void set(
+ int newNumSubSamples,
+ int[] newNumBytesOfClearData,
+ int[] newNumBytesOfEncryptedData,
+ byte[] newKey,
+ byte[] newIV,
+ int newMode) {
+ numSubSamples = newNumSubSamples;
+ numBytesOfClearData = newNumBytesOfClearData;
+ numBytesOfEncryptedData = newNumBytesOfEncryptedData;
+ key = newKey;
+ iv = newIV;
+ mode = newMode;
+ }
+
+ /** The number of subSamples that make up the buffer's contents. */
+ public int numSubSamples;
+ /** The number of leading unencrypted bytes in each subSample. */
+ public int[] numBytesOfClearData;
+ /** The number of trailing encrypted bytes in each subSample. */
+ public int[] numBytesOfEncryptedData;
+ /** A 16-byte opaque key */
+ public byte[] key;
+ /** A 16-byte initialization vector */
+ public byte[] iv;
+ /** The type of encryption that has been applied */
+ public int mode;
+ };
+
/** 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.
+ * potentially encrypted.
* @param index The index of a client-owned input buffer previously returned
* in a call to {@link #dequeueInputBuffer}.
* @param offset The byte offset into the input buffer at which the data starts.
- * @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.
+ * @param presentationTimeUs The time at which this buffer should be rendered.
+ * @param flags A bitmask of flags {@link #FLAG_SYNCFRAME},
+ * {@link #FLAG_CODECCONFIG} or {@link #FLAG_EOS}.
*/
public native final void queueSecureInputBuffer(
int index,
int offset,
- int[] numBytesOfClearData,
- int[] numBytesOfEncryptedData,
- int numSubSamples,
- byte[] key,
- byte[] iv,
- int mode,
+ CryptoInfo info,
long presentationTimeUs,
int flags);
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 3f8b2ca..3b17a7d 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -19,6 +19,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
import android.net.Uri;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -235,6 +236,15 @@
/** Returns the current sample's flags. */
public native int getSampleFlags();
+ /** If the sample flags indicate that the current sample is at least
+ * partially encrypted, this call returns relevant information about
+ * the structure of the sample data required for decryption.
+ * @param info The android.media.MediaCodec.CryptoInfo structure
+ * to be filled in.
+ * @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED}
+ */
+ public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info);
+
private static native final void native_init();
private native final void native_setup();
private native final void native_finalize();
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 979ffb0..a120a2f 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -49,6 +49,13 @@
struct fields_t {
jfieldID context;
+
+ jfieldID cryptoInfoNumSubSamplesID;
+ jfieldID cryptoInfoNumBytesOfClearDataID;
+ jfieldID cryptoInfoNumBytesOfEncryptedDataID;
+ jfieldID cryptoInfoKeyID;
+ jfieldID cryptoInfoIVID;
+ jfieldID cryptoInfoModeID;
};
static fields_t gFields;
@@ -387,12 +394,7 @@
jobject thiz,
jint index,
jint offset,
- jintArray numBytesOfClearDataObj,
- jintArray numBytesOfEncryptedDataObj,
- jint numSubSamples,
- jbyteArray keyObj,
- jbyteArray ivObj,
- jint mode,
+ jobject cryptoInfoObj,
jlong timestampUs,
jint flags) {
ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
@@ -404,6 +406,25 @@
return;
}
+ jint numSubSamples =
+ env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
+
+ jintArray numBytesOfClearDataObj =
+ (jintArray)env->GetObjectField(
+ cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
+
+ jintArray numBytesOfEncryptedDataObj =
+ (jintArray)env->GetObjectField(
+ cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
+
+ jbyteArray keyObj =
+ (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
+
+ jbyteArray ivObj =
+ (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
+
+ jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+
status_t err = OK;
CryptoPlugin::SubSample *subSamples = NULL;
@@ -612,6 +633,30 @@
gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
CHECK(gFields.context != NULL);
+
+ clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
+ CHECK(clazz != NULL);
+
+ gFields.cryptoInfoNumSubSamplesID =
+ env->GetFieldID(clazz, "numSubSamples", "I");
+ CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
+
+ gFields.cryptoInfoNumBytesOfClearDataID =
+ env->GetFieldID(clazz, "numBytesOfClearData", "[I");
+ CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
+
+ gFields.cryptoInfoNumBytesOfEncryptedDataID =
+ env->GetFieldID(clazz, "numBytesOfEncryptedData", "[I");
+ CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
+
+ gFields.cryptoInfoKeyID = env->GetFieldID(clazz, "key", "[B");
+ CHECK(gFields.cryptoInfoKeyID != NULL);
+
+ gFields.cryptoInfoIVID = env->GetFieldID(clazz, "iv", "[B");
+ CHECK(gFields.cryptoInfoIVID != NULL);
+
+ gFields.cryptoInfoModeID = env->GetFieldID(clazz, "mode", "I");
+ CHECK(gFields.cryptoInfoModeID != NULL);
}
static void android_media_MediaCodec_native_setup(
@@ -666,7 +711,7 @@
{ "queueInputBuffer", "(IIIJI)V",
(void *)android_media_MediaCodec_queueInputBuffer },
- { "queueSecureInputBuffer", "(II[I[II[B[BIJI)V",
+ { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
(void *)android_media_MediaCodec_queueSecureInputBuffer },
{ "dequeueInputBuffer", "(J)I",
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index e2cdbca..adf0e66 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -30,12 +30,15 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
#include <media/stagefright/NuMediaExtractor.h>
namespace android {
struct fields_t {
jfieldID context;
+
+ jmethodID cryptoInfoSetID;
};
static fields_t gFields;
@@ -166,7 +169,32 @@
}
status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
- return mImpl->getSampleFlags(sampleFlags);
+ *sampleFlags = 0;
+
+ sp<MetaData> meta;
+ status_t err = mImpl->getSampleMeta(&meta);
+
+ if (err != OK) {
+ return err;
+ }
+
+ int32_t val;
+ if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+ (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
+ }
+
+ return OK;
+}
+
+status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
+ return mImpl->getSampleMeta(sampleMeta);
}
} // namespace android
@@ -369,6 +397,110 @@
return sampleFlags;
}
+static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
+ JNIEnv *env, jobject thiz, jobject cryptoInfoObj) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return -1ll;
+ }
+
+ sp<MetaData> meta;
+ status_t err = extractor->getSampleMeta(&meta);
+
+ if (err != OK) {
+ return false;
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+ return false;
+ }
+
+ size_t numSubSamples = size / sizeof(size_t);
+
+ if (numSubSamples == 0) {
+ return false;
+ }
+
+ jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples);
+ jboolean isCopy;
+ jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ dst[i] = ((const size_t *)data)[i];
+ }
+ env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
+ dst = NULL;
+
+ size_t encSize = size;
+ jintArray numBytesOfPlainDataObj = NULL;
+ if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+ if (size != encSize) {
+ // The two must be of the same length.
+ return false;
+ }
+
+ numBytesOfPlainDataObj = env->NewIntArray(numSubSamples);
+ jboolean isCopy;
+ jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ dst[i] = ((const size_t *)data)[i];
+ }
+ env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
+ dst = NULL;
+ }
+
+ jbyteArray keyObj = NULL;
+ if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
+ if (size != 16) {
+ // Keys must be 16 bytes in length.
+ return false;
+ }
+
+ keyObj = env->NewByteArray(size);
+ jboolean isCopy;
+ jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy);
+ memcpy(dst, data, size);
+ env->ReleaseByteArrayElements(keyObj, dst, 0);
+ dst = NULL;
+ }
+
+ jbyteArray ivObj = NULL;
+ if (meta->findData(kKeyCryptoIV, &type, &data, &size)) {
+ if (size != 16) {
+ // IVs must be 16 bytes in length.
+ return false;
+ }
+
+ ivObj = env->NewByteArray(size);
+ jboolean isCopy;
+ jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy);
+ memcpy(dst, data, size);
+ env->ReleaseByteArrayElements(ivObj, dst, 0);
+ dst = NULL;
+ }
+
+ int32_t mode;
+ if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+ mode = 0;
+ }
+
+ env->CallVoidMethod(
+ cryptoInfoObj,
+ gFields.cryptoInfoSetID,
+ numSubSamples,
+ numBytesOfPlainDataObj,
+ numBytesOfEncryptedDataObj,
+ keyObj,
+ ivObj,
+ mode);
+
+ return true;
+}
+
static void android_media_MediaExtractor_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/MediaExtractor");
CHECK(clazz != NULL);
@@ -376,6 +508,12 @@
gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
CHECK(gFields.context != NULL);
+ clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
+ CHECK(clazz != NULL);
+
+ gFields.cryptoInfoSetID =
+ env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V");
+
DataSource::RegisterDefaultSniffers();
}
@@ -485,6 +623,9 @@
{ "getSampleFlags", "()I",
(void *)android_media_MediaExtractor_getSampleFlags },
+ { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z",
+ (void *)android_media_MediaExtractor_getSampleCryptoInfo },
+
{ "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
{ "native_setup", "()V",
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 1aacea2..f7ce2ff 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -27,6 +27,7 @@
namespace android {
+struct MetaData;
struct NuMediaExtractor;
struct JMediaExtractor : public RefBase {
@@ -50,6 +51,7 @@
status_t getSampleTrackIndex(size_t *trackIndex);
status_t getSampleTime(int64_t *sampleTimeUs);
status_t getSampleFlags(uint32_t *sampleFlags);
+ status_t getSampleMeta(sp<MetaData> *sampleMeta);
protected:
virtual ~JMediaExtractor();