codec2: add a sample filter plugin
Bug: 160259947
Test: manual
Change-Id: I1f78ae608ad1d139824b91587727badddc11e7a0
diff --git a/media/codec2/hidl/plugin/samples/Android.bp b/media/codec2/hidl/plugin/samples/Android.bp
new file mode 100644
index 0000000..c823e31
--- /dev/null
+++ b/media/codec2/hidl/plugin/samples/Android.bp
@@ -0,0 +1,49 @@
+cc_defaults {
+ name: "sample-codec2-hidl-plugin-defaults",
+
+ srcs: [
+ "SampleFilterPlugin.cpp",
+ ],
+
+ defaults: [
+ "libcodec2-impl-defaults",
+ ],
+
+ header_libs: [
+ "libcodec2_hidl_plugin_headers",
+ "libgui_headers",
+ ],
+
+ shared_libs: [
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libGLESv3",
+ "libbase",
+ "libcodec2",
+ "libcutils",
+ "libprocessgroup",
+ "libsfplugin_ccodec_utils",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+
+ static_libs: [
+ "librenderfright",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
+
+cc_library {
+ name: "sample-codec2-hidl-plugin",
+ vendor: true,
+
+ defaults: [
+ "sample-codec2-hidl-plugin-defaults",
+ ],
+}
diff --git a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
new file mode 100644
index 0000000..94811fc
--- /dev/null
+++ b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
@@ -0,0 +1,969 @@
+/*
+ * 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_TAG "SampleFilterPlugin"
+#include <android-base/logging.h>
+
+#include <chrono>
+#include <thread>
+
+#include <codec2/hidl/plugin/FilterPlugin.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2Config.h>
+#include <C2PlatformSupport.h>
+#include <Codec2Mapper.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <renderengine/RenderEngine.h>
+#include <system/window.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+
+typedef C2StreamParam<C2Info, C2ColorAspectsStruct,
+ kParamIndexColorAspects | C2Param::CoreIndex::IS_REQUEST_FLAG>
+ C2StreamColorAspectsRequestInfo;
+
+namespace android {
+
+using namespace std::literals::chrono_literals;
+
+class SampleToneMappingFilter
+ : public C2Component, public std::enable_shared_from_this<SampleToneMappingFilter> {
+public:
+ class Interface : public C2ComponentInterface {
+ public:
+ static const std::string NAME;
+ static const FilterPlugin_V1::Descriptor DESCRIPTOR;
+
+ explicit Interface(c2_node_id_t id)
+ : mId(id),
+ mReflector(std::make_shared<C2ReflectorHelper>()),
+ mHelper(mReflector) {
+ }
+ ~Interface() override = default;
+ C2String getName() const override { return NAME; }
+ c2_node_id_t getId() const override { return mId; }
+
+ 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 {
+ return mHelper.query(stackParams, heapParamIndices, mayBlock, heapParams);
+ }
+ c2_status_t config_vb(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+ return mHelper.config(params, mayBlock, failures);
+ }
+ c2_status_t querySupportedParams_nb(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
+ return mHelper.querySupportedParams(params);
+ }
+ c2_status_t querySupportedValues_vb(
+ std::vector<C2FieldSupportedValuesQuery> &fields,
+ c2_blocking_t mayBlock) const override {
+ return mHelper.querySupportedValues(fields, mayBlock);
+ }
+ 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; }
+
+ uint32_t getDataSpace() {
+ Helper::Lock lock = mHelper.lock();
+ uint32_t dataspace = HAL_DATASPACE_UNKNOWN;
+ C2Mapper::map(
+ mHelper.mInputColorAspectInfo->range,
+ mHelper.mInputColorAspectInfo->primaries,
+ mHelper.mInputColorAspectInfo->matrix,
+ mHelper.mInputColorAspectInfo->transfer,
+ &dataspace);
+ return dataspace;
+ }
+ std::shared_ptr<C2StreamHdrStaticInfo::input> getHdrStaticMetadata() {
+ Helper::Lock lock = mHelper.lock();
+ return mHelper.mInputHdrStaticInfo;
+ }
+ C2BlockPool::local_id_t getPoolId() {
+ Helper::Lock lock = mHelper.lock();
+ return mHelper.mOutputPoolIds->m.values[0];
+ }
+
+ static bool IsFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) {
+ C2StreamColorAspectsRequestInfo::output info(0u);
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ c2_status_t err = intf->query_vb({&info}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
+ LOG(WARNING) << "SampleToneMappingFilter::Interface::IsFilteringEnabled: "
+ << "query failed for " << intf->getName();
+ return false;
+ }
+ return info && info.transfer == C2Color::TRANSFER_170M;
+ }
+ private:
+ const c2_node_id_t mId;
+ std::shared_ptr<C2ReflectorHelper> mReflector;
+ struct Helper : public C2InterfaceHelper {
+ explicit Helper(std::shared_ptr<C2ReflectorHelper> reflector)
+ : C2InterfaceHelper(reflector) {
+ setDerivedInstance(this);
+
+ addParameter(
+ DefineParam(mApiFeatures, C2_PARAMKEY_API_FEATURES)
+ .withConstValue(new C2ApiFeaturesSetting(C2Config::api_feature_t(
+ API_REFLECTION |
+ API_VALUES |
+ API_CURRENT_VALUES |
+ API_DEPENDENCY |
+ API_SAME_INPUT_BUFFER)))
+ .build());
+
+ mName = C2ComponentNameSetting::AllocShared(NAME.size() + 1);
+ strncpy(mName->m.value, NAME.c_str(), NAME.size() + 1);
+ addParameter(
+ DefineParam(mName, C2_PARAMKEY_COMPONENT_NAME)
+ .withConstValue(mName)
+ .build());
+
+ addParameter(
+ DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND)
+ .withConstValue(new C2ComponentKindSetting(C2Component::KIND_OTHER))
+ .build());
+
+ addParameter(
+ DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN)
+ .withConstValue(new C2ComponentDomainSetting(C2Component::DOMAIN_VIDEO))
+ .build());
+
+ addParameter(
+ DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT)
+ .withConstValue(new C2PortStreamCountTuning::input(1))
+ .build());
+
+ addParameter(
+ DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT)
+ .withConstValue(new C2PortStreamCountTuning::output(1))
+ .build());
+
+ addParameter(
+ DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
+ .withConstValue(new C2StreamBufferTypeSetting::input(
+ 0u, C2BufferData::GRAPHIC))
+ .build());
+
+ static const std::string kRawMediaType = "video/raw";
+ mInputMediaType = C2PortMediaTypeSetting::input::AllocShared(
+ kRawMediaType.size() + 1);
+ strncpy(mInputMediaType->m.value, kRawMediaType.c_str(), kRawMediaType.size() + 1);
+ addParameter(
+ DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
+ .withConstValue(mInputMediaType)
+ .build());
+
+ addParameter(
+ DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
+ .withConstValue(new C2StreamBufferTypeSetting::output(
+ 0u, C2BufferData::GRAPHIC))
+ .build());
+
+ mOutputMediaType = C2PortMediaTypeSetting::output::AllocShared(
+ kRawMediaType.size() + 1);
+ strncpy(mOutputMediaType->m.value, kRawMediaType.c_str(), kRawMediaType.size() + 1);
+ addParameter(
+ DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
+ .withConstValue(mOutputMediaType)
+ .build());
+
+ addParameter(
+ DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+ .withConstValue(new C2PortActualDelayTuning::input(0u))
+ .build());
+
+ addParameter(
+ DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+ .withConstValue(new C2PortActualDelayTuning::output(0u))
+ .build());
+
+ addParameter(
+ DefineParam(mActualPipelineDelay, C2_PARAMKEY_PIPELINE_DELAY)
+ .withConstValue(new C2ActualPipelineDelayTuning(0u))
+ .build());
+
+ C2BlockPool::local_id_t outputPoolIds[1] = { C2BlockPool::BASIC_GRAPHIC };
+ addParameter(
+ DefineParam(mOutputPoolIds, C2_PARAMKEY_OUTPUT_BLOCK_POOLS)
+ .withDefault(C2PortBlockPoolsTuning::output::AllocShared(outputPoolIds))
+ .withFields({ C2F(mOutputPoolIds, m.values[0]).any(),
+ C2F(mOutputPoolIds, m.values).inRange(0, 1) })
+ .withSetter(OutputBlockPoolSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mInputHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+ .withDefault(new C2StreamHdrStaticInfo::input(0u))
+ .withFields({
+ C2F(mInputHdrStaticInfo, mastering.red.x).any(),
+ })
+ .withSetter(HdrStaticInfoSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mOutputHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+ .withConstValue(new C2StreamHdrStaticInfo::output(0u))
+ .build());
+
+ addParameter(
+ DefineParam(mInputColorAspectInfo, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::input(0u))
+ .withFields({
+ C2F(mInputColorAspectInfo, range).any(),
+ C2F(mInputColorAspectInfo, primaries).any(),
+ C2F(mInputColorAspectInfo, transfer).any(),
+ C2F(mInputColorAspectInfo, matrix).any(),
+ })
+ .withSetter(InputColorAspectsSetter)
+ .build());
+
+ addParameter(
+ DefineParam(
+ mColorAspectRequestInfo,
+ (std::string(C2_PARAMKEY_COLOR_ASPECTS) + ".request").c_str())
+ .withDefault(new C2StreamColorAspectsRequestInfo::output(0u))
+ .withFields({
+ C2F(mColorAspectRequestInfo, range).any(),
+ C2F(mColorAspectRequestInfo, primaries).any(),
+ C2F(mColorAspectRequestInfo, transfer).oneOf({
+ C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_170M,
+ }),
+ C2F(mColorAspectRequestInfo, matrix).any(),
+ })
+ .withSetter(ColorAspectsRequestSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mOutputColorAspectInfo, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::output(0u))
+ .withFields({
+ C2F(mOutputColorAspectInfo, range).any(),
+ C2F(mOutputColorAspectInfo, primaries).any(),
+ C2F(mOutputColorAspectInfo, transfer).any(),
+ C2F(mOutputColorAspectInfo, matrix).any(),
+ })
+ .withSetter(OutputColorAspectsSetter,
+ mInputColorAspectInfo,
+ mColorAspectRequestInfo)
+ .build());
+ }
+
+ static C2R OutputBlockPoolSetter(
+ bool mayBlock,
+ C2P<C2PortBlockPoolsTuning::output> &me) {
+ (void)mayBlock, (void)me;
+ return C2R::Ok();
+ }
+
+ static C2R HdrStaticInfoSetter(
+ bool mayBlock,
+ C2P<C2StreamHdrStaticInfo::input> &me) {
+ (void)mayBlock, (void)me;
+ return C2R::Ok();
+ }
+
+ static C2R InputColorAspectsSetter(
+ bool mayBlock,
+ C2P<C2StreamColorAspectsInfo::input> &me) {
+ (void)mayBlock, (void)me;
+ return C2R::Ok();
+ }
+
+ static C2R OutputColorAspectsSetter(
+ bool mayBlock,
+ C2P<C2StreamColorAspectsInfo::output> &me,
+ const C2P<C2StreamColorAspectsInfo::input> &inputColor,
+ const C2P<C2StreamColorAspectsRequestInfo::output> &request) {
+ (void)mayBlock;
+ me.set().range = inputColor.v.range;
+ me.set().primaries = inputColor.v.primaries;
+ me.set().transfer = inputColor.v.transfer;
+ if (request.v.transfer == C2Color::TRANSFER_170M) {
+ me.set().transfer = C2Color::TRANSFER_170M;
+ }
+ me.set().matrix = inputColor.v.matrix;
+ return C2R::Ok();
+ }
+
+ static C2R ColorAspectsRequestSetter(
+ bool mayBlock,
+ C2P<C2StreamColorAspectsRequestInfo::output> &me) {
+ (void)mayBlock;
+ if (me.v.range != C2Color::RANGE_UNSPECIFIED) {
+ me.set().range = C2Color::RANGE_UNSPECIFIED;
+ }
+ if (me.v.primaries != C2Color::PRIMARIES_UNSPECIFIED) {
+ me.set().primaries = C2Color::PRIMARIES_UNSPECIFIED;
+ }
+ if (me.v.transfer != C2Color::TRANSFER_170M) {
+ me.set().transfer = C2Color::TRANSFER_UNSPECIFIED;
+ }
+ if (me.v.matrix != C2Color::MATRIX_UNSPECIFIED) {
+ me.set().matrix = C2Color::MATRIX_UNSPECIFIED;
+ }
+ return C2R::Ok();
+ }
+
+ std::shared_ptr<C2ApiFeaturesSetting> mApiFeatures;
+
+ std::shared_ptr<C2ComponentNameSetting> mName;
+ std::shared_ptr<C2ComponentAliasesSetting> mAliases;
+ std::shared_ptr<C2ComponentKindSetting> mKind;
+ std::shared_ptr<C2ComponentDomainSetting> mDomain;
+
+ std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
+ std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
+ std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
+ std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
+
+ std::shared_ptr<C2PortActualDelayTuning::input> mActualInputDelay;
+ std::shared_ptr<C2PortActualDelayTuning::output> mActualOutputDelay;
+ std::shared_ptr<C2ActualPipelineDelayTuning> mActualPipelineDelay;
+
+ std::shared_ptr<C2PortStreamCountTuning::input> mInputStreamCount;
+ std::shared_ptr<C2PortStreamCountTuning::output> mOutputStreamCount;
+
+ std::shared_ptr<C2PortBlockPoolsTuning::output> mOutputPoolIds;
+
+ std::shared_ptr<C2StreamHdrStaticInfo::input> mInputHdrStaticInfo;
+ std::shared_ptr<C2StreamHdrStaticInfo::output> mOutputHdrStaticInfo;
+ std::shared_ptr<C2StreamColorAspectsInfo::input> mInputColorAspectInfo;
+ std::shared_ptr<C2StreamColorAspectsInfo::output> mOutputColorAspectInfo;
+ std::shared_ptr<C2StreamColorAspectsRequestInfo::output> mColorAspectRequestInfo;
+ } mHelper;
+ };
+
+ explicit SampleToneMappingFilter(c2_node_id_t id)
+ : mIntf(std::make_shared<Interface>(id)) {
+ }
+ ~SampleToneMappingFilter() override {
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+
+ c2_status_t setListener_vb(
+ const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override {
+ std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 5ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == RELEASED) {
+ return C2_BAD_STATE;
+ }
+ if (mState == RUNNING && listener) {
+ return C2_BAD_STATE;
+ }
+ if (mState != STOPPED) {
+ return C2_BAD_STATE;
+ }
+ }
+ std::unique_lock lock(mListenerMutex, std::try_to_lock);
+ if (lock) {
+ mListener = listener;
+ return C2_OK;
+ }
+ if (mayBlock == C2_DONT_BLOCK) {
+ return C2_BLOCKING;
+ }
+ lock.try_lock_until(deadline);
+ if (!lock) {
+ return C2_TIMED_OUT;
+ }
+ mListener = listener;
+ return C2_OK;
+ }
+
+ c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override {
+ if (!items) {
+ return C2_BAD_VALUE;
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ std::unique_lock lock(mQueueMutex);
+ mQueue.splice(mQueue.end(), *items);
+ return C2_OK;
+ }
+
+ 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 {
+ if (!flushedWork) {
+ return C2_BAD_VALUE;
+ }
+ if (mode == FLUSH_CHAIN) {
+ return C2_BAD_VALUE;
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueue.swap(*flushedWork);
+ }
+ // NOTE: this component does not have internal state to flush.
+ return C2_OK;
+ }
+
+ c2_status_t drain_nb(drain_mode_t mode) override {
+ if (mode == DRAIN_CHAIN) {
+ return C2_BAD_VALUE;
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ // NOTE: this component does not wait for work items before processing.
+ return C2_OK;
+ }
+
+ c2_status_t start() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == STARTING) {
+ return C2_DUPLICATE;
+ }
+ if (mState != STOPPED) {
+ return C2_BAD_STATE;
+ }
+ mState = STARTING;
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (!mProcessingThread.joinable()) {
+ mProcessingThread = std::thread([this]() {
+ processLoop(shared_from_this());
+ });
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = RUNNING;
+ }
+ return C2_OK;
+ }
+
+ c2_status_t stop() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == STOPPING) {
+ return C2_DUPLICATE;
+ }
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ mState = STOPPING;
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueueCondition.notify_all();
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = STOPPED;
+ }
+ return C2_OK;
+ }
+
+ c2_status_t reset() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == RESETTING) {
+ return C2_DUPLICATE;
+ }
+ if (mState == RELEASED) {
+ return C2_BAD_STATE;
+ }
+ mState = RESETTING;
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueueCondition.notify_all();
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = STOPPED;
+ }
+ return C2_OK;
+ }
+
+ c2_status_t release() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == RELEASED || mState == RELEASING) {
+ return C2_DUPLICATE;
+ }
+ // TODO: return C2_BAD_STATE if not stopped
+ mState = RELEASING;
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueueCondition.notify_all();
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = RELEASED;
+ }
+ return C2_OK;
+ }
+
+ std::shared_ptr<C2ComponentInterface> intf() override {
+ return mIntf;
+ }
+
+private:
+ void processLoop(std::shared_ptr<SampleToneMappingFilter> thiz) {
+ constexpr float kDefaultMaxLumiance = 500.0;
+ constexpr float kDefaultMaxMasteringLuminance = 1000.0;
+ constexpr float kDefaultMaxContentLuminance = 1000.0;
+ constexpr uint32_t kDstUsage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+ int32_t workCount = 0;
+ std::unique_ptr<renderengine::RenderEngine> renderEngine = renderengine::RenderEngine::create(
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(2 /*maxFrameBufferAcquiredBuffers*/)
+ .setUseColorManagerment(true)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::LOW)
+ .build());
+ if (!renderEngine) {
+ std::unique_lock lock(mListenerMutex);
+ mListener->onError_nb(thiz, C2_CORRUPTED);
+ return;
+ }
+ uint32_t textureName = 0;
+ renderEngine->genTextures(1, &textureName);
+
+ while (true) {
+ // Before doing anything, verify the state
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ break;
+ }
+ }
+ // Extract one work item
+ std::unique_ptr<C2Work> work;
+ {
+ std::unique_lock lock(mQueueMutex);
+ if (mQueue.empty()) {
+ mQueueCondition.wait_for(lock, 1s);
+ }
+ if (mQueue.empty()) {
+ continue;
+ }
+ mQueue.front().swap(work);
+ mQueue.pop_front();
+ ++workCount;
+ }
+ LOG(VERBOSE) << "work #" << workCount << ": flags=" << work->input.flags
+ << " timestamp=" << work->input.ordinal.timestamp.peek();;
+ std::shared_ptr<C2StreamHdrStaticInfo::input> hdrStaticInfo =
+ mIntf->getHdrStaticMetadata();
+ uint32_t dataspace = mIntf->getDataSpace();
+
+ std::shared_ptr<C2Buffer> buffer;
+ if (!work->input.buffers.empty()) {
+ buffer = work->input.buffers.front();
+ }
+ std::shared_ptr<C2Buffer> outC2Buffer;
+ status_t err = OK;
+ if (buffer) {
+ if (buffer->hasInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE)) {
+ std::shared_ptr<const C2Info> info =
+ buffer->getInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE);
+ std::unique_ptr<C2Param> flipped = C2Param::CopyAsStream(
+ *info, false /* output */, info->stream());
+ hdrStaticInfo.reset(static_cast<C2StreamHdrStaticInfo::input *>(
+ flipped.release()));
+ }
+ const C2Handle *c2Handle =
+ buffer->data().graphicBlocks().front().handle();
+ uint32_t width, height, format, stride, igbp_slot, generation;
+ uint64_t usage, igbp_id;
+ _UnwrapNativeCodec2GrallocMetadata(
+ c2Handle, &width, &height, &format, &usage, &stride, &generation,
+ &igbp_id, &igbp_slot);
+ native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle);
+ sp<GraphicBuffer> srcBuffer = new GraphicBuffer(
+ grallocHandle, GraphicBuffer::CLONE_HANDLE,
+ width, height, format, 1, usage, stride);
+
+ std::shared_ptr<C2GraphicBlock> dstBlock;
+ C2BlockPool::local_id_t poolId = mIntf->getPoolId();
+ std::shared_ptr<C2BlockPool> pool;
+ GetCodec2BlockPool(poolId, thiz, &pool);
+ pool->fetchGraphicBlock(
+ width, height, HAL_PIXEL_FORMAT_RGBA_8888, C2AndroidMemoryUsage::FromGrallocUsage(kDstUsage),
+ &dstBlock);
+ outC2Buffer = C2Buffer::CreateGraphicBuffer(
+ dstBlock->share(C2Rect(width, height), C2Fence()));
+ c2Handle = dstBlock->handle();
+ _UnwrapNativeCodec2GrallocMetadata(
+ c2Handle, &width, &height, &format, &usage, &stride, &generation,
+ &igbp_id, &igbp_slot);
+ grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle);
+ sp<GraphicBuffer> dstBuffer = new GraphicBuffer(
+ grallocHandle, GraphicBuffer::CLONE_HANDLE,
+ width, height, format, 1, usage, stride);
+
+ Rect sourceCrop(0, 0, width, height);
+
+ renderengine::DisplaySettings clientCompositionDisplay;
+ std::vector<const renderengine::LayerSettings*> clientCompositionLayers;
+
+ clientCompositionDisplay.physicalDisplay = sourceCrop;
+ clientCompositionDisplay.clip = sourceCrop;
+
+ clientCompositionDisplay.outputDataspace = ui::Dataspace::V0_SRGB;
+ clientCompositionDisplay.maxLuminance = kDefaultMaxLumiance;
+ clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
+ renderengine::LayerSettings layerSettings;
+ layerSettings.geometry.boundaries = sourceCrop.toFloatRect();
+ layerSettings.alpha = 1.0f;
+
+ layerSettings.sourceDataspace = static_cast<ui::Dataspace>(dataspace);
+
+ // from BufferLayer
+ layerSettings.source.buffer.buffer = srcBuffer;
+ layerSettings.source.buffer.isOpaque = true;
+ // TODO: fence
+ layerSettings.source.buffer.fence = Fence::NO_FENCE;
+ layerSettings.source.buffer.textureName = textureName;
+ layerSettings.source.buffer.usePremultipliedAlpha = false;
+ layerSettings.source.buffer.isY410BT2020 =
+ (layerSettings.sourceDataspace == ui::Dataspace::BT2020_ITU_PQ ||
+ layerSettings.sourceDataspace == ui::Dataspace::BT2020_ITU_HLG) &&
+ format == HAL_PIXEL_FORMAT_RGBA_1010102;
+ layerSettings.source.buffer.maxMasteringLuminance =
+ (hdrStaticInfo && *hdrStaticInfo &&
+ hdrStaticInfo->mastering.maxLuminance > 0 &&
+ hdrStaticInfo->mastering.minLuminance > 0)
+ ? hdrStaticInfo->mastering.maxLuminance : kDefaultMaxMasteringLuminance;
+ layerSettings.source.buffer.maxContentLuminance =
+ (hdrStaticInfo && *hdrStaticInfo && hdrStaticInfo->maxCll > 0)
+ ? hdrStaticInfo->maxCll : kDefaultMaxContentLuminance;
+
+ // Set filtering to false since the capture itself doesn't involve
+ // any scaling, metadata retriever JNI is scaling the bitmap if
+ // display size is different from decoded size. If that scaling
+ // needs to be handled by server side, consider enable this based
+ // display size vs decoded size.
+ layerSettings.source.buffer.useTextureFiltering = false;
+ layerSettings.source.buffer.textureTransform = mat4();
+ clientCompositionLayers.push_back(&layerSettings);
+
+ // Use an empty fence for the buffer fence, since we just created the buffer so
+ // there is no need for synchronization with the GPU.
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+ renderEngine->useProtectedContext(false);
+ err = renderEngine->drawLayers(
+ clientCompositionDisplay, clientCompositionLayers, dstBuffer.get(),
+ /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
+
+ sp<Fence> fence = new Fence(std::move(drawFence));
+
+ // We can move waiting for fence & sending it back on a separate thread to improve
+ // efficiency, but leaving it here for simplicity.
+ if (err != OK) {
+ LOG(ERROR) << "drawLayers returned err " << err;
+ } else {
+ err = fence->wait(500);
+ if (err != OK) {
+ LOG(WARNING) << "wait for fence returned err " << err;
+ }
+ }
+ renderEngine->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ }
+
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->worklets.front()->output.flags = work->input.flags;
+ if (err == OK) {
+ work->workletsProcessed = 1;
+ if (outC2Buffer) {
+ work->worklets.front()->output.buffers.push_back(outC2Buffer);
+ }
+ work->result = C2_OK;
+ } else {
+ work->result = C2_CORRUPTED;
+ }
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+
+ std::unique_lock lock(mListenerMutex);
+ mListener->onWorkDone_nb(thiz, std::move(items));
+ LOG(VERBOSE) << "sent work #" << workCount;
+ }
+ }
+
+ mutable std::timed_mutex mListenerMutex;
+ std::shared_ptr<Listener> mListener;
+
+ mutable std::mutex mQueueMutex;
+ mutable std::condition_variable mQueueCondition;
+ std::list<std::unique_ptr<C2Work>> mQueue;
+
+ const std::shared_ptr<Interface> mIntf;
+
+ mutable std::mutex mStateMutex;
+ enum State {
+ STOPPED,
+ RUNNING,
+ RELEASED,
+ STARTING, // STOPPED -> RUNNING
+ STOPPING, // RUNNING -> STOPPED
+ RESETTING, // <<ANY>> -> STOPPED
+ RELEASING, // STOPPED -> RELEASED
+ } mState;
+
+ mutable std::mutex mProcessingMutex;
+ std::thread mProcessingThread;
+
+};
+
+// static
+const std::string SampleToneMappingFilter::Interface::NAME = "c2.sample.tone-mapper";
+// static
+const FilterPlugin_V1::Descriptor SampleToneMappingFilter::Interface::DESCRIPTOR = {
+ // controlParams
+ { C2StreamColorAspectsRequestInfo::output::PARAM_TYPE },
+ // affectedParams
+ {
+ C2StreamHdrStaticInfo::output::PARAM_TYPE,
+ C2StreamHdr10PlusInfo::output::PARAM_TYPE,
+ C2StreamColorAspectsInfo::output::PARAM_TYPE,
+ },
+};
+
+class SampleC2ComponentStore : public C2ComponentStore {
+public:
+ SampleC2ComponentStore()
+ : mReflector(std::make_shared<C2ReflectorHelper>()),
+ mIntf(mReflector),
+ mFactories(CreateFactories()) {
+ }
+ ~SampleC2ComponentStore() = default;
+
+ C2String getName() const override { return "android.sample.filter-plugin-store"; }
+ c2_status_t createComponent(
+ C2String name, std::shared_ptr<C2Component>* const component) override {
+ if (mFactories.count(name) == 0) {
+ return C2_BAD_VALUE;
+ }
+ return mFactories.at(name)->createComponent(++mNodeId, component);
+ }
+ c2_status_t createInterface(
+ C2String name, std::shared_ptr<C2ComponentInterface>* const interface) override {
+ if (mFactories.count(name) == 0) {
+ return C2_BAD_VALUE;
+ }
+ return mFactories.at(name)->createInterface(++mNodeId, interface);
+ }
+ std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override {
+ std::vector<std::shared_ptr<const C2Component::Traits>> ret;
+ for (const auto &[name, factory] : mFactories) {
+ ret.push_back(factory->getTraits());
+ }
+ return ret;
+ }
+ c2_status_t copyBuffer(
+ std::shared_ptr<C2GraphicBuffer>, std::shared_ptr<C2GraphicBuffer>) override {
+ return C2_OMITTED;
+ }
+ c2_status_t query_sm(
+ const std::vector<C2Param*> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
+ return mIntf.query(stackParams, heapParamIndices, C2_MAY_BLOCK, heapParams);
+ }
+ c2_status_t config_sm(
+ const std::vector<C2Param*> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+ return mIntf.config(params, C2_MAY_BLOCK, failures);
+ }
+ std::shared_ptr<C2ParamReflector> getParamReflector() const override {
+ return mReflector;
+ }
+ c2_status_t querySupportedParams_nb(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
+ return mIntf.querySupportedParams(params);
+ }
+ c2_status_t querySupportedValues_sm(
+ std::vector<C2FieldSupportedValuesQuery> &fields) const override {
+ return mIntf.querySupportedValues(fields, C2_MAY_BLOCK);
+ }
+
+private:
+ class ComponentFactory {
+ public:
+ virtual ~ComponentFactory() = default;
+
+ const std::shared_ptr<const C2Component::Traits> &getTraits() { return mTraits; }
+
+ virtual c2_status_t createComponent(
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component) const = 0;
+ virtual c2_status_t createInterface(
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface) const = 0;
+ protected:
+ ComponentFactory(const std::shared_ptr<const C2Component::Traits> &traits)
+ : mTraits(traits) {
+ }
+ private:
+ const std::shared_ptr<const C2Component::Traits> mTraits;
+ };
+
+ template <class T>
+ struct ComponentFactoryImpl : public ComponentFactory {
+ public:
+ ComponentFactoryImpl(const std::shared_ptr<const C2Component::Traits> &traits)
+ : ComponentFactory(traits) {
+ }
+ ~ComponentFactoryImpl() override = default;
+ c2_status_t createComponent(
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component) const override {
+ *component = std::make_shared<T>(id);
+ return C2_OK;
+ }
+ c2_status_t createInterface(
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface) const override {
+ *interface = std::make_shared<typename T::Interface>(id);
+ return C2_OK;
+ }
+ };
+
+ template <class T>
+ static void AddFactory(std::map<C2String, std::unique_ptr<ComponentFactory>> *factories) {
+ std::shared_ptr<C2ComponentInterface> intf{new typename T::Interface(0)};
+ std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
+ CHECK(C2InterfaceUtils::FillTraitsFromInterface(traits.get(), intf))
+ << "Failed to fill traits from interface";
+ factories->emplace(traits->name, new ComponentFactoryImpl<T>(traits));
+ }
+
+ static std::map<C2String, std::unique_ptr<ComponentFactory>> CreateFactories() {
+ std::map<C2String, std::unique_ptr<ComponentFactory>> factories;
+ AddFactory<SampleToneMappingFilter>(&factories);
+ return factories;
+ }
+
+
+ std::shared_ptr<C2ReflectorHelper> mReflector;
+ struct Interface : public C2InterfaceHelper {
+ explicit Interface(std::shared_ptr<C2ReflectorHelper> reflector)
+ : C2InterfaceHelper(reflector) {
+ }
+ } mIntf;
+
+ const std::map<C2String, std::unique_ptr<ComponentFactory>> mFactories;
+
+ std::atomic_int32_t mNodeId{0};
+};
+
+class SampleFilterPlugin : public FilterPlugin_V1 {
+public:
+ SampleFilterPlugin() : mStore(new SampleC2ComponentStore) {}
+ ~SampleFilterPlugin() override = default;
+
+ std::shared_ptr<C2ComponentStore> getComponentStore() override {
+ return mStore;
+ }
+
+ bool describe(C2String name, Descriptor *desc) override {
+ if (name == SampleToneMappingFilter::Interface::NAME) {
+ *desc = SampleToneMappingFilter::Interface::DESCRIPTOR;
+ return true;
+ }
+ return false;
+ }
+
+ bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) override {
+ if (intf->getName() == SampleToneMappingFilter::Interface::NAME) {
+ return SampleToneMappingFilter::Interface::IsFilteringEnabled(intf);
+ }
+ return false;
+ }
+
+private:
+ std::shared_ptr<C2ComponentStore> mStore;
+};
+
+} // namespace android
+
+extern "C" {
+
+int32_t GetFilterPluginVersion() {
+ return ::android::SampleFilterPlugin::VERSION;
+}
+
+void *CreateFilterPlugin() {
+ return new ::android::SampleFilterPlugin;
+}
+
+void DestroyFilterPlugin(void *plugin) {
+ delete (::android::SampleFilterPlugin *)plugin;
+}
+
+} // extern "C"