Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 142095f7 authored by Andreas Huber's avatar Andreas Huber Committed by Android (Google) Code Review
Browse files

Merge "Information required to decrypt buffers is now packaged into MediaCodec.CryptoInfo"

parents 4b12171c 91befdc0
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -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();
+43 −22
Original line number Diff line number Diff line
@@ -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);

+10 −0
Original line number Diff line number Diff line
@@ -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();
+52 −7
Original line number Diff line number Diff line
@@ -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",
+142 −1
Original line number Diff line number Diff line
@@ -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",
Loading