codec2: add filter plugin interface & wrapper implementation
Bug: 160259947
Test: manually run the logic with a sample plugin
Change-Id: I9e96695101262ecf0e8428574ecc56c0c6e17ae7
diff --git a/media/codec2/hidl/plugin/Android.bp b/media/codec2/hidl/plugin/Android.bp
new file mode 100644
index 0000000..4708b12
--- /dev/null
+++ b/media/codec2/hidl/plugin/Android.bp
@@ -0,0 +1,66 @@
+cc_library_headers {
+ name: "libcodec2_hidl_plugin_headers",
+ vendor_available: true,
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_library {
+ name: "libcodec2_hidl_plugin_stub",
+
+ srcs: [
+ "DefaultFilterPlugin.cpp",
+ "FilterWrapperStub.cpp",
+ ],
+
+ header_libs: [
+ "libcodec2_internal", // private
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcodec2",
+ "libcodec2_vndk",
+ "liblog",
+ "libutils",
+ ],
+
+ export_include_dirs: [
+ "include",
+ "internal",
+ ],
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
+}
+
+cc_library {
+ name: "libcodec2_hidl_plugin",
+ vendor: true,
+
+ srcs: [
+ "DefaultFilterPlugin.cpp",
+ "FilterWrapper.cpp",
+ ],
+
+ header_libs: [
+ "libcodec2_internal", // private
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcodec2",
+ "libcodec2_vndk",
+ "liblog",
+ "libutils",
+ ],
+
+ export_include_dirs: [
+ "include",
+ "internal",
+ ],
+}
diff --git a/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp b/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp
new file mode 100644
index 0000000..cd1bcb0
--- /dev/null
+++ b/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2020 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-DefaultFilterPlugin"
+#include <android-base/logging.h>
+
+#include <set>
+
+#include <dlfcn.h>
+
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <C2ParamInternal.h>
+
+#include <codec2/hidl/plugin/FilterPlugin.h>
+
+#include <DefaultFilterPlugin.h>
+#include <FilterWrapper.h>
+
+namespace android {
+
+DefaultFilterPlugin::DefaultFilterPlugin(const char *pluginPath)
+ : mInit(NO_INIT),
+ mHandle(nullptr),
+ mDestroyPlugin(nullptr),
+ mPlugin(nullptr) {
+ mHandle = dlopen(pluginPath, RTLD_NOW | RTLD_NODELETE);
+ if (!mHandle) {
+ LOG(DEBUG) << "FilterPlugin: no plugin detected";
+ return;
+ }
+ GetFilterPluginVersionFunc getVersion =
+ (GetFilterPluginVersionFunc)dlsym(mHandle, "GetFilterPluginVersion");
+ if (!getVersion) {
+ LOG(WARNING) << "FilterPlugin: GetFilterPluginVersion undefined";
+ return;
+ }
+ int32_t version = getVersion();
+ if (version != FilterPlugin_V1::VERSION) {
+ LOG(WARNING) << "FilterPlugin: unrecognized version (" << version << ")";
+ return;
+ }
+ CreateFilterPluginFunc createPlugin =
+ (CreateFilterPluginFunc)dlsym(mHandle, "CreateFilterPlugin");
+ if (!createPlugin) {
+ LOG(WARNING) << "FilterPlugin: CreateFilterPlugin undefined";
+ return;
+ }
+ mDestroyPlugin =
+ (DestroyFilterPluginFunc)dlsym(mHandle, "DestroyFilterPlugin");
+ if (!mDestroyPlugin) {
+ LOG(WARNING) << "FilterPlugin: DestroyFilterPlugin undefined";
+ return;
+ }
+ mPlugin = (FilterPlugin_V1 *)createPlugin();
+ if (!mPlugin) {
+ LOG(WARNING) << "FilterPlugin: CreateFilterPlugin returned nullptr";
+ return;
+ }
+ mStore = mPlugin->getComponentStore();
+ if (!mStore) {
+ LOG(WARNING) << "FilterPlugin: FilterPlugin_V1::getComponentStore returned nullptr";
+ return;
+ }
+ mInit = OK;
+}
+
+DefaultFilterPlugin::~DefaultFilterPlugin() {
+ if (mHandle) {
+ if (mDestroyPlugin && mPlugin) {
+ mDestroyPlugin(mPlugin);
+ mPlugin = nullptr;
+ }
+ dlclose(mHandle);
+ mHandle = nullptr;
+ mDestroyPlugin = nullptr;
+ }
+}
+
+bool DefaultFilterPlugin::describe(C2String name, FilterWrapper::Descriptor *desc) {
+ if (mInit != OK) {
+ return false;
+ }
+ return mPlugin->describe(name, desc);
+}
+
+bool DefaultFilterPlugin::isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) {
+ if (mInit != OK) {
+ return false;
+ }
+ return mPlugin->isFilteringEnabled(intf);
+}
+
+} // namespace android
diff --git a/media/codec2/hidl/plugin/FilterWrapper.cpp b/media/codec2/hidl/plugin/FilterWrapper.cpp
new file mode 100644
index 0000000..0b38bc1
--- /dev/null
+++ b/media/codec2/hidl/plugin/FilterWrapper.cpp
@@ -0,0 +1,921 @@
+/*
+ * Copyright 2020 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-FilterWrapper"
+#include <android-base/logging.h>
+
+#include <set>
+#include <sstream>
+
+#include <dlfcn.h>
+
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <C2ParamInternal.h>
+
+#include <codec2/hidl/plugin/FilterPlugin.h>
+
+#include <FilterWrapper.h>
+
+namespace android {
+
+namespace {
+
+// Indices that the last filter in the chain should consume.
+static constexpr uint32_t kTypesForLastFilter[] = {
+ // In case we have an output surface, we want to use the block pool
+ // backed by the output surface for the output buffer going to the client.
+ C2PortBlockPoolsTuning::output::PARAM_TYPE,
+};
+
+class WrappedDecoderInterface : public C2ComponentInterface {
+public:
+ WrappedDecoderInterface(
+ std::shared_ptr<C2ComponentInterface> intf,
+ std::vector<FilterWrapper::Component> &&filters)
+ : mIntf(intf) {
+ takeFilters(std::move(filters));
+ }
+
+ ~WrappedDecoderInterface() override = default;
+
+ void takeFilters(std::vector<FilterWrapper::Component> &&filters) {
+ std::unique_lock lock(mMutex);
+ std::vector<std::unique_ptr<C2Param>> lastFilterParams;
+ if (!mFilters.empty()) {
+ std::vector<C2Param::Index> indices;
+ std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
+ c2_status_t err = mFilters.back().intf->querySupportedParams_nb(¶mDescs);
+ if (err != C2_OK) {
+ LOG(WARNING) << "WrappedDecoderInterface: " << mFilters.back().traits.name
+ << " returned error for querySupportedParams_nb; err=" << err;
+ paramDescs.clear();
+ }
+ for (const std::shared_ptr<C2ParamDescriptor> ¶mDesc : paramDescs) {
+ C2Param::Index index = paramDesc->index();
+ if (std::count(
+ std::begin(kTypesForLastFilter),
+ std::end(kTypesForLastFilter),
+ index.type()) != 0) {
+ if (index.forStream()) {
+ // querySupportedParams does not return per-stream params.
+ // We only support stream-0 for now.
+ index = index.withStream(0u);
+ }
+ indices.push_back(index);
+ }
+ }
+ if (!indices.empty()) {
+ mFilters.back().intf->query_vb({}, indices, C2_MAY_BLOCK, &lastFilterParams);
+ }
+ }
+
+ // TODO: documentation
+ mFilters = std::move(filters);
+ mTypeToIndexForQuery.clear();
+ mTypeToIndexForConfig.clear();
+ for (size_t i = 0; i < mFilters.size(); ++i) {
+ if (i == 0) {
+ transferParams_l(mIntf, mFilters[0].intf, C2_MAY_BLOCK);
+ } else {
+ transferParams_l(mFilters[i - 1].intf, mFilters[i].intf, C2_MAY_BLOCK);
+ }
+ for (C2Param::Type type : mFilters[i].desc.controlParams) {
+ mTypeToIndexForQuery[type.type()] = i;
+ mTypeToIndexForConfig[type.type() & ~C2Param::CoreIndex::IS_REQUEST_FLAG] = i;
+ }
+ for (C2Param::Type type : mFilters[i].desc.affectedParams) {
+ mTypeToIndexForQuery[type.type()] = i;
+ }
+ }
+ if (!mFilters.empty()) {
+ for (uint32_t type : kTypesForLastFilter) {
+ mTypeToIndexForQuery[type] = mFilters.size() - 1;
+ mTypeToIndexForConfig[type & ~C2Param::CoreIndex::IS_REQUEST_FLAG] =
+ mFilters.size() - 1;
+ }
+ if (!lastFilterParams.empty()) {
+ std::vector<C2Param *> paramPtrs(lastFilterParams.size());
+ std::transform(
+ lastFilterParams.begin(),
+ lastFilterParams.end(),
+ paramPtrs.begin(),
+ [](const std::unique_ptr<C2Param> ¶m) {
+ return param.get();
+ });
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ mFilters.back().intf->config_vb(paramPtrs, C2_MAY_BLOCK, &failures);
+ }
+ }
+ }
+
+ C2String getName() const override { return mIntf->getName(); }
+
+ c2_node_id_t getId() const override { return mIntf->getId(); }
+
+ c2_status_t query_vb(
+ const std::vector<C2Param *> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
+ std::unique_lock lock(mMutex);
+ std::list<C2Param *> stackParamsList(stackParams.size());
+ std::copy_n(stackParams.begin(), stackParams.size(), stackParamsList.begin());
+ heapParams->clear();
+ c2_status_t result = C2_OK;
+ // TODO: loop optimization
+ for (size_t i = 0; i < mFilters.size(); ++i) {
+ // Filter stack params according to mTypeToIndexForQuery
+ std::vector<C2Param *> stackParamsForFilter;
+ for (auto it = stackParamsList.begin(); it != stackParamsList.end(); ) {
+ C2Param *param = *it;
+ uint32_t type = param->type().type();
+ auto it2 = mTypeToIndexForQuery.find(type);
+ if (it2 == mTypeToIndexForQuery.end() || it2->second != i) {
+ ++it;
+ continue;
+ }
+ stackParamsForFilter.push_back(param);
+ it = stackParamsList.erase(it);
+ }
+ // Filter heap params according to mTypeToIndexForQuery
+ std::vector<C2Param::Index> heapParamIndicesForFilter;
+ for (size_t j = 0; j < heapParamIndices.size(); ++j) {
+ uint32_t type = heapParamIndices[j].type();
+ auto it = mTypeToIndexForQuery.find(type);
+ if (it == mTypeToIndexForQuery.end() || it->second != i) {
+ continue;
+ }
+ heapParamIndicesForFilter.push_back(heapParamIndices[j]);
+ }
+ std::vector<std::unique_ptr<C2Param>> heapParamsForFilter;
+ const std::shared_ptr<C2ComponentInterface> &filter = mFilters[i].intf;
+ c2_status_t err = filter->query_vb(
+ stackParamsForFilter, heapParamIndicesForFilter, mayBlock,
+ &heapParamsForFilter);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
+ LOG(WARNING) << "WrappedDecoderInterface: " << filter->getName()
+ << " returned error for query_vb; err=" << err;
+ result = err;
+ continue;
+ }
+ heapParams->insert(
+ heapParams->end(),
+ std::make_move_iterator(heapParamsForFilter.begin()),
+ std::make_move_iterator(heapParamsForFilter.end()));
+ }
+
+ std::vector<C2Param *> stackParamsForIntf;
+ std::copy_n(stackParamsList.begin(), stackParamsList.size(), stackParamsForIntf.begin());
+
+ // Gather heap params that did not get queried from the filter interfaces above.
+ // These need to be queried from the decoder interface.
+ std::vector<C2Param::Index> heapParamIndicesForIntf;
+ for (size_t j = 0; j < heapParamIndices.size(); ++j) {
+ uint32_t type = heapParamIndices[j].type();
+ if (mTypeToIndexForQuery.find(type) != mTypeToIndexForQuery.end()) {
+ continue;
+ }
+ heapParamIndicesForIntf.push_back(heapParamIndices[j]);
+ }
+
+ std::vector<std::unique_ptr<C2Param>> heapParamsForIntf;
+ c2_status_t err = mIntf->query_vb(
+ stackParamsForIntf, heapParamIndicesForIntf, mayBlock, &heapParamsForIntf);
+ if (err != C2_OK) {
+ LOG(err == C2_BAD_INDEX ? VERBOSE : WARNING)
+ << "WrappedDecoderInterface: " << mIntf->getName()
+ << " returned error for query_vb; err=" << err;
+ result = err;
+ }
+
+ // TODO: params needs to preserve the order
+ heapParams->insert(
+ heapParams->end(),
+ std::make_move_iterator(heapParamsForIntf.begin()),
+ std::make_move_iterator(heapParamsForIntf.end()));
+
+ return result;
+ }
+
+ c2_status_t config_vb(
+ const std::vector<C2Param *> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+ std::unique_lock lock(mMutex);
+ c2_status_t result = C2_OK;
+ std::vector<C2Param *> paramsForIntf;
+ for (C2Param* param : params) {
+ auto it = mTypeToIndexForConfig.find(param->type().type());
+ if (it != mTypeToIndexForConfig.end()) {
+ continue;
+ }
+ paramsForIntf.push_back(param);
+ }
+ c2_status_t err = mIntf->config_vb(paramsForIntf, mayBlock, failures);
+ if (err != C2_OK) {
+ LOG(err == C2_BAD_INDEX ? VERBOSE : WARNING)
+ << "WrappedDecoderInterface: " << mIntf->getName()
+ << " returned error for config_vb; err=" << err;
+ result = err;
+ }
+ for (size_t i = 0; i < mFilters.size(); ++i) {
+ if (i == 0) {
+ transferParams_l(mIntf, mFilters[0].intf, mayBlock);
+ } else {
+ transferParams_l(mFilters[i - 1].intf, mFilters[i].intf, mayBlock);
+ }
+ const std::shared_ptr<C2ComponentInterface> &filter = mFilters[i].intf;
+ std::vector<std::unique_ptr<C2SettingResult>> filterFailures;
+ std::vector<C2Param *> paramsForFilter;
+ for (C2Param* param : params) {
+ auto it = mTypeToIndexForConfig.find(param->type().type());
+ if (it != mTypeToIndexForConfig.end() && it->second != i) {
+ continue;
+ }
+ paramsForFilter.push_back(param);
+ }
+ c2_status_t err = filter->config_vb(paramsForFilter, mayBlock, &filterFailures);
+ if (err != C2_OK) {
+ LOG(err == C2_BAD_INDEX ? VERBOSE : WARNING)
+ << "WrappedDecoderInterface: " << filter->getName()
+ << " returned error for config_vb; err=" << err;
+ result = err;
+ }
+ }
+
+ return result;
+ }
+
+ c2_status_t createTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+ c2_status_t releaseTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+
+ c2_status_t querySupportedParams_nb(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
+ std::unique_lock lock(mMutex);
+ c2_status_t result = mIntf->querySupportedParams_nb(params);
+ if (result != C2_OK) {
+ LOG(WARNING) << "WrappedDecoderInterface: " << mIntf->getName()
+ << " returned error for querySupportedParams_nb; err=" << result;
+ return result;
+ }
+ // TODO: optimization idea --- pre-compute at takeFilter().
+ for (const FilterWrapper::Component &filter : mFilters) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> filterParams;
+ c2_status_t err = filter.intf->querySupportedParams_nb(&filterParams);
+ if (err != C2_OK) {
+ LOG(WARNING) << "WrappedDecoderInterface: " << filter.intf->getName()
+ << " returned error for querySupportedParams_nb; err=" << result;
+ result = err;
+ continue;
+ }
+ for (const std::shared_ptr<C2ParamDescriptor> ¶mDesc : filterParams) {
+ if (std::count(
+ filter.desc.controlParams.begin(),
+ filter.desc.controlParams.end(),
+ paramDesc->index().type()) == 0) {
+ continue;
+ }
+ params->push_back(paramDesc);
+ }
+ }
+ return result;
+ }
+
+ c2_status_t querySupportedValues_vb(
+ std::vector<C2FieldSupportedValuesQuery> &fields,
+ c2_blocking_t mayBlock) const override {
+ std::unique_lock lock(mMutex);
+ c2_status_t result = mIntf->querySupportedValues_vb(fields, mayBlock);
+ if (result != C2_OK && result != C2_BAD_INDEX) {
+ LOG(WARNING) << "WrappedDecoderInterface: " << mIntf->getName()
+ << " returned error for querySupportedParams_nb; err=" << result;
+ return result;
+ }
+ for (const FilterWrapper::Component &filter : mFilters) {
+ std::vector<C2FieldSupportedValuesQuery> filterFields;
+ std::vector<size_t> indices;
+ for (size_t i = 0; i < fields.size(); ++i) {
+ const C2FieldSupportedValuesQuery &field = fields[i];
+ uint32_t type = C2Param::Index(_C2ParamInspector::GetIndex(field.field())).type();
+ if (std::count(
+ filter.desc.controlParams.begin(),
+ filter.desc.controlParams.end(),
+ type) == 0) {
+ continue;
+ }
+ filterFields.push_back(field);
+ indices.push_back(i);
+ }
+ c2_status_t err = filter.intf->querySupportedValues_vb(filterFields, mayBlock);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
+ LOG(WARNING) << "WrappedDecoderInterface: " << filter.intf->getName()
+ << " returned error for querySupportedParams_nb; err=" << result;
+ result = err;
+ continue;
+ }
+ for (size_t i = 0; i < filterFields.size(); ++i) {
+ fields[indices[i]] = filterFields[i];
+ }
+ }
+ return result;
+ }
+
+private:
+ mutable std::mutex mMutex;
+ std::shared_ptr<C2ComponentInterface> mIntf;
+ std::vector<FilterWrapper::Component> mFilters;
+ std::map<uint32_t, size_t> mTypeToIndexForQuery;
+ std::map<uint32_t, size_t> mTypeToIndexForConfig;
+
+ c2_status_t transferParams_l(
+ const std::shared_ptr<C2ComponentInterface> &curr,
+ const std::shared_ptr<C2ComponentInterface> &next,
+ c2_blocking_t mayBlock) {
+ // NOTE: this implementation is preliminary --- it could change once
+ // we define what parameters needs to be propagated in component chaining.
+ std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
+ c2_status_t err = next->querySupportedParams_nb(¶mDescs);
+ if (err != C2_OK) {
+ LOG(DEBUG) << "WrappedDecoderInterface: " << next->getName()
+ << " returned error for querySupportedParams_nb; err=" << err;
+ return err;
+ }
+ // Find supported input params from the next interface and flip direction
+ // so they become output params.
+ std::vector<C2Param::Index> indices;
+ for (const std::shared_ptr<C2ParamDescriptor> ¶mDesc : paramDescs) {
+ C2Param::Index index = paramDesc->index();
+ if (!index.forInput() || paramDesc->isReadOnly()) {
+ continue;
+ }
+ if (index.forStream()) {
+ uint32_t stream = index.stream();
+ index = index.withPort(true /* output */).withStream(stream);
+ } else {
+ index = index.withPort(true /* output */);
+ }
+ indices.push_back(index);
+ }
+ // Query those output params from the current interface
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = curr->query_vb({}, indices, mayBlock, &heapParams);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
+ LOG(DEBUG) << "WrappedDecoderInterface: " << curr->getName()
+ << " returned error for query_vb; err=" << err;
+ return err;
+ }
+ // Flip the direction of the queried params, so they become input parameters.
+ // Configure the next interface with the params.
+ std::vector<C2Param *> configParams;
+ for (size_t i = 0; i < heapParams.size(); ++i) {
+ if (heapParams[i]->forStream()) {
+ heapParams[i] = C2Param::CopyAsStream(
+ *heapParams[i], false /* output */, heapParams[i]->stream());
+ } else {
+ heapParams[i] = C2Param::CopyAsPort(*heapParams[i], false /* output */);
+ }
+ configParams.push_back(heapParams[i].get());
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ err = next->config_vb(configParams, mayBlock, &failures);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
+ LOG(DEBUG) << "WrappedDecoderInterface: " << next->getName()
+ << " returned error for config_vb; err=" << err;
+ return err;
+ }
+ return C2_OK;
+ }
+};
+
+class WrappedDecoder : public C2Component, public std::enable_shared_from_this<WrappedDecoder> {
+public:
+ WrappedDecoder(
+ std::shared_ptr<C2Component> comp,
+ std::vector<FilterWrapper::Component> &&filters,
+ std::weak_ptr<FilterWrapper> filterWrapper)
+ : mComp(comp), mFilters(std::move(filters)), mFilterWrapper(filterWrapper) {
+ std::vector<FilterWrapper::Component> filtersDup(mFilters);
+ mIntf = std::make_shared<WrappedDecoderInterface>(
+ comp->intf(), std::move(filtersDup));
+ }
+
+ ~WrappedDecoder() override = default;
+
+ std::shared_ptr<C2ComponentInterface> intf() override { return mIntf; }
+
+ c2_status_t setListener_vb(
+ const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override {
+ if (listener) {
+ setListenerInternal(mFilters, listener, mayBlock);
+ } else {
+ mComp->setListener_vb(nullptr, mayBlock);
+ for (FilterWrapper::Component &filter : mFilters) {
+ filter.comp->setListener_vb(nullptr, mayBlock);
+ }
+ }
+ mListener = listener;
+ return C2_OK;
+ }
+
+ c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override {
+ return mComp->queue_nb(items);
+ }
+
+ c2_status_t announce_nb(const std::vector<C2WorkOutline> &) override {
+ return C2_OMITTED;
+ }
+
+ c2_status_t flush_sm(
+ flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override {
+ c2_status_t result = mComp->flush_sm(mode, flushedWork);
+ std::list<std::unique_ptr<C2Work>> filterFlushedWork;
+ for (FilterWrapper::Component filter : mRunningFilters) {
+ c2_status_t err = filter.comp->flush_sm(mode, &filterFlushedWork);
+ if (err != C2_OK) {
+ result = err;
+ }
+ flushedWork->splice(flushedWork->end(), filterFlushedWork);
+ }
+ return result;
+ }
+
+ c2_status_t drain_nb(drain_mode_t mode) override {
+ // TODO: simplify using comp->drain_nb(mode)
+ switch (mode) {
+ case DRAIN_COMPONENT_WITH_EOS: {
+ std::unique_ptr<C2Work> eosWork{new C2Work};
+ eosWork->input.flags = C2FrameData::FLAG_END_OF_STREAM;
+ eosWork->worklets.push_back(std::make_unique<C2Worklet>());
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(eosWork));
+ mComp->queue_nb(&items);
+ return C2_OK;
+ }
+ case DRAIN_COMPONENT_NO_EOS:
+ case DRAIN_CHAIN:
+ default:
+ return C2_BAD_VALUE;
+ }
+ }
+
+ c2_status_t start() override {
+ std::vector<FilterWrapper::Component> filters;
+ if (std::shared_ptr<FilterWrapper> filterWrapper = mFilterWrapper.lock()) {
+ // Let's check if we have filters that we can skip
+ for (FilterWrapper::Component &filter : mFilters) {
+ if (!filterWrapper->isFilteringEnabled(filter.intf)) {
+ LOG(VERBOSE) << "filtering disabled for " << filter.traits.name;
+ continue;
+ }
+ LOG(VERBOSE) << "filtering enabled for " << filter.traits.name;
+ filters.push_back(filter);
+ }
+ if (filters.size() < mFilters.size()) {
+ LOG(VERBOSE) << (mFilters.size() - filters.size()) << " filter(s) skipped";
+ setListenerInternal(filters, mListener, C2_MAY_BLOCK);
+ std::vector filtersCopy(filters);
+ mIntf->takeFilters(std::move(filtersCopy));
+ }
+ }
+
+ c2_status_t err = mComp->start();
+ if (err != C2_OK) {
+ return err;
+ }
+ for (FilterWrapper::Component &filter : filters) {
+ c2_status_t err = filter.comp->start();
+ if (err != C2_OK) {
+ // Previous components are already started successfully;
+ // we ended up in an incoherent state.
+ return C2_CORRUPTED;
+ }
+ }
+ mRunningFilters = std::move(filters);
+ return C2_OK;
+ }
+
+ c2_status_t stop() override {
+ c2_status_t err = mComp->stop();
+ if (err != C2_OK) {
+ return err;
+ }
+ for (FilterWrapper::Component filter : mRunningFilters) {
+ c2_status_t err = filter.comp->stop();
+ if (err != C2_OK) {
+ // Previous components are already stopped successfully;
+ // we ended up in an incoherent state.
+ return C2_CORRUPTED;
+ }
+ }
+ mRunningFilters.clear();
+ return C2_OK;
+ }
+
+ c2_status_t reset() override {
+ c2_status_t result = mComp->reset();
+ if (result != C2_OK) {
+ result = C2_CORRUPTED;
+ }
+ for (FilterWrapper::Component filter : mFilters) {
+ c2_status_t err = filter.comp->reset();
+ if (err != C2_OK) {
+ // Previous components are already reset successfully;
+ // we ended up in an incoherent state.
+ result = C2_CORRUPTED;
+ // continue for the rest of the chain
+ }
+ }
+ mRunningFilters.clear();
+ return result;
+ }
+
+ c2_status_t release() override {
+ c2_status_t result = mComp->release();
+ if (result != C2_OK) {
+ result = C2_CORRUPTED;
+ }
+ for (FilterWrapper::Component filter : mFilters) {
+ c2_status_t err = filter.comp->release();
+ if (err != C2_OK) {
+ // Previous components are already released successfully;
+ // we ended up in an incoherent state.
+ result = C2_CORRUPTED;
+ // continue for the rest of the chain
+ }
+ }
+ mRunningFilters.clear();
+ return result;
+ }
+
+private:
+ class PassingListener : public Listener {
+ public:
+ PassingListener(
+ std::shared_ptr<C2Component> wrappedComponent,
+ const std::shared_ptr<Listener> &wrappedComponentListener,
+ std::shared_ptr<C2Component> nextComponent)
+ : mWrappedComponent(wrappedComponent),
+ mWrappedComponentListener(wrappedComponentListener),
+ mNextComponent(nextComponent) {
+ }
+
+ void onWorkDone_nb(
+ std::weak_ptr<C2Component>,
+ std::list<std::unique_ptr<C2Work>> workItems) override {
+ std::shared_ptr<C2Component> nextComponent = mNextComponent.lock();
+ std::list<std::unique_ptr<C2Work>> failedWorkItems;
+ if (!nextComponent) {
+ for (std::unique_ptr<C2Work> &work : workItems) {
+ // Next component unexpectedly released while the work is
+ // in-flight. Report C2_CORRUPTED to the client.
+ work->result = C2_CORRUPTED;
+ failedWorkItems.push_back(std::move(work));
+ }
+ workItems.clear();
+ } else {
+ for (auto it = workItems.begin(); it != workItems.end(); ) {
+ const std::unique_ptr<C2Work> &work = *it;
+ if (work->result != C2_OK
+ || work->worklets.size() != 1) {
+ failedWorkItems.push_back(std::move(*it));
+ it = workItems.erase(it);
+ continue;
+ }
+ C2FrameData &output = work->worklets.front()->output;
+ c2_cntr64_t customOrdinal = work->input.ordinal.customOrdinal;
+ work->input = std::move(output);
+ work->input.ordinal.customOrdinal = customOrdinal;
+ output.flags = C2FrameData::flags_t(0);
+ output.buffers.clear();
+ output.configUpdate.clear();
+ output.infoBuffers.clear();
+ ++it;
+ }
+ }
+ if (!failedWorkItems.empty()) {
+ for (const std::unique_ptr<C2Work> &work : failedWorkItems) {
+ LOG(VERBOSE) << "work #" << work->input.ordinal.frameIndex.peek()
+ << " failed: err=" << work->result
+ << " worklets.size()=" << work->worklets.size();
+ }
+ if (std::shared_ptr<Listener> wrappedComponentListener =
+ mWrappedComponentListener.lock()) {
+ wrappedComponentListener->onWorkDone_nb(
+ mWrappedComponent, std::move(failedWorkItems));
+ }
+ }
+ if (!workItems.empty()) {
+ nextComponent->queue_nb(&workItems);
+ }
+ }
+
+ void onTripped_nb(
+ std::weak_ptr<C2Component>,
+ std::vector<std::shared_ptr<C2SettingResult>>) override {
+ // Trip not supported
+ }
+
+ void onError_nb(std::weak_ptr<C2Component>, uint32_t errorCode) {
+ if (std::shared_ptr<Listener> wrappedComponentListener =
+ mWrappedComponentListener.lock()) {
+ wrappedComponentListener->onError_nb(mWrappedComponent, errorCode);
+ }
+ }
+
+ private:
+ std::weak_ptr<C2Component> mWrappedComponent;
+ std::weak_ptr<Listener> mWrappedComponentListener;
+ std::weak_ptr<C2Component> mNextComponent;
+ };
+
+ class LastListener : public Listener {
+ public:
+ LastListener(
+ std::shared_ptr<C2Component> wrappedComponent,
+ const std::shared_ptr<Listener> &wrappedComponentListener)
+ : mWrappedComponent(wrappedComponent),
+ mWrappedComponentListener(wrappedComponentListener) {
+ }
+
+ void onWorkDone_nb(
+ std::weak_ptr<C2Component>,
+ std::list<std::unique_ptr<C2Work>> workItems) override {
+ if (mWrappedComponent.expired()) {
+ return;
+ }
+ if (std::shared_ptr<Listener> wrappedComponentListener =
+ mWrappedComponentListener.lock()) {
+ wrappedComponentListener->onWorkDone_nb(
+ mWrappedComponent, std::move(workItems));
+ }
+ }
+
+ void onTripped_nb(
+ std::weak_ptr<C2Component>,
+ std::vector<std::shared_ptr<C2SettingResult>>) override {
+ // Trip not supported
+ }
+
+ void onError_nb(std::weak_ptr<C2Component>, uint32_t errorCode) {
+ if (mWrappedComponent.expired()) {
+ return;
+ }
+ if (std::shared_ptr<Listener> wrappedComponentListener =
+ mWrappedComponentListener.lock()) {
+ wrappedComponentListener->onError_nb(mWrappedComponent, errorCode);
+ }
+ }
+
+ private:
+ std::weak_ptr<C2Component> mWrappedComponent;
+ std::weak_ptr<Listener> mWrappedComponentListener;
+ };
+
+ std::shared_ptr<C2Component> mComp;
+ std::shared_ptr<WrappedDecoderInterface> mIntf;
+ std::vector<FilterWrapper::Component> mFilters;
+ std::vector<FilterWrapper::Component> mRunningFilters;
+ std::weak_ptr<FilterWrapper> mFilterWrapper;
+ std::shared_ptr<Listener> mListener;
+#if defined(LOG_NDEBUG) && !LOG_NDEBUG
+ base::ScopedLogSeverity mScopedLogSeverity{base::VERBOSE};
+#endif
+
+ c2_status_t setListenerInternal(
+ const std::vector<FilterWrapper::Component> &filters,
+ const std::shared_ptr<Listener> &listener,
+ c2_blocking_t mayBlock) {
+ if (filters.empty()) {
+ return mComp->setListener_vb(listener, mayBlock);
+ }
+ std::shared_ptr passingListener = std::make_shared<PassingListener>(
+ shared_from_this(),
+ listener,
+ filters.front().comp);
+ mComp->setListener_vb(passingListener, mayBlock);
+ for (size_t i = 0; i < filters.size() - 1; ++i) {
+ filters[i].comp->setListener_vb(
+ std::make_shared<PassingListener>(
+ shared_from_this(),
+ listener,
+ filters[i + 1].comp),
+ mayBlock);
+ }
+ filters.back().comp->setListener_vb(
+ std::make_shared<LastListener>(shared_from_this(), listener), mayBlock);
+ return C2_OK;
+ }
+};
+
+} // anonymous namespace
+
+FilterWrapper::FilterWrapper(std::unique_ptr<Plugin> &&plugin)
+ : mInit(NO_INIT),
+ mPlugin(std::move(plugin)) {
+ if (mPlugin->status() != OK) {
+ LOG(ERROR) << "plugin not OK: " << mPlugin->status();
+ mPlugin.reset();
+ return;
+ }
+ mStore = mPlugin->getStore();
+ if (!mStore) {
+ LOG(ERROR) << "no store";
+ mPlugin.reset();
+ return;
+ }
+ std::vector<std::shared_ptr<const C2Component::Traits>> traits =
+ mStore->listComponents();
+ std::sort(
+ traits.begin(),
+ traits.end(),
+ [](std::shared_ptr<const C2Component::Traits> &a,
+ std::shared_ptr<const C2Component::Traits> &b) {
+ return a->rank < b->rank;
+ });
+ for (size_t i = 0; i < traits.size(); ++i) {
+ const std::shared_ptr<const C2Component::Traits> &trait = traits[i];
+ if (trait->domain == C2Component::DOMAIN_OTHER
+ || trait->domain == C2Component::DOMAIN_AUDIO
+ || trait->kind != C2Component::KIND_OTHER) {
+ LOG(DEBUG) << trait->name << " is ignored because of domain/kind: "
+ << trait->domain << "/" << trait->kind;
+ continue;
+ }
+ Descriptor desc;
+ if (!mPlugin->describe(trait->name, &desc)) {
+ LOG(DEBUG) << trait->name << " is ignored because describe() failed";
+ continue;
+ }
+ mComponents.push_back({nullptr, nullptr, *trait, desc});
+ }
+ if (mComponents.empty()) {
+ LOG(DEBUG) << "FilterWrapper: no filter component found";
+ mPlugin.reset();
+ return;
+ }
+ mInit = OK;
+}
+
+FilterWrapper::~FilterWrapper() {
+}
+
+std::vector<FilterWrapper::Component> FilterWrapper::createFilters() {
+ std::vector<FilterWrapper::Component> filters;
+ for (const FilterWrapper::Component &filter : mComponents) {
+ std::shared_ptr<C2Component> comp;
+ std::shared_ptr<C2ComponentInterface> intf;
+ if (C2_OK != mStore->createComponent(filter.traits.name, &comp)) {
+ return {};
+ }
+ if (C2_OK != mStore->createInterface(filter.traits.name, &intf)) {
+ return {};
+ }
+ filters.push_back({comp, intf, filter.traits, filter.desc});
+ }
+ return filters;
+}
+
+C2Component::Traits FilterWrapper::getTraits(
+ const std::shared_ptr<C2ComponentInterface> &intf) {
+ {
+ std::unique_lock lock(mCacheMutex);
+ if (mCachedTraits.count(intf->getName())) {
+ return mCachedTraits.at(intf->getName());
+ }
+ }
+ C2ComponentDomainSetting domain;
+ C2ComponentKindSetting kind;
+ c2_status_t err = intf->query_vb({&domain, &kind}, {}, C2_MAY_BLOCK, nullptr);
+ C2Component::Traits traits = {
+ "query failed", // name
+ C2Component::DOMAIN_OTHER,
+ C2Component::KIND_OTHER,
+ 0, // rank, unused
+ "", // media type, unused
+ "", // owner, unused
+ {}, // aliases, unused
+ };
+ if (err == C2_OK) {
+ traits = {
+ intf->getName(),
+ domain.value,
+ kind.value,
+ 0, // rank, unused
+ "", // media type, unused
+ "", // owner, unused
+ {}, // aliases, unused
+ };
+ std::unique_lock lock(mCacheMutex);
+ mCachedTraits[traits.name] = traits;
+ }
+ return traits;
+}
+
+std::shared_ptr<C2ComponentInterface> FilterWrapper::maybeWrapInterface(
+ const std::shared_ptr<C2ComponentInterface> intf) {
+ if (mInit != OK) {
+ LOG(VERBOSE) << "maybeWrapInterface: Wrapper not initialized: "
+ << intf->getName() << " is not wrapped.";
+ return intf;
+ }
+ C2Component::Traits traits = getTraits(intf);
+ if (traits.name != intf->getName()) {
+ LOG(INFO) << "maybeWrapInterface: Querying traits from " << intf->getName()
+ << " failed; not wrapping the interface";
+ return intf;
+ }
+ if ((traits.domain != C2Component::DOMAIN_VIDEO && traits.domain != C2Component::DOMAIN_IMAGE)
+ || traits.kind != C2Component::KIND_DECODER) {
+ LOG(VERBOSE) << "maybeWrapInterface: " << traits.name
+ << " is not video/image decoder; not wrapping the interface";
+ return intf;
+ }
+ return std::make_shared<WrappedDecoderInterface>(intf, createFilters());
+}
+
+std::shared_ptr<C2Component> FilterWrapper::maybeWrapComponent(
+ const std::shared_ptr<C2Component> comp) {
+ if (mInit != OK) {
+ LOG(VERBOSE) << "maybeWrapComponent: Wrapper not initialized: "
+ << comp->intf()->getName() << " is not wrapped.";
+ return comp;
+ }
+ C2Component::Traits traits = getTraits(comp->intf());
+ if (traits.name != comp->intf()->getName()) {
+ LOG(INFO) << "maybeWrapComponent: Querying traits from " << comp->intf()->getName()
+ << " failed; not wrapping the component";
+ return comp;
+ }
+ if ((traits.domain != C2Component::DOMAIN_VIDEO && traits.domain != C2Component::DOMAIN_IMAGE)
+ || traits.kind != C2Component::KIND_DECODER) {
+ LOG(VERBOSE) << "maybeWrapComponent: " << traits.name
+ << " is not video/image decoder; not wrapping the component";
+ return comp;
+ }
+ std::vector<Component> filters = createFilters();
+ std::shared_ptr wrapped = std::make_shared<WrappedDecoder>(
+ comp, std::move(filters), weak_from_this());
+ {
+ std::unique_lock lock(mWrappedComponentsMutex);
+ std::vector<std::weak_ptr<const C2Component>> &components =
+ mWrappedComponents.emplace_back();
+ components.push_back(wrapped);
+ components.push_back(comp);
+ for (const Component &filter : filters) {
+ components.push_back(filter.comp);
+ }
+ }
+ return wrapped;
+}
+
+bool FilterWrapper::isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) {
+ if (mInit != OK) {
+ LOG(WARNING) << "isFilteringEnabled: Wrapper not initialized: ";
+ return false;
+ }
+ return mPlugin->isFilteringEnabled(intf);
+}
+
+c2_status_t FilterWrapper::createBlockPool(
+ C2PlatformAllocatorStore::id_t allocatorId,
+ std::shared_ptr<const C2Component> component,
+ std::shared_ptr<C2BlockPool> *pool) {
+ std::unique_lock lock(mWrappedComponentsMutex);
+ for (auto it = mWrappedComponents.begin(); it != mWrappedComponents.end(); ) {
+ std::shared_ptr<const C2Component> comp = it->front().lock();
+ if (!comp) {
+ it = mWrappedComponents.erase(it);
+ continue;
+ }
+ if (component == comp) {
+ std::vector<std::shared_ptr<const C2Component>> components(it->size());
+ std::transform(
+ it->begin(), it->end(), components.begin(),
+ [](const std::weak_ptr<const C2Component> &el) {
+ return el.lock();
+ });
+ if (C2_OK == CreateCodec2BlockPool(allocatorId, components, pool)) {
+ return C2_OK;
+ }
+ }
+ ++it;
+ }
+ return CreateCodec2BlockPool(allocatorId, component, pool);
+}
+
+} // namespace android
diff --git a/media/codec2/hidl/plugin/FilterWrapperStub.cpp b/media/codec2/hidl/plugin/FilterWrapperStub.cpp
new file mode 100644
index 0000000..1b94a1a
--- /dev/null
+++ b/media/codec2/hidl/plugin/FilterWrapperStub.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 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-FilterWrapperStub"
+
+#include <FilterWrapper.h>
+
+namespace android {
+
+FilterWrapper::FilterWrapper(std::unique_ptr<Plugin> &&) {
+}
+
+FilterWrapper::~FilterWrapper() {
+}
+
+std::shared_ptr<C2ComponentInterface> FilterWrapper::maybeWrapInterface(
+ const std::shared_ptr<C2ComponentInterface> intf) {
+ return intf;
+}
+
+std::shared_ptr<C2Component> FilterWrapper::maybeWrapComponent(
+ const std::shared_ptr<C2Component> comp) {
+ return comp;
+}
+
+bool FilterWrapper::isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &) {
+ return false;
+}
+
+c2_status_t FilterWrapper::createBlockPool(
+ C2PlatformAllocatorStore::id_t,
+ std::shared_ptr<const C2Component>,
+ std::shared_ptr<C2BlockPool> *) {
+ return C2_OMITTED;
+}
+
+} // namespace android
diff --git a/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h b/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h
new file mode 100644
index 0000000..6f1f907
--- /dev/null
+++ b/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018, 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 CODEC2_HIDL_PLUGIN_FILTER_PLUGIN_H
+
+#define CODEC2_HIDL_PLUGIN_FILTER_PLUGIN_H
+
+#include <memory>
+
+#include <C2Component.h>
+
+namespace android {
+
+class FilterPlugin_V1 {
+public:
+ static constexpr int32_t VERSION = 1;
+
+ virtual ~FilterPlugin_V1() = default;
+
+ /**
+ * Returns a C2ComponentStore object with which clients can create
+ * filter components / interfaces.
+ */
+ virtual std::shared_ptr<C2ComponentStore> getComponentStore() = 0;
+ struct Descriptor {
+ // Parameters that client sets for filter control.
+ std::initializer_list<C2Param::Type> controlParams;
+ // Parameters that the component changes after filtering.
+ std::initializer_list<C2Param::Type> affectedParams;
+ };
+
+ /**
+ * Describe a filter component.
+ *
+ * @param name[in] filter's name
+ * @param desc[out] pointer to filter descriptor to be populated
+ * @return true if |name| is in the store and |desc| is populated;
+ * false if |name| is not recognized
+ */
+ virtual bool describe(C2String name, Descriptor *desc) = 0;
+
+ /**
+ * Returns true if a component will apply filtering after all given the
+ * current configuration; false if it will be no-op.
+ */
+ virtual bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) = 0;
+};
+
+} // namespace android
+
+extern "C" {
+
+typedef int32_t (*GetFilterPluginVersionFunc)();
+int32_t GetFilterPluginVersion();
+
+typedef void* (*CreateFilterPluginFunc)();
+void *CreateFilterPlugin();
+
+typedef void (*DestroyFilterPluginFunc)(void *);
+void DestroyFilterPlugin(void *plugin);
+
+} // extern "C"
+
+#endif // CODEC2_HIDL_PLUGIN_FILTER_PLUGIN_H
diff --git a/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h b/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h
new file mode 100644
index 0000000..f856324
--- /dev/null
+++ b/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020, 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 CODEC2_HIDL_PLUGIN_DEFAULT_FILTER_PLUGIN_H
+
+#define CODEC2_HIDL_PLUGIN_DEFAULT_FILTER_PLUGIN_H
+
+#include <codec2/hidl/plugin/FilterPlugin.h>
+
+#include <FilterWrapper.h>
+
+namespace android {
+
+class DefaultFilterPlugin : public FilterWrapper::Plugin {
+public:
+ explicit DefaultFilterPlugin(const char *pluginPath);
+
+ ~DefaultFilterPlugin();
+
+ status_t status() const override { return mInit; }
+
+ std::shared_ptr<C2ComponentStore> getStore() override { return mStore; }
+ bool describe(C2String name, FilterWrapper::Descriptor *desc) override;
+ bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) override;
+
+private:
+ status_t mInit;
+ void *mHandle;
+ DestroyFilterPluginFunc mDestroyPlugin;
+ FilterPlugin_V1 *mPlugin;
+ std::shared_ptr<C2ComponentStore> mStore;
+};
+
+} // namespace android
+
+#endif // CODEC2_HIDL_PLUGIN_DEFAULT_FILTER_PLUGIN_H
diff --git a/media/codec2/hidl/plugin/internal/FilterWrapper.h b/media/codec2/hidl/plugin/internal/FilterWrapper.h
new file mode 100644
index 0000000..5ced435
--- /dev/null
+++ b/media/codec2/hidl/plugin/internal/FilterWrapper.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2020, 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 CODEC2_HIDL_PLUGIN_FILTER_WRAPPER_H
+
+#define CODEC2_HIDL_PLUGIN_FILTER_WRAPPER_H
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include <C2Component.h>
+#include <C2PlatformSupport.h>
+
+#include <codec2/hidl/plugin/FilterPlugin.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+// TODO: documentation
+class FilterWrapper : public std::enable_shared_from_this<FilterWrapper> {
+public:
+ using Descriptor = FilterPlugin_V1::Descriptor;
+
+ class Plugin {
+ public:
+ Plugin() = default;
+ virtual ~Plugin() = default;
+ virtual status_t status() const = 0;
+ virtual std::shared_ptr<C2ComponentStore> getStore() = 0;
+ virtual bool describe(C2String name, Descriptor *desc) = 0;
+ virtual bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) = 0;
+ C2_DO_NOT_COPY(Plugin);
+ };
+
+ struct Component {
+ const std::shared_ptr<C2Component> comp;
+ const std::shared_ptr<C2ComponentInterface> intf;
+ const C2Component::Traits traits;
+ const Descriptor desc;
+ };
+
+private:
+ explicit FilterWrapper(std::unique_ptr<Plugin> &&plugin);
+public:
+ static std::shared_ptr<FilterWrapper> Create(std::unique_ptr<Plugin> &&plugin) {
+ return std::shared_ptr<FilterWrapper>(new FilterWrapper(std::move(plugin)));
+ }
+ ~FilterWrapper();
+
+ /**
+ * Returns wrapped interface, or |intf| if wrapping is not possible / needed.
+ */
+ std::shared_ptr<C2ComponentInterface> maybeWrapInterface(
+ const std::shared_ptr<C2ComponentInterface> intf);
+
+ /**
+ * Returns wrapped component, or |comp| if wrapping is not possible / needed.
+ */
+ std::shared_ptr<C2Component> maybeWrapComponent(
+ const std::shared_ptr<C2Component> comp);
+
+ /**
+ * Returns ture iff the filtering will apply to the buffer in current configuration.
+ */
+ bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf);
+
+ c2_status_t createBlockPool(
+ C2PlatformAllocatorStore::id_t allocatorId,
+ std::shared_ptr<const C2Component> component,
+ std::shared_ptr<C2BlockPool> *pool);
+
+private:
+ status_t mInit;
+ std::unique_ptr<Plugin> mPlugin;
+ std::shared_ptr<C2ComponentStore> mStore;
+ std::list<FilterWrapper::Component> mComponents;
+
+ std::mutex mCacheMutex;
+ std::map<std::string, C2Component::Traits> mCachedTraits;
+
+ std::mutex mWrappedComponentsMutex;
+ std::list<std::vector<std::weak_ptr<const C2Component>>> mWrappedComponents;
+
+ std::vector<FilterWrapper::Component> createFilters();
+ C2Component::Traits getTraits(const std::shared_ptr<C2ComponentInterface> &intf);
+
+ C2_DO_NOT_COPY(FilterWrapper);
+};
+
+} // namespace android
+
+#endif // CODEC2_HIDL_PLUGIN_FILTER_WRAPPER_H