1 /*
2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "Codec2-Component@1.2"
19 #include <android-base/logging.h>
20
21 #include <codec2/hidl/1.2/Component.h>
22 #include <codec2/hidl/1.2/ComponentStore.h>
23 #include <codec2/hidl/1.2/InputBufferManager.h>
24
25 #ifndef __ANDROID_APEX__
26 #include <FilterWrapper.h>
27 #endif
28
29 #include <hidl/HidlBinderSupport.h>
30 #include <utils/Timers.h>
31
32 #include <C2BqBufferPriv.h>
33 #include <C2Debug.h>
34 #include <C2PlatformSupport.h>
35
36 #include <chrono>
37 #include <thread>
38
39 namespace android {
40 namespace hardware {
41 namespace media {
42 namespace c2 {
43 namespace V1_2 {
44 namespace utils {
45
46 using namespace ::android;
47
48 // ComponentListener wrapper
49 struct Component::Listener : public C2Component::Listener {
50
Listenerandroid::hardware::media::c2::V1_2::utils::Component::Listener51 Listener(const sp<Component>& component) :
52 mComponent(component),
53 mListener(component->mListener) {
54 }
55
onError_nbandroid::hardware::media::c2::V1_2::utils::Component::Listener56 virtual void onError_nb(
57 std::weak_ptr<C2Component> /* c2component */,
58 uint32_t errorCode) override {
59 sp<IComponentListener> listener = mListener.promote();
60 if (listener) {
61 Return<void> transStatus = listener->onError(Status::OK, errorCode);
62 if (!transStatus.isOk()) {
63 LOG(ERROR) << "Component::Listener::onError_nb -- "
64 << "transaction failed.";
65 }
66 }
67 }
68
onTripped_nbandroid::hardware::media::c2::V1_2::utils::Component::Listener69 virtual void onTripped_nb(
70 std::weak_ptr<C2Component> /* c2component */,
71 std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
72 ) override {
73 sp<IComponentListener> listener = mListener.promote();
74 if (listener) {
75 hidl_vec<SettingResult> settingResults(c2settingResult.size());
76 size_t ix = 0;
77 for (const std::shared_ptr<C2SettingResult> &c2result :
78 c2settingResult) {
79 if (c2result) {
80 if (!objcpy(&settingResults[ix++], *c2result)) {
81 break;
82 }
83 }
84 }
85 settingResults.resize(ix);
86 Return<void> transStatus = listener->onTripped(settingResults);
87 if (!transStatus.isOk()) {
88 LOG(ERROR) << "Component::Listener::onTripped_nb -- "
89 << "transaction failed.";
90 }
91 }
92 }
93
onWorkDone_nbandroid::hardware::media::c2::V1_2::utils::Component::Listener94 virtual void onWorkDone_nb(
95 std::weak_ptr<C2Component> /* c2component */,
96 std::list<std::unique_ptr<C2Work>> c2workItems) override {
97 for (const std::unique_ptr<C2Work>& work : c2workItems) {
98 if (work) {
99 if (work->worklets.empty()
100 || !work->worklets.back()
101 || (work->worklets.back()->output.flags &
102 C2FrameData::FLAG_INCOMPLETE) == 0) {
103 InputBufferManager::
104 unregisterFrameData(mListener, work->input);
105 }
106 }
107 }
108
109 sp<IComponentListener> listener = mListener.promote();
110 if (listener) {
111 WorkBundle workBundle;
112
113 sp<Component> strongComponent = mComponent.promote();
114 beginTransferBufferQueueBlocks(c2workItems, true);
115 if (!objcpy(&workBundle, c2workItems, strongComponent ?
116 &strongComponent->mBufferPoolSender : nullptr)) {
117 LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
118 << "received corrupted work items.";
119 endTransferBufferQueueBlocks(c2workItems, false, true);
120 return;
121 }
122 Return<void> transStatus = listener->onWorkDone(workBundle);
123 if (!transStatus.isOk()) {
124 LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
125 << "transaction failed.";
126 endTransferBufferQueueBlocks(c2workItems, false, true);
127 return;
128 }
129 endTransferBufferQueueBlocks(c2workItems, true, true);
130 }
131 }
132
133 protected:
134 wp<Component> mComponent;
135 wp<IComponentListener> mListener;
136 };
137
138 // Component::Sink
139 struct Component::Sink : public IInputSink {
140 std::shared_ptr<Component> mComponent;
141 sp<IConfigurable> mConfigurable;
142
queueandroid::hardware::media::c2::V1_2::utils::Component::Sink143 virtual Return<Status> queue(const WorkBundle& workBundle) override {
144 return mComponent->queue(workBundle);
145 }
146
getConfigurableandroid::hardware::media::c2::V1_2::utils::Component::Sink147 virtual Return<sp<IConfigurable>> getConfigurable() override {
148 return mConfigurable;
149 }
150
151 Sink(const std::shared_ptr<Component>& component);
152 virtual ~Sink() override;
153
154 // Process-wide map: Component::Sink -> C2Component.
155 static std::mutex sSink2ComponentMutex;
156 static std::map<IInputSink*, std::weak_ptr<C2Component>> sSink2Component;
157
158 static std::shared_ptr<C2Component> findLocalComponent(
159 const sp<IInputSink>& sink);
160 };
161
162 std::mutex
163 Component::Sink::sSink2ComponentMutex{};
164 std::map<IInputSink*, std::weak_ptr<C2Component>>
165 Component::Sink::sSink2Component{};
166
Sink(const std::shared_ptr<Component> & component)167 Component::Sink::Sink(const std::shared_ptr<Component>& component)
168 : mComponent{component},
__anon7bdb10a30102() 169 mConfigurable{[&component]() -> sp<IConfigurable> {
170 Return<sp<IComponentInterface>> ret1 = component->getInterface();
171 if (!ret1.isOk()) {
172 LOG(ERROR) << "Sink::Sink -- component's transaction failed.";
173 return nullptr;
174 }
175 Return<sp<IConfigurable>> ret2 =
176 static_cast<sp<IComponentInterface>>(ret1)->
177 getConfigurable();
178 if (!ret2.isOk()) {
179 LOG(ERROR) << "Sink::Sink -- interface's transaction failed.";
180 return nullptr;
181 }
182 return static_cast<sp<IConfigurable>>(ret2);
183 }()} {
184 std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
185 sSink2Component.emplace(this, component->mComponent);
186 }
187
~Sink()188 Component::Sink::~Sink() {
189 std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
190 sSink2Component.erase(this);
191 }
192
findLocalComponent(const sp<IInputSink> & sink)193 std::shared_ptr<C2Component> Component::Sink::findLocalComponent(
194 const sp<IInputSink>& sink) {
195 std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
196 auto i = sSink2Component.find(sink.get());
197 if (i == sSink2Component.end()) {
198 return nullptr;
199 }
200 return i->second.lock();
201 }
202
203 // Component
Component(const std::shared_ptr<C2Component> & component,const sp<IComponentListener> & listener,const sp<ComponentStore> & store,const sp<::android::hardware::media::bufferpool::V2_0::IClientManager> & clientPoolManager)204 Component::Component(
205 const std::shared_ptr<C2Component>& component,
206 const sp<IComponentListener>& listener,
207 const sp<ComponentStore>& store,
208 const sp<::android::hardware::media::bufferpool::V2_0::
209 IClientManager>& clientPoolManager)
210 : mComponent{component},
211 mInterface{new ComponentInterface(component->intf(),
212 store->getParameterCache())},
213 mListener{listener},
214 mStore{store},
215 mBufferPoolSender{clientPoolManager} {
216 // Retrieve supported parameters from store
217 // TODO: We could cache this per component/interface type
218 mInit = mInterface->status();
219 }
220
status() const221 c2_status_t Component::status() const {
222 return mInit;
223 }
224
225 // Methods from ::android::hardware::media::c2::V1_1::IComponent
queue(const WorkBundle & workBundle)226 Return<Status> Component::queue(const WorkBundle& workBundle) {
227 std::list<std::unique_ptr<C2Work>> c2works;
228
229 if (!objcpy(&c2works, workBundle)) {
230 return Status::CORRUPTED;
231 }
232
233 // Register input buffers.
234 for (const std::unique_ptr<C2Work>& work : c2works) {
235 if (work) {
236 InputBufferManager::
237 registerFrameData(mListener, work->input);
238 }
239 }
240
241 return static_cast<Status>(mComponent->queue_nb(&c2works));
242 }
243
flush(flush_cb _hidl_cb)244 Return<void> Component::flush(flush_cb _hidl_cb) {
245 std::list<std::unique_ptr<C2Work>> c2flushedWorks;
246 c2_status_t c2res = mComponent->flush_sm(
247 C2Component::FLUSH_COMPONENT,
248 &c2flushedWorks);
249
250 // Unregister input buffers.
251 for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
252 if (work) {
253 if (work->worklets.empty()
254 || !work->worklets.back()
255 || (work->worklets.back()->output.flags &
256 C2FrameData::FLAG_INCOMPLETE) == 0) {
257 InputBufferManager::
258 unregisterFrameData(mListener, work->input);
259 }
260 }
261 }
262
263 WorkBundle flushedWorkBundle;
264 Status res = static_cast<Status>(c2res);
265 beginTransferBufferQueueBlocks(c2flushedWorks, true);
266 if (c2res == C2_OK) {
267 if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) {
268 res = Status::CORRUPTED;
269 }
270 }
271 _hidl_cb(res, flushedWorkBundle);
272 endTransferBufferQueueBlocks(c2flushedWorks, true, true);
273 return Void();
274 }
275
drain(bool withEos)276 Return<Status> Component::drain(bool withEos) {
277 return static_cast<Status>(mComponent->drain_nb(withEos ?
278 C2Component::DRAIN_COMPONENT_WITH_EOS :
279 C2Component::DRAIN_COMPONENT_NO_EOS));
280 }
281
setOutputSurface(uint64_t blockPoolId,const sp<HGraphicBufferProducer2> & surface)282 Return<Status> Component::setOutputSurface(
283 uint64_t blockPoolId,
284 const sp<HGraphicBufferProducer2>& surface) {
285 std::shared_ptr<C2BlockPool> pool;
286 GetCodec2BlockPool(blockPoolId, mComponent, &pool);
287 if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
288 std::shared_ptr<C2BufferQueueBlockPool> bqPool =
289 std::static_pointer_cast<C2BufferQueueBlockPool>(pool);
290 C2BufferQueueBlockPool::OnRenderCallback cb =
291 [this](uint64_t producer, int32_t slot, int64_t nsecs) {
292 // TODO: batch this
293 hidl_vec<IComponentListener::RenderedFrame> rendered;
294 rendered.resize(1);
295 rendered[0] = { producer, slot, nsecs };
296 (void)mListener->onFramesRendered(rendered).isOk();
297 };
298 if (bqPool) {
299 bqPool->setRenderCallback(cb);
300 bqPool->configureProducer(surface);
301 }
302 }
303 return Status::OK;
304 }
305
connectToInputSurface(const sp<IInputSurface> & inputSurface,connectToInputSurface_cb _hidl_cb)306 Return<void> Component::connectToInputSurface(
307 const sp<IInputSurface>& inputSurface,
308 connectToInputSurface_cb _hidl_cb) {
309 Status status;
310 sp<IInputSurfaceConnection> connection;
311 auto transStatus = inputSurface->connect(
312 asInputSink(),
313 [&status, &connection](
314 Status s, const sp<IInputSurfaceConnection>& c) {
315 status = s;
316 connection = c;
317 }
318 );
319 _hidl_cb(status, connection);
320 return Void();
321 }
322
connectToOmxInputSurface(const sp<HGraphicBufferProducer1> & producer,const sp<::android::hardware::media::omx::V1_0::IGraphicBufferSource> & source,connectToOmxInputSurface_cb _hidl_cb)323 Return<void> Component::connectToOmxInputSurface(
324 const sp<HGraphicBufferProducer1>& producer,
325 const sp<::android::hardware::media::omx::V1_0::
326 IGraphicBufferSource>& source,
327 connectToOmxInputSurface_cb _hidl_cb) {
328 (void)producer;
329 (void)source;
330 (void)_hidl_cb;
331 return Void();
332 }
333
disconnectFromInputSurface()334 Return<Status> Component::disconnectFromInputSurface() {
335 // TODO implement
336 return Status::OK;
337 }
338
339 namespace /* unnamed */ {
340
341 struct BlockPoolIntf : public ConfigurableC2Intf {
BlockPoolIntfandroid::hardware::media::c2::V1_2::utils::__anon7bdb10a30411::BlockPoolIntf342 BlockPoolIntf(const std::shared_ptr<C2BlockPool>& pool)
343 : ConfigurableC2Intf{
344 "C2BlockPool:" +
345 (pool ? std::to_string(pool->getLocalId()) : "null"),
346 0},
347 mPool{pool} {
348 }
349
configandroid::hardware::media::c2::V1_2::utils::__anon7bdb10a30411::BlockPoolIntf350 virtual c2_status_t config(
351 const std::vector<C2Param*>& params,
352 c2_blocking_t mayBlock,
353 std::vector<std::unique_ptr<C2SettingResult>>* const failures
354 ) override {
355 (void)params;
356 (void)mayBlock;
357 (void)failures;
358 return C2_OK;
359 }
360
queryandroid::hardware::media::c2::V1_2::utils::__anon7bdb10a30411::BlockPoolIntf361 virtual c2_status_t query(
362 const std::vector<C2Param::Index>& indices,
363 c2_blocking_t mayBlock,
364 std::vector<std::unique_ptr<C2Param>>* const params
365 ) const override {
366 (void)indices;
367 (void)mayBlock;
368 (void)params;
369 return C2_OK;
370 }
371
querySupportedParamsandroid::hardware::media::c2::V1_2::utils::__anon7bdb10a30411::BlockPoolIntf372 virtual c2_status_t querySupportedParams(
373 std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
374 ) const override {
375 (void)params;
376 return C2_OK;
377 }
378
querySupportedValuesandroid::hardware::media::c2::V1_2::utils::__anon7bdb10a30411::BlockPoolIntf379 virtual c2_status_t querySupportedValues(
380 std::vector<C2FieldSupportedValuesQuery>& fields,
381 c2_blocking_t mayBlock) const override {
382 (void)fields;
383 (void)mayBlock;
384 return C2_OK;
385 }
386
387 protected:
388 std::shared_ptr<C2BlockPool> mPool;
389 };
390
391 } // unnamed namespace
392
createBlockPool(uint32_t allocatorId,createBlockPool_cb _hidl_cb)393 Return<void> Component::createBlockPool(
394 uint32_t allocatorId,
395 createBlockPool_cb _hidl_cb) {
396 std::shared_ptr<C2BlockPool> blockPool;
397 #ifdef __ANDROID_APEX__
398 c2_status_t status = CreateCodec2BlockPool(
399 static_cast<C2PlatformAllocatorStore::id_t>(allocatorId),
400 mComponent,
401 &blockPool);
402 #else
403 c2_status_t status = ComponentStore::GetFilterWrapper()->createBlockPool(
404 static_cast<C2PlatformAllocatorStore::id_t>(allocatorId),
405 mComponent,
406 &blockPool);
407 #endif
408 if (status != C2_OK) {
409 blockPool = nullptr;
410 }
411 if (blockPool) {
412 mBlockPoolsMutex.lock();
413 mBlockPools.emplace(blockPool->getLocalId(), blockPool);
414 mBlockPoolsMutex.unlock();
415 } else if (status == C2_OK) {
416 status = C2_CORRUPTED;
417 }
418
419 _hidl_cb(static_cast<Status>(status),
420 blockPool ? blockPool->getLocalId() : 0,
421 new CachedConfigurable(
422 std::make_unique<BlockPoolIntf>(blockPool)));
423 return Void();
424 }
425
destroyBlockPool(uint64_t blockPoolId)426 Return<Status> Component::destroyBlockPool(uint64_t blockPoolId) {
427 std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
428 return mBlockPools.erase(blockPoolId) == 1 ?
429 Status::OK : Status::CORRUPTED;
430 }
431
start()432 Return<Status> Component::start() {
433 return static_cast<Status>(mComponent->start());
434 }
435
stop()436 Return<Status> Component::stop() {
437 InputBufferManager::unregisterFrameData(mListener);
438 return static_cast<Status>(mComponent->stop());
439 }
440
reset()441 Return<Status> Component::reset() {
442 Status status = static_cast<Status>(mComponent->reset());
443 {
444 std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
445 mBlockPools.clear();
446 }
447 InputBufferManager::unregisterFrameData(mListener);
448 return status;
449 }
450
release()451 Return<Status> Component::release() {
452 Status status = static_cast<Status>(mComponent->release());
453 {
454 std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
455 mBlockPools.clear();
456 }
457 InputBufferManager::unregisterFrameData(mListener);
458 return status;
459 }
460
getInterface()461 Return<sp<IComponentInterface>> Component::getInterface() {
462 return sp<IComponentInterface>(mInterface);
463 }
464
asInputSink()465 Return<sp<IInputSink>> Component::asInputSink() {
466 std::lock_guard<std::mutex> lock(mSinkMutex);
467 if (!mSink) {
468 mSink = new Sink(shared_from_this());
469 }
470 return {mSink};
471 }
472
configureVideoTunnel(uint32_t avSyncHwId,configureVideoTunnel_cb _hidl_cb)473 Return<void> Component::configureVideoTunnel(
474 uint32_t avSyncHwId, configureVideoTunnel_cb _hidl_cb) {
475 (void)avSyncHwId;
476 _hidl_cb(Status::OMITTED, hidl_handle{});
477 return Void();
478 }
479
setOutputSurfaceWithSyncObj(uint64_t blockPoolId,const sp<HGraphicBufferProducer2> & surface,const SurfaceSyncObj & syncObject)480 Return<Status> Component::setOutputSurfaceWithSyncObj(
481 uint64_t blockPoolId, const sp<HGraphicBufferProducer2>& surface,
482 const SurfaceSyncObj& syncObject) {
483 std::shared_ptr<C2BlockPool> pool;
484 GetCodec2BlockPool(blockPoolId, mComponent, &pool);
485 if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
486 std::shared_ptr<C2BufferQueueBlockPool> bqPool =
487 std::static_pointer_cast<C2BufferQueueBlockPool>(pool);
488 C2BufferQueueBlockPool::OnRenderCallback cb =
489 [this](uint64_t producer, int32_t slot, int64_t nsecs) {
490 // TODO: batch this
491 hidl_vec<IComponentListener::RenderedFrame> rendered;
492 rendered.resize(1);
493 rendered[0] = { producer, slot, nsecs };
494 (void)mListener->onFramesRendered(rendered).isOk();
495 };
496 if (bqPool) {
497 const native_handle_t *h = syncObject.syncMemory;
498 native_handle_t *syncMemory = h ? native_handle_clone(h) : nullptr;
499 uint64_t bqId = syncObject.bqId;
500 uint32_t generationId = syncObject.generationId;
501 uint64_t consumerUsage = syncObject.consumerUsage;
502
503 bqPool->setRenderCallback(cb);
504 bqPool->configureProducer(surface, syncMemory, bqId,
505 generationId, consumerUsage);
506 }
507 }
508 return Status::OK;
509 }
510
findLocalComponent(const sp<IInputSink> & sink)511 std::shared_ptr<C2Component> Component::findLocalComponent(
512 const sp<IInputSink>& sink) {
513 return Component::Sink::findLocalComponent(sink);
514 }
515
initListener(const sp<Component> & self)516 void Component::initListener(const sp<Component>& self) {
517 std::shared_ptr<C2Component::Listener> c2listener =
518 std::make_shared<Listener>(self);
519 c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
520 if (res != C2_OK) {
521 mInit = res;
522 }
523
524 struct ListenerDeathRecipient : public HwDeathRecipient {
525 ListenerDeathRecipient(const wp<Component>& comp)
526 : component{comp} {
527 }
528
529 virtual void serviceDied(
530 uint64_t /* cookie */,
531 const wp<::android::hidl::base::V1_0::IBase>& /* who */
532 ) override {
533 auto strongComponent = component.promote();
534 if (strongComponent) {
535 LOG(INFO) << "Client died ! release the component !!";
536 strongComponent->release();
537 } else {
538 LOG(ERROR) << "Client died ! no component to release !!";
539 }
540 }
541
542 wp<Component> component;
543 };
544
545 mDeathRecipient = new ListenerDeathRecipient(self);
546 Return<bool> transStatus = mListener->linkToDeath(
547 mDeathRecipient, 0);
548 if (!transStatus.isOk()) {
549 LOG(ERROR) << "Listener linkToDeath() transaction failed.";
550 }
551 if (!static_cast<bool>(transStatus)) {
552 LOG(DEBUG) << "Listener linkToDeath() call failed.";
553 }
554 }
555
~Component()556 Component::~Component() {
557 InputBufferManager::unregisterFrameData(mListener);
558 mStore->reportComponentDeath(this);
559 }
560
561 } // namespace utils
562 } // namespace V1_2
563 } // namespace c2
564 } // namespace media
565 } // namespace hardware
566 } // namespace android
567