From 564f3a93caf6053b8be8d574604827ba729f326d Mon Sep 17 00:00:00 2001 From: Arun Johnson Date: Thu, 1 Feb 2024 19:09:45 +0000 Subject: [PATCH] Implement secure API functions for large audio frames Functions added - queueSecureInputBuffers - attachEncryptedBuffers - cryptoAsync for multiple access units Bug: 298052174 Test: atest android.media.drmframework.cts.MediaDrmCodecMultiAccessUnitTest Change-Id: I8053f40c2e15d29132441de06bacf2fa2f9ac1f2 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 256 ++++++++++++++++++ media/codec2/sfplugin/CCodecBufferChannel.h | 10 + media/codec2/sfplugin/Codec2Buffer.cpp | 31 +++ media/codec2/sfplugin/Codec2Buffer.h | 11 + media/libstagefright/CryptoAsync.cpp | 64 ++++- media/libstagefright/MediaCodec.cpp | 215 ++++++++++----- .../include/media/stagefright/CodecBase.h | 72 +++++ .../include/media/stagefright/CryptoAsync.h | 12 + .../include/media/stagefright/MediaCodec.h | 18 +- 9 files changed, 607 insertions(+), 82 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 7b1721e7f7..40656ff62a 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -483,6 +483,130 @@ int32_t CCodecBufferChannel::getHeapSeqNum(const sp &memory) { return heapSeqNum; } +typedef WrapperObject> BufferInfosWrapper; +typedef WrapperObject>> CryptoInfosWrapper; +status_t CCodecBufferChannel::attachEncryptedBuffers( + const sp &memory, + size_t offset, + const sp &buffer, + bool secure, + AString* errorDetailMsg) { + static const C2MemoryUsage kDefaultReadWriteUsage{ + C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + if (!hasCryptoOrDescrambler()) { + ALOGE("attachEncryptedBuffers requires Crypto/descrambler object"); + return -ENOSYS; + } + size_t size = 0; + CHECK(buffer->meta()->findSize("ssize", &size)); + if (size == 0) { + buffer->setRange(0, 0); + return OK; + } + sp obj; + CHECK(buffer->meta()->findObject("cryptoInfos", &obj)); + sp cryptoInfos{(CryptoInfosWrapper *)obj.get()}; + CHECK(buffer->meta()->findObject("accessUnitInfo", &obj)); + sp bufferInfos{(BufferInfosWrapper *)obj.get()}; + if (secure || (mCrypto == nullptr)) { + if (cryptoInfos->value.size() != 1) { + ALOGE("Cannot decrypt multiple access units"); + return -ENOSYS; + } + // we are dealing with just one cryptoInfo or descrambler. + std::unique_ptr info = std::move(cryptoInfos->value[0]); + if (info == nullptr) { + ALOGE("Cannot decrypt, CryptoInfos are null."); + return -ENOSYS; + } + return attachEncryptedBuffer( + memory, + secure, + info->mKey, + info->mIv, + info->mMode, + info->mPattern, + offset, + info->mSubSamples, + info->mNumSubSamples, + buffer, + errorDetailMsg); + } + std::shared_ptr pool = mBlockPools.lock()->inputPool; + std::shared_ptr block; + c2_status_t err = pool->fetchLinearBlock( + size, + kDefaultReadWriteUsage, + &block); + if (err != C2_OK) { + ALOGI("[%s] attachEncryptedBuffers: fetchLinearBlock failed: size = %zu (%s) err = %d", + mName, size, secure ? "secure" : "non-secure", err); + return NO_MEMORY; + } + ensureDecryptDestination(size); + C2WriteView wView = block->map().get(); + if (wView.error() != C2_OK) { + ALOGI("[%s] attachEncryptedBuffers: block map error: %d (non-secure)", + mName, wView.error()); + return UNKNOWN_ERROR; + } + + ssize_t result = -1; + ssize_t codecDataOffset = 0; + size_t inBufferOffset = 0; + size_t outBufferSize = 0; + uint32_t cryptoInfoIdx = 0; + int32_t heapSeqNum = getHeapSeqNum(memory); + hardware::drm::V1_0::SharedBuffer src{(uint32_t)heapSeqNum, offset, size}; + hardware::drm::V1_0::DestinationBuffer dst; + dst.type = DrmBufferType::SHARED_MEMORY; + IMemoryToSharedBuffer( + mDecryptDestination, mHeapSeqNum, &dst.nonsecureMemory); + for (int i = 0; i < bufferInfos->value.size(); i++) { + if (bufferInfos->value[i].mSize > 0) { + std::unique_ptr info = std::move(cryptoInfos->value[cryptoInfoIdx++]); + result = mCrypto->decrypt( + (uint8_t*)info->mKey, + (uint8_t*)info->mIv, + info->mMode, + info->mPattern, + src, + inBufferOffset, + info->mSubSamples, + info->mNumSubSamples, + dst, + errorDetailMsg); + inBufferOffset += bufferInfos->value[i].mSize; + if (result < 0) { + ALOGI("[%s] attachEncryptedBuffers: decrypt failed: result = %zd", + mName, result); + return result; + } + if (wView.error() == C2_OK) { + if (wView.size() < result) { + ALOGI("[%s] attachEncryptedBuffers: block size too small:" + "size=%u result=%zd (non-secure)", mName, wView.size(), result); + return UNKNOWN_ERROR; + } + memcpy(wView.data(), mDecryptDestination->unsecurePointer(), result); + bufferInfos->value[i].mSize = result; + wView.setOffset(wView.offset() + result); + } + outBufferSize += result; + } + } + if (wView.error() == C2_OK) { + wView.setOffset(0); + } + std::shared_ptr c2Buffer{C2Buffer::CreateLinearBuffer( + block->share(codecDataOffset, outBufferSize - codecDataOffset, C2Fence{}))}; + if (!buffer->copy(c2Buffer)) { + ALOGI("[%s] attachEncryptedBuffers: buffer copy failed", mName); + return -ENOSYS; + } + return OK; +} + status_t CCodecBufferChannel::attachEncryptedBuffer( const sp &memory, bool secure, @@ -777,6 +901,138 @@ status_t CCodecBufferChannel::queueSecureInputBuffer( return queueInputBufferInternal(buffer, block, bufferSize); } +status_t CCodecBufferChannel::queueSecureInputBuffers( + const sp &buffer, + bool secure, + AString *errorDetailMsg) { + QueueGuard guard(mSync); + if (!guard.isRunning()) { + ALOGD("[%s] No more buffers should be queued at current state.", mName); + return -ENOSYS; + } + + if (!hasCryptoOrDescrambler()) { + ALOGE("queueSecureInputBuffers requires a Crypto/descrambler Object"); + return -ENOSYS; + } + sp obj; + CHECK(buffer->meta()->findObject("cryptoInfos", &obj)); + sp cryptoInfos{(CryptoInfosWrapper *)obj.get()}; + CHECK(buffer->meta()->findObject("accessUnitInfo", &obj)); + sp bufferInfos{(BufferInfosWrapper *)obj.get()}; + if (secure || mCrypto == nullptr) { + if (cryptoInfos->value.size() != 1) { + ALOGE("Cannot decrypt multiple access units on native handles"); + return -ENOSYS; + } + std::unique_ptr info = std::move(cryptoInfos->value[0]); + if (info == nullptr) { + ALOGE("Cannot decrypt, CryptoInfos are null"); + return -ENOSYS; + } + return queueSecureInputBuffer( + buffer, + secure, + info->mKey, + info->mIv, + info->mMode, + info->mPattern, + info->mSubSamples, + info->mNumSubSamples, + errorDetailMsg); + } + sp encryptedBuffer((EncryptedLinearBlockBuffer *)buffer.get()); + + std::shared_ptr block; + size_t allocSize = buffer->size(); + size_t bufferSize = 0; + c2_status_t blockRes = C2_OK; + bool copied = false; + ScopedTrace trace(ATRACE_TAG, android::base::StringPrintf( + "CCodecBufferChannel::decrypt(%s)", mName).c_str()); + if (mSendEncryptedInfoBuffer) { + static const C2MemoryUsage kDefaultReadWriteUsage{ + C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + constexpr int kAllocGranule0 = 1024 * 64; + constexpr int kAllocGranule1 = 1024 * 1024; + std::shared_ptr pool = mBlockPools.lock()->inputPool; + // round up encrypted sizes to limit fragmentation and encourage buffer reuse + if (allocSize <= kAllocGranule1) { + bufferSize = align(allocSize, kAllocGranule0); + } else { + bufferSize = align(allocSize, kAllocGranule1); + } + blockRes = pool->fetchLinearBlock( + bufferSize, kDefaultReadWriteUsage, &block); + + if (blockRes == C2_OK) { + C2WriteView view = block->map().get(); + if (view.error() == C2_OK && view.size() == bufferSize) { + copied = true; + // TODO: only copy clear sections + memcpy(view.data(), buffer->data(), allocSize); + } + } + } + + if (!copied) { + block.reset(); + } + // size of cryptoInfo and accessUnitInfo should be the same? + ssize_t result = -1; + ssize_t codecDataOffset = 0; + size_t inBufferOffset = 0; + size_t outBufferSize = 0; + uint32_t cryptoInfoIdx = 0; + { + // scoped this block to enable destruction of mappedBlock + std::unique_ptr mappedBlock = nullptr; + hardware::drm::V1_0::DestinationBuffer destination; + destination.type = DrmBufferType::SHARED_MEMORY; + IMemoryToSharedBuffer( + mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory); + encryptedBuffer->getMappedBlock(&mappedBlock); + hardware::drm::V1_0::SharedBuffer source; + encryptedBuffer->fillSourceBuffer(&source); + for (int i = 0 ; i < bufferInfos->value.size(); i++) { + if (bufferInfos->value[i].mSize > 0) { + std::unique_ptr info = + std::move(cryptoInfos->value[cryptoInfoIdx++]); + if (info->mNumSubSamples == 1 + && info->mSubSamples[0].mNumBytesOfClearData == 0 + && info->mSubSamples[0].mNumBytesOfEncryptedData == 0) { + // no data so we only populate the bufferInfo + result = 0; + } else { + result = mCrypto->decrypt( + (uint8_t*)info->mKey, + (uint8_t*)info->mIv, + info->mMode, + info->mPattern, + source, + inBufferOffset, + info->mSubSamples, + info->mNumSubSamples, + destination, + errorDetailMsg); + inBufferOffset += bufferInfos->value[i].mSize; + if (result < 0) { + ALOGI("[%s] decrypt failed: result=%zd", mName, result); + return result; + } + if (destination.type == DrmBufferType::SHARED_MEMORY && mappedBlock) { + mappedBlock->copyDecryptedContent(mDecryptDestination, result); + } + bufferInfos->value[i].mSize = result; + outBufferSize += result; + } + } + } + buffer->setRange(codecDataOffset, outBufferSize - codecDataOffset); + } + return queueInputBufferInternal(buffer, block, bufferSize); +} + void CCodecBufferChannel::feedInputBufferIfAvailable() { QueueGuard guard(mSync); if (!guard.isRunning()) { diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 8dc9fb6984..b47065529b 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -73,6 +73,10 @@ class CCodecBufferChannel const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, AString *errorDetailMsg) override; + status_t queueSecureInputBuffers( + const sp &buffer, + bool secure, + AString *errorDetailMsg) override; status_t attachBuffer( const std::shared_ptr &c2Buffer, const sp &buffer) override; @@ -88,6 +92,12 @@ class CCodecBufferChannel size_t numSubSamples, const sp &buffer, AString* errorDetailMsg) override; + status_t attachEncryptedBuffers( + const sp &memory, + size_t offset, + const sp &buffer, + bool secure, + AString* errorDetailMsg) override; status_t renderOutputBuffer( const sp &buffer, int64_t timestampNs) override; void pollForRenderedBuffers() override; diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 4f466c5330..9c514f2eb3 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -1036,6 +1036,37 @@ native_handle_t *EncryptedLinearBlockBuffer::handle() const { return const_cast(mBlock->handle()); } +void EncryptedLinearBlockBuffer::getMappedBlock( + std::unique_ptr * const mappedBlock) const { + if (mappedBlock) { + mappedBlock->reset(new EncryptedLinearBlockBuffer::MappedBlock(mBlock)); + } + return; +} + +EncryptedLinearBlockBuffer::MappedBlock::MappedBlock( + const std::shared_ptr &block) : mView(block->map().get()) { +} + +bool EncryptedLinearBlockBuffer::MappedBlock::copyDecryptedContent( + const sp &decrypted, size_t length) { + if (mView.error() != C2_OK) { + return false; + } + if (mView.size() < length) { + ALOGE("View size(%d) less than decrypted length(%zu)", + mView.size(), length); + return false; + } + memcpy(mView.data(), decrypted->unsecurePointer(), length); + mView.setOffset(mView.offset() + length); + return true; +} + +EncryptedLinearBlockBuffer::MappedBlock::~MappedBlock() { + mView.setOffset(0); +} + using ::aidl::android::hardware::graphics::common::Cta861_3; using ::aidl::android::hardware::graphics::common::Smpte2086; diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h index b73acab69c..5e969219c2 100644 --- a/media/codec2/sfplugin/Codec2Buffer.h +++ b/media/codec2/sfplugin/Codec2Buffer.h @@ -384,6 +384,17 @@ class EncryptedLinearBlockBuffer : public Codec2Buffer { */ native_handle_t *handle() const; + class MappedBlock { + public: + explicit MappedBlock(const std::shared_ptr &block); + virtual ~MappedBlock(); + bool copyDecryptedContent(const sp &decrypted, size_t length); + private: + C2WriteView mView; + }; + + void getMappedBlock(std::unique_ptr * const mappedBlock) const; + private: std::shared_ptr mBlock; diff --git a/media/libstagefright/CryptoAsync.cpp b/media/libstagefright/CryptoAsync.cpp index 8b5c8ed36b..0fc78ec195 100644 --- a/media/libstagefright/CryptoAsync.cpp +++ b/media/libstagefright/CryptoAsync.cpp @@ -30,6 +30,36 @@ namespace android { +CryptoAsync::CryptoAsyncInfo::CryptoAsyncInfo(const std::unique_ptr &info) { + if (info == nullptr) { + return; + } + size_t key_len = (info->mKey != nullptr)? 16 : 0; + size_t iv_len = (info->mIv != nullptr)? 16 : 0; + mNumSubSamples = info->mNumSubSamples; + mMode = info->mMode; + mPattern = info->mPattern; + if (key_len > 0) { + mKeyBuffer = ABuffer::CreateAsCopy((void*)info->mKey, key_len); + mKey = (uint8_t*)(mKeyBuffer.get() != nullptr ? mKeyBuffer.get()->data() : nullptr); + } + if (iv_len > 0) { + mIvBuffer = ABuffer::CreateAsCopy((void*)info->mIv, iv_len); + mIv = (uint8_t*)(mIvBuffer.get() != nullptr ? mIvBuffer.get()->data() : nullptr); + } + mSubSamplesBuffer = + new ABuffer(sizeof(CryptoPlugin::SubSample) * mNumSubSamples); + if (mSubSamplesBuffer.get()) { + CryptoPlugin::SubSample * samples = + (CryptoPlugin::SubSample *)(mSubSamplesBuffer.get()->data()); + for (int s = 0 ; s < mNumSubSamples ; s++) { + samples[s].mNumBytesOfClearData = info->mSubSamples[s].mNumBytesOfClearData; + samples[s].mNumBytesOfEncryptedData = info->mSubSamples[s].mNumBytesOfEncryptedData; + } + mSubSamples = (CryptoPlugin::SubSample *)mSubSamplesBuffer.get()->data(); + } +} + CryptoAsync::~CryptoAsync() { } @@ -79,23 +109,27 @@ status_t CryptoAsync::decryptAndQueue(sp & msg) { sp keyBuffer; sp ivBuffer; sp subSamplesBuffer; - msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks); - msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks); - msg->findBuffer("key", &keyBuffer); - msg->findBuffer("iv", &ivBuffer); - msg->findBuffer("subSamples", &subSamplesBuffer); - msg->findInt32("secure", &secure); - msg->findSize("numSubSamples", &numSubSamples); - msg->findObject("buffer", &obj); - msg->findInt32("mode", (int32_t*)&mode); AString errorDetailMsg; - const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr; - const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr; - const CryptoPlugin::SubSample * subSamples = - (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data()); + msg->findObject("buffer", &obj); + msg->findInt32("secure", &secure); sp buffer = static_cast(obj.get()); - err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode, - pattern, subSamples, numSubSamples, &errorDetailMsg); + if (buffer->meta()->findObject("cryptoInfos", &obj)) { + err = channel->queueSecureInputBuffers(buffer, secure, &errorDetailMsg); + } else { + msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks); + msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks); + msg->findBuffer("key", &keyBuffer); + msg->findBuffer("iv", &ivBuffer); + msg->findBuffer("subSamples", &subSamplesBuffer); + msg->findSize("numSubSamples", &numSubSamples); + msg->findInt32("mode", (int32_t*)&mode); + const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr; + const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr; + const CryptoPlugin::SubSample * subSamples = + (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data()); + err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode, + pattern, subSamples, numSubSamples, &errorDetailMsg); + } if (err != OK) { std::list> errorList; msg->removeEntryByName("buffer"); diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 770da65c7b..305d42fda7 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -300,7 +300,6 @@ static status_t generateFlagsFromAccessUnitInfo( return -EINVAL; } msg->setInt32("flags", bufferFlags); - msg->setObject("accessUnitInfo", bufferInfos); } return OK; } @@ -3299,6 +3298,58 @@ status_t MediaCodec::queueSecureInputBuffer( return err; } +status_t MediaCodec::queueSecureInputBuffers( + size_t index, + size_t offset, + size_t size, + const sp &auInfo, + const sp &cryptoInfos, + AString *errorDetailMsg) { + if (errorDetailMsg != NULL) { + errorDetailMsg->clear(); + } + sp msg = new AMessage(kWhatQueueInputBuffer, this); + uint32_t bufferFlags = 0; + uint32_t flagsinAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODECCONFIG; + uint32_t andFlags = flagsinAllAU; + if (auInfo == nullptr + || auInfo->value.empty() + || cryptoInfos == nullptr + || cryptoInfos->value.empty()) { + ALOGE("ERROR: Large Audio frame with no BufferInfo/CryptoInfo"); + return BAD_VALUE; + } + int infoIdx = 0; + std::vector &accessUnitInfo = auInfo->value; + int64_t minTimeUs = accessUnitInfo.front().mTimestamp; + bool foundEndOfStream = false; + for ( ; infoIdx < accessUnitInfo.size() && !foundEndOfStream; ++infoIdx) { + bufferFlags |= accessUnitInfo[infoIdx].mFlags; + andFlags &= accessUnitInfo[infoIdx].mFlags; + if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) { + foundEndOfStream = true; + } + } + bufferFlags = bufferFlags & (andFlags | (~flagsinAllAU)); + if (infoIdx != accessUnitInfo.size()) { + ALOGE("queueInputBuffers has incorrect access-units"); + return -EINVAL; + } + msg->setSize("index", index); + msg->setSize("offset", offset); + msg->setSize("ssize", size); + msg->setInt64("timeUs", minTimeUs); + msg->setInt32("flags", bufferFlags); + msg->setObject("accessUnitInfo", auInfo); + msg->setObject("cryptoInfos", cryptoInfos); + msg->setPointer("errorDetailMsg", errorDetailMsg); + + sp response; + status_t err = PostAndAwaitResponse(msg, &response); + + return err; +} + status_t MediaCodec::queueBuffer( size_t index, const std::shared_ptr &buffer, @@ -3320,6 +3371,7 @@ status_t MediaCodec::queueBuffer( if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) { return err; } + msg->setObject("accessUnitInfo", bufferInfos); if (tunings && tunings->countEntries() > 0) { msg->setMessage("tunings", tunings); } @@ -3334,13 +3386,9 @@ status_t MediaCodec::queueEncryptedBuffer( size_t index, const sp &buffer, size_t offset, - const CryptoPlugin::SubSample *subSamples, - size_t numSubSamples, - const uint8_t key[16], - const uint8_t iv[16], - CryptoPlugin::Mode mode, - const CryptoPlugin::Pattern &pattern, + size_t size, const sp &bufferInfos, + const sp &cryptoInfos, const sp &tunings, AString *errorDetailMsg) { if (errorDetailMsg != NULL) { @@ -3349,6 +3397,9 @@ status_t MediaCodec::queueEncryptedBuffer( if (bufferInfos == nullptr || bufferInfos->value.empty()) { return BAD_VALUE; } + if (cryptoInfos == nullptr || cryptoInfos->value.empty()) { + return BAD_VALUE; + } status_t err = OK; sp msg = new AMessage(kWhatQueueInputBuffer, this); msg->setSize("index", index); @@ -3356,13 +3407,9 @@ status_t MediaCodec::queueEncryptedBuffer( new WrapperObject>{buffer}}; msg->setObject("memory", memory); msg->setSize("offset", offset); - msg->setPointer("subSamples", (void *)subSamples); - msg->setSize("numSubSamples", numSubSamples); - msg->setPointer("key", (void *)key); - msg->setPointer("iv", (void *)iv); - msg->setInt32("mode", mode); - msg->setInt32("encryptBlocks", pattern.mEncryptBlocks); - msg->setInt32("skipBlocks", pattern.mSkipBlocks); + msg->setSize("ssize", size); + msg->setObject("cryptoInfos", cryptoInfos); + msg->setObject("accessUnitInfo", bufferInfos); if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) { return err; } @@ -6072,22 +6119,26 @@ status_t MediaCodec::onQueueInputBuffer(const sp &msg) { mErrorLog.log(LOG_TAG, "queuing secure buffer without mCrypto or mDescrambler!"); return -EINVAL; } - CHECK(msg->findPointer("subSamples", (void **)&subSamples)); - CHECK(msg->findSize("numSubSamples", &numSubSamples)); - CHECK(msg->findPointer("key", (void **)&key)); - CHECK(msg->findPointer("iv", (void **)&iv)); - CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks)); - CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks)); - - int32_t tmp; - 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; + sp obj; + if (msg->findObject("cryptoInfos", &obj)) { + CHECK(msg->findSize("ssize", &size)); + } else { + CHECK(msg->findPointer("subSamples", (void **)&subSamples)); + CHECK(msg->findSize("numSubSamples", &numSubSamples)); + CHECK(msg->findPointer("key", (void **)&key)); + CHECK(msg->findPointer("iv", (void **)&iv)); + CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks)); + CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks)); + + int32_t tmp; + 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; + } } } @@ -6114,7 +6165,7 @@ status_t MediaCodec::onQueueInputBuffer(const sp &msg) { status_t err = OK; sp obj; if (msg->findObject("accessUnitInfo", &obj)) { - buffer->meta()->setObject("accessUnitInfo", obj); + buffer->meta()->setObject("accessUnitInfo", obj); } buffer->meta()->setInt64("timeUs", timeUs); if (flags & BUFFER_FLAG_EOS) { @@ -6152,35 +6203,48 @@ status_t MediaCodec::onQueueInputBuffer(const sp &msg) { return err; }; auto buildCryptoInfoAMessage = [&](const sp & cryptoInfo, int32_t action) { - size_t key_len = (key != nullptr)? 16 : 0; - size_t iv_len = (iv != nullptr)? 16 : 0; - sp shared_key; - sp shared_iv; - if (key_len > 0) { - shared_key = ABuffer::CreateAsCopy((void*)key, key_len); - } - if (iv_len > 0) { - shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len); - } - sp subSamples_buffer = - new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples); - CryptoPlugin::SubSample * samples = - (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data()); - for (int s = 0 ; s < numSubSamples ; s++) { - samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData; - samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData; - } // set decrypt Action cryptoInfo->setInt32("action", action); cryptoInfo->setObject("buffer", buffer); cryptoInfo->setInt32("secure", mFlags & kFlagIsSecure); - cryptoInfo->setBuffer("key", shared_key); - cryptoInfo->setBuffer("iv", shared_iv); - cryptoInfo->setInt32("mode", (int)mode); - cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks); - cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks); - cryptoInfo->setBuffer("subSamples", subSamples_buffer); - cryptoInfo->setSize("numSubSamples", numSubSamples); + sp obj; + if (msg->findObject("cryptoInfos", &obj)) { + sp infos{(CryptoInfosWrapper*)obj.get()}; + sp asyncInfos{ + new CryptoInfosWrapper(std::vector>())}; + for (std::unique_ptr &info : infos->value) { + if (info) { + asyncInfos->value.emplace_back(new CryptoAsync::CryptoAsyncInfo(info)); + } + } + buffer->meta()->setObject("cryptoInfos", asyncInfos); + } else { + size_t key_len = (key != nullptr)? 16 : 0; + size_t iv_len = (iv != nullptr)? 16 : 0; + sp shared_key; + sp shared_iv; + if (key_len > 0) { + shared_key = ABuffer::CreateAsCopy((void*)key, key_len); + } + if (iv_len > 0) { + shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len); + } + sp subSamples_buffer = + new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples); + CryptoPlugin::SubSample * samples = + (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data()); + for (int s = 0 ; s < numSubSamples ; s++) { + samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData; + samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData; + } + cryptoInfo->setBuffer("key", shared_key); + cryptoInfo->setBuffer("iv", shared_iv); + cryptoInfo->setInt32("mode", (int)mode); + cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks); + cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks); + cryptoInfo->setBuffer("subSamples", subSamples_buffer); + cryptoInfo->setSize("numSubSamples", numSubSamples); + } }; if (c2Buffer || memory) { sp tunings = NULL; @@ -6190,15 +6254,37 @@ status_t MediaCodec::onQueueInputBuffer(const sp &msg) { status_t err = OK; if (c2Buffer) { err = mBufferChannel->attachBuffer(c2Buffer, buffer); + // to prevent unnecessary copy for single info case. + if (msg->findObject("accessUnitInfo", &obj)) { + sp infos{(BufferInfosWrapper*)(obj.get())}; + if (infos->value.size() == 1) { + msg->removeEntryByName("accessUnitInfo"); + } + } } else if (memory) { AString errorDetailMsg; - err = mBufferChannel->attachEncryptedBuffer( - memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern, - offset, subSamples, numSubSamples, buffer, &errorDetailMsg); + if (msg->findObject("cryptoInfos", &obj)) { + buffer->meta()->setSize("ssize", size); + buffer->meta()->setObject("cryptoInfos", obj); + if (msg->findObject("accessUnitInfo", &obj)) { + // the reference will be same here and + // setBufferParams + buffer->meta()->setObject("accessUnitInfo", obj); + } + err = mBufferChannel->attachEncryptedBuffers( + memory, + offset, + buffer, + (mFlags & kFlagIsSecure), + &errorDetailMsg); + } else { + err = mBufferChannel->attachEncryptedBuffer( + memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern, + offset, subSamples, numSubSamples, buffer, &errorDetailMsg); + } if (err != OK && hasCryptoOrDescrambler() && (mFlags & kFlagUseCryptoAsync)) { // create error detail - AString errorDetailMsg; sp cryptoErrorInfo = new AMessage(); buildCryptoInfoAMessage(cryptoErrorInfo, CryptoAsync::kActionDecrypt); cryptoErrorInfo->setInt32("err", err); @@ -6270,10 +6356,17 @@ status_t MediaCodec::onQueueInputBuffer(const sp &msg) { } } if (mCryptoAsync) { + // TODO b/316565675 - enable async path for audio // prepare a message and enqueue sp cryptoInfo = new AMessage(); buildCryptoInfoAMessage(cryptoInfo, CryptoAsync::kActionDecrypt); mCryptoAsync->decrypt(cryptoInfo); + } else if (msg->findObject("cryptoInfos", &obj)) { + buffer->meta()->setObject("cryptoInfos", obj); + err = mBufferChannel->queueSecureInputBuffers( + buffer, + (mFlags & kFlagIsSecure), + errorDetailMsg); } else { err = mBufferChannel->queueSecureInputBuffer( buffer, @@ -6647,7 +6740,7 @@ void MediaCodec::onOutputBufferAvailable() { if (accessUnitInfoObj) { outputCallbackID = CB_LARGE_FRAME_OUTPUT_AVAILABLE; msg->setObject("accessUnitInfo", accessUnitInfoObj); - sp auInfo( + sp auInfo( (decltype(auInfo.get()))accessUnitInfoObj.get()); auInfo->value.back().mFlags |= flags & BUFFER_FLAG_END_OF_STREAM; } diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h index 8741daa0b2..bffb29488d 100644 --- a/media/libstagefright/include/media/stagefright/CodecBase.h +++ b/media/libstagefright/include/media/stagefright/CodecBase.h @@ -71,6 +71,26 @@ struct AccessUnitInfo { ~AccessUnitInfo() {} }; +struct CodecCryptoInfo { + size_t mNumSubSamples{0}; + CryptoPlugin::SubSample *mSubSamples{nullptr}; + uint8_t *mIv{nullptr}; + uint8_t *mKey{nullptr}; + enum CryptoPlugin::Mode mMode; + CryptoPlugin::Pattern mPattern; + + virtual ~CodecCryptoInfo() {} +protected: + CodecCryptoInfo(): + mNumSubSamples(0), + mSubSamples(nullptr), + mIv(nullptr), + mKey(nullptr), + mMode{CryptoPlugin::kMode_Unencrypted}, + mPattern{0, 0} { + } +}; + struct CodecParameterDescriptor { std::string name; AMessage::Type type; @@ -372,6 +392,30 @@ class BufferChannelBase { const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, AString *errorDetailMsg) = 0; + + /** + * Queue a secure input buffer with multiple access units into the buffer channel. + * + * @param buffer The buffer to queue. The access unit delimiters and crypto + * subsample information is included in the buffer metadata. + * @param secure Whether the buffer is secure. + * @param errorDetailMsg The error message to be set in case of error. + * @return OK if successful; + * -ENOENT of the buffer is not known + * -ENOSYS if mCrypto is not set so that decryption is not + * possible; + * other errors if decryption failed. + */ + virtual status_t queueSecureInputBuffers( + const sp &buffer, + bool secure, + AString *errorDetailMsg) { + (void)buffer; + (void)secure; + (void)errorDetailMsg; + return -ENOSYS; + } + /** * Attach a Codec 2.0 buffer to MediaCodecBuffer. * @@ -418,6 +462,34 @@ class BufferChannelBase { (void)errorDetailMsg; return -ENOSYS; } + + /** + * Attach an encrypted HidlMemory buffer containing multiple access units to an index + * + * @param memory The memory to attach. + * @param offset index??? + * @param buffer The MediaCodecBuffer to attach the memory to. The access + * unit delimiters and crypto subsample information is included + * in the buffer metadata. + * @param secure Whether the buffer is secure. + * @param errorDetailMsg The error message to be set if an error occurs. + * @return OK if successful; + * -ENOENT if index is not recognized + * -ENOSYS if attaching buffer is not possible or not supported + */ + virtual status_t attachEncryptedBuffers( + const sp &memory, + size_t offset, + const sp &buffer, + bool secure, + AString* errorDetailMsg) { + (void)memory; + (void)offset; + (void)buffer; + (void)secure; + (void)errorDetailMsg; + return -ENOSYS; + } /** * Request buffer rendering at specified time. * diff --git a/media/libstagefright/include/media/stagefright/CryptoAsync.h b/media/libstagefright/include/media/stagefright/CryptoAsync.h index b675518d01..acb3daef51 100644 --- a/media/libstagefright/include/media/stagefright/CryptoAsync.h +++ b/media/libstagefright/include/media/stagefright/CryptoAsync.h @@ -85,6 +85,18 @@ class CryptoAsync: public AHandler { kActionDecrypt = (1 << 0), kActionAttachEncryptedBuffer = (1 << 1) }; + + // This struct is meant to copy the mapped contents from the original info. + struct CryptoAsyncInfo : public CodecCryptoInfo { + public: + explicit CryptoAsyncInfo(const std::unique_ptr &info); + virtual ~CryptoAsyncInfo() = default; + protected: + // all backup buffers for the base object. + sp mKeyBuffer; + sp mIvBuffer; + sp mSubSamplesBuffer; + }; protected: // Message types for the looper diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h index b0b1427e73..9ecb12e946 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodec.h +++ b/media/libstagefright/include/media/stagefright/MediaCodec.h @@ -59,6 +59,7 @@ struct BatteryChecker; class BufferChannelBase; struct AccessUnitInfo; struct CodecBase; +struct CodecCryptoInfo; struct CodecParameterDescriptor; class IBatteryStats; struct ICrypto; @@ -81,6 +82,7 @@ using aidl::android::media::MediaResourceParcel; using aidl::android::media::ClientConfigParcel; typedef WrapperObject> BufferInfosWrapper; +typedef WrapperObject>> CryptoInfosWrapper; struct MediaCodec : public AHandler { enum Domain { @@ -210,6 +212,14 @@ struct MediaCodec : public AHandler { uint32_t flags, AString *errorDetailMsg = NULL); + status_t queueSecureInputBuffers( + size_t index, + size_t offset, + size_t size, + const sp &accessUnitInfo, + const sp &cryptoInfos, + AString *errorDetailMsg = NULL); + status_t queueBuffer( size_t index, const std::shared_ptr &buffer, @@ -221,13 +231,9 @@ struct MediaCodec : public AHandler { size_t index, const sp &memory, size_t offset, - const CryptoPlugin::SubSample *subSamples, - size_t numSubSamples, - const uint8_t key[16], - const uint8_t iv[16], - CryptoPlugin::Mode mode, - const CryptoPlugin::Pattern &pattern, + size_t size, const sp &bufferInfos, + const sp &cryptoInfos, const sp &tunings, AString *errorDetailMsg = NULL);