Implementing CryptoAsync
Decryption is done in a seperate thread when configured
with CONFIGURE_FLAG_USE_CRYPTO_ASYNC
Bug: 254050543
Change-Id: Ib192a5da27f28335b3ed00025b0084e99a511e9c
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 8c469df..171be08 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -237,6 +237,7 @@
"CallbackMediaSource.cpp",
"CameraSource.cpp",
"CameraSourceTimeLapse.cpp",
+ "CryptoAsync.cpp",
"FrameDecoder.cpp",
"HevcUtils.cpp",
"InterfaceUtils.cpp",
diff --git a/media/libstagefright/CryptoAsync.cpp b/media/libstagefright/CryptoAsync.cpp
new file mode 100644
index 0000000..32fd3be
--- /dev/null
+++ b/media/libstagefright/CryptoAsync.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2022 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 "CryptoAsync"
+
+#include <log/log.h>
+
+#include "hidl/HidlSupport.h"
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <media/MediaCodecBuffer.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/CryptoAsync.h>
+
+namespace android {
+
+CryptoAsync::~CryptoAsync() {
+}
+
+status_t CryptoAsync::decrypt(sp<AMessage> &msg) {
+ int32_t decryptAction;
+ CHECK(msg->findInt32("action", &decryptAction));
+ if (mCallback == nullptr) {
+ ALOGE("Crypto callback channel is not set");
+ return -ENOSYS;
+ }
+ bool shouldPost = false;
+ Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
+ if (mState != kCryptoAsyncActive) {
+ ALOGE("Cannot decrypt in errored state");
+ return -ENOSYS;
+ }
+ shouldPost = pendingBuffers->size() == 0 ? true : false;
+ pendingBuffers->push_back(std::move(msg));
+ if (shouldPost) {
+ sp<AMessage> decryptMsg = new AMessage(kWhatDecrypt, this);
+ decryptMsg->post();
+ }
+ return OK;
+}
+
+void CryptoAsync::stop(std::list<sp<AMessage>> * const buffers) {
+ sp<AMessage> stopMsg = new AMessage(kWhatStop, this);
+ stopMsg->setPointer("remaining", static_cast<void*>(buffers));
+ sp<AMessage> response;
+ status_t err = stopMsg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ } else {
+ ALOGE("Error handling stop in CryptoAsync");
+ //TODO: handle the error here.
+ }
+}
+
+status_t CryptoAsync::decryptAndQueue(sp<AMessage> & msg) {
+ std::shared_ptr<BufferChannelBase> channel = mBufferChannel.lock();
+ status_t err = OK;
+ sp<RefBase> obj;
+ size_t numSubSamples = 0;
+ int32_t secure = 0;
+ CryptoPlugin::Mode mode;
+ CryptoPlugin::Pattern pattern;
+ sp<ABuffer> keyBuffer;
+ sp<ABuffer> ivBuffer;
+ sp<ABuffer> 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());
+ sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
+ err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode,
+ pattern, subSamples, numSubSamples, &errorDetailMsg);
+ if (err != OK) {
+ std::list<sp<AMessage>> errorList;
+ msg->removeEntryByName("buffer");
+ msg->setInt32("err", err);
+ msg->setInt32("actionCode", ACTION_CODE_FATAL);
+ msg->setString("errorDetail", errorDetailMsg);
+ errorList.push_back(std::move(msg));
+ mCallback->onDecryptError(errorList);
+ }
+ return err;
+}
+
+status_t CryptoAsync::attachEncryptedBufferAndQueue(sp<AMessage> & msg) {
+ std::shared_ptr<BufferChannelBase> channel = mBufferChannel.lock();
+ status_t err = OK;
+ sp<RefBase> obj;
+ sp<RefBase> mem_obj;
+ sp<hardware::HidlMemory> memory;
+ size_t numSubSamples = 0;
+ int32_t secure = 0;
+ size_t offset;
+ size_t size;
+ CryptoPlugin::Mode mode;
+ CryptoPlugin::Pattern pattern;
+ sp<ABuffer> keyBuffer;
+ sp<ABuffer> ivBuffer;
+ sp<ABuffer> 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);
+ CHECK(msg->findObject("memory", &mem_obj));
+ CHECK(msg->findSize("offset", (size_t*)&offset));
+ AString errorDetailMsg;
+ // get key info
+ const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr;
+ // get iv info
+ const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr;
+
+ const CryptoPlugin::SubSample * subSamples =
+ (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data());
+
+ // get MediaCodecBuffer
+ sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
+
+ // get HidlMemory
+ memory = static_cast<MediaCodec::WrapperObject<sp<hardware::HidlMemory>> *>
+ (mem_obj.get())->value;
+
+ // attach buffer
+ err = channel->attachEncryptedBuffer(
+ memory, secure, key, iv, mode, pattern,
+ offset, subSamples, numSubSamples, buffer);
+
+ // a generic error
+ auto handleError = [this, &err, &msg]() {
+ std::list<sp<AMessage>> errorList;
+ msg->removeEntryByName("buffer");
+ msg->setInt32("err", err);
+ msg->setInt32("actionCode", ACTION_CODE_FATAL);
+ errorList.push_back(std::move(msg));
+ mCallback->onDecryptError(errorList);
+ };
+ if (err != OK) {
+ handleError();
+ return err;
+ }
+ offset = buffer->offset();
+ size = buffer->size();
+
+ if (offset + size > buffer->capacity()) {
+ err = -ENOSYS;
+ handleError();
+ return err;
+ }
+ buffer->setRange(offset, size);
+ err = channel->queueInputBuffer(buffer);
+ if (err != OK) {
+ handleError();
+ return err;
+ }
+ return err;
+}
+
+void CryptoAsync::onMessageReceived(const sp<AMessage> & msg) {
+ status_t err = OK;
+ auto getCurrentAndNextTask =
+ [this](sp<AMessage> * const current, uint32_t & nextTask) -> status_t {
+ sp<AMessage> obj;
+ Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
+ if ((pendingBuffers->size() == 0) || (mState != kCryptoAsyncActive)) {
+ return -ENOMSG;
+ }
+ *current = std::move(*(pendingBuffers->begin()));
+ pendingBuffers->pop_front();
+ //Try to see if we will be able to process next buffer
+ while((nextTask == kWhatDoNothing) && pendingBuffers->size() > 0)
+ {
+ sp<AMessage> & nextBuffer = pendingBuffers->front();
+ if (nextBuffer == nullptr) {
+ pendingBuffers->pop_front();
+ continue;
+ }
+ nextTask = kWhatDecrypt;
+ }
+ return OK;
+ };
+ switch(msg->what()) {
+ case kWhatDecrypt:
+ {
+ sp<AMessage> thisMsg;
+ uint32_t nextTask = kWhatDoNothing;
+ if(OK != getCurrentAndNextTask(&thisMsg, nextTask)) {
+ return;
+ }
+ if (thisMsg != nullptr) {
+ int32_t action;
+ err = OK;
+ CHECK(thisMsg->findInt32("action", &action));
+ switch(action) {
+ case kActionDecrypt:
+ {
+ err = decryptAndQueue(thisMsg);
+ break;
+ }
+
+ case kActionAttachEncryptedBuffer:
+ {
+ err = attachEncryptedBufferAndQueue(thisMsg);
+ break;
+ }
+
+ default:
+ {
+ ALOGE("Unrecognized action in decrypt");
+ }
+ }
+ if (err != OK) {
+ Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
+ mState = kCryptoAsyncError;
+ }
+ }
+ // we won't take next buffers if buffer caused
+ // an error. We want the caller to deal with the error first
+ // Expected behahiour is that the caller acknowledge the error
+ // with a call to stop() which clear the queues.
+ // Then move forward with processing of next set of buffers.
+ if (mState == kCryptoAsyncActive && nextTask != kWhatDoNothing) {
+ sp<AMessage> nextMsg = new AMessage(nextTask,this);
+ nextMsg->post();
+ }
+ break;
+ }
+
+ case kWhatStop:
+ {
+ typedef std::list<sp<AMessage>> ReturnListType;
+ ReturnListType * returnList = nullptr;
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ sp<AMessage> response = new AMessage;
+ msg->findPointer("remaining", (void**)(&returnList));
+ Mutexed<std::list<sp<AMessage>>>::Locked pendingBuffers(mPendingBuffers);
+ if (returnList) {
+ returnList->clear();
+ returnList->splice(returnList->end(), std::move(*pendingBuffers));
+ }
+ pendingBuffers->clear();
+ mState = kCryptoAsyncActive;
+ response->setInt32("err", OK);
+ response->postReply(replyID);
+
+ break;
+ }
+
+ default:
+ {
+ status_t err = OK;
+ //TODO: do something with error here.
+ (void)err;
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index a1ada4f..c73679b 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -69,6 +69,7 @@
#include <media/stagefright/BatteryChecker.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/CCodec.h>
+#include <media/stagefright/CryptoAsync.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaCodecList.h>
@@ -496,6 +497,7 @@
kWhatReleaseCompleted = 'rcom',
kWhatFlushCompleted = 'fcom',
kWhatError = 'erro',
+ kWhatCryptoError = 'ercp',
kWhatComponentAllocated = 'cAll',
kWhatComponentConfigured = 'cCon',
kWhatInputSurfaceCreated = 'isfc',
@@ -506,6 +508,40 @@
kWhatFirstTunnelFrameReady = 'ftfR',
};
+class CryptoAsyncCallback : public CryptoAsync::CryptoAsyncCallback {
+public:
+
+ explicit CryptoAsyncCallback(const sp<AMessage> & notify):mNotify(notify) {
+ }
+
+ ~CryptoAsyncCallback() {}
+
+ void onDecryptComplete(const sp<AMessage> &result) override {
+ (void)result;
+ }
+
+ void onDecryptError(const std::list<sp<AMessage>> &errorMsgs) override {
+ // This error may be decrypt/queue error.
+ status_t errorCode ;
+ for (auto &emsg : errorMsgs) {
+ sp<AMessage> notify(mNotify->dup());
+ if(emsg->findInt32("err", &errorCode)) {
+ if (isCryptoError(errorCode)) {
+ notify->setInt32("what", kWhatCryptoError);
+ } else {
+ notify->setInt32("what", kWhatError);
+ }
+ notify->extend(emsg);
+ notify->post();
+ } else {
+ ALOGW("Buffers with no errorCode are not expected");
+ }
+ }
+ }
+private:
+ const sp<AMessage> mNotify;
+};
+
class BufferCallback : public CodecBase::BufferCallback {
public:
explicit BufferCallback(const sp<AMessage> ¬ify);
@@ -1689,7 +1725,6 @@
mBufferChannel->setCallback(
std::unique_ptr<CodecBase::BufferCallback>(
new BufferCallback(new AMessage(kWhatCodecNotify, this))));
-
sp<AMessage> msg = new AMessage(kWhatInit, this);
if (mCodecInfo) {
msg->setObject("codecInfo", mCodecInfo);
@@ -1802,7 +1837,6 @@
if (!format->findInt32("rotation-degrees", &mRotationDegrees)) {
mRotationDegrees = 0;
}
-
if (nextMetricsHandle != 0) {
mediametrics_setInt32(nextMetricsHandle, kCodecWidth, mWidth);
mediametrics_setInt32(nextMetricsHandle, kCodecHeight, mHeight);
@@ -3278,9 +3312,10 @@
{
int32_t what;
CHECK(msg->findInt32("what", &what));
-
+ AString codecErrorState;
switch (what) {
case kWhatError:
+ case kWhatCryptoError:
{
int32_t err, actionCode;
CHECK(msg->findInt32("err", &err));
@@ -3293,11 +3328,20 @@
mFlags |= kFlagSawMediaServerDie;
mFlags &= ~kFlagIsComponentAllocated;
}
-
bool sendErrorResponse = true;
- std::string origin{"kWhatError:"};
+ std::string origin;
+ if (what == kWhatCryptoError) {
+ origin = "kWhatCryptoError:";
+ } else {
+ origin = "kWhatError:";
+ //TODO: add a new error state
+ }
+ codecErrorState = kCodecErrorState;
origin += stateString(mState);
-
+ if (mCryptoAsync) {
+ //TODO: do some book keeping on the buffers
+ mCryptoAsync->stop();
+ }
switch (mState) {
case INITIALIZING:
{
@@ -3399,7 +3443,11 @@
cancelPendingDequeueOperations();
if (mFlags & kFlagIsAsync) {
- onError(err, actionCode);
+ if (what == kWhatError) {
+ onError(err, actionCode);
+ } else if (what == kWhatCryptoError) {
+ onCryptoError(msg);
+ }
}
switch (actionCode) {
case ACTION_CODE_TRANSIENT:
@@ -3431,7 +3479,11 @@
actionCode = ACTION_CODE_FATAL;
}
if (mFlags & kFlagIsAsync) {
- onError(err, actionCode);
+ if (what == kWhatError) {
+ onError(err, actionCode);
+ } else if (what == kWhatCryptoError) {
+ onCryptoError(msg);
+ }
}
switch (actionCode) {
case ACTION_CODE_TRANSIENT:
@@ -4113,12 +4165,24 @@
uint32_t flags;
CHECK(msg->findInt32("flags", (int32_t *)&flags));
- if (flags & CONFIGURE_FLAG_USE_BLOCK_MODEL) {
+ if (flags & CONFIGURE_FLAG_USE_BLOCK_MODEL ||
+ flags & CONFIGURE_FLAG_USE_CRYPTO_ASYNC) {
if (!(mFlags & kFlagIsAsync)) {
+ ALOGE("Error: configuration requires async operation");
PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
- mFlags |= kFlagUseBlockModel;
+ if (flags & CONFIGURE_FLAG_USE_BLOCK_MODEL) {
+ mFlags |= kFlagUseBlockModel;
+ }
+ if (flags & CONFIGURE_FLAG_USE_CRYPTO_ASYNC) {
+ // silently disable crytoasync with blockmodel
+ if (!(mFlags & kFlagUseBlockModel)) {
+ mFlags |= kFlagUseCryptoAsync;
+ } else {
+ ALOGW("CrytoAsync not yet enabled for block model, falling back to normal");
+ }
+ }
}
mReplyID = replyID;
setState(CONFIGURING);
@@ -4144,6 +4208,21 @@
mDescrambler = static_cast<IDescrambler *>(descrambler);
mBufferChannel->setDescrambler(mDescrambler);
+ if ((mFlags & kFlagUseCryptoAsync) &&
+ mCrypto && (mDomain == DOMAIN_VIDEO)) {
+ mCryptoAsync = new CryptoAsync(mBufferChannel);
+ mCryptoAsync->setCallback(
+ std::make_unique<CryptoAsyncCallback>(new AMessage(kWhatCodecNotify, this)));
+ mCryptoLooper = new ALooper();
+ mCryptoLooper->setName("CryptoAsyncLooper");
+ mCryptoLooper->registerHandler(mCryptoAsync);
+ status_t err = mCryptoLooper->start();
+ if (err != OK) {
+ ALOGE("Crypto Looper failed to start");
+ mCryptoAsync = nullptr;
+ mCryptoLooper = nullptr;
+ }
+ }
format->setInt32("flags", flags);
if (flags & CONFIGURE_FLAG_ENCODE) {
@@ -4313,7 +4392,9 @@
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
-
+ if (mCryptoAsync) {
+ mCryptoAsync->stop();
+ }
sp<AMessage> asyncNotify;
(void)msg->findMessage("async", &asyncNotify);
// post asyncNotify if going out of scope.
@@ -4733,7 +4814,11 @@
mReplyID = replyID;
// TODO: skip flushing if already FLUSHED
setState(FLUSHING);
-
+ if (mCryptoAsync) {
+ std::list<sp<AMessage>> pendingBuffers;
+ mCryptoAsync->stop(&pendingBuffers);
+ //TODO: do something with these buffers
+ }
mCodec->signalFlush();
returnBuffersToCodec();
TunnelPeekState previousState = mTunnelPeekState;
@@ -5171,7 +5256,7 @@
CHECK(msg->findSize("offset", &offset));
}
const CryptoPlugin::SubSample *subSamples;
- size_t numSubSamples;
+ size_t numSubSamples = 0;
const uint8_t *key = NULL;
const uint8_t *iv = NULL;
CryptoPlugin::Mode mode = CryptoPlugin::kMode_Unencrypted;
@@ -5197,7 +5282,6 @@
mComponentName.c_str());
return -EINVAL;
}
-
CHECK(msg->findPointer("subSamples", (void **)&subSamples));
CHECK(msg->findSize("numSubSamples", &numSubSamples));
CHECK(msg->findPointer("key", (void **)&key));
@@ -5223,13 +5307,83 @@
BufferInfo *info = &mPortBuffers[kPortIndexInput][index];
sp<MediaCodecBuffer> buffer = info->mData;
+ if (buffer == nullptr || !info->mOwnedByClient) {
+ return -EACCES;
+ }
+ auto setInputBufferParams = [this, &buffer]
+ (int64_t timeUs, uint32_t flags = 0) -> status_t {
+ status_t err = OK;
+ buffer->meta()->setInt64("timeUs", timeUs);
+ if (flags & BUFFER_FLAG_EOS) {
+ buffer->meta()->setInt32("eos", true);
+ }
+ if (flags & BUFFER_FLAG_CODECCONFIG) {
+ buffer->meta()->setInt32("csd", true);
+ }
+ bool isBufferDecodeOnly = ((flags & BUFFER_FLAG_DECODE_ONLY) != 0);
+ if (isBufferDecodeOnly) {
+ buffer->meta()->setInt32("decode-only", true);
+ }
+ if (mTunneled && !isBufferDecodeOnly) {
+ TunnelPeekState previousState = mTunnelPeekState;
+ switch(mTunnelPeekState){
+ case TunnelPeekState::kEnabledNoBuffer:
+ buffer->meta()->setInt32("tunnel-first-frame", 1);
+ mTunnelPeekState = TunnelPeekState::kEnabledQueued;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(mTunnelPeekState));
+ break;
+ case TunnelPeekState::kDisabledNoBuffer:
+ buffer->meta()->setInt32("tunnel-first-frame", 1);
+ mTunnelPeekState = TunnelPeekState::kDisabledQueued;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(mTunnelPeekState));
+ break;
+ default:
+ break;
+ }
+ }
+ return err;
+ };
+ auto buildCryptoInfoAMessage = [&](const sp<AMessage> & cryptoInfo, int32_t action) {
+ size_t key_len = (key != nullptr)? 16 : 0;
+ size_t iv_len = (iv != nullptr)? 16 : 0;
+ sp<ABuffer> shared_key;
+ sp<ABuffer> 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<ABuffer> 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);
+ };
if (c2Buffer || memory) {
sp<AMessage> tunings = NULL;
if (msg->findMessage("tunings", &tunings) && tunings != NULL) {
onSetParameters(tunings);
}
-
status_t err = OK;
if (c2Buffer) {
err = mBufferChannel->attachBuffer(c2Buffer, buffer);
@@ -5240,7 +5394,6 @@
} else {
err = UNKNOWN_ERROR;
}
-
if (err == OK && !buffer->asC2Buffer()
&& c2Buffer && c2Buffer->data().type() == C2BufferData::LINEAR) {
C2ConstLinearBlock block{c2Buffer->data().linearBlocks().front()};
@@ -5256,63 +5409,23 @@
flags &= ~BUFFER_FLAG_EOS;
}
}
-
offset = buffer->offset();
size = buffer->size();
if (err != OK) {
ALOGI("block model buffer attach failed: err = %s (%d)",
- StrMediaError(err).c_str(), err);
+ StrMediaError(err).c_str(), err);
return err;
}
}
-
- if (buffer == nullptr || !info->mOwnedByClient) {
- return -EACCES;
- }
-
if (offset + size > buffer->capacity()) {
return -EINVAL;
}
-
buffer->setRange(offset, size);
- buffer->meta()->setInt64("timeUs", timeUs);
-
- if (flags & BUFFER_FLAG_EOS) {
- buffer->meta()->setInt32("eos", true);
- }
-
- if (flags & BUFFER_FLAG_CODECCONFIG) {
- buffer->meta()->setInt32("csd", true);
- }
-
- bool isBufferDecodeOnly = ((flags & BUFFER_FLAG_DECODE_ONLY) != 0);
- if (isBufferDecodeOnly) {
- buffer->meta()->setInt32("decode-only", true);
- }
-
- if (mTunneled && !isBufferDecodeOnly) {
- TunnelPeekState previousState = mTunnelPeekState;
- switch(mTunnelPeekState){
- case TunnelPeekState::kEnabledNoBuffer:
- buffer->meta()->setInt32("tunnel-first-frame", 1);
- mTunnelPeekState = TunnelPeekState::kEnabledQueued;
- ALOGV("TunnelPeekState: %s -> %s",
- asString(previousState),
- asString(mTunnelPeekState));
- break;
- case TunnelPeekState::kDisabledNoBuffer:
- buffer->meta()->setInt32("tunnel-first-frame", 1);
- mTunnelPeekState = TunnelPeekState::kDisabledQueued;
- ALOGV("TunnelPeekState: %s -> %s",
- asString(previousState),
- asString(mTunnelPeekState));
- break;
- default:
- break;
- }
- }
-
status_t err = OK;
+ err = setInputBufferParams(timeUs, flags);
+ if (err != OK) {
+ return -EINVAL;
+ }
if (hasCryptoOrDescrambler() && !c2Buffer && !memory) {
AString *errorDetailMsg;
CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
@@ -5328,7 +5441,13 @@
}
}
}
- err = mBufferChannel->queueSecureInputBuffer(
+ if (mCryptoAsync) {
+ // prepare a message and enqueue
+ sp<AMessage> cryptoInfo = new AMessage();
+ buildCryptoInfoAMessage(cryptoInfo, CryptoAsync::kActionDecrypt);
+ mCryptoAsync->decrypt(cryptoInfo);
+ } else {
+ err = mBufferChannel->queueSecureInputBuffer(
buffer,
(mFlags & kFlagIsSecure),
key,
@@ -5338,6 +5457,7 @@
subSamples,
numSubSamples,
errorDetailMsg);
+ }
if (err != OK) {
mediametrics_setInt32(mMetricsHandle, kCodecQueueSecureInputBufferError, err);
ALOGW("Log queueSecureInputBuffer error: %d", err);
@@ -5629,7 +5749,14 @@
msg->post();
}
}
-
+void MediaCodec::onCryptoError(const sp<AMessage> & msg) {
+ if (mCallback != NULL) {
+ sp<AMessage> cb_msg = mCallback->dup();
+ cb_msg->setInt32("callbackID", CB_CRYPTO_ERROR);
+ cb_msg->extend(msg);
+ cb_msg->post();
+ }
+}
void MediaCodec::onError(status_t err, int32_t actionCode, const char *detail) {
if (mCallback != NULL) {
sp<AMessage> msg = mCallback->dup();
diff --git a/media/libstagefright/include/media/stagefright/CryptoAsync.h b/media/libstagefright/include/media/stagefright/CryptoAsync.h
new file mode 100644
index 0000000..b675518
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/CryptoAsync.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2022 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_ASYNC_H_
+#define CRYPTO_ASYNC_H_
+
+#include <media/stagefright/CodecBase.h>
+#include <media/stagefright/foundation/Mutexed.h>
+namespace android {
+
+class CryptoAsync: public AHandler {
+public:
+
+ class CryptoAsyncCallback {
+ public:
+
+ virtual ~CryptoAsyncCallback() = default;
+
+ /*
+ * Callback with result for queuing the decrypted buffer to the
+ * underlying codec. Cannot block this function
+ */
+ virtual void onDecryptComplete(const sp<AMessage>& result) = 0;
+
+ /*
+ * Callback with error information while decryption. Cannot block
+ * this call. The return should contain the error information
+ * and the buffer the caused the error.
+ */
+ virtual void onDecryptError(const std::list<sp<AMessage>>& errorMsg) = 0;
+ };
+
+ // Ideally we should be returning the output of the decryption in
+ // onDecryptComple() calback and let the next module take over the
+ // rest of the processing. In the current state, the next step will
+ // be to queue the output the codec which is done using BufferChannel
+
+ // In order to prevent thread hop to just do that, we have created
+ // a dependency on BufferChannel here to queue the buffer to the codec
+ // immediately after decryption.
+ CryptoAsync(std::weak_ptr<BufferChannelBase> bufferChannel)
+ :mState(kCryptoAsyncActive) {
+ mBufferChannel = std::move(bufferChannel);
+ }
+
+ // Destructor
+ virtual ~CryptoAsync();
+
+ inline void setCallback(std::unique_ptr<CryptoAsyncCallback>&& callback) {
+ mCallback = std::move(callback);
+ }
+
+ // Call this function to decrypt the buffer in the message.
+ status_t decrypt(sp<AMessage>& msg);
+
+ // This function stops further processing in the thread and returns
+ // with any unprocessed buffers from the queue.
+ // We can use this method in case of flush or clearing the queue
+ // upon error. When the processing hits an error, the self processing
+ // in this looper stops and in-fact., there is a need to clear (call stop())
+ // for the queue to become operational again. Also acts like a rest.
+ void stop(std::list<sp<AMessage>> * const buffers = nullptr);
+
+ // Describes two actions for decrypt();
+ // kActionDecrypt - decrypts the buffer and queues to codec
+ // kActionAttachEncryptedBuffer - decrypts and attaches the buffer
+ // and queues to the codec.
+ // TODO: kActionAttachEncryptedBuffer is meant to work with
+ // BLOCK_MODEL which is not yet implemented.
+ enum : uint32_t {
+ // decryption types
+ kActionDecrypt = (1 << 0),
+ kActionAttachEncryptedBuffer = (1 << 1)
+ };
+protected:
+
+ // Message types for the looper
+ enum : uint32_t {
+ // used with decrypt()
+ // Exact decryption type as described by the above enum
+ // decides what "action" to take. The "action" should be
+ // part of this message
+ kWhatDecrypt = 1,
+ // used with stop()
+ kWhatStop = 2,
+ // place holder
+ kWhatDoNothing = 10
+ };
+
+ // Defines the staste of this thread.
+ typedef enum : uint32_t {
+ // kCryptoAsyncActive as long as we have not encountered
+ // any errors during processing. Any errors will
+ // put the state to error and the thread now refuses to
+ // do further processing until the error state is cleared
+ // with a call to stop()
+
+ kCryptoAsyncActive = (0 << 0),
+ // state of the looper when encountered with error during
+ // processing
+ kCryptoAsyncError = (1 << 8)
+ } CryptoAsyncState;
+
+ // Implements kActionDecrypt
+ status_t decryptAndQueue(sp<AMessage>& msg);
+
+ // Implements kActionAttachEncryptedBuffer
+ status_t attachEncryptedBufferAndQueue(sp<AMessage>& msg);
+
+ // Implements the Looper
+ void onMessageReceived(const sp<AMessage>& msg) override;
+
+ std::unique_ptr<CryptoAsyncCallback> mCallback;
+private:
+
+ CryptoAsyncState mState;
+
+ // Queue holding any pending buffers
+ Mutexed<std::list<sp<AMessage>>> mPendingBuffers;
+
+ std::weak_ptr<BufferChannelBase> mBufferChannel;
+};
+
+} // namespace android
+
+#endif // CRYPTO_ASYNC_H_
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index dbc97db..65d9f7d 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -55,6 +55,7 @@
struct CodecParameterDescriptor;
class IBatteryStats;
struct ICrypto;
+class CryptoAsync;
class MediaCodecBuffer;
class IMemory;
struct PersistentSurface;
@@ -82,6 +83,7 @@
enum ConfigureFlags {
CONFIGURE_FLAG_ENCODE = 1,
CONFIGURE_FLAG_USE_BLOCK_MODEL = 2,
+ CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4,
};
enum BufferFlags {
@@ -106,6 +108,7 @@
CB_ERROR = 3,
CB_OUTPUT_FORMAT_CHANGED = 4,
CB_RESOURCE_RECLAIMED = 5,
+ CB_CRYPTO_ERROR = 6,
};
static const pid_t kNoPid = -1;
@@ -376,6 +379,7 @@
kFlagIsComponentAllocated = 2048,
kFlagPushBlankBuffersOnShutdown = 4096,
kFlagUseBlockModel = 8192,
+ kFlagUseCryptoAsync = 16384,
};
struct BufferInfo {
@@ -530,6 +534,8 @@
bool mCpuBoostRequested;
std::shared_ptr<BufferChannelBase> mBufferChannel;
+ sp<CryptoAsync> mCryptoAsync;
+ sp<ALooper> mCryptoLooper;
std::unique_ptr<PlaybackDurationAccumulator> mPlaybackDurationAccumulator;
bool mIsSurfaceToScreen;
@@ -583,6 +589,7 @@
void onInputBufferAvailable();
void onOutputBufferAvailable();
+ void onCryptoError(const sp<AMessage> &msg);
void onError(status_t err, int32_t actionCode, const char *detail = NULL);
void onOutputFormatChanged();