Android-MediaCodec中DRM的实现
前⾔:
安卓系统中播放⼀个视频⽂件⼀般有两个接⼝,⼀个是MediaPlayer,⼀个是MediaCodec。那么在播放加密视频时这两个接⼝分别是怎么处理的呢?本⽂将⼀⼀揭晓。
MediaCodec API组的DRM处理:
如果使⽤MediaCodec进⾏decode的时候,configure()⽅法需要传进⼀个MediaCrypto。
// APP通过JNI调⽤MediaCodec::configure,并把crypto传⼊。
status_t MediaCodec::configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
const sp<ICrypto> &crypto, // 传⼊crypto
uint32_t flags) {
return configure(format, nativeWindow, crypto, NULL, flags);
}
status_t MediaCodec::configure(
const sp<AMessage> &format,
const sp<Surface> &surface,
const sp<ICrypto> &crypto,
const sp<IDescrambler> &descrambler,
uint32_t flags) {
sp<AMessage> msg = new AMessage(kWhatConfigure, this); // kWhatConfigure
......
if (crypto != NULL || descrambler != NULL) {
if (crypto != NULL) {
msg->tPointer("crypto", ()); // 把crypto设给msg
} el {
msg->tPointer("descrambler", ());
}
if (mAnalyticsItem != NULL) {
mAnalyticsItem->tInt32(kCodecCrypto, 1);
}
} el if (mFlags & kFlagIsSecure) {
ALOGW("Crypto or descrambler should be given for cure codec");
}
.
.....
PostAndAwaitRespon(msg, &respon); // 发送kWhatConfigure消息
......
}
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
{
......
ca kWhatConfigure: // MediaCodec收到kWhatConfigure消息
if (!msg->findPointer("crypto", &crypto)) {
crypto = NULL;
}
ALOGV("kWhatConfigure: Old mCrypto: %p (%d)",
<(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
和外国人聊天
mCrypto = static_cast<ICrypto *>(crypto); // 更新mCrypto
mBufferChannel->tCrypto(mCrypto); // 把mCrypto给mBufferChannel
ALOGV("kWhatConfigure: New mCrypto: %p (%d)",
<(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
void *descrambler;
if (!msg->findPointer("descrambler", &descrambler)) {
descrambler = NULL;
}
mDescrambler = static_cast<IDescrambler *>(descrambler);
mBufferChannel->tDescrambler(mDescrambler);
uint32_t flags;
CHECK(msg->findInt32("flags", (int32_t *)&flags));
if (flags & CONFIGURE_FLAG_ENCODE) {
format->tInt32("encoder", true);
mFlags |= kFlagIsEncoder;
}
extractCSD(format);
mCodec->initiateConfigureComponent(format);
break;
.
.....
}
}
MediaCodec::configure 在此函数中设置 msg->tPointer("crypto", ());发送msg。
MediaCodec::onMessageReceived 收到 configure 发送的消息,取出crypto,并调⽤mBufferChannel->tCrypto(mCrypto);下⾯看看mBufferChannel中对crypto的应⽤:
void BufferChannelBa::tCrypto(const sp<ICrypto> &crypto) {
mCrypto = crypto; // 先把crypto赋值给mCrypto
}
status_t ACodecBufferChannel::queueSecureInputBuffer( // 然后在此函数中应⽤mCrypto元旦小品剧本
const sp<MediaCodecBuffer> &buffer, bool cure, const uint8_t *key,
const uint8_t *iv, CryptoPlugin::Mode mode, CryptoPlugin::Pattern pattern,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
AString *errorDetailMsg) {
......
ssize_t result = -1;
if (mCrypto != NULL) {
ICrypto::DestinationBuffer destination;
if (cure) {
destination.mType = ICrypto::kDestinationTypeNativeHandle;
destination.mHandle = cureHandle;
} el {
destination.mType = ICrypto::kDestinationTypeSharedMemory;
翻糖蛋糕培训班
destination.mSharedMemory = mDecryptDestination;
}
ICrypto::SourceBuffer source;
source.mSharedMemory = it->mSharedEncryptedBuffer;
source.mHeapSeqNum = mHeapSeqNum;
result = mCrypto->decrypt(key, iv, mode, pattern, // 解密过程
source, it->mClientBuffer->offt(),
subSamples, numSubSamples, destination, errorDetailMsg);
if (result < 0) {
return result;
}
if (destination.mType == ICrypto::kDestinationTypeSharedMemory) {
memcpy(it->mCodecBuffer->ba(), destination.mSharedMemory->pointer(), result);
}
} el {
......
}
......
}
ACodecBufferChannel.cpp中对mCrypto的应⽤就是在调⽤queueSecureInputBuffer时对input buffer进⾏解密。接下来看看,Mediacodec中对mCrypto的应⽤:
// 当APP给input buffer写⼊数据之后就会调⽤queueSecureInputBuffer把input buffer给mediacodec
status_t MediaCodec::queueSecureInputBuffer(
size_t index,
size_t offt,
const CryptoPlugin::SubSample *subSamples,
size_t numSubSamples,
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
const CryptoPlugin::Pattern &pattern,
int64_t prentationTimeUs,
uint32_t flags,
AString *errorDetailMsg) {
if (errorDetailMsg != NULL) {
errorDetailMsg->clear();
}
sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
msg->tSize("index", index);
msg->tSize("offt", offt);
msg->tPointer("subSamples", (void *)subSamples);
msg->tSize("numSubSamples", numSubSamples);
msg->tPointer("key", (void *)key); // key
msg->tPointer("iv", (void *)iv);
msg->tInt32("mode", mode);
msg->tInt32("encryptBlocks", pattern.mEncryptBlocks);
msg->tInt32("skipBlocks", pattern.mSkipBlocks);
msg->tInt64("timeUs", prentationTimeUs);
msg->tInt32("flags", flags);
msg->tPointer("errorDetailMsg", errorDetailMsg);
sp<AMessage> respon;
status_t err = PostAndAwaitRespon(msg, &respon); // 发送kWhatQueueInputBuffer
return err;
}
status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
// 响应kWhatQueueInputBuffer
size_t index;
size_t offt;
size_t size;
int64_t timeUs;
uint32_t flags;
CHECK(msg->findSize("index", &index));
CHECK(msg->findSize("offt", &offt));
CHECK(msg->findInt64("timeUs", &timeUs));
CHECK(msg->findInt32("flags", (int32_t *)&flags));
const CryptoPlugin::SubSample *subSamples;
size_t numSubSamples;
const uint8_t *key;
const uint8_t *iv;
CryptoPlugin::Mode mode = CryptoPlugin::kMode_Unencrypted;
// We allow the simpler queueInputBuffer API to be ud even in
// cure mode, by fabricating a single unencrypted subSample.
CryptoPlugin::SubSample ss;
CryptoPlugin::Pattern pattern;
if (msg->findSize("size", &size)) {
if (hasCryptoOrDescrambler()) {
// 未加密,忽略
}
} el {
if (!hasCryptoOrDescrambler()) {
// 加密但⽆mCrypto or mDescrambler,报错,退出
}
CHECK(msg->findPointer("subSamples", (void **)&subSamples));
CHECK(msg->findSize("numSubSamples", &numSubSamples));
CHECK(msg->findPointer("key", (void **)&key)); // 把key取出来
CHECK(msg->findPointer("iv", (void **)&iv));
CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));
communication是什么意思
CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));
int32_t tmp;
quiksilver
CHECK(msg->findInt32("mode", &tmp));
mode = (CryptoPlugin::Mode)tmp;
size = 0;
for (size_t i = 0; i < numSubSamples; ++i) {
size += subSamples[i].mNumBytesOfClearData;
size += subSamples[i].mNumBytesOfEncryptedData;
}
}
if (index >= mPortBuffers[kPortIndexInput].size()) {
return -ERANGE;
}
BufferInfo *info = &mPortBuffers[kPortIndexInput][index];
if (info->mData == nullptr || !info->mOwnedByClient) {
return -EACCES;
}
if (offt + size > info->mData->capacity()) {
if ( ((int)size < 0) && !(flags & BUFFER_FLAG_EOS)) {
西安教育size = 0;
ALOGD("EOS, ret size to zero");
} el {
return -EINVAL;kangnam
}
}
info->mData->tRange(offt, size);
国庆节英语手抄报info->mData->meta()->tInt64("timeUs", timeUs);
if (flags & BUFFER_FLAG_EOS) {
info->mData->meta()->tInt32("eos", true);
}
if (flags & BUFFER_FLAG_CODECCONFIG) {
info->mData->meta()->tInt32("csd", true);
}
sp<MediaCodecBuffer> buffer = info->mData;
status_t err = OK;
if (hasCryptoOrDescrambler()) {
AString *errorDetailMsg;
CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
err = mBufferChannel->queueSecureInputBuffer(
// 调⽤ACodecBufferChannel的queueSecureInputBuffer函数
buffer,
(mFlags & kFlagIsSecure),
key, // 把key传⼊
iv,
mode,
pattern,
subSamples,
四六级满分多少numSubSamples,
errorDetailMsg);
} el {
err = mBufferChannel->queueInputBuffer(buffer);
}
if (err == OK) {
// synchronization boundary for getBufferAndFormat
Mutex::Autolock al(mBufferLock);
info->mOwnedByClient = fal;
info->mData.clear();
statsBufferSent(timeUs);
}condition