| /* |
| * Copyright (C) 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 "Codec2Client" |
| #include <log/log.h> |
| |
| #include <codec2/hidl/client.h> |
| |
| #include <deque> |
| #include <limits> |
| #include <map> |
| #include <type_traits> |
| #include <vector> |
| |
| #include <android-base/properties.h> |
| #include <bufferpool/ClientManager.h> |
| #include <cutils/native_handle.h> |
| #include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> |
| #include <hidl/HidlSupport.h> |
| #include <media/stagefright/bqhelper/WGraphicBufferProducer.h> |
| #undef LOG |
| |
| #include <android/hardware/media/bufferpool/1.0/IClientManager.h> |
| #include <android/hardware/media/c2/1.0/IComponent.h> |
| #include <android/hardware/media/c2/1.0/IComponentInterface.h> |
| #include <android/hardware/media/c2/1.0/IComponentListener.h> |
| #include <android/hardware/media/c2/1.0/IComponentStore.h> |
| #include <android/hardware/media/c2/1.0/IConfigurable.h> |
| |
| #include <C2Debug.h> |
| #include <C2BufferPriv.h> |
| #include <C2PlatformSupport.h> |
| |
| namespace android { |
| |
| using ::android::hardware::hidl_vec; |
| using ::android::hardware::hidl_string; |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| using ::android::TWGraphicBufferProducer; |
| |
| using namespace ::android::hardware::media::c2::V1_0; |
| using namespace ::android::hardware::media::c2::V1_0::utils; |
| using namespace ::android::hardware::media::bufferpool::V1_0; |
| using namespace ::android::hardware::media::bufferpool::V1_0::implementation; |
| |
| namespace /* unnamed */ { |
| |
| // c2_status_t value that corresponds to hwbinder transaction failure. |
| constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED; |
| |
| // List of known IComponentStore services in the decreasing order of preference. |
| constexpr const char* kClientNames[] = { |
| "default", |
| "software", |
| }; |
| |
| // Number of known IComponentStore services. |
| constexpr size_t kNumClients = std::extent<decltype(kClientNames)>::value; |
| |
| typedef std::array<std::shared_ptr<Codec2Client>, kNumClients> ClientList; |
| |
| // Convenience methods to obtain known clients. |
| std::shared_ptr<Codec2Client> getClient(size_t index) { |
| return Codec2Client::CreateFromService(kClientNames[index]); |
| } |
| |
| ClientList getClientList() { |
| ClientList list; |
| for (size_t i = 0; i < list.size(); ++i) { |
| list[i] = getClient(i); |
| } |
| return list; |
| } |
| |
| } // unnamed |
| |
| // Codec2ConfigurableClient |
| |
| const C2String& Codec2ConfigurableClient::getName() const { |
| return mName; |
| } |
| |
| Codec2ConfigurableClient::Base* Codec2ConfigurableClient::base() const { |
| return static_cast<Base*>(mBase.get()); |
| } |
| |
| Codec2ConfigurableClient::Codec2ConfigurableClient( |
| const sp<Codec2ConfigurableClient::Base>& base) : mBase(base) { |
| Return<void> transStatus = base->getName( |
| [this](const hidl_string& name) { |
| mName = name.c_str(); |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("Cannot obtain name from IConfigurable."); |
| } |
| } |
| |
| c2_status_t Codec2ConfigurableClient::query( |
| const std::vector<C2Param*> &stackParams, |
| const std::vector<C2Param::Index> &heapParamIndices, |
| c2_blocking_t mayBlock, |
| std::vector<std::unique_ptr<C2Param>>* const heapParams) const { |
| hidl_vec<ParamIndex> indices( |
| stackParams.size() + heapParamIndices.size()); |
| size_t numIndices = 0; |
| for (C2Param* const& stackParam : stackParams) { |
| if (!stackParam) { |
| ALOGW("query -- 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 = base()->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) { |
| ALOGE("query -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| std::vector<C2Param*> paramPointers; |
| c2_status_t parseStatus = parseParamsBlob(¶mPointers, p); |
| if (parseStatus != C2_OK) { |
| ALOGE("query -- error while parsing params. " |
| "Error code = %d", static_cast<int>(status)); |
| status = parseStatus; |
| return; |
| } |
| size_t i = 0; |
| for (auto it = paramPointers.begin(); it != paramPointers.end(); ) { |
| C2Param* paramPointer = *it; |
| if (numStackIndices > 0) { |
| --numStackIndices; |
| if (!paramPointer) { |
| ALOGW("query -- null stack param."); |
| ++it; |
| continue; |
| } |
| for (; i < stackParams.size() && !stackParams[i]; ) { |
| ++i; |
| } |
| if (i >= stackParams.size()) { |
| ALOGE("query -- unexpected error."); |
| status = C2_CORRUPTED; |
| return; |
| } |
| if (stackParams[i]->index() != paramPointer->index()) { |
| ALOGW("query -- param skipped. index = %d", |
| static_cast<int>(stackParams[i]->index())); |
| stackParams[i++]->invalidate(); |
| continue; |
| } |
| if (!stackParams[i++]->updateFrom(*paramPointer)) { |
| ALOGW("query -- param update failed. index = %d", |
| static_cast<int>(paramPointer->index())); |
| } |
| } else { |
| if (!paramPointer) { |
| ALOGW("query -- null heap param."); |
| ++it; |
| continue; |
| } |
| if (!heapParams) { |
| ALOGW("query -- unexpected extra stack param."); |
| } else { |
| heapParams->emplace_back(C2Param::Copy(*paramPointer)); |
| } |
| } |
| ++it; |
| } |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("query -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2ConfigurableClient::config( |
| const std::vector<C2Param*> ¶ms, |
| c2_blocking_t mayBlock, |
| std::vector<std::unique_ptr<C2SettingResult>>* const failures) { |
| Params hidlParams; |
| Status hidlStatus = createParamsBlob(&hidlParams, params); |
| if (hidlStatus != Status::OK) { |
| ALOGE("config -- bad input."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status; |
| Return<void> transStatus = base()->config( |
| hidlParams, |
| mayBlock == C2_MAY_BLOCK, |
| [&status, ¶ms, failures]( |
| Status s, |
| const hidl_vec<SettingResult> f, |
| const Params& o) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| ALOGD("config -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| size_t i = failures->size(); |
| failures->resize(i + f.size()); |
| for (const SettingResult& sf : f) { |
| status = objcpy(&(*failures)[i++], sf); |
| if (status != C2_OK) { |
| ALOGE("config -- invalid returned SettingResult. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| } |
| status = updateParamsFromBlob(params, o); |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("config -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2ConfigurableClient::querySupportedParams( |
| std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const { |
| // TODO: Cache and query properly! |
| c2_status_t status; |
| Return<void> transStatus = base()->querySupportedParams( |
| std::numeric_limits<uint32_t>::min(), |
| std::numeric_limits<uint32_t>::max(), |
| [&status, params]( |
| Status s, |
| const hidl_vec<ParamDescriptor>& p) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| ALOGE("querySupportedParams -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| size_t i = params->size(); |
| params->resize(i + p.size()); |
| for (const ParamDescriptor& sp : p) { |
| status = objcpy(&(*params)[i++], sp); |
| if (status != C2_OK) { |
| ALOGE("querySupportedParams -- " |
| "invalid returned ParamDescriptor. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| } |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("querySupportedParams -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2ConfigurableClient::querySupportedValues( |
| std::vector<C2FieldSupportedValuesQuery>& fields, |
| c2_blocking_t mayBlock) const { |
| hidl_vec<FieldSupportedValuesQuery> inFields(fields.size()); |
| for (size_t i = 0; i < fields.size(); ++i) { |
| Status hidlStatus = objcpy(&inFields[i], fields[i]); |
| if (hidlStatus != Status::OK) { |
| ALOGE("querySupportedValues -- bad input"); |
| return C2_TRANSACTION_FAILED; |
| } |
| } |
| |
| c2_status_t status; |
| Return<void> transStatus = base()->querySupportedValues( |
| inFields, |
| mayBlock == C2_MAY_BLOCK, |
| [&status, &inFields, &fields]( |
| Status s, |
| const hidl_vec<FieldSupportedValuesQueryResult>& r) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| ALOGE("querySupportedValues -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| if (r.size() != fields.size()) { |
| ALOGE("querySupportedValues -- input and output lists " |
| "have different sizes."); |
| status = C2_CORRUPTED; |
| return; |
| } |
| for (size_t i = 0; i < fields.size(); ++i) { |
| status = objcpy(&fields[i], inFields[i], r[i]); |
| if (status != C2_OK) { |
| ALOGE("querySupportedValues -- invalid returned value. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| } |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("querySupportedValues -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return status; |
| } |
| |
| // Codec2Client::Component::HidlListener |
| struct Codec2Client::Component::HidlListener : public IComponentListener { |
| std::weak_ptr<Component> component; |
| std::weak_ptr<Listener> base; |
| |
| virtual Return<void> onWorkDone(const WorkBundle& workBundle) override { |
| std::list<std::unique_ptr<C2Work>> workItems; |
| c2_status_t status = objcpy(&workItems, workBundle); |
| if (status != C2_OK) { |
| ALOGI("onWorkDone -- received corrupted WorkBundle. " |
| "status = %d.", static_cast<int>(status)); |
| return Void(); |
| } |
| // release input buffers potentially held by the component from queue |
| size_t numDiscardedInputBuffers = 0; |
| std::shared_ptr<Codec2Client::Component> strongComponent = component.lock(); |
| if (strongComponent) { |
| numDiscardedInputBuffers = strongComponent->handleOnWorkDone(workItems); |
| } |
| if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) { |
| listener->onWorkDone(component, workItems, numDiscardedInputBuffers); |
| } else { |
| ALOGD("onWorkDone -- listener died."); |
| } |
| return Void(); |
| } |
| |
| virtual Return<void> onTripped( |
| const hidl_vec<SettingResult>& settingResults) override { |
| std::vector<std::shared_ptr<C2SettingResult>> c2SettingResults( |
| settingResults.size()); |
| c2_status_t status; |
| for (size_t i = 0; i < settingResults.size(); ++i) { |
| std::unique_ptr<C2SettingResult> c2SettingResult; |
| status = objcpy(&c2SettingResult, settingResults[i]); |
| if (status != C2_OK) { |
| ALOGI("onTripped -- received corrupted SettingResult. " |
| "status = %d.", static_cast<int>(status)); |
| return Void(); |
| } |
| c2SettingResults[i] = std::move(c2SettingResult); |
| } |
| if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) { |
| listener->onTripped(component, c2SettingResults); |
| } else { |
| ALOGD("onTripped -- listener died."); |
| } |
| return Void(); |
| } |
| |
| virtual Return<void> onError(Status s, uint32_t errorCode) override { |
| ALOGD("onError -- status = %d, errorCode = %u.", |
| static_cast<int>(s), |
| static_cast<unsigned>(errorCode)); |
| if (std::shared_ptr<Listener> listener = base.lock()) { |
| listener->onError(component, s == Status::OK ? |
| errorCode : static_cast<c2_status_t>(s)); |
| } else { |
| ALOGD("onError -- listener died."); |
| } |
| return Void(); |
| } |
| |
| virtual Return<void> onFramesRendered( |
| const hidl_vec<RenderedFrame>& renderedFrames) override { |
| std::shared_ptr<Listener> listener = base.lock(); |
| std::vector<Codec2Client::Listener::RenderedFrame> rfs; |
| rfs.reserve(renderedFrames.size()); |
| for (const RenderedFrame& rf : renderedFrames) { |
| if (rf.slotId >= 0) { |
| if (listener) { |
| rfs.emplace_back(rf.bufferQueueId, |
| rf.slotId, |
| rf.timestampNs); |
| } |
| } else { |
| std::shared_ptr<Codec2Client::Component> strongComponent = |
| component.lock(); |
| if (strongComponent) { |
| uint64_t frameIndex = rf.bufferQueueId; |
| size_t bufferIndex = static_cast<size_t>(~rf.slotId); |
| ALOGV("Received death notification of input buffer: " |
| "frameIndex = %llu, bufferIndex = %zu.", |
| static_cast<long long unsigned>(frameIndex), |
| bufferIndex); |
| std::shared_ptr<C2Buffer> buffer = |
| strongComponent->freeInputBuffer( |
| frameIndex, bufferIndex); |
| if (buffer) { |
| listener->onInputBufferDone(buffer); |
| } |
| } |
| } |
| } |
| if (!rfs.empty()) { |
| if (listener) { |
| listener->onFramesRendered(rfs); |
| } else { |
| ALOGD("onFramesRendered -- listener died."); |
| } |
| } |
| return Void(); |
| } |
| }; |
| |
| // Codec2Client |
| Codec2Client::Base* Codec2Client::base() const { |
| return static_cast<Base*>(mBase.get()); |
| } |
| |
| Codec2Client::Codec2Client(const sp<Codec2Client::Base>& base, std::string instanceName) : |
| Codec2ConfigurableClient(base), mListed(false), mInstanceName(instanceName) { |
| Return<sp<IClientManager>> transResult = base->getPoolClientManager(); |
| if (!transResult.isOk()) { |
| ALOGE("getPoolClientManager -- failed transaction."); |
| } else { |
| mHostPoolManager = static_cast<sp<IClientManager>>(transResult); |
| } |
| } |
| |
| c2_status_t Codec2Client::createComponent( |
| const C2String& name, |
| const std::shared_ptr<Codec2Client::Listener>& listener, |
| std::shared_ptr<Codec2Client::Component>* const component) { |
| |
| // TODO: Add support for Bufferpool |
| |
| |
| c2_status_t status; |
| sp<Component::HidlListener> hidlListener = new Component::HidlListener(); |
| hidlListener->base = listener; |
| Return<void> transStatus = base()->createComponent( |
| name, |
| hidlListener, |
| ClientManager::getInstance(), |
| [&status, component, hidlListener]( |
| Status s, |
| const sp<IComponent>& c) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| return; |
| } |
| *component = std::make_shared<Codec2Client::Component>(c); |
| hidlListener->component = *component; |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("createComponent -- failed transaction."); |
| return C2_TRANSACTION_FAILED; |
| } |
| |
| if (status != C2_OK) { |
| return status; |
| } |
| |
| if (!*component) { |
| ALOGE("createComponent -- null component."); |
| return C2_CORRUPTED; |
| } |
| |
| status = (*component)->setDeathListener(*component, listener); |
| if (status != C2_OK) { |
| ALOGE("createComponent -- setDeathListener returned error: %d.", |
| static_cast<int>(status)); |
| } |
| |
| (*component)->mBufferPoolSender.setReceiver(mHostPoolManager); |
| return status; |
| } |
| |
| c2_status_t Codec2Client::createInterface( |
| const C2String& name, |
| std::shared_ptr<Codec2Client::Interface>* const interface) { |
| c2_status_t status; |
| Return<void> transStatus = base()->createInterface( |
| name, |
| [&status, interface]( |
| Status s, |
| const sp<IComponentInterface>& i) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| ALOGE("createInterface -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| *interface = std::make_shared<Codec2Client::Interface>(i); |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("createInterface -- failed transaction."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2Client::createInputSurface( |
| std::shared_ptr<Codec2Client::InputSurface>* const inputSurface) { |
| Return<sp<IInputSurface>> transResult = base()->createInputSurface(); |
| if (!transResult.isOk()) { |
| ALOGE("createInputSurface -- failed transaction."); |
| return C2_TRANSACTION_FAILED; |
| } |
| sp<IInputSurface> result = static_cast<sp<IInputSurface>>(transResult); |
| if (!result) { |
| *inputSurface = nullptr; |
| return C2_OK; |
| } |
| *inputSurface = std::make_shared<InputSurface>(result); |
| if (!*inputSurface) { |
| ALOGE("createInputSurface -- unknown error."); |
| return C2_CORRUPTED; |
| } |
| return C2_OK; |
| } |
| |
| const std::vector<C2Component::Traits>& Codec2Client::listComponents() const { |
| std::lock_guard<std::mutex> lock(mMutex); |
| if (mListed) { |
| return mTraitsList; |
| } |
| Return<void> transStatus = base()->listComponents( |
| [this](const hidl_vec<IComponentStore::ComponentTraits>& t) { |
| mTraitsList.resize(t.size()); |
| mAliasesBuffer.resize(t.size()); |
| for (size_t i = 0; i < t.size(); ++i) { |
| c2_status_t status = objcpy( |
| &mTraitsList[i], &mAliasesBuffer[i], t[i]); |
| if (status != C2_OK) { |
| ALOGE("listComponents -- corrupted output."); |
| return; |
| } |
| } |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("listComponents -- failed transaction."); |
| } |
| mListed = true; |
| return mTraitsList; |
| } |
| |
| c2_status_t Codec2Client::copyBuffer( |
| const std::shared_ptr<C2Buffer>& src, |
| const std::shared_ptr<C2Buffer>& dst) { |
| // TODO: Implement? |
| (void)src; |
| (void)dst; |
| ALOGE("copyBuffer not implemented"); |
| return C2_OMITTED; |
| } |
| |
| std::shared_ptr<C2ParamReflector> |
| Codec2Client::getParamReflector() { |
| // TODO: this is not meant to be exposed as C2ParamReflector on the client side; instead, it |
| // should reflect the HAL API. |
| struct SimpleParamReflector : public C2ParamReflector { |
| virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) const { |
| hidl_vec<ParamIndex> indices(1); |
| indices[0] = static_cast<ParamIndex>(coreIndex.coreIndex()); |
| std::unique_ptr<C2StructDescriptor> descriptor; |
| Return<void> transStatus = mBase->getStructDescriptors( |
| indices, |
| [&descriptor]( |
| Status s, |
| const hidl_vec<StructDescriptor>& sd) { |
| c2_status_t status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| ALOGE("getStructDescriptors -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| descriptor.reset(); |
| return; |
| } |
| if (sd.size() != 1) { |
| ALOGD("getStructDescriptors -- returned vector of size %zu.", |
| sd.size()); |
| descriptor.reset(); |
| return; |
| } |
| status = objcpy(&descriptor, sd[0]); |
| if (status != C2_OK) { |
| ALOGD("getStructDescriptors -- failed to convert. " |
| "Error code = %d", static_cast<int>(status)); |
| descriptor.reset(); |
| return; |
| } |
| }); |
| return descriptor; |
| } |
| |
| SimpleParamReflector(sp<Base> base) |
| : mBase(base) { } |
| |
| sp<Base> mBase; |
| }; |
| |
| return std::make_shared<SimpleParamReflector>(base()); |
| }; |
| |
| std::shared_ptr<Codec2Client> Codec2Client::CreateFromService( |
| const char* instanceName, bool waitForService) { |
| if (!instanceName) { |
| return nullptr; |
| } |
| sp<Base> baseStore = waitForService ? |
| Base::getService(instanceName) : |
| Base::tryGetService(instanceName); |
| if (!baseStore) { |
| if (waitForService) { |
| ALOGE("Codec2.0 service inaccessible. Check the device manifest."); |
| } else { |
| ALOGW("Codec2.0 service not available right now. Try again later."); |
| } |
| return nullptr; |
| } |
| return std::make_shared<Codec2Client>(baseStore, instanceName); |
| } |
| |
| c2_status_t Codec2Client::ForAllStores( |
| const std::string &key, |
| std::function<c2_status_t(const std::shared_ptr<Codec2Client>&)> predicate) { |
| c2_status_t status = C2_NO_INIT; // no IComponentStores present |
| |
| // Cache the mapping key -> index of Codec2Client in getClient(). |
| static std::mutex key2IndexMutex; |
| static std::map<std::string, size_t> key2Index; |
| |
| // By default try all stores. However, try the last known client first. If the last known |
| // client fails, retry once. We do this by pushing the last known client in front of the |
| // list of all clients. |
| std::deque<size_t> indices; |
| for (size_t index = kNumClients; index > 0; ) { |
| indices.push_front(--index); |
| } |
| |
| bool wasMapped = false; |
| std::unique_lock<std::mutex> lock(key2IndexMutex); |
| auto it = key2Index.find(key); |
| if (it != key2Index.end()) { |
| indices.push_front(it->second); |
| wasMapped = true; |
| } |
| lock.unlock(); |
| |
| for (size_t index : indices) { |
| std::shared_ptr<Codec2Client> client = getClient(index); |
| if (client) { |
| status = predicate(client); |
| if (status == C2_OK) { |
| lock.lock(); |
| key2Index[key] = index; // update last known client index |
| return status; |
| } |
| } |
| if (wasMapped) { |
| ALOGI("Could not find '%s' in last instance. Retrying...", key.c_str()); |
| wasMapped = false; |
| } |
| } |
| return status; // return the last status from a valid client |
| } |
| |
| std::shared_ptr<Codec2Client::Component> |
| Codec2Client::CreateComponentByName( |
| const char* componentName, |
| const std::shared_ptr<Listener>& listener, |
| std::shared_ptr<Codec2Client>* owner) { |
| std::shared_ptr<Component> component; |
| c2_status_t status = ForAllStores( |
| componentName, |
| [owner, &component, componentName, &listener]( |
| const std::shared_ptr<Codec2Client> &client) -> c2_status_t { |
| c2_status_t status = client->createComponent(componentName, listener, &component); |
| if (status == C2_OK) { |
| if (owner) { |
| *owner = client; |
| } |
| } else if (status != C2_NOT_FOUND) { |
| ALOGD("IComponentStore(%s)::createComponent('%s') returned %s", |
| client->getInstanceName().c_str(), componentName, asString(status)); |
| } |
| return status; |
| }); |
| if (status != C2_OK) { |
| ALOGI("Could not create component '%s' (%s)", componentName, asString(status)); |
| } |
| return component; |
| } |
| |
| std::shared_ptr<Codec2Client::Interface> |
| Codec2Client::CreateInterfaceByName( |
| const char* interfaceName, |
| std::shared_ptr<Codec2Client>* owner) { |
| std::shared_ptr<Interface> interface; |
| c2_status_t status = ForAllStores( |
| interfaceName, |
| [owner, &interface, interfaceName]( |
| const std::shared_ptr<Codec2Client> &client) -> c2_status_t { |
| c2_status_t status = client->createInterface(interfaceName, &interface); |
| if (status == C2_OK) { |
| if (owner) { |
| *owner = client; |
| } |
| } else if (status != C2_NOT_FOUND) { |
| ALOGD("IComponentStore(%s)::createInterface('%s') returned %s", |
| client->getInstanceName().c_str(), interfaceName, asString(status)); |
| } |
| return status; |
| }); |
| if (status != C2_OK) { |
| ALOGI("Could not create interface '%s' (%s)", interfaceName, asString(status)); |
| } |
| return interface; |
| } |
| |
| std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface() { |
| uint32_t serviceMask = ::android::base::GetUintProperty( |
| "debug.stagefright.c2inputsurface", uint32_t(0)); |
| for (size_t i = 0; i < kNumClients; ++i) { |
| if ((1 << i) & serviceMask) { |
| std::shared_ptr<Codec2Client> client = getClient(i); |
| std::shared_ptr<Codec2Client::InputSurface> inputSurface; |
| if (client && |
| client->createInputSurface(&inputSurface) == C2_OK && |
| inputSurface) { |
| return inputSurface; |
| } |
| } |
| } |
| ALOGW("Could not create an input surface from any Codec2.0 services."); |
| return nullptr; |
| } |
| |
| const std::vector<C2Component::Traits>& Codec2Client::ListComponents() { |
| static std::vector<C2Component::Traits> traitsList = [](){ |
| std::vector<C2Component::Traits> list; |
| size_t listSize = 0; |
| ClientList clientList = getClientList(); |
| for (const std::shared_ptr<Codec2Client>& client : clientList) { |
| if (!client) { |
| continue; |
| } |
| listSize += client->listComponents().size(); |
| } |
| list.reserve(listSize); |
| for (const std::shared_ptr<Codec2Client>& client : clientList) { |
| if (!client) { |
| continue; |
| } |
| list.insert( |
| list.end(), |
| client->listComponents().begin(), |
| client->listComponents().end()); |
| } |
| return list; |
| }(); |
| |
| return traitsList; |
| } |
| |
| // Codec2Client::Listener |
| |
| Codec2Client::Listener::~Listener() { |
| } |
| |
| // Codec2Client::Component |
| |
| Codec2Client::Component::Base* Codec2Client::Component::base() const { |
| return static_cast<Base*>(mBase.get()); |
| } |
| |
| Codec2Client::Component::Component(const sp<Codec2Client::Component::Base>& base) : |
| Codec2Client::Configurable(base), |
| mBufferPoolSender(nullptr) { |
| } |
| |
| Codec2Client::Component::~Component() { |
| } |
| |
| c2_status_t Codec2Client::Component::createBlockPool( |
| C2Allocator::id_t id, |
| C2BlockPool::local_id_t* blockPoolId, |
| std::shared_ptr<Codec2Client::Configurable>* configurable) { |
| c2_status_t status; |
| Return<void> transStatus = base()->createBlockPool( |
| static_cast<uint32_t>(id), |
| [&status, blockPoolId, configurable]( |
| Status s, |
| uint64_t pId, |
| const sp<IConfigurable>& c) { |
| status = static_cast<c2_status_t>(s); |
| configurable->reset(); |
| if (status != C2_OK) { |
| ALOGE("createBlockPool -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| *blockPoolId = static_cast<C2BlockPool::local_id_t>(pId); |
| *configurable = std::make_shared<Codec2Client::Configurable>(c); |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("createBlockPool -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::destroyBlockPool( |
| C2BlockPool::local_id_t localId) { |
| Return<Status> transResult = base()->destroyBlockPool( |
| static_cast<uint64_t>(localId)); |
| if (!transResult.isOk()) { |
| ALOGE("destroyBlockPool -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return static_cast<c2_status_t>(static_cast<Status>(transResult)); |
| } |
| |
| size_t Codec2Client::Component::handleOnWorkDone( |
| const std::list<std::unique_ptr<C2Work>> &workItems) { |
| // Input buffers' lifetime management |
| std::vector<uint64_t> inputDone; |
| for (const std::unique_ptr<C2Work> &work : workItems) { |
| if (work) { |
| if (work->worklets.empty() |
| || !work->worklets.back() |
| || (work->worklets.back()->output.flags & C2FrameData::FLAG_INCOMPLETE) == 0) { |
| // input is complete |
| inputDone.emplace_back(work->input.ordinal.frameIndex.peeku()); |
| } |
| } |
| } |
| |
| size_t numDiscardedInputBuffers = 0; |
| { |
| std::lock_guard<std::mutex> lock(mInputBuffersMutex); |
| for (uint64_t inputIndex : inputDone) { |
| auto it = mInputBuffers.find(inputIndex); |
| if (it == mInputBuffers.end()) { |
| ALOGV("onWorkDone -- returned consumed/unknown " |
| "input frame: index %llu", |
| (long long)inputIndex); |
| } else { |
| ALOGV("onWorkDone -- processed input frame: " |
| "index %llu (containing %zu buffers)", |
| (long long)inputIndex, it->second.size()); |
| mInputBuffers.erase(it); |
| mInputBufferCount.erase(inputIndex); |
| ++numDiscardedInputBuffers; |
| } |
| } |
| } |
| |
| // Output bufferqueue-based blocks' lifetime management |
| mOutputBufferQueueMutex.lock(); |
| sp<IGraphicBufferProducer> igbp = mOutputIgbp; |
| uint64_t bqId = mOutputBqId; |
| uint32_t generation = mOutputGeneration; |
| mOutputBufferQueueMutex.unlock(); |
| |
| if (igbp) { |
| holdBufferQueueBlocks(workItems, igbp, bqId, generation); |
| } |
| return numDiscardedInputBuffers; |
| } |
| |
| std::shared_ptr<C2Buffer> Codec2Client::Component::freeInputBuffer( |
| uint64_t frameIndex, |
| size_t bufferIndex) { |
| std::shared_ptr<C2Buffer> buffer; |
| std::lock_guard<std::mutex> lock(mInputBuffersMutex); |
| auto it = mInputBuffers.find(frameIndex); |
| if (it == mInputBuffers.end()) { |
| ALOGI("freeInputBuffer -- Unrecognized input frame index %llu.", |
| static_cast<long long unsigned>(frameIndex)); |
| return nullptr; |
| } |
| if (bufferIndex >= it->second.size()) { |
| ALOGI("freeInputBuffer -- Input buffer no. %zu is invalid in " |
| "input frame index %llu.", |
| bufferIndex, static_cast<long long unsigned>(frameIndex)); |
| return nullptr; |
| } |
| buffer = it->second[bufferIndex]; |
| if (!buffer) { |
| ALOGI("freeInputBuffer -- Input buffer no. %zu in " |
| "input frame index %llu has already been freed.", |
| bufferIndex, static_cast<long long unsigned>(frameIndex)); |
| return nullptr; |
| } |
| it->second[bufferIndex] = nullptr; |
| if (--mInputBufferCount[frameIndex] == 0) { |
| mInputBuffers.erase(it); |
| mInputBufferCount.erase(frameIndex); |
| } |
| return buffer; |
| } |
| |
| c2_status_t Codec2Client::Component::queue( |
| std::list<std::unique_ptr<C2Work>>* const items) { |
| // remember input buffers queued to hold reference to them |
| { |
| std::lock_guard<std::mutex> lock(mInputBuffersMutex); |
| for (const std::unique_ptr<C2Work> &work : *items) { |
| if (!work) { |
| continue; |
| } |
| if (work->input.buffers.size() == 0) { |
| continue; |
| } |
| |
| uint64_t inputIndex = work->input.ordinal.frameIndex.peeku(); |
| auto res = mInputBuffers.emplace(inputIndex, work->input.buffers); |
| if (!res.second) { |
| // TODO: append? - for now we are replacing |
| res.first->second = work->input.buffers; |
| ALOGI("queue -- duplicate input frame: index %llu. " |
| "Discarding the old input frame...", |
| (long long)inputIndex); |
| } |
| mInputBufferCount[inputIndex] = work->input.buffers.size(); |
| ALOGV("queue -- queueing input frame: " |
| "index %llu (containing %zu buffers)", |
| (long long)inputIndex, work->input.buffers.size()); |
| } |
| } |
| |
| WorkBundle workBundle; |
| Status hidlStatus = objcpy(&workBundle, *items, &mBufferPoolSender); |
| if (hidlStatus != Status::OK) { |
| ALOGE("queue -- bad input."); |
| return C2_TRANSACTION_FAILED; |
| } |
| Return<Status> transStatus = base()->queue(workBundle); |
| if (!transStatus.isOk()) { |
| ALOGE("queue -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("queue -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::flush( |
| C2Component::flush_mode_t mode, |
| std::list<std::unique_ptr<C2Work>>* const flushedWork) { |
| (void)mode; // Flush mode isn't supported in HIDL yet. |
| c2_status_t status; |
| Return<void> transStatus = base()->flush( |
| [&status, flushedWork]( |
| Status s, const WorkBundle& wb) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| ALOGE("flush -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| status = objcpy(flushedWork, wb); |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("flush -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| |
| // Indices of flushed work items. |
| std::vector<uint64_t> flushedIndices; |
| for (const std::unique_ptr<C2Work> &work : *flushedWork) { |
| if (work) { |
| if (work->worklets.empty() |
| || !work->worklets.back() |
| || (work->worklets.back()->output.flags & |
| C2FrameData::FLAG_INCOMPLETE) == 0) { |
| // input is complete |
| flushedIndices.emplace_back( |
| work->input.ordinal.frameIndex.peeku()); |
| } |
| } |
| } |
| |
| // Input buffers' lifetime management |
| for (uint64_t flushedIndex : flushedIndices) { |
| std::lock_guard<std::mutex> lock(mInputBuffersMutex); |
| auto it = mInputBuffers.find(flushedIndex); |
| if (it == mInputBuffers.end()) { |
| ALOGV("flush -- returned consumed/unknown input frame: " |
| "index %llu", |
| (long long)flushedIndex); |
| } else { |
| ALOGV("flush -- returned unprocessed input frame: " |
| "index %llu (containing %zu buffers)", |
| (long long)flushedIndex, mInputBufferCount[flushedIndex]); |
| mInputBuffers.erase(it); |
| mInputBufferCount.erase(flushedIndex); |
| } |
| } |
| |
| // Output bufferqueue-based blocks' lifetime management |
| mOutputBufferQueueMutex.lock(); |
| sp<IGraphicBufferProducer> igbp = mOutputIgbp; |
| uint64_t bqId = mOutputBqId; |
| uint32_t generation = mOutputGeneration; |
| mOutputBufferQueueMutex.unlock(); |
| |
| if (igbp) { |
| holdBufferQueueBlocks(*flushedWork, igbp, bqId, generation); |
| } |
| |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) { |
| Return<Status> transStatus = base()->drain( |
| mode == C2Component::DRAIN_COMPONENT_WITH_EOS); |
| if (!transStatus.isOk()) { |
| ALOGE("drain -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("drain -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::start() { |
| Return<Status> transStatus = base()->start(); |
| if (!transStatus.isOk()) { |
| ALOGE("start -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("start -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::stop() { |
| Return<Status> transStatus = base()->stop(); |
| if (!transStatus.isOk()) { |
| ALOGE("stop -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("stop -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| mInputBuffersMutex.lock(); |
| mInputBuffers.clear(); |
| mInputBufferCount.clear(); |
| mInputBuffersMutex.unlock(); |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::reset() { |
| Return<Status> transStatus = base()->reset(); |
| if (!transStatus.isOk()) { |
| ALOGE("reset -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("reset -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| mInputBuffersMutex.lock(); |
| mInputBuffers.clear(); |
| mInputBufferCount.clear(); |
| mInputBuffersMutex.unlock(); |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::release() { |
| Return<Status> transStatus = base()->release(); |
| if (!transStatus.isOk()) { |
| ALOGE("release -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("release -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| mInputBuffersMutex.lock(); |
| mInputBuffers.clear(); |
| mInputBufferCount.clear(); |
| mInputBuffersMutex.unlock(); |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::setOutputSurface( |
| C2BlockPool::local_id_t blockPoolId, |
| const sp<IGraphicBufferProducer>& surface, |
| uint32_t generation) { |
| sp<HGraphicBufferProducer> igbp = surface->getHalInterface(); |
| if (!igbp) { |
| igbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(surface); |
| } |
| |
| Return<Status> transStatus = base()->setOutputSurface( |
| static_cast<uint64_t>(blockPoolId), igbp); |
| if (!transStatus.isOk()) { |
| ALOGE("setOutputSurface -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("setOutputSurface -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } else { |
| std::lock_guard<std::mutex> lock(mOutputBufferQueueMutex); |
| if (mOutputIgbp != surface) { |
| mOutputIgbp = surface; |
| if (!surface) { |
| mOutputBqId = 0; |
| } else if (surface->getUniqueId(&mOutputBqId) != OK) { |
| ALOGE("setOutputSurface -- cannot obtain bufferqueue id."); |
| } |
| } |
| mOutputGeneration = generation; |
| } |
| return status; |
| } |
| |
| status_t Codec2Client::Component::queueToOutputSurface( |
| const C2ConstGraphicBlock& block, |
| const QueueBufferInput& input, |
| QueueBufferOutput* output) { |
| uint32_t generation; |
| uint64_t bqId; |
| int32_t bqSlot; |
| if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) || |
| bqId == 0) { |
| // Block not from bufferqueue -- it must be attached before queuing. |
| |
| mOutputBufferQueueMutex.lock(); |
| sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp; |
| uint32_t outputGeneration = mOutputGeneration; |
| mOutputBufferQueueMutex.unlock(); |
| |
| status_t status = !attachToBufferQueue(block, |
| outputIgbp, |
| outputGeneration, |
| &bqSlot); |
| if (status != OK) { |
| ALOGW("queueToOutputSurface -- attaching failed."); |
| return INVALID_OPERATION; |
| } |
| |
| status = outputIgbp->queueBuffer(static_cast<int>(bqSlot), |
| input, output); |
| if (status != OK) { |
| ALOGE("queueToOutputSurface -- queueBuffer() failed " |
| "on non-bufferqueue-based block. " |
| "Error code = %d.", |
| static_cast<int>(status)); |
| return status; |
| } |
| return OK; |
| } |
| |
| mOutputBufferQueueMutex.lock(); |
| sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp; |
| uint64_t outputBqId = mOutputBqId; |
| uint32_t outputGeneration = mOutputGeneration; |
| mOutputBufferQueueMutex.unlock(); |
| |
| if (!outputIgbp) { |
| ALOGV("queueToOutputSurface -- output surface is null."); |
| return NO_INIT; |
| } |
| |
| if (bqId != outputBqId) { |
| ALOGV("queueToOutputSurface -- bufferqueue ids mismatch."); |
| return DEAD_OBJECT; |
| } |
| |
| if (generation != outputGeneration) { |
| ALOGV("queueToOutputSurface -- generation numbers mismatch."); |
| return DEAD_OBJECT; |
| } |
| |
| status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot), |
| input, output); |
| if (status != OK) { |
| ALOGD("queueToOutputSurface -- queueBuffer() failed " |
| "on bufferqueue-based block. " |
| "Error code = %d.", |
| static_cast<int>(status)); |
| return status; |
| } |
| if (!yieldBufferQueueBlock(block)) { |
| ALOGD("queueToOutputSurface -- cannot yield bufferqueue-based block " |
| "to the bufferqueue."); |
| return UNKNOWN_ERROR; |
| } |
| return OK; |
| } |
| |
| c2_status_t Codec2Client::Component::connectToOmxInputSurface( |
| const sp<HGraphicBufferProducer>& producer, |
| const sp<HGraphicBufferSource>& source) { |
| Return<Status> transStatus = base()->connectToOmxInputSurface( |
| producer, source); |
| if (!transStatus.isOk()) { |
| ALOGE("connectToOmxInputSurface -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("connectToOmxInputSurface -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::disconnectFromInputSurface() { |
| Return<Status> transStatus = base()->disconnectFromInputSurface(); |
| if (!transStatus.isOk()) { |
| ALOGE("disconnectToInputSurface -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| c2_status_t status = |
| static_cast<c2_status_t>(static_cast<Status>(transStatus)); |
| if (status != C2_OK) { |
| ALOGE("disconnectFromInputSurface -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| } |
| return status; |
| } |
| |
| c2_status_t Codec2Client::Component::setDeathListener( |
| const std::shared_ptr<Component>& component, |
| const std::shared_ptr<Listener>& listener) { |
| |
| struct HidlDeathRecipient : public hardware::hidl_death_recipient { |
| std::weak_ptr<Component> component; |
| std::weak_ptr<Listener> base; |
| |
| virtual void serviceDied( |
| uint64_t /* cookie */, |
| const wp<::android::hidl::base::V1_0::IBase>& /* who */ |
| ) override { |
| if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) { |
| listener->onDeath(component); |
| } else { |
| ALOGW("onDeath -- listener died."); |
| } |
| } |
| }; |
| |
| sp<HidlDeathRecipient> deathRecipient = new HidlDeathRecipient(); |
| deathRecipient->base = listener; |
| deathRecipient->component = component; |
| |
| component->mDeathRecipient = deathRecipient; |
| Return<bool> transResult = component->base()->linkToDeath( |
| component->mDeathRecipient, 0); |
| if (!transResult.isOk()) { |
| ALOGE("setDeathListener -- failed transaction: linkToDeath."); |
| return C2_TRANSACTION_FAILED; |
| } |
| if (!static_cast<bool>(transResult)) { |
| ALOGE("setDeathListener -- linkToDeath call failed."); |
| return C2_CORRUPTED; |
| } |
| return C2_OK; |
| } |
| |
| // Codec2Client::InputSurface |
| |
| Codec2Client::InputSurface::Base* Codec2Client::InputSurface::base() const { |
| return static_cast<Base*>(mBase.get()); |
| } |
| |
| Codec2Client::InputSurface::InputSurface(const sp<IInputSurface>& base) : |
| mBase(base), |
| mGraphicBufferProducer(new |
| ::android::hardware::graphics::bufferqueue::V1_0::utils:: |
| H2BGraphicBufferProducer(base)) { |
| } |
| |
| c2_status_t Codec2Client::InputSurface::connectToComponent( |
| const std::shared_ptr<Codec2Client::Component>& component, |
| std::shared_ptr<Connection>* connection) { |
| c2_status_t status; |
| Return<void> transStatus = base()->connectToComponent( |
| component->base(), |
| [&status, connection]( |
| Status s, |
| const sp<IInputSurfaceConnection>& c) { |
| status = static_cast<c2_status_t>(s); |
| if (status != C2_OK) { |
| ALOGE("connectToComponent -- call failed. " |
| "Error code = %d", static_cast<int>(status)); |
| return; |
| } |
| *connection = std::make_shared<Connection>(c); |
| }); |
| if (!transStatus.isOk()) { |
| ALOGE("connect -- transaction failed."); |
| return C2_TRANSACTION_FAILED; |
| } |
| return status; |
| } |
| |
| std::shared_ptr<Codec2Client::Configurable> |
| Codec2Client::InputSurface::getConfigurable() const { |
| Return<sp<IConfigurable>> transResult = base()->getConfigurable(); |
| if (!transResult.isOk()) { |
| ALOGW("getConfigurable -- transaction failed."); |
| return nullptr; |
| } |
| if (!static_cast<sp<IConfigurable>>(transResult)) { |
| ALOGW("getConfigurable -- null pointer."); |
| return nullptr; |
| } |
| return std::make_shared<Configurable>(transResult); |
| } |
| |
| const sp<IGraphicBufferProducer>& |
| Codec2Client::InputSurface::getGraphicBufferProducer() const { |
| return mGraphicBufferProducer; |
| } |
| |
| const sp<IInputSurface>& Codec2Client::InputSurface::getHalInterface() const { |
| return mBase; |
| } |
| |
| // Codec2Client::InputSurfaceConnection |
| |
| Codec2Client::InputSurfaceConnection::Base* |
| Codec2Client::InputSurfaceConnection::base() const { |
| return static_cast<Base*>(mBase.get()); |
| } |
| |
| Codec2Client::InputSurfaceConnection::InputSurfaceConnection( |
| const sp<Codec2Client::InputSurfaceConnection::Base>& base) : |
| mBase(base) { |
| } |
| |
| c2_status_t Codec2Client::InputSurfaceConnection::disconnect() { |
| Return<Status> transResult = base()->disconnect(); |
| return static_cast<c2_status_t>(static_cast<Status>(transResult)); |
| } |
| |
| } // namespace android |
| |