| /* |
| * 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. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "Codec2-InputSurfaceConnection" |
| #include <android-base/logging.h> |
| |
| #include <codec2/hidl/1.0/InputSurfaceConnection.h> |
| #include <codec2/hidl/1.0/InputSurfaceConnection.h> |
| |
| #include <memory> |
| #include <list> |
| #include <mutex> |
| #include <atomic> |
| |
| #include <hidl/HidlSupport.h> |
| #include <media/stagefright/bqhelper/ComponentWrapper.h> |
| #include <system/graphics.h> |
| #include <ui/GraphicBuffer.h> |
| #include <utils/Errors.h> |
| |
| #include <C2.h> |
| #include <C2AllocatorGralloc.h> |
| #include <C2BlockInternal.h> |
| #include <C2Buffer.h> |
| #include <C2Component.h> |
| #include <C2Config.h> |
| #include <C2Debug.h> |
| #include <C2PlatformSupport.h> |
| #include <C2Work.h> |
| |
| namespace android { |
| namespace hardware { |
| namespace media { |
| namespace c2 { |
| namespace V1_0 { |
| namespace utils { |
| |
| constexpr int32_t kBufferCount = 16; |
| |
| using namespace ::android; |
| using ::android::hardware::hidl_string; |
| using ::android::hardware::hidl_vec; |
| using ::android::hardware::Return; |
| |
| namespace /* unnamed */ { |
| |
| class Buffer2D : public C2Buffer { |
| public: |
| explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) { |
| } |
| }; |
| |
| } // unnamed namespace |
| |
| // Derived class of ComponentWrapper for use with |
| // GraphicBufferSource::configure(). |
| // |
| struct InputSurfaceConnection::Impl : public ComponentWrapper { |
| |
| Impl(const sp<GraphicBufferSource>& source, |
| const std::shared_ptr<C2Component>& localComp) |
| : mSource{source}, mLocalComp{localComp}, mSink{}, mFrameIndex{0} { |
| std::shared_ptr<C2ComponentInterface> intf = localComp->intf(); |
| mSinkName = intf ? intf->getName() : ""; |
| } |
| |
| Impl(const sp<GraphicBufferSource>& source, |
| const sp<IInputSink>& sink) |
| : mSource{source}, mLocalComp{}, mSink{sink}, mFrameIndex{0} { |
| Return<sp<IConfigurable>> transResult = sink->getConfigurable(); |
| if (!transResult.isOk()) { |
| LOG(ERROR) << "Remote sink is dead."; |
| return; |
| } |
| mSinkConfigurable = |
| static_cast<sp<IConfigurable>>(transResult); |
| if (!mSinkConfigurable) { |
| LOG(ERROR) << "Remote sink is not configurable."; |
| mSinkName = ""; |
| return; |
| } |
| |
| hidl_string name; |
| Return<void> transStatus = mSinkConfigurable->getName( |
| [&name](const hidl_string& n) { |
| name = n; |
| }); |
| if (!transStatus.isOk()) { |
| LOG(ERROR) << "Remote sink's configurable is dead."; |
| mSinkName = ""; |
| return; |
| } |
| mSinkName = name.c_str(); |
| } |
| |
| virtual ~Impl() { |
| mSource->stop(); |
| mSource->release(); |
| } |
| |
| bool init() { |
| if (mSource == nullptr) { |
| return false; |
| } |
| status_t err = mSource->initCheck(); |
| if (err != OK) { |
| LOG(WARNING) << "Impl::init -- GraphicBufferSource init failed: " |
| << "status = " << err << "."; |
| return false; |
| } |
| |
| // TODO: read settings properly from the interface |
| C2StreamPictureSizeInfo::input inputSize; |
| C2StreamUsageTuning::input usage; |
| c2_status_t c2Status = queryFromSink({ &inputSize, &usage }, |
| {}, |
| C2_MAY_BLOCK, |
| nullptr); |
| if (c2Status != C2_OK) { |
| LOG(WARNING) << "Impl::init -- cannot query information from " |
| "the component interface: " |
| << "status = " << asString(c2Status) << "."; |
| return false; |
| } |
| |
| // TODO: proper color aspect & dataspace |
| android_dataspace dataSpace = HAL_DATASPACE_BT709; |
| |
| // TODO: use the usage read from intf |
| // uint32_t grallocUsage = |
| // C2AndroidMemoryUsage(C2MemoryUsage(usage.value)). |
| // asGrallocUsage(); |
| |
| uint32_t grallocUsage = |
| mSinkName.compare(0, 11, "c2.android.") == 0 ? |
| GRALLOC_USAGE_SW_READ_OFTEN : |
| GRALLOC_USAGE_HW_VIDEO_ENCODER; |
| |
| err = mSource->configure( |
| this, dataSpace, kBufferCount, |
| inputSize.width, inputSize.height, |
| grallocUsage); |
| if (err != OK) { |
| LOG(WARNING) << "Impl::init -- GBS configure failed: " |
| << "status = " << err << "."; |
| return false; |
| } |
| for (int32_t i = 0; i < kBufferCount; ++i) { |
| if (mSource->onInputBufferAdded(i) != OK) { |
| LOG(WARNING) << "Impl::init: failed to populate GBS slots."; |
| return false; |
| } |
| } |
| if (mSource->start() != OK) { |
| LOG(WARNING) << "Impl::init -- GBS failed to start."; |
| return false; |
| } |
| mAllocatorMutex.lock(); |
| c2_status_t c2err = GetCodec2PlatformAllocatorStore()->fetchAllocator( |
| C2AllocatorStore::PLATFORM_START + 1, // GRALLOC |
| &mAllocator); |
| mAllocatorMutex.unlock(); |
| if (c2err != OK) { |
| LOG(WARNING) << "Impl::init -- failed to fetch gralloc allocator: " |
| << "status = " << asString(c2err) << "."; |
| return false; |
| } |
| return true; |
| } |
| |
| // From ComponentWrapper |
| virtual status_t submitBuffer( |
| int32_t bufferId, |
| const sp<GraphicBuffer>& buffer, |
| int64_t timestamp, |
| int fenceFd) override { |
| LOG(VERBOSE) << "Impl::submitBuffer -- bufferId = " << bufferId << "."; |
| // TODO: Use fd to construct fence |
| (void)fenceFd; |
| |
| std::shared_ptr<C2GraphicAllocation> alloc; |
| C2Handle* handle = WrapNativeCodec2GrallocHandle( |
| buffer->handle, |
| buffer->width, buffer->height, |
| buffer->format, buffer->usage, buffer->stride); |
| mAllocatorMutex.lock(); |
| c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc); |
| mAllocatorMutex.unlock(); |
| if (err != OK) { |
| return UNKNOWN_ERROR; |
| } |
| std::shared_ptr<C2GraphicBlock> block = |
| _C2BlockFactory::CreateGraphicBlock(alloc); |
| |
| std::unique_ptr<C2Work> work(new C2Work); |
| work->input.flags = (C2FrameData::flags_t)0; |
| work->input.ordinal.timestamp = timestamp; |
| work->input.ordinal.frameIndex = mFrameIndex.fetch_add( |
| 1, std::memory_order_relaxed); |
| work->input.buffers.clear(); |
| std::shared_ptr<C2Buffer> c2Buffer( |
| // TODO: fence |
| new Buffer2D(block->share( |
| C2Rect(block->width(), block->height()), ::C2Fence())), |
| [bufferId, source = mSource](C2Buffer* ptr) { |
| delete ptr; |
| if (source != nullptr) { |
| // TODO: fence |
| (void)source->onInputBufferEmptied(bufferId, -1); |
| } |
| }); |
| work->input.buffers.push_back(c2Buffer); |
| work->worklets.clear(); |
| work->worklets.emplace_back(new C2Worklet); |
| std::list<std::unique_ptr<C2Work>> items; |
| items.push_back(std::move(work)); |
| |
| err = queueToSink(&items); |
| return (err == C2_OK) ? OK : UNKNOWN_ERROR; |
| } |
| |
| virtual status_t submitEos(int32_t bufferId) override { |
| LOG(VERBOSE) << "Impl::submitEos -- bufferId = " << bufferId << "."; |
| (void)bufferId; |
| |
| std::unique_ptr<C2Work> work(new C2Work); |
| work->input.flags = (C2FrameData::flags_t)0; |
| work->input.ordinal.frameIndex = mFrameIndex.fetch_add( |
| 1, std::memory_order_relaxed); |
| work->input.buffers.clear(); |
| work->worklets.clear(); |
| work->worklets.emplace_back(new C2Worklet); |
| std::list<std::unique_ptr<C2Work>> items; |
| items.push_back(std::move(work)); |
| |
| c2_status_t err = queueToSink(&items); |
| return (err == C2_OK) ? OK : UNKNOWN_ERROR; |
| } |
| |
| virtual void dispatchDataSpaceChanged( |
| int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override { |
| // TODO |
| (void)dataSpace; |
| (void)aspects; |
| (void)pixelFormat; |
| } |
| |
| // Configurable interface for InputSurfaceConnection::Impl. |
| // |
| // This class is declared as an inner class so that it will have access to |
| // all Impl's members. |
| struct ConfigurableIntf : public ConfigurableC2Intf { |
| sp<Impl> mConnection; |
| ConfigurableIntf(const sp<Impl>& connection) |
| : ConfigurableC2Intf{"input-surface-connection", 0}, |
| mConnection{connection} {} |
| virtual c2_status_t config( |
| const std::vector<C2Param*> ¶ms, |
| c2_blocking_t mayBlock, |
| std::vector<std::unique_ptr<C2SettingResult>> *const failures |
| ) override; |
| virtual c2_status_t query( |
| const std::vector<C2Param::Index> &indices, |
| c2_blocking_t mayBlock, |
| std::vector<std::unique_ptr<C2Param>> *const params) const override; |
| virtual c2_status_t querySupportedParams( |
| std::vector<std::shared_ptr<C2ParamDescriptor>> *const params |
| ) const override; |
| virtual c2_status_t querySupportedValues( |
| std::vector<C2FieldSupportedValuesQuery> &fields, |
| c2_blocking_t mayBlock) const override; |
| }; |
| |
| private: |
| c2_status_t queryFromSink( |
| const std::vector<C2Param*> &stackParams, |
| const std::vector<C2Param::Index> &heapParamIndices, |
| c2_blocking_t mayBlock, |
| std::vector<std::unique_ptr<C2Param>>* const heapParams) { |
| if (mLocalComp) { |
| std::shared_ptr<C2ComponentInterface> intf = mLocalComp->intf(); |
| if (intf) { |
| return intf->query_vb(stackParams, |
| heapParamIndices, |
| mayBlock, |
| heapParams); |
| } else { |
| LOG(ERROR) << "queryFromSink -- " |
| << "component does not have an interface."; |
| return C2_BAD_STATE; |
| } |
| } |
| |
| CHECK(mSink) << "-- queryFromSink " |
| << "-- connection has no sink."; |
| CHECK(mSinkConfigurable) << "-- queryFromSink " |
| << "-- sink has no configurable."; |
| |
| hidl_vec<ParamIndex> indices( |
| stackParams.size() + heapParamIndices.size()); |
| size_t numIndices = 0; |
| for (C2Param* const& stackParam : stackParams) { |
| if (!stackParam) { |
| LOG(DEBUG) << "queryFromSink -- null stack param encountered."; |
| continue; |
| } |
| indices[numIndices++] = static_cast<ParamIndex>(stackParam->index()); |
| } |
| size_t numStackIndices = numIndices; |
| for (const C2Param::Index& index : heapParamIndices) { |
| indices[numIndices++] = |
| static_cast<ParamIndex>(static_cast<uint32_t>(index)); |
| } |
| indices.resize(numIndices); |
| if (heapParams) { |
| heapParams->reserve(heapParams->size() + numIndices); |
| } |
| c2_status_t status; |
| Return<void> transStatus = mSinkConfigurable->query( |
| indices, |
| mayBlock == C2_MAY_BLOCK, |
| [&status, &numStackIndices, &stackParams, heapParams]( |
| Status s, const Params& p) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK && status != C2_BAD_INDEX) { |
| LOG(DEBUG) << "queryFromSink -- call failed: " |
| << "status = " << asString(status) << "."; |
| return; |
| } |
| std::vector<C2Param*> paramPointers; |
| if (!parseParamsBlob(¶mPointers, p)) { |
| LOG(DEBUG) << "queryFromSink -- error while " |
| << "parsing params."; |
| status = C2_CORRUPTED; |
| return; |
| } |
| size_t i = 0; |
| for (auto it = paramPointers.begin(); |
| it != paramPointers.end(); ) { |
| C2Param* paramPointer = *it; |
| if (numStackIndices > 0) { |
| --numStackIndices; |
| if (!paramPointer) { |
| LOG(DEBUG) << "queryFromSink -- " |
| "null stack param."; |
| ++it; |
| continue; |
| } |
| for (; i < stackParams.size() && |
| !stackParams[i]; ) { |
| ++i; |
| } |
| CHECK(i < stackParams.size()); |
| if (stackParams[i]->index() != |
| paramPointer->index()) { |
| LOG(DEBUG) << "queryFromSink -- " |
| "param skipped (index = " |
| << stackParams[i]->index() << ")."; |
| stackParams[i++]->invalidate(); |
| continue; |
| } |
| if (!stackParams[i++]->updateFrom(*paramPointer)) { |
| LOG(DEBUG) << "queryFromSink -- " |
| "param update failed (index = " |
| << paramPointer->index() << ")."; |
| } |
| } else { |
| if (!paramPointer) { |
| LOG(DEBUG) << "queryFromSink -- " |
| "null heap param."; |
| ++it; |
| continue; |
| } |
| if (!heapParams) { |
| LOG(WARNING) << "queryFromSink -- " |
| "too many stack params."; |
| break; |
| } |
| heapParams->emplace_back(C2Param::Copy(*paramPointer)); |
| } |
| ++it; |
| } |
| }); |
| if (!transStatus.isOk()) { |
| LOG(ERROR) << "queryFromSink -- transaction failed."; |
| return C2_CORRUPTED; |
| } |
| return status; |
| } |
| |
| c2_status_t queueToSink(std::list<std::unique_ptr<C2Work>>* const items) { |
| if (mLocalComp) { |
| return mLocalComp->queue_nb(items); |
| } |
| |
| CHECK(mSink) << "-- queueToSink " |
| << "-- connection has no sink."; |
| |
| WorkBundle workBundle; |
| if (!objcpy(&workBundle, *items, nullptr)) { |
| LOG(ERROR) << "queueToSink -- bad input."; |
| return C2_CORRUPTED; |
| } |
| Return<Status> transStatus = mSink->queue(workBundle); |
| if (!transStatus.isOk()) { |
| LOG(ERROR) << "queueToSink -- transaction failed."; |
| return C2_CORRUPTED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| LOG(DEBUG) << "queueToSink -- call failed: " |
| << asString(status); |
| } |
| return status; |
| } |
| |
| sp<GraphicBufferSource> mSource; |
| std::shared_ptr<C2Component> mLocalComp; |
| sp<IInputSink> mSink; |
| sp<IConfigurable> mSinkConfigurable; |
| std::string mSinkName; |
| |
| // Needed for ComponentWrapper implementation |
| std::mutex mAllocatorMutex; |
| std::shared_ptr<C2Allocator> mAllocator; |
| std::atomic_uint64_t mFrameIndex; |
| |
| }; |
| |
| InputSurfaceConnection::InputSurfaceConnection( |
| const sp<GraphicBufferSource>& source, |
| const std::shared_ptr<C2Component>& comp, |
| const std::shared_ptr<ParameterCache>& cache) |
| : mImpl{new Impl(source, comp)}, |
| mConfigurable{new CachedConfigurable( |
| std::make_unique<Impl::ConfigurableIntf>(mImpl))} { |
| mConfigurable->init(cache); |
| } |
| |
| InputSurfaceConnection::InputSurfaceConnection( |
| const sp<GraphicBufferSource>& source, |
| const sp<IInputSink>& sink, |
| const std::shared_ptr<ParameterCache>& cache) |
| : mImpl{new Impl(source, sink)}, |
| mConfigurable{new CachedConfigurable( |
| std::make_unique<Impl::ConfigurableIntf>(mImpl))} { |
| mConfigurable->init(cache); |
| } |
| |
| Return<Status> InputSurfaceConnection::disconnect() { |
| std::lock_guard<std::mutex> lock(mImplMutex); |
| mImpl = nullptr; |
| return Status::OK; |
| } |
| |
| InputSurfaceConnection::~InputSurfaceConnection() { |
| mImpl = nullptr; |
| } |
| |
| bool InputSurfaceConnection::init() { |
| std::lock_guard<std::mutex> lock(mImplMutex); |
| return mImpl->init(); |
| } |
| |
| Return<sp<IConfigurable>> InputSurfaceConnection::getConfigurable() { |
| return mConfigurable; |
| } |
| |
| // Configurable interface for InputSurfaceConnection::Impl |
| c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::config( |
| const std::vector<C2Param*> ¶ms, |
| c2_blocking_t mayBlock, |
| std::vector<std::unique_ptr<C2SettingResult>> *const failures) { |
| // TODO: implement |
| (void)params; |
| (void)mayBlock; |
| (void)failures; |
| return C2_OK; |
| } |
| |
| c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::query( |
| const std::vector<C2Param::Index> &indices, |
| c2_blocking_t mayBlock, |
| std::vector<std::unique_ptr<C2Param>> *const params) const { |
| // TODO: implement |
| (void)indices; |
| (void)mayBlock; |
| (void)params; |
| return C2_OK; |
| } |
| |
| c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::querySupportedParams( |
| std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const { |
| // TODO: implement |
| (void)params; |
| return C2_OK; |
| } |
| |
| c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::querySupportedValues( |
| std::vector<C2FieldSupportedValuesQuery> &fields, |
| c2_blocking_t mayBlock) const { |
| // TODO: implement |
| (void)fields; |
| (void)mayBlock; |
| return C2_OK; |
| } |
| |
| } // namespace utils |
| } // namespace V1_0 |
| } // namespace c2 |
| } // namespace media |
| } // namespace hardware |
| } // namespace android |
| |