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
This commit is contained in:
Andreas Huber
2012-04-18 12:19:51 -07:00
parent 26fbf27ad1
commit 91befdc0c4
6 changed files with 262 additions and 31 deletions

View File

@@ -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();

View File

@@ -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);

View File

@@ -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();

View File

@@ -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",

View File

@@ -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",

View File

@@ -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();