blob: eb64a4a52637be11f2fd718cbbf5d5fc19475bee [file] [log] [blame]
/*
* Copyright 2021 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 "Codec2-Component-Aidl"
#include <android-base/logging.h>
#include <codec2/aidl/Component.h>
#include <codec2/aidl/ComponentStore.h>
#include <codec2/aidl/InputBufferManager.h>
#ifndef __ANDROID_APEX__
#include <FilterWrapper.h>
#endif
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
#include <utils/Timers.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
#include <chrono>
#include <thread>
namespace aidl {
namespace android {
namespace hardware {
namespace media {
namespace c2 {
namespace utils {
using ::aidl::android::hardware::common::NativeHandle;
using ::aidl::android::hardware::media::bufferpool2::IClientManager;
using ::ndk::ScopedAStatus;
using ::android::MultiAccessUnitInterface;
using ::android::MultiAccessUnitHelper;
// ComponentListener wrapper
struct Component::Listener : public C2Component::Listener {
Listener(const std::shared_ptr<Component>& component) :
mComponent(component),
mListener(component->mListener) {
CHECK(component);
CHECK(component->mListener);
}
virtual void onError_nb(
std::weak_ptr<C2Component> /* c2component */,
uint32_t errorCode) override {
std::shared_ptr<IComponentListener> listener = mListener.lock();
if (listener) {
ScopedAStatus transStatus = listener->onError(Status{Status::OK}, errorCode);
if (!transStatus.isOk()) {
LOG(ERROR) << "Component::Listener::onError_nb -- "
<< "transaction failed.";
}
}
}
virtual void onTripped_nb(
std::weak_ptr<C2Component> /* c2component */,
std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
) override {
std::shared_ptr<IComponentListener> listener = mListener.lock();
if (listener) {
std::vector<SettingResult> settingResults(c2settingResult.size());
size_t ix = 0;
for (const std::shared_ptr<C2SettingResult> &c2result :
c2settingResult) {
if (c2result) {
if (!ToAidl(&settingResults[ix++], *c2result)) {
break;
}
}
}
settingResults.resize(ix);
ScopedAStatus transStatus = listener->onTripped(settingResults);
if (!transStatus.isOk()) {
LOG(ERROR) << "Component::Listener::onTripped_nb -- "
<< "transaction failed.";
}
}
}
virtual void onWorkDone_nb(
std::weak_ptr<C2Component> /* c2component */,
std::list<std::unique_ptr<C2Work>> c2workItems) override {
for (const std::unique_ptr<C2Work>& work : c2workItems) {
if (work) {
if (work->worklets.empty()
|| !work->worklets.back()
|| (work->worklets.back()->output.flags &
C2FrameData::FLAG_INCOMPLETE) == 0) {
InputBufferManager::
unregisterFrameData(mListener, work->input);
}
}
}
std::shared_ptr<IComponentListener> listener = mListener.lock();
if (listener) {
WorkBundle workBundle;
std::shared_ptr<Component> strongComponent = mComponent.lock();
if (!ToAidl(&workBundle, c2workItems, strongComponent ?
&strongComponent->mBufferPoolSender : nullptr)) {
LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
<< "received corrupted work items.";
return;
}
ScopedAStatus transStatus = listener->onWorkDone(workBundle);
if (!transStatus.isOk()) {
LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
<< "transaction failed.";
return;
}
// If output blocks are originally owned by the client(not by HAL),
// return the ownership to the client. (Since the blocks are
// transferred to the client here.)
ReturnOutputBlocksToClientIfNeeded(c2workItems);
}
}
protected:
std::weak_ptr<Component> mComponent;
std::weak_ptr<IComponentListener> mListener;
};
// Component listener for handle multiple access-units
struct MultiAccessUnitListener : public Component::Listener {
MultiAccessUnitListener(const std::shared_ptr<Component>& component,
const std::shared_ptr<MultiAccessUnitHelper> &helper):
Listener(component), mHelper(helper) {
}
virtual void onError_nb(
std::weak_ptr<C2Component> c2component,
uint32_t errorCode) override {
if (mHelper) {
std::list<std::unique_ptr<C2Work>> worklist;
mHelper->error(&worklist);
if (!worklist.empty()) {
Listener::onWorkDone_nb(c2component, std::move(worklist));
}
}
Listener::onError_nb(c2component, errorCode);
}
virtual void onTripped_nb(
std::weak_ptr<C2Component> c2component,
std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
) override {
Listener::onTripped_nb(c2component,
c2settingResult);
}
virtual void onWorkDone_nb(
std::weak_ptr<C2Component> c2component,
std::list<std::unique_ptr<C2Work>> c2workItems) override {
if (mHelper) {
std::list<std::unique_ptr<C2Work>> processedWork;
mHelper->gather(c2workItems, &processedWork);
if (!processedWork.empty()) {
Listener::onWorkDone_nb(c2component, std::move(processedWork));
}
} else {
Listener::onWorkDone_nb(c2component, std::move(c2workItems));
}
}
protected:
std::shared_ptr<MultiAccessUnitHelper> mHelper;
};
// Component::DeathContext
struct Component::DeathContext {
std::weak_ptr<Component> mWeakComp;
};
// Component
Component::Component(
const std::shared_ptr<C2Component>& component,
const std::shared_ptr<IComponentListener>& listener,
const std::shared_ptr<ComponentStore>& store,
const std::shared_ptr<IClientManager>& clientPoolManager)
: mComponent{component},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager},
mDeathContext(nullptr) {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
mMultiAccessUnitIntf = store->tryCreateMultiAccessUnitInterface(component->intf());
mInterface = SharedRefBase::make<ComponentInterface>(
component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
c2_status_t Component::status() const {
return mInit;
}
// Methods from ::android::hardware::media::c2::V1_1::IComponent
ScopedAStatus Component::queue(const WorkBundle& workBundle) {
std::list<std::unique_ptr<C2Work>> c2works;
if (!FromAidl(&c2works, workBundle)) {
return ScopedAStatus::fromServiceSpecificError(Status::CORRUPTED);
}
// Register input buffers.
for (const std::unique_ptr<C2Work>& work : c2works) {
if (work) {
InputBufferManager::
registerFrameData(mListener, work->input);
}
}
c2_status_t err = C2_OK;
if (mMultiAccessUnitHelper) {
std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
for (auto &c2worklist : c2worklists) {
err = mComponent->queue_nb(&c2worklist);
if (err != C2_OK) {
LOG(ERROR) << "Error Queuing to component.";
return ScopedAStatus::fromServiceSpecificError(err);
}
}
return ScopedAStatus::ok();
}
err = mComponent->queue_nb(&c2works);
if (err == C2_OK) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(err);
}
ScopedAStatus Component::flush(WorkBundle *flushedWorkBundle) {
std::list<std::unique_ptr<C2Work>> c2flushedWorks;
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
if (mMultiAccessUnitHelper) {
c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
}
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
if (work) {
if (work->worklets.empty()
|| !work->worklets.back()
|| (work->worklets.back()->output.flags &
C2FrameData::FLAG_INCOMPLETE) == 0) {
InputBufferManager::
unregisterFrameData(mListener, work->input);
}
}
}
if (c2res == C2_OK) {
if (!ToAidl(flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) {
c2res = C2_CORRUPTED;
}
}
// If output blocks are originally owned by the client(not by HAL),
// return the ownership to the client. (Since the blocks are
// transferred to the client here.)
ReturnOutputBlocksToClientIfNeeded(c2flushedWorks);
if (c2res == C2_OK) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(c2res);
}
ScopedAStatus Component::drain(bool withEos) {
c2_status_t res = mComponent->drain_nb(withEos ?
C2Component::DRAIN_COMPONENT_WITH_EOS :
C2Component::DRAIN_COMPONENT_NO_EOS);
if (res == C2_OK) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(res);
}
namespace /* unnamed */ {
struct BlockPoolIntf : public ConfigurableC2Intf {
BlockPoolIntf(const std::shared_ptr<C2BlockPool>& pool)
: ConfigurableC2Intf{
"C2BlockPool:" +
(pool ? std::to_string(pool->getLocalId()) : "null"),
0},
mPool{pool} {
}
virtual c2_status_t config(
const std::vector<C2Param*>& params,
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2SettingResult>>* const failures
) override {
(void)params;
(void)mayBlock;
(void)failures;
return C2_OK;
}
virtual c2_status_t query(
const std::vector<C2Param::Index>& indices,
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2Param>>* const params
) const override {
(void)indices;
(void)mayBlock;
(void)params;
return C2_OK;
}
virtual c2_status_t querySupportedParams(
std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
) const override {
(void)params;
return C2_OK;
}
virtual c2_status_t querySupportedValues(
std::vector<C2FieldSupportedValuesQuery>& fields,
c2_blocking_t mayBlock) const override {
(void)fields;
(void)mayBlock;
return C2_OK;
}
protected:
std::shared_ptr<C2BlockPool> mPool;
};
} // unnamed namespace
ScopedAStatus Component::createBlockPool(
const IComponent::BlockPoolAllocator &allocator,
IComponent::BlockPool *blockPool) {
std::shared_ptr<C2BlockPool> c2BlockPool;
c2_status_t status = C2_OK;
::android::C2PlatformAllocatorDesc allocatorParam;
allocatorParam.allocatorId = allocator.allocatorId;
switch (allocator.allocatorId) {
case ::android::C2PlatformAllocatorStore::IGBA: {
allocatorParam.igba = allocator.gbAllocator->igba;
allocatorParam.waitableFd.reset(
allocator.gbAllocator->waitableFd.dup().release());
}
break;
default: {
// no-op
}
break;
}
#ifdef __ANDROID_APEX__
status = ::android::CreateCodec2BlockPool(
allocatorParam,
mComponent,
&c2BlockPool);
#else
status = ComponentStore::GetFilterWrapper()->createBlockPool(
allocatorParam,
mComponent,
&c2BlockPool);
#endif
if (status != C2_OK) {
return ScopedAStatus::fromServiceSpecificError(status);
}
{
mBlockPoolsMutex.lock();
mBlockPools.emplace(c2BlockPool->getLocalId(), c2BlockPool);
mBlockPoolsMutex.unlock();
}
blockPool->blockPoolId = c2BlockPool ? c2BlockPool->getLocalId() : 0;
blockPool->configurable = SharedRefBase::make<CachedConfigurable>(
std::make_unique<BlockPoolIntf>(c2BlockPool));
return ScopedAStatus::ok();
}
ScopedAStatus Component::destroyBlockPool(int64_t blockPoolId) {
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
if (mBlockPools.erase(blockPoolId) == 1) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(Status::CORRUPTED);
}
ScopedAStatus Component::start() {
c2_status_t status = mComponent->start();
if (status == C2_OK) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(status);
}
ScopedAStatus Component::stop() {
InputBufferManager::unregisterFrameData(mListener);
c2_status_t status = mComponent->stop();
if (status == C2_OK) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(status);
}
ScopedAStatus Component::reset() {
c2_status_t status = mComponent->reset();
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
if (mMultiAccessUnitHelper) {
mMultiAccessUnitHelper->reset();
}
InputBufferManager::unregisterFrameData(mListener);
if (status == C2_OK) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(status);
}
ScopedAStatus Component::release() {
c2_status_t status = mComponent->release();
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
if (mMultiAccessUnitHelper) {
mMultiAccessUnitHelper->reset();
}
InputBufferManager::unregisterFrameData(mListener);
if (status == C2_OK) {
return ScopedAStatus::ok();
}
return ScopedAStatus::fromServiceSpecificError(status);
}
ScopedAStatus Component::getInterface(
std::shared_ptr<IComponentInterface> *intf) {
*intf = mInterface;
return ScopedAStatus::ok();
}
ScopedAStatus Component::configureVideoTunnel(
int32_t avSyncHwId, NativeHandle *handle) {
(void)avSyncHwId;
(void)handle;
return ScopedAStatus::fromServiceSpecificError(Status::OMITTED);
}
ScopedAStatus Component::connectToInputSurface(
const std::shared_ptr<IInputSurface>& inputSurface,
std::shared_ptr<IInputSurfaceConnection> *connection) {
// TODO
(void)inputSurface;
(void)connection;
return ScopedAStatus::fromServiceSpecificError(Status::OMITTED);
}
ScopedAStatus Component::asInputSink(
std::shared_ptr<IInputSink> *sink) {
// TODO
(void)sink;
return ScopedAStatus::fromServiceSpecificError(Status::OMITTED);
}
void Component::initListener(const std::shared_ptr<Component>& self) {
if (__builtin_available(android __ANDROID_API_T__, *)) {
std::shared_ptr<C2Component::Listener> c2listener;
if (mMultiAccessUnitIntf) {
mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
}
c2listener = mMultiAccessUnitHelper ?
std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
mInit = res;
}
// b/321902635, mListener should not be null.
CHECK(mListener);
mDeathRecipient = ::ndk::ScopedAIBinder_DeathRecipient(
AIBinder_DeathRecipient_new(OnBinderDied));
mDeathContext = new DeathContext{ref<Component>()};
AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), OnBinderUnlinked);
AIBinder_linkToDeath(mListener->asBinder().get(), mDeathRecipient.get(), mDeathContext);
} else {
mInit = C2_NO_INIT;
}
}
// static
void Component::OnBinderDied(void *cookie) {
DeathContext *context = (DeathContext *)cookie;
std::shared_ptr<Component> comp = context->mWeakComp.lock();
if (comp) {
comp->release();
}
}
// static
void Component::OnBinderUnlinked(void *cookie) {
delete (DeathContext *)cookie;
}
Component::~Component() {
InputBufferManager::unregisterFrameData(mListener);
mStore->reportComponentDeath(this);
if (mDeathRecipient.get()) {
AIBinder_unlinkToDeath(mListener->asBinder().get(), mDeathRecipient.get(), mDeathContext);
}
}
} // namespace utils
} // namespace c2
} // namespace media
} // namespace hardware
} // namespace android
} // namespace aidl