| /* |
| * 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 <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, |
| std::weak_ptr<FilterWrapper> filterWrapper) |
| : mIntf(intf), mFilterWrapper(filterWrapper) { |
| 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; |
| } |
| } |
| for (size_t i = mFilters.size(); i > 0; --i) { |
| if (i == 1) { |
| backPropagateParams_l(mIntf, mFilters[0].intf, C2_MAY_BLOCK); |
| } else { |
| backPropagateParams_l(mFilters[i - 2].intf, mFilters[i - 1].intf, C2_MAY_BLOCK); |
| } |
| } |
| 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; |
| } |
| } |
| for (size_t i = mFilters.size(); i > 0; --i) { |
| if (i == 1) { |
| backPropagateParams_l(mIntf, mFilters[0].intf, mayBlock); |
| } else { |
| backPropagateParams_l(mFilters[i - 2].intf, mFilters[i - 1].intf, mayBlock); |
| } |
| } |
| |
| 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::weak_ptr<FilterWrapper> mFilterWrapper; |
| 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]) { |
| continue; |
| } |
| 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; |
| } |
| |
| c2_status_t backPropagateParams_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::shared_ptr<FilterWrapper> filterWrapper = mFilterWrapper.lock(); |
| if (!filterWrapper) { |
| LOG(DEBUG) << "WrappedDecoderInterface: FilterWrapper not found"; |
| return C2_OK; |
| } |
| std::vector<std::unique_ptr<C2Param>> params; |
| c2_status_t err = filterWrapper->queryParamsForPreviousComponent(next, ¶ms); |
| if (err != C2_OK) { |
| LOG(DEBUG) << "WrappedDecoderInterface: FilterWrapper returned error for " |
| << "queryParamsForPreviousComponent; intf=" << next->getName() << " err=" << err; |
| return C2_OK; |
| } |
| std::vector<C2Param *> configParams; |
| for (size_t i = 0; i < params.size(); ++i) { |
| if (!params[i]) { |
| continue; |
| } |
| configParams.push_back(params[i].get()); |
| } |
| std::vector<std::unique_ptr<C2SettingResult>> failures; |
| curr->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), filterWrapper); |
| } |
| |
| ~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 {}; |
| } |
| filters.push_back({comp, 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(), weak_from_this()); |
| } |
| |
| 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::vector(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); |
| } |
| |
| c2_status_t FilterWrapper::queryParamsForPreviousComponent( |
| const std::shared_ptr<C2ComponentInterface> &intf, |
| std::vector<std::unique_ptr<C2Param>> *params) { |
| if (mInit != OK) { |
| LOG(WARNING) << "queryParamsForPreviousComponent: Wrapper not initialized: "; |
| return C2_NO_INIT; |
| } |
| return mPlugin->queryParamsForPreviousComponent(intf, params); |
| } |
| |
| } // namespace android |