Provisional support for secure decryption of media streams.
Change-Id: Ib3982a9c960bfdb0cb7e1b174440b141b194cfbe
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <media/stagefright/foundation/AMessage.h>
|
||||
#include <media/stagefright/DataSource.h>
|
||||
#include <media/stagefright/MediaCodec.h>
|
||||
#include <media/stagefright/MediaCodecList.h>
|
||||
#include <media/stagefright/MediaDefs.h>
|
||||
#include <media/stagefright/NuMediaExtractor.h>
|
||||
#include <gui/SurfaceComposerClient.h>
|
||||
@@ -36,7 +37,9 @@ static void usage(const char *me) {
|
||||
fprintf(stderr, "usage: %s [-a] use audio\n"
|
||||
"\t\t[-v] use video\n"
|
||||
"\t\t[-p] playback\n"
|
||||
"\t\t[-S] allocate buffers from a surface\n", me);
|
||||
"\t\t[-S] allocate buffers from a surface\n"
|
||||
"\t\t[-D] decrypt input buffers\n",
|
||||
me);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
@@ -63,7 +66,8 @@ static int decode(
|
||||
const char *path,
|
||||
bool useAudio,
|
||||
bool useVideo,
|
||||
const android::sp<android::Surface> &surface) {
|
||||
const android::sp<android::Surface> &surface,
|
||||
bool decryptInputBuffers) {
|
||||
using namespace android;
|
||||
|
||||
static int64_t kTimeout = 500ll;
|
||||
@@ -109,13 +113,31 @@ static int decode(
|
||||
state->mNumBuffersDecoded = 0;
|
||||
state->mIsAudio = isAudio;
|
||||
|
||||
state->mCodec = MediaCodec::CreateByType(
|
||||
looper, mime.c_str(), false /* encoder */);
|
||||
if (decryptInputBuffers && !isAudio) {
|
||||
static const MediaCodecList *list = MediaCodecList::getInstance();
|
||||
|
||||
ssize_t index =
|
||||
list->findCodecByType(mime.c_str(), false /* encoder */);
|
||||
|
||||
CHECK_GE(index, 0);
|
||||
|
||||
const char *componentName = list->getCodecName(index);
|
||||
|
||||
AString fullName = componentName;
|
||||
fullName.append(".secure");
|
||||
|
||||
state->mCodec = MediaCodec::CreateByComponentName(
|
||||
looper, fullName.c_str());
|
||||
} else {
|
||||
state->mCodec = MediaCodec::CreateByType(
|
||||
looper, mime.c_str(), false /* encoder */);
|
||||
}
|
||||
|
||||
CHECK(state->mCodec != NULL);
|
||||
|
||||
err = state->mCodec->configure(
|
||||
format, isVideo ? surface : NULL, 0 /* flags */);
|
||||
format, isVideo ? surface : NULL,
|
||||
decryptInputBuffers ? MediaCodec::CONFIGURE_FLAG_SECURE : 0);
|
||||
|
||||
CHECK_EQ(err, (status_t)OK);
|
||||
|
||||
@@ -202,12 +224,24 @@ static int decode(
|
||||
err = extractor->getSampleTime(&timeUs);
|
||||
CHECK_EQ(err, (status_t)OK);
|
||||
|
||||
uint32_t bufferFlags = 0;
|
||||
|
||||
uint32_t sampleFlags;
|
||||
err = extractor->getSampleFlags(&sampleFlags);
|
||||
CHECK_EQ(err, (status_t)OK);
|
||||
|
||||
if (sampleFlags & NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED) {
|
||||
CHECK(decryptInputBuffers);
|
||||
|
||||
bufferFlags |= MediaCodec::BUFFER_FLAG_ENCRYPTED;
|
||||
}
|
||||
|
||||
err = state->mCodec->queueInputBuffer(
|
||||
index,
|
||||
0 /* offset */,
|
||||
buffer->size(),
|
||||
timeUs,
|
||||
0 /* flags */);
|
||||
bufferFlags);
|
||||
|
||||
CHECK_EQ(err, (status_t)OK);
|
||||
|
||||
@@ -341,9 +375,10 @@ int main(int argc, char **argv) {
|
||||
bool useVideo = false;
|
||||
bool playback = false;
|
||||
bool useSurface = false;
|
||||
bool decryptInputBuffers = false;
|
||||
|
||||
int res;
|
||||
while ((res = getopt(argc, argv, "havpS")) >= 0) {
|
||||
while ((res = getopt(argc, argv, "havpSD")) >= 0) {
|
||||
switch (res) {
|
||||
case 'a':
|
||||
{
|
||||
@@ -369,6 +404,12 @@ int main(int argc, char **argv) {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'D':
|
||||
{
|
||||
decryptInputBuffers = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
@@ -440,7 +481,8 @@ int main(int argc, char **argv) {
|
||||
player->stop();
|
||||
player->reset();
|
||||
} else {
|
||||
decode(looper, argv[0], useAudio, useVideo, surface);
|
||||
decode(looper, argv[0],
|
||||
useAudio, useVideo, surface, decryptInputBuffers);
|
||||
}
|
||||
|
||||
if (playback || (useSurface && useVideo)) {
|
||||
|
||||
@@ -287,6 +287,11 @@ private:
|
||||
|
||||
msg->setInt32("channel-count", numChannels);
|
||||
msg->setInt32("sample-rate", sampleRate);
|
||||
|
||||
int32_t isADTS;
|
||||
if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
|
||||
msg->setInt32("is-adts", true);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t type;
|
||||
|
||||
63
include/media/ICrypto.h
Normal file
63
include/media/ICrypto.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <binder/IInterface.h>
|
||||
#include <media/stagefright/foundation/ABase.h>
|
||||
|
||||
#ifndef ANDROID_ICRYPTO_H_
|
||||
|
||||
#define ANDROID_ICRYPTO_H_
|
||||
|
||||
namespace android {
|
||||
|
||||
struct ICrypto : public IInterface {
|
||||
DECLARE_META_INTERFACE(Crypto);
|
||||
|
||||
virtual status_t initialize() = 0;
|
||||
virtual status_t terminate() = 0;
|
||||
|
||||
virtual status_t setEntitlementKey(
|
||||
const void *key, size_t keyLength) = 0;
|
||||
|
||||
virtual status_t setEntitlementControlMessage(
|
||||
const void *msg, size_t msgLength) = 0;
|
||||
|
||||
// "dstData" is in media_server's address space (but inaccessible).
|
||||
virtual ssize_t decryptVideo(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataOffset) = 0;
|
||||
|
||||
// "dstData" is in the calling process' address space.
|
||||
virtual ssize_t decryptAudio(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataSize) = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(ICrypto);
|
||||
};
|
||||
|
||||
struct BnCrypto : public BnInterface<ICrypto> {
|
||||
virtual status_t onTransact(
|
||||
uint32_t code, const Parcel &data, Parcel *reply,
|
||||
uint32_t flags = 0);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_ICRYPTO_H_
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
struct ICrypto;
|
||||
class IMediaRecorder;
|
||||
class IOMX;
|
||||
struct IStreamSource;
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
|
||||
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
|
||||
virtual sp<IOMX> getOMX() = 0;
|
||||
virtual sp<ICrypto> makeCrypto() = 0;
|
||||
|
||||
// codecs and audio devices usage tracking for the battery app
|
||||
enum BatteryDataBits {
|
||||
|
||||
@@ -89,6 +89,10 @@ private:
|
||||
kPortIndexOutput = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
kFlagIsSecure = 1,
|
||||
};
|
||||
|
||||
struct BufferInfo {
|
||||
enum Status {
|
||||
OWNED_BY_US,
|
||||
@@ -118,6 +122,7 @@ private:
|
||||
sp<FlushingState> mFlushingState;
|
||||
|
||||
AString mComponentName;
|
||||
uint32_t mFlags;
|
||||
uint32_t mQuirks;
|
||||
sp<IOMX> mOMX;
|
||||
IOMX::node_id mNode;
|
||||
@@ -176,7 +181,8 @@ private:
|
||||
|
||||
status_t setupAACCodec(
|
||||
bool encoder,
|
||||
int32_t numChannels, int32_t sampleRate, int32_t bitRate);
|
||||
int32_t numChannels, int32_t sampleRate, int32_t bitRate,
|
||||
bool isADTS);
|
||||
|
||||
status_t selectAudioPortFormat(
|
||||
OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
|
||||
|
||||
@@ -27,18 +27,21 @@ namespace android {
|
||||
struct ABuffer;
|
||||
struct ACodec;
|
||||
struct AMessage;
|
||||
struct ICrypto;
|
||||
struct SoftwareRenderer;
|
||||
struct SurfaceTextureClient;
|
||||
|
||||
struct MediaCodec : public AHandler {
|
||||
enum ConfigureFlags {
|
||||
CONFIGURE_FLAG_ENCODE = 1,
|
||||
CONFIGURE_FLAG_SECURE = 2,
|
||||
};
|
||||
|
||||
enum BufferFlags {
|
||||
BUFFER_FLAG_SYNCFRAME = 1,
|
||||
BUFFER_FLAG_CODECCONFIG = 2,
|
||||
BUFFER_FLAG_EOS = 4,
|
||||
BUFFER_FLAG_ENCRYPTED = 8,
|
||||
};
|
||||
|
||||
static sp<MediaCodec> CreateByType(
|
||||
@@ -137,11 +140,13 @@ private:
|
||||
kFlagStickyError = 8,
|
||||
kFlagDequeueInputPending = 16,
|
||||
kFlagDequeueOutputPending = 32,
|
||||
kFlagIsSecure = 64,
|
||||
};
|
||||
|
||||
struct BufferInfo {
|
||||
void *mBufferID;
|
||||
sp<ABuffer> mData;
|
||||
sp<ABuffer> mEncryptedData;
|
||||
sp<AMessage> mNotify;
|
||||
bool mOwnedByClient;
|
||||
};
|
||||
@@ -165,6 +170,8 @@ private:
|
||||
int32_t mDequeueOutputTimeoutGeneration;
|
||||
uint32_t mDequeueOutputReplyID;
|
||||
|
||||
sp<ICrypto> mCrypto;
|
||||
|
||||
MediaCodec(const sp<ALooper> &looper);
|
||||
|
||||
static status_t PostAndAwaitResponse(
|
||||
|
||||
@@ -128,6 +128,12 @@ enum {
|
||||
kKeyTextFormatData = 'text', // raw data
|
||||
|
||||
kKeyRequiresSecureBuffers = 'secu', // bool (int32_t)
|
||||
|
||||
kKeyScrambling = 'scrm', // int32_t
|
||||
kKeyEMM = 'emm ', // raw data
|
||||
kKeyECM = 'ecm ', // raw data
|
||||
|
||||
kKeyIsADTS = 'adts', // bool (int32_t)
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -31,6 +31,11 @@ struct MediaExtractor;
|
||||
struct MediaSource;
|
||||
|
||||
struct NuMediaExtractor : public RefBase {
|
||||
enum SampleFlags {
|
||||
SAMPLE_FLAG_SYNC = 1,
|
||||
SAMPLE_FLAG_ENCRYPTED = 2,
|
||||
};
|
||||
|
||||
NuMediaExtractor();
|
||||
|
||||
status_t setDataSource(const char *path);
|
||||
@@ -46,6 +51,7 @@ struct NuMediaExtractor : public RefBase {
|
||||
status_t readSampleData(const sp<ABuffer> &buffer);
|
||||
status_t getSampleTrackIndex(size_t *trackIndex);
|
||||
status_t getSampleTime(int64_t *sampleTimeUs);
|
||||
status_t getSampleFlags(uint32_t *sampleFlags);
|
||||
|
||||
protected:
|
||||
virtual ~NuMediaExtractor();
|
||||
@@ -61,7 +67,9 @@ private:
|
||||
status_t mFinalResult;
|
||||
MediaBuffer *mSample;
|
||||
int64_t mSampleTimeUs;
|
||||
uint32_t mFlags; // bitmask of "TrackFlags"
|
||||
uint32_t mSampleFlags;
|
||||
|
||||
uint32_t mTrackFlags; // bitmask of "TrackFlags"
|
||||
};
|
||||
|
||||
sp<MediaExtractor> mImpl;
|
||||
|
||||
@@ -238,7 +238,11 @@ private:
|
||||
void setComponentRole();
|
||||
|
||||
void setAMRFormat(bool isWAMR, int32_t bitRate);
|
||||
status_t setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate);
|
||||
|
||||
status_t setAACFormat(
|
||||
int32_t numChannels, int32_t sampleRate, int32_t bitRate,
|
||||
bool isADTS);
|
||||
|
||||
void setG711Format(int32_t numChannels);
|
||||
|
||||
status_t setVideoPortFormatType(
|
||||
|
||||
@@ -73,6 +73,7 @@ struct AString {
|
||||
int compare(const AString &other) const;
|
||||
|
||||
bool startsWith(const char *prefix) const;
|
||||
bool endsWith(const char *suffix) const;
|
||||
|
||||
void tolower();
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ public class MediaCodec
|
||||
public static int FLAG_SYNCFRAME = 1;
|
||||
public static int FLAG_CODECCONFIG = 2;
|
||||
public static int FLAG_EOS = 4;
|
||||
public static int FLAG_ENCRYPTED = 8;
|
||||
|
||||
/** Instantiate a codec component by mime type. For decoder components
|
||||
this is the mime type of media that this decoder should be able to
|
||||
@@ -82,6 +83,7 @@ public class MediaCodec
|
||||
public native final void release();
|
||||
|
||||
public static int CONFIGURE_FLAG_ENCODE = 1;
|
||||
public static int CONFIGURE_FLAG_SECURE = 2;
|
||||
|
||||
/** Configures a component.
|
||||
* @param format A map of string/value pairs describing the input format
|
||||
|
||||
@@ -65,6 +65,13 @@ public class MediaExtractor
|
||||
// Returns the current sample's presentation time in microseconds.
|
||||
public native long getSampleTime();
|
||||
|
||||
// Keep these in sync with their equivalents in NuMediaExtractor.h
|
||||
public static final int SAMPLE_FLAG_SYNC = 1;
|
||||
public static final int SAMPLE_FLAG_ENCRYPTED = 2;
|
||||
|
||||
// Returns the current sample's flags.
|
||||
public native int getSampleFlags();
|
||||
|
||||
private static native final void native_init();
|
||||
private native final void native_setup(String path);
|
||||
private native final void native_finalize();
|
||||
|
||||
@@ -160,6 +160,10 @@ status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
|
||||
return mImpl->getSampleTime(sampleTimeUs);
|
||||
}
|
||||
|
||||
status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
|
||||
return mImpl->getSampleFlags(sampleFlags);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -343,6 +347,28 @@ static jlong android_media_MediaExtractor_getSampleTime(
|
||||
return sampleTimeUs;
|
||||
}
|
||||
|
||||
static jint android_media_MediaExtractor_getSampleFlags(
|
||||
JNIEnv *env, jobject thiz) {
|
||||
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
||||
|
||||
if (extractor == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
return -1ll;
|
||||
}
|
||||
|
||||
uint32_t sampleFlags;
|
||||
status_t err = extractor->getSampleFlags(&sampleFlags);
|
||||
|
||||
if (err == ERROR_END_OF_STREAM) {
|
||||
return -1ll;
|
||||
} else if (err != OK) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return sampleFlags;
|
||||
}
|
||||
|
||||
static void android_media_MediaExtractor_native_init(JNIEnv *env) {
|
||||
jclass clazz = env->FindClass("android/media/MediaExtractor");
|
||||
CHECK(clazz != NULL);
|
||||
@@ -412,6 +438,9 @@ static JNINativeMethod gMethods[] = {
|
||||
{ "getSampleTime", "()J",
|
||||
(void *)android_media_MediaExtractor_getSampleTime },
|
||||
|
||||
{ "getSampleFlags", "()I",
|
||||
(void *)android_media_MediaExtractor_getSampleFlags },
|
||||
|
||||
{ "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
|
||||
|
||||
{ "native_setup", "(Ljava/lang/String;)V",
|
||||
|
||||
@@ -43,6 +43,7 @@ struct JMediaExtractor : public RefBase {
|
||||
status_t readSampleData(jobject byteBuf, size_t offset, size_t *sampleSize);
|
||||
status_t getSampleTrackIndex(size_t *trackIndex);
|
||||
status_t getSampleTime(int64_t *sampleTimeUs);
|
||||
status_t getSampleFlags(uint32_t *sampleFlags);
|
||||
|
||||
protected:
|
||||
virtual ~JMediaExtractor();
|
||||
|
||||
@@ -324,7 +324,7 @@ status_t ConvertKeyValueArraysToMessage(
|
||||
env->DeleteLocalRef(byteArray); byteArray = NULL;
|
||||
}
|
||||
|
||||
msg->setObject(key.c_str(), buffer);
|
||||
msg->setBuffer(key.c_str(), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \
|
||||
IAudioFlingerClient.cpp \
|
||||
IAudioTrack.cpp \
|
||||
IAudioRecord.cpp \
|
||||
ICrypto.cpp \
|
||||
AudioRecord.cpp \
|
||||
AudioSystem.cpp \
|
||||
mediaplayer.cpp \
|
||||
|
||||
293
media/libmedia/ICrypto.cpp
Normal file
293
media/libmedia/ICrypto.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "ICrypto"
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <media/ICrypto.h>
|
||||
#include <media/stagefright/foundation/ADebug.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
INITIALIZE = IBinder::FIRST_CALL_TRANSACTION,
|
||||
TERMINATE,
|
||||
SET_ENTITLEMENT_KEY,
|
||||
SET_ECM,
|
||||
DECRYPT_VIDEO,
|
||||
DECRYPT_AUDIO,
|
||||
};
|
||||
|
||||
struct BpCrypto : public BpInterface<ICrypto> {
|
||||
BpCrypto(const sp<IBinder> &impl)
|
||||
: BpInterface<ICrypto>(impl) {
|
||||
}
|
||||
|
||||
virtual status_t initialize() {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
|
||||
remote()->transact(INITIALIZE, data, &reply);
|
||||
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t terminate() {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
|
||||
remote()->transact(TERMINATE, data, &reply);
|
||||
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t setEntitlementKey(
|
||||
const void *key, size_t keyLength) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
|
||||
data.writeInt32(keyLength);
|
||||
data.write(key, keyLength);
|
||||
remote()->transact(SET_ENTITLEMENT_KEY, data, &reply);
|
||||
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t setEntitlementControlMessage(
|
||||
const void *msg, size_t msgLength) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
|
||||
data.writeInt32(msgLength);
|
||||
data.write(msg, msgLength);
|
||||
remote()->transact(SET_ECM, data, &reply);
|
||||
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual ssize_t decryptVideo(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataOffset) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
|
||||
if (iv == NULL) {
|
||||
if (ivLength > 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data.writeInt32(-1);
|
||||
} else {
|
||||
data.writeInt32(ivLength);
|
||||
data.write(iv, ivLength);
|
||||
}
|
||||
|
||||
data.writeInt32(srcDataSize);
|
||||
data.write(srcData, srcDataSize);
|
||||
|
||||
data.writeIntPtr((intptr_t)dstData);
|
||||
data.writeInt32(dstDataOffset);
|
||||
|
||||
remote()->transact(DECRYPT_VIDEO, data, &reply);
|
||||
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual ssize_t decryptAudio(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataSize) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
|
||||
if (iv == NULL) {
|
||||
if (ivLength > 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data.writeInt32(-1);
|
||||
} else {
|
||||
data.writeInt32(ivLength);
|
||||
data.write(iv, ivLength);
|
||||
}
|
||||
|
||||
data.writeInt32(srcDataSize);
|
||||
data.write(srcData, srcDataSize);
|
||||
data.writeInt32(dstDataSize);
|
||||
|
||||
remote()->transact(DECRYPT_AUDIO, data, &reply);
|
||||
|
||||
ssize_t res = reply.readInt32();
|
||||
|
||||
if (res <= 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
reply.read(dstData, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
|
||||
};
|
||||
|
||||
IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
status_t BnCrypto::onTransact(
|
||||
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
|
||||
switch (code) {
|
||||
case INITIALIZE:
|
||||
{
|
||||
CHECK_INTERFACE(ICrypto, data, reply);
|
||||
reply->writeInt32(initialize());
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
case TERMINATE:
|
||||
{
|
||||
CHECK_INTERFACE(ICrypto, data, reply);
|
||||
reply->writeInt32(terminate());
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
case SET_ENTITLEMENT_KEY:
|
||||
{
|
||||
CHECK_INTERFACE(ICrypto, data, reply);
|
||||
|
||||
size_t keyLength = data.readInt32();
|
||||
void *key = malloc(keyLength);
|
||||
data.read(key, keyLength);
|
||||
|
||||
reply->writeInt32(setEntitlementKey(key, keyLength));
|
||||
|
||||
free(key);
|
||||
key = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
case SET_ECM:
|
||||
{
|
||||
CHECK_INTERFACE(ICrypto, data, reply);
|
||||
|
||||
size_t msgLength = data.readInt32();
|
||||
void *msg = malloc(msgLength);
|
||||
data.read(msg, msgLength);
|
||||
|
||||
reply->writeInt32(setEntitlementControlMessage(msg, msgLength));
|
||||
|
||||
free(msg);
|
||||
msg = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
case DECRYPT_VIDEO:
|
||||
{
|
||||
CHECK_INTERFACE(ICrypto, data, reply);
|
||||
|
||||
void *iv = NULL;
|
||||
|
||||
int32_t ivLength = data.readInt32();
|
||||
if (ivLength >= 0) {
|
||||
iv = malloc(ivLength);
|
||||
data.read(iv, ivLength);
|
||||
}
|
||||
|
||||
size_t srcDataSize = data.readInt32();
|
||||
void *srcData = malloc(srcDataSize);
|
||||
data.read(srcData, srcDataSize);
|
||||
|
||||
void *dstData = (void *)data.readIntPtr();
|
||||
size_t dstDataOffset = data.readInt32();
|
||||
|
||||
reply->writeInt32(
|
||||
decryptVideo(
|
||||
iv,
|
||||
ivLength < 0 ? 0 : ivLength,
|
||||
srcData,
|
||||
srcDataSize,
|
||||
dstData,
|
||||
dstDataOffset));
|
||||
|
||||
free(srcData);
|
||||
srcData = NULL;
|
||||
|
||||
if (iv != NULL) {
|
||||
free(iv);
|
||||
iv = NULL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
case DECRYPT_AUDIO:
|
||||
{
|
||||
CHECK_INTERFACE(ICrypto, data, reply);
|
||||
|
||||
void *iv = NULL;
|
||||
|
||||
int32_t ivLength = data.readInt32();
|
||||
if (ivLength >= 0) {
|
||||
iv = malloc(ivLength);
|
||||
data.read(iv, ivLength);
|
||||
}
|
||||
|
||||
size_t srcDataSize = data.readInt32();
|
||||
void *srcData = malloc(srcDataSize);
|
||||
data.read(srcData, srcDataSize);
|
||||
|
||||
size_t dstDataSize = data.readInt32();
|
||||
void *dstData = malloc(dstDataSize);
|
||||
|
||||
ssize_t res =
|
||||
decryptAudio(
|
||||
iv,
|
||||
ivLength < 0 ? 0 : ivLength,
|
||||
srcData,
|
||||
srcDataSize,
|
||||
dstData,
|
||||
dstDataSize);
|
||||
|
||||
reply->writeInt32(res);
|
||||
|
||||
if (res > 0) {
|
||||
reply->write(dstData, res);
|
||||
}
|
||||
|
||||
free(dstData);
|
||||
dstData = NULL;
|
||||
|
||||
free(srcData);
|
||||
srcData = NULL;
|
||||
|
||||
if (iv != NULL) {
|
||||
free(iv);
|
||||
iv = NULL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
default:
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <binder/IMemory.h>
|
||||
#include <media/ICrypto.h>
|
||||
#include <media/IMediaPlayerService.h>
|
||||
#include <media/IMediaRecorder.h>
|
||||
#include <media/IOMX.h>
|
||||
@@ -36,6 +37,7 @@ enum {
|
||||
CREATE_MEDIA_RECORDER,
|
||||
CREATE_METADATA_RETRIEVER,
|
||||
GET_OMX,
|
||||
MAKE_CRYPTO,
|
||||
ADD_BATTERY_DATA,
|
||||
PULL_BATTERY_DATA
|
||||
};
|
||||
@@ -111,6 +113,13 @@ public:
|
||||
return interface_cast<IOMX>(reply.readStrongBinder());
|
||||
}
|
||||
|
||||
virtual sp<ICrypto> makeCrypto() {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
|
||||
remote()->transact(MAKE_CRYPTO, data, &reply);
|
||||
return interface_cast<ICrypto>(reply.readStrongBinder());
|
||||
}
|
||||
|
||||
virtual void addBatteryData(uint32_t params) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
|
||||
@@ -191,6 +200,12 @@ status_t BnMediaPlayerService::onTransact(
|
||||
reply->writeStrongBinder(omx->asBinder());
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case MAKE_CRYPTO: {
|
||||
CHECK_INTERFACE(IMediaPlayerService, data, reply);
|
||||
sp<ICrypto> crypto = makeCrypto();
|
||||
reply->writeStrongBinder(crypto->asBinder());
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case ADD_BATTERY_DATA: {
|
||||
CHECK_INTERFACE(IMediaPlayerService, data, reply);
|
||||
uint32_t params = data.readInt32();
|
||||
|
||||
@@ -7,6 +7,7 @@ LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
Crypto.cpp \
|
||||
MediaRecorderClient.cpp \
|
||||
MediaPlayerService.cpp \
|
||||
MetadataRetrieverClient.cpp \
|
||||
@@ -42,7 +43,7 @@ LOCAL_C_INCLUDES := \
|
||||
$(TOP)/frameworks/base/media/libstagefright/include \
|
||||
$(TOP)/frameworks/base/media/libstagefright/rtsp \
|
||||
$(TOP)/frameworks/native/include/media/openmax \
|
||||
$(TOP)/external/tremolo/Tremolo
|
||||
$(TOP)/external/tremolo/Tremolo \
|
||||
|
||||
LOCAL_MODULE:= libmediaplayerservice
|
||||
|
||||
|
||||
65
media/libmediaplayerservice/Crypto.cpp
Normal file
65
media/libmediaplayerservice/Crypto.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "Crypto"
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "Crypto.h"
|
||||
|
||||
#include <media/stagefright/MediaErrors.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
Crypto::Crypto() {
|
||||
}
|
||||
|
||||
Crypto::~Crypto() {
|
||||
}
|
||||
|
||||
status_t Crypto::initialize() {
|
||||
return ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
status_t Crypto::terminate() {
|
||||
return ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
status_t Crypto::setEntitlementKey(
|
||||
const void *key, size_t keyLength) {
|
||||
return ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
status_t Crypto::setEntitlementControlMessage(
|
||||
const void *msg, size_t msgLength) {
|
||||
return ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
ssize_t Crypto::decryptVideo(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataOffset) {
|
||||
return ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
ssize_t Crypto::decryptAudio(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataSize) {
|
||||
return ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
57
media/libmediaplayerservice/Crypto.h
Normal file
57
media/libmediaplayerservice/Crypto.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CRYPTO_H_
|
||||
|
||||
#define CRYPTO_H_
|
||||
|
||||
#include <media/ICrypto.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
struct Crypto : public BnCrypto {
|
||||
Crypto();
|
||||
|
||||
virtual status_t initialize();
|
||||
virtual status_t terminate();
|
||||
|
||||
virtual status_t setEntitlementKey(
|
||||
const void *key, size_t keyLength);
|
||||
|
||||
virtual status_t setEntitlementControlMessage(
|
||||
const void *msg, size_t msgLength);
|
||||
|
||||
virtual ssize_t decryptVideo(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataOffset);
|
||||
|
||||
virtual ssize_t decryptAudio(
|
||||
const void *iv, size_t ivLength,
|
||||
const void *srcData, size_t srcDataSize,
|
||||
void *dstData, size_t dstDataSize);
|
||||
|
||||
protected:
|
||||
virtual ~Crypto();
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(Crypto);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // CRYPTO_H_
|
||||
@@ -70,6 +70,8 @@
|
||||
|
||||
#include <OMX.h>
|
||||
|
||||
#include "Crypto.h"
|
||||
|
||||
namespace android {
|
||||
sp<MediaPlayerBase> createAAH_TXPlayer();
|
||||
sp<MediaPlayerBase> createAAH_RXPlayer();
|
||||
@@ -292,6 +294,16 @@ sp<IOMX> MediaPlayerService::getOMX() {
|
||||
return mOMX;
|
||||
}
|
||||
|
||||
sp<ICrypto> MediaPlayerService::makeCrypto() {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
if (mCrypto == NULL) {
|
||||
mCrypto = new Crypto;
|
||||
}
|
||||
|
||||
return mCrypto;
|
||||
}
|
||||
|
||||
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
|
||||
{
|
||||
const size_t SIZE = 256;
|
||||
|
||||
@@ -240,6 +240,7 @@ public:
|
||||
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
|
||||
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
|
||||
virtual sp<IOMX> getOMX();
|
||||
virtual sp<ICrypto> makeCrypto();
|
||||
|
||||
virtual status_t dump(int fd, const Vector<String16>& args);
|
||||
|
||||
@@ -419,6 +420,7 @@ private:
|
||||
SortedVector< wp<MediaRecorderClient> > mMediaRecorderClients;
|
||||
int32_t mNextConnId;
|
||||
sp<IOMX> mOMX;
|
||||
sp<ICrypto> mCrypto;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -125,6 +125,11 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
|
||||
|
||||
msg->setInt32("channel-count", numChannels);
|
||||
msg->setInt32("sample-rate", sampleRate);
|
||||
|
||||
int32_t isADTS;
|
||||
if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
|
||||
msg->setInt32("is-adts", true);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t maxInputSize;
|
||||
|
||||
@@ -427,24 +427,34 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
|
||||
sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
|
||||
CHECK(mem.get() != NULL);
|
||||
|
||||
IOMX::buffer_id buffer;
|
||||
BufferInfo info;
|
||||
info.mStatus = BufferInfo::OWNED_BY_US;
|
||||
|
||||
uint32_t requiresAllocateBufferBit =
|
||||
(portIndex == kPortIndexInput)
|
||||
? OMXCodec::kRequiresAllocateBufferOnInputPorts
|
||||
: OMXCodec::kRequiresAllocateBufferOnOutputPorts;
|
||||
|
||||
if (mQuirks & requiresAllocateBufferBit) {
|
||||
if (portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) {
|
||||
mem.clear();
|
||||
|
||||
void *ptr;
|
||||
err = mOMX->allocateBuffer(
|
||||
mNode, portIndex, def.nBufferSize, &info.mBufferID,
|
||||
&ptr);
|
||||
|
||||
info.mData = new ABuffer(ptr, def.nBufferSize);
|
||||
} else if (mQuirks & requiresAllocateBufferBit) {
|
||||
err = mOMX->allocateBufferWithBackup(
|
||||
mNode, portIndex, mem, &buffer);
|
||||
mNode, portIndex, mem, &info.mBufferID);
|
||||
} else {
|
||||
err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
|
||||
err = mOMX->useBuffer(mNode, portIndex, mem, &info.mBufferID);
|
||||
}
|
||||
|
||||
if (mem != NULL) {
|
||||
info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
|
||||
}
|
||||
|
||||
BufferInfo info;
|
||||
info.mBufferID = buffer;
|
||||
info.mStatus = BufferInfo::OWNED_BY_US;
|
||||
info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
|
||||
mBuffers[portIndex].push(info);
|
||||
}
|
||||
}
|
||||
@@ -840,7 +850,13 @@ status_t ACodec::configureCodec(
|
||||
|| !msg->findInt32("sample-rate", &sampleRate)) {
|
||||
err = INVALID_OPERATION;
|
||||
} else {
|
||||
err = setupAACCodec(encoder, numChannels, sampleRate, bitRate);
|
||||
int32_t isADTS;
|
||||
if (!msg->findInt32("is-adts", &isADTS)) {
|
||||
isADTS = 0;
|
||||
}
|
||||
|
||||
err = setupAACCodec(
|
||||
encoder, numChannels, sampleRate, bitRate, isADTS != 0);
|
||||
}
|
||||
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
|
||||
err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
|
||||
@@ -934,7 +950,11 @@ status_t ACodec::selectAudioPortFormat(
|
||||
|
||||
status_t ACodec::setupAACCodec(
|
||||
bool encoder,
|
||||
int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
|
||||
int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
|
||||
if (encoder && isADTS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status_t err = setupRawAudioFormat(
|
||||
encoder ? kPortIndexInput : kPortIndexOutput,
|
||||
sampleRate,
|
||||
@@ -1021,7 +1041,11 @@ status_t ACodec::setupAACCodec(
|
||||
|
||||
profile.nChannels = numChannels;
|
||||
profile.nSampleRate = sampleRate;
|
||||
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
|
||||
|
||||
profile.eAACStreamFormat =
|
||||
isADTS
|
||||
? OMX_AUDIO_AACStreamFormatMP4ADTS
|
||||
: OMX_AUDIO_AACStreamFormatMP4FF;
|
||||
|
||||
return mOMX->setParameter(
|
||||
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
|
||||
@@ -2653,6 +2677,12 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
|
||||
observer->setNotificationMessage(notify);
|
||||
|
||||
mCodec->mComponentName = componentName;
|
||||
mCodec->mFlags = 0;
|
||||
|
||||
if (componentName.endsWith(".secure")) {
|
||||
mCodec->mFlags |= kFlagIsSecure;
|
||||
}
|
||||
|
||||
mCodec->mQuirks = quirks;
|
||||
mCodec->mOMX = omx;
|
||||
mCodec->mNode = node;
|
||||
@@ -2701,6 +2731,7 @@ void ACodec::LoadedState::onShutdown(bool keepComponentAllocated) {
|
||||
mCodec->mNode = NULL;
|
||||
mCodec->mOMX.clear();
|
||||
mCodec->mQuirks = 0;
|
||||
mCodec->mFlags = 0;
|
||||
mCodec->mComponentName.clear();
|
||||
|
||||
mCodec->changeState(mCodec->mUninitializedState);
|
||||
|
||||
@@ -22,10 +22,14 @@
|
||||
|
||||
#include "include/SoftwareRenderer.h"
|
||||
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <gui/SurfaceTextureClient.h>
|
||||
#include <media/ICrypto.h>
|
||||
#include <media/IMediaPlayerService.h>
|
||||
#include <media/stagefright/foundation/ABuffer.h>
|
||||
#include <media/stagefright/foundation/ADebug.h>
|
||||
#include <media/stagefright/foundation/AMessage.h>
|
||||
#include <media/stagefright/foundation/hexdump.h>
|
||||
#include <media/stagefright/ACodec.h>
|
||||
#include <media/stagefright/MediaErrors.h>
|
||||
#include <media/stagefright/MetaData.h>
|
||||
@@ -528,6 +532,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
|
||||
info.mOwnedByClient = false;
|
||||
CHECK(msg->findBuffer(name.c_str(), &info.mData));
|
||||
|
||||
if (portIndex == kPortIndexInput
|
||||
&& (mFlags & kFlagIsSecure)) {
|
||||
info.mEncryptedData =
|
||||
new ABuffer(info.mData->capacity());
|
||||
}
|
||||
|
||||
buffers->push_back(info);
|
||||
}
|
||||
|
||||
@@ -742,6 +752,59 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
|
||||
format->setInt32("encoder", true);
|
||||
}
|
||||
|
||||
if (flags & CONFIGURE_FLAG_SECURE) {
|
||||
mFlags |= kFlagIsSecure;
|
||||
|
||||
sp<IServiceManager> sm = defaultServiceManager();
|
||||
|
||||
sp<IBinder> binder =
|
||||
sm->getService(String16("media.player"));
|
||||
|
||||
sp<IMediaPlayerService> service =
|
||||
interface_cast<IMediaPlayerService>(binder);
|
||||
|
||||
CHECK(service != NULL);
|
||||
|
||||
mCrypto = service->makeCrypto();
|
||||
|
||||
status_t err = mCrypto->initialize();
|
||||
|
||||
if (err == OK) {
|
||||
sp<ABuffer> emm;
|
||||
if (format->findBuffer("emm", &emm)) {
|
||||
err = mCrypto->setEntitlementKey(
|
||||
emm->data(), emm->size());
|
||||
}
|
||||
}
|
||||
|
||||
if (err == OK) {
|
||||
sp<ABuffer> ecm;
|
||||
if (format->findBuffer("ecm", &ecm)) {
|
||||
CHECK_EQ(ecm->size(), 80u);
|
||||
|
||||
// bytes 16..47 of the original ecm stream data.
|
||||
err = mCrypto->setEntitlementControlMessage(
|
||||
ecm->data() + 16, 32);
|
||||
}
|
||||
}
|
||||
|
||||
if (err != OK) {
|
||||
ALOGE("failed to instantiate crypto service.");
|
||||
|
||||
mCrypto.clear();
|
||||
|
||||
setState(INITIALIZED);
|
||||
|
||||
sp<AMessage> response = new AMessage;
|
||||
response->setInt32("err", UNKNOWN_ERROR);
|
||||
|
||||
response->postReply(mReplyID);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
mFlags &= ~kFlagIsSecure;
|
||||
}
|
||||
|
||||
mCodec->initiateConfigureComponent(format);
|
||||
break;
|
||||
}
|
||||
@@ -983,7 +1046,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
|
||||
for (size_t i = 0; i < srcBuffers.size(); ++i) {
|
||||
const BufferInfo &info = srcBuffers.itemAt(i);
|
||||
|
||||
dstBuffers->push_back(info.mData);
|
||||
dstBuffers->push_back(
|
||||
(portIndex == kPortIndexInput
|
||||
&& (mFlags & kFlagIsSecure))
|
||||
? info.mEncryptedData : info.mData);
|
||||
}
|
||||
|
||||
(new AMessage)->postReply(replyID);
|
||||
@@ -1037,10 +1103,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
|
||||
}
|
||||
|
||||
void MediaCodec::setState(State newState) {
|
||||
if (newState == UNINITIALIZED) {
|
||||
if (newState == INITIALIZED) {
|
||||
delete mSoftRenderer;
|
||||
mSoftRenderer = NULL;
|
||||
|
||||
if (mCrypto != NULL) {
|
||||
mCrypto->terminate();
|
||||
mCrypto.clear();
|
||||
}
|
||||
|
||||
mNativeWindow.clear();
|
||||
|
||||
mOutputFormat.clear();
|
||||
@@ -1150,6 +1221,43 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
|
||||
info->mData->meta()->setInt32("csd", true);
|
||||
}
|
||||
|
||||
if (mFlags & kFlagIsSecure) {
|
||||
uint8_t iv[16];
|
||||
memset(iv, 0, sizeof(iv));
|
||||
|
||||
ssize_t outLength;
|
||||
|
||||
if (mFlags & kFlagIsSoftwareCodec) {
|
||||
outLength = mCrypto->decryptAudio(
|
||||
(flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL,
|
||||
(flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0,
|
||||
info->mEncryptedData->base() + offset,
|
||||
size,
|
||||
info->mData->base(),
|
||||
info->mData->capacity());
|
||||
} else {
|
||||
outLength = mCrypto->decryptVideo(
|
||||
(flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL,
|
||||
(flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0,
|
||||
info->mEncryptedData->base() + offset,
|
||||
size,
|
||||
info->mData->base(),
|
||||
0 /* offset */);
|
||||
}
|
||||
|
||||
if (outLength < 0) {
|
||||
return outLength;
|
||||
}
|
||||
|
||||
if ((size_t)outLength > info->mEncryptedData->capacity()) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
info->mData->setRange(0, outLength);
|
||||
} else if (flags & BUFFER_FLAG_ENCRYPTED) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reply->setBuffer("buffer", info->mData);
|
||||
reply->post();
|
||||
|
||||
|
||||
@@ -107,6 +107,11 @@ status_t NuMediaExtractor::getTrackFormat(
|
||||
|
||||
msg->setInt32("channel-count", numChannels);
|
||||
msg->setInt32("sample-rate", sampleRate);
|
||||
|
||||
int32_t isADTS;
|
||||
if (meta->findInt32(kKeyIsADTS, &isADTS)) {
|
||||
msg->setInt32("is-adts", true);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t maxInputSize;
|
||||
@@ -232,6 +237,20 @@ status_t NuMediaExtractor::getTrackFormat(
|
||||
msg->setBuffer("csd-1", buffer);
|
||||
}
|
||||
|
||||
if (meta->findData(kKeyEMM, &type, &data, &size)) {
|
||||
sp<ABuffer> emm = new ABuffer(size);
|
||||
memcpy(emm->data(), data, size);
|
||||
|
||||
msg->setBuffer("emm", emm);
|
||||
}
|
||||
|
||||
if (meta->findData(kKeyECM, &type, &data, &size)) {
|
||||
sp<ABuffer> ecm = new ABuffer(size);
|
||||
memcpy(ecm->data(), data, size);
|
||||
|
||||
msg->setBuffer("ecm", ecm);
|
||||
}
|
||||
|
||||
*format = msg;
|
||||
|
||||
return OK;
|
||||
@@ -267,13 +286,14 @@ status_t NuMediaExtractor::selectTrack(size_t index) {
|
||||
info->mFinalResult = OK;
|
||||
info->mSample = NULL;
|
||||
info->mSampleTimeUs = -1ll;
|
||||
info->mFlags = 0;
|
||||
info->mSampleFlags = 0;
|
||||
info->mTrackFlags = 0;
|
||||
|
||||
const char *mime;
|
||||
CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
|
||||
|
||||
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
|
||||
info->mFlags |= kIsVorbis;
|
||||
info->mTrackFlags |= kIsVorbis;
|
||||
}
|
||||
|
||||
return OK;
|
||||
@@ -288,6 +308,7 @@ void NuMediaExtractor::releaseTrackSamples() {
|
||||
info->mSample = NULL;
|
||||
|
||||
info->mSampleTimeUs = -1ll;
|
||||
info->mSampleFlags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,6 +327,7 @@ ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
|
||||
info->mSample->release();
|
||||
info->mSample = NULL;
|
||||
info->mSampleTimeUs = -1ll;
|
||||
info->mSampleFlags = 0;
|
||||
}
|
||||
} else if (info->mFinalResult != OK) {
|
||||
continue;
|
||||
@@ -323,11 +345,25 @@ ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
|
||||
|
||||
info->mFinalResult = err;
|
||||
info->mSampleTimeUs = -1ll;
|
||||
info->mSampleFlags = 0;
|
||||
continue;
|
||||
} else {
|
||||
CHECK(info->mSample != NULL);
|
||||
CHECK(info->mSample->meta_data()->findInt64(
|
||||
kKeyTime, &info->mSampleTimeUs));
|
||||
|
||||
info->mSampleFlags = 0;
|
||||
|
||||
int32_t val;
|
||||
if (info->mSample->meta_data()->findInt32(
|
||||
kKeyIsSyncFrame, &val) && val != 0) {
|
||||
info->mSampleFlags |= SAMPLE_FLAG_SYNC;
|
||||
}
|
||||
|
||||
if (info->mSample->meta_data()->findInt32(
|
||||
kKeyScrambling, &val) && val != 0) {
|
||||
info->mSampleFlags |= SAMPLE_FLAG_ENCRYPTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +407,7 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
|
||||
|
||||
size_t sampleSize = info->mSample->range_length();
|
||||
|
||||
if (info->mFlags & kIsVorbis) {
|
||||
if (info->mTrackFlags & kIsVorbis) {
|
||||
// Each sample's data is suffixed by the number of page samples
|
||||
// or -1 if not available.
|
||||
sampleSize += sizeof(int32_t);
|
||||
@@ -387,7 +423,7 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
|
||||
|
||||
memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
|
||||
|
||||
if (info->mFlags & kIsVorbis) {
|
||||
if (info->mTrackFlags & kIsVorbis) {
|
||||
int32_t numPageSamples;
|
||||
if (!info->mSample->meta_data()->findInt32(
|
||||
kKeyValidSamples, &numPageSamples)) {
|
||||
@@ -430,4 +466,17 @@ status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t NuMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
|
||||
ssize_t minIndex = fetchTrackSamples();
|
||||
|
||||
if (minIndex < 0) {
|
||||
return ERROR_END_OF_STREAM;
|
||||
}
|
||||
|
||||
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
|
||||
*sampleFlags = info->mSampleFlags;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -515,7 +515,12 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
|
||||
CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
|
||||
CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
|
||||
|
||||
status_t err = setAACFormat(numChannels, sampleRate, bitRate);
|
||||
int32_t isADTS;
|
||||
if (!meta->findInt32(kKeyIsADTS, &isADTS)) {
|
||||
isADTS = false;
|
||||
}
|
||||
|
||||
status_t err = setAACFormat(numChannels, sampleRate, bitRate, isADTS);
|
||||
if (err != OK) {
|
||||
CODEC_LOGE("setAACFormat() failed (err = %d)", err);
|
||||
return err;
|
||||
@@ -3386,11 +3391,17 @@ void OMXCodec::setAMRFormat(bool isWAMR, int32_t bitRate) {
|
||||
}
|
||||
}
|
||||
|
||||
status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
|
||||
if (numChannels > 2)
|
||||
status_t OMXCodec::setAACFormat(
|
||||
int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
|
||||
if (numChannels > 2) {
|
||||
ALOGW("Number of channels: (%d) \n", numChannels);
|
||||
}
|
||||
|
||||
if (mIsEncoder) {
|
||||
if (isADTS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
//////////////// input port ////////////////////
|
||||
setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
|
||||
|
||||
@@ -3445,7 +3456,9 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t
|
||||
&profile, sizeof(profile));
|
||||
|
||||
if (err != OK) {
|
||||
CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err);
|
||||
CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
|
||||
"(err = %d)",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
@@ -3459,13 +3472,19 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t
|
||||
|
||||
profile.nChannels = numChannels;
|
||||
profile.nSampleRate = sampleRate;
|
||||
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
|
||||
|
||||
profile.eAACStreamFormat =
|
||||
isADTS
|
||||
? OMX_AUDIO_AACStreamFormatMP4ADTS
|
||||
: OMX_AUDIO_AACStreamFormatMP4FF;
|
||||
|
||||
err = mOMX->setParameter(
|
||||
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
|
||||
|
||||
if (err != OK) {
|
||||
CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err);
|
||||
CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
|
||||
"(err = %d)",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "pvmp4audiodecoder_api.h"
|
||||
|
||||
#include <media/stagefright/foundation/ADebug.h>
|
||||
#include <media/stagefright/foundation/hexdump.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -42,6 +43,7 @@ SoftAAC::SoftAAC(
|
||||
OMX_COMPONENTTYPE **component)
|
||||
: SimpleSoftOMXComponent(name, callbacks, appData, component),
|
||||
mConfig(new tPVMP4AudioDecoderExternal),
|
||||
mIsADTS(false),
|
||||
mDecoderBuf(NULL),
|
||||
mInputBufferCount(0),
|
||||
mUpsamplingFactor(2),
|
||||
@@ -140,7 +142,12 @@ OMX_ERRORTYPE SoftAAC::internalGetParameter(
|
||||
aacParams->nAACtools = 0;
|
||||
aacParams->nAACERtools = 0;
|
||||
aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
|
||||
aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
|
||||
|
||||
aacParams->eAACStreamFormat =
|
||||
mIsADTS
|
||||
? OMX_AUDIO_AACStreamFormatMP4ADTS
|
||||
: OMX_AUDIO_AACStreamFormatMP4FF;
|
||||
|
||||
aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
|
||||
|
||||
if (!isConfigured()) {
|
||||
@@ -215,6 +222,15 @@ OMX_ERRORTYPE SoftAAC::internalSetParameter(
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
|
||||
if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) {
|
||||
mIsADTS = false;
|
||||
} else if (aacParams->eAACStreamFormat
|
||||
== OMX_AUDIO_AACStreamFormatMP4ADTS) {
|
||||
mIsADTS = true;
|
||||
} else {
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
||||
@@ -299,8 +315,35 @@ void SoftAAC::onQueueFilled(OMX_U32 portIndex) {
|
||||
mNumSamplesOutput = 0;
|
||||
}
|
||||
|
||||
mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
|
||||
mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
|
||||
if (mIsADTS) {
|
||||
// skip 30 bits, aac_frame_length follows.
|
||||
// ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
|
||||
|
||||
const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
|
||||
|
||||
CHECK_GE(inHeader->nFilledLen, 7);
|
||||
|
||||
bool protectionAbsent = (adtsHeader[1] & 1);
|
||||
|
||||
unsigned aac_frame_length =
|
||||
((adtsHeader[3] & 3) << 11)
|
||||
| (adtsHeader[4] << 3)
|
||||
| (adtsHeader[5] >> 5);
|
||||
|
||||
CHECK_GE(inHeader->nFilledLen, aac_frame_length);
|
||||
|
||||
size_t headerSize = (protectionAbsent ? 7 : 9);
|
||||
|
||||
mConfig->pInputBuffer = (UChar *)adtsHeader + headerSize;
|
||||
mConfig->inputBufferCurrentLength = aac_frame_length - headerSize;
|
||||
|
||||
inHeader->nOffset += headerSize;
|
||||
inHeader->nFilledLen -= headerSize;
|
||||
} else {
|
||||
mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
|
||||
mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
|
||||
}
|
||||
|
||||
mConfig->inputBufferMaxLength = 0;
|
||||
mConfig->inputBufferUsedLength = 0;
|
||||
mConfig->remainderBits = 0;
|
||||
|
||||
@@ -49,6 +49,7 @@ private:
|
||||
};
|
||||
|
||||
tPVMP4AudioDecoderExternal *mConfig;
|
||||
bool mIsADTS;
|
||||
void *mDecoderBuf;
|
||||
|
||||
size_t mInputBufferCount;
|
||||
|
||||
@@ -310,6 +310,16 @@ bool AString::startsWith(const char *prefix) const {
|
||||
return !strncmp(mData, prefix, strlen(prefix));
|
||||
}
|
||||
|
||||
bool AString::endsWith(const char *suffix) const {
|
||||
size_t suffixLen = strlen(suffix);
|
||||
|
||||
if (mSize < suffixLen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !strcmp(mData + mSize - suffixLen, suffix);
|
||||
}
|
||||
|
||||
AString StringPrintf(const char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
@@ -117,6 +117,12 @@ status_t AnotherPacketSource::read(
|
||||
|
||||
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
|
||||
|
||||
int32_t scrambling;
|
||||
if (buffer->meta()->findInt32("scrambling", &scrambling)
|
||||
&& scrambling != 0) {
|
||||
mediaBuffer->meta_data()->setInt32(kKeyScrambling, scrambling);
|
||||
}
|
||||
|
||||
*out = mediaBuffer;
|
||||
return OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user