diff options
author | Andreas Huber <andih@google.com> | 2012-04-18 12:19:51 -0700 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2012-04-18 12:19:51 -0700 |
commit | 91befdc0c4710234840cdfd853e7d30e8f9de62c (patch) | |
tree | e37fa5820ab6348549d7a59a342ad3ee70e7e55c | |
parent | 26fbf27ad1e9f9c26a82097fe306f07ec36e8410 (diff) |
Information required to decrypt buffers is now packaged into MediaCodec.CryptoInfo
New API on MediaExtractor to retrieve a CryptoInfo structure where applicable.
Change-Id: I18edfc9ac56a4544c8f17cba24401b96dacbff7d
related-to-bug: 6275919
-rw-r--r-- | api/current.txt | 14 | ||||
-rw-r--r-- | media/java/android/media/MediaCodec.java | 65 | ||||
-rw-r--r-- | media/java/android/media/MediaExtractor.java | 10 | ||||
-rw-r--r-- | media/jni/android_media_MediaCodec.cpp | 59 | ||||
-rw-r--r-- | media/jni/android_media_MediaExtractor.cpp | 143 | ||||
-rw-r--r-- | media/jni/android_media_MediaExtractor.h | 2 |
6 files changed, 262 insertions, 31 deletions
diff --git a/api/current.txt b/api/current.txt index 8d12a566a6b9..fef1c839bd83 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10960,7 +10960,7 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 a65c2aa846e9..2efacd877a59 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -309,36 +309,57 @@ final public class MediaCodec { 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 3f8b2ca327a6..3b17a7d7703a 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -19,6 +19,7 @@ package android.media; 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 @@ final public class MediaExtractor { /** 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 979ffb024a8a..a120a2f5cd42 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -49,6 +49,13 @@ enum { 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 @@ static void android_media_MediaCodec_queueSecureInputBuffer( 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 @@ static void android_media_MediaCodec_queueSecureInputBuffer( 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 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { 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 @@ static JNINativeMethod gMethods[] = { { "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 e2cdbca38add..adf0e666488d 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::getSampleTime(int64_t *sampleTimeUs) { } 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 @@ static jint android_media_MediaExtractor_getSampleFlags( 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 @@ static void android_media_MediaExtractor_native_init(JNIEnv *env) { 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 @@ static JNINativeMethod gMethods[] = { { "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 1aacea25fdc5..f7ce2ff5f9fd 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 @@ struct JMediaExtractor : public RefBase { 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(); |