blob: b158f8f2b005fbba5ac256ed441c14457af0f7c7 [file] [log] [blame]
Pawin Vongmasa36653902018-11-15 00:10:25 -08001/*
2 * Copyright (C) 2017 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 "SimpleC2Component"
19#include <log/log.h>
20
21#include <cutils/properties.h>
22#include <media/stagefright/foundation/AMessage.h>
23
24#include <inttypes.h>
25
26#include <C2Config.h>
27#include <C2Debug.h>
28#include <C2PlatformSupport.h>
29#include <SimpleC2Component.h>
30
31namespace android {
32
33std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
34 std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
35 mQueue.pop_front();
36 return work;
37}
38
39void SimpleC2Component::WorkQueue::push_back(std::unique_ptr<C2Work> work) {
40 mQueue.push_back({ std::move(work), NO_DRAIN });
41}
42
43bool SimpleC2Component::WorkQueue::empty() const {
44 return mQueue.empty();
45}
46
47void SimpleC2Component::WorkQueue::clear() {
48 mQueue.clear();
49}
50
51uint32_t SimpleC2Component::WorkQueue::drainMode() const {
52 return mQueue.front().drainMode;
53}
54
55void SimpleC2Component::WorkQueue::markDrain(uint32_t drainMode) {
56 mQueue.push_back({ nullptr, drainMode });
57}
58
59////////////////////////////////////////////////////////////////////////////////
60
61SimpleC2Component::WorkHandler::WorkHandler() : mRunning(false) {}
62
63void SimpleC2Component::WorkHandler::setComponent(
64 const std::shared_ptr<SimpleC2Component> &thiz) {
65 mThiz = thiz;
66}
67
68static void Reply(const sp<AMessage> &msg, int32_t *err = nullptr) {
69 sp<AReplyToken> replyId;
70 CHECK(msg->senderAwaitsResponse(&replyId));
71 sp<AMessage> reply = new AMessage;
72 if (err) {
73 reply->setInt32("err", *err);
74 }
75 reply->postReply(replyId);
76}
77
78void SimpleC2Component::WorkHandler::onMessageReceived(const sp<AMessage> &msg) {
79 std::shared_ptr<SimpleC2Component> thiz = mThiz.lock();
80 if (!thiz) {
81 ALOGD("component not yet set; msg = %s", msg->debugString().c_str());
82 sp<AReplyToken> replyId;
83 if (msg->senderAwaitsResponse(&replyId)) {
84 sp<AMessage> reply = new AMessage;
85 reply->setInt32("err", C2_CORRUPTED);
86 reply->postReply(replyId);
87 }
88 return;
89 }
90
91 switch (msg->what()) {
92 case kWhatProcess: {
93 if (mRunning) {
94 if (thiz->processQueue()) {
95 (new AMessage(kWhatProcess, this))->post();
96 }
97 } else {
98 ALOGV("Ignore process message as we're not running");
99 }
100 break;
101 }
102 case kWhatInit: {
103 int32_t err = thiz->onInit();
104 Reply(msg, &err);
105 [[fallthrough]];
106 }
107 case kWhatStart: {
108 mRunning = true;
109 break;
110 }
111 case kWhatStop: {
112 int32_t err = thiz->onStop();
113 Reply(msg, &err);
114 break;
115 }
116 case kWhatReset: {
117 thiz->onReset();
118 mRunning = false;
119 Reply(msg);
120 break;
121 }
122 case kWhatRelease: {
123 thiz->onRelease();
124 mRunning = false;
125 Reply(msg);
126 break;
127 }
128 default: {
129 ALOGD("Unrecognized msg: %d", msg->what());
130 break;
131 }
132 }
133}
134
Wonsik Kim0cb5a092019-01-03 16:38:22 -0800135class SimpleC2Component::BlockingBlockPool : public C2BlockPool {
136public:
137 BlockingBlockPool(const std::shared_ptr<C2BlockPool>& base): mBase{base} {}
138
139 virtual local_id_t getLocalId() const override {
140 return mBase->getLocalId();
141 }
142
143 virtual C2Allocator::id_t getAllocatorId() const override {
144 return mBase->getAllocatorId();
145 }
146
147 virtual c2_status_t fetchLinearBlock(
148 uint32_t capacity,
149 C2MemoryUsage usage,
150 std::shared_ptr<C2LinearBlock>* block) {
151 c2_status_t status;
152 do {
153 status = mBase->fetchLinearBlock(capacity, usage, block);
Sungtak Lee5f3fb6f2019-01-31 11:53:22 -0800154 } while (status == C2_BLOCKING);
Wonsik Kim0cb5a092019-01-03 16:38:22 -0800155 return status;
156 }
157
158 virtual c2_status_t fetchCircularBlock(
159 uint32_t capacity,
160 C2MemoryUsage usage,
161 std::shared_ptr<C2CircularBlock>* block) {
162 c2_status_t status;
163 do {
164 status = mBase->fetchCircularBlock(capacity, usage, block);
Sungtak Lee5f3fb6f2019-01-31 11:53:22 -0800165 } while (status == C2_BLOCKING);
Wonsik Kim0cb5a092019-01-03 16:38:22 -0800166 return status;
167 }
168
169 virtual c2_status_t fetchGraphicBlock(
170 uint32_t width, uint32_t height, uint32_t format,
171 C2MemoryUsage usage,
172 std::shared_ptr<C2GraphicBlock>* block) {
173 c2_status_t status;
174 do {
175 status = mBase->fetchGraphicBlock(width, height, format, usage,
176 block);
Sungtak Lee5f3fb6f2019-01-31 11:53:22 -0800177 } while (status == C2_BLOCKING);
Wonsik Kim0cb5a092019-01-03 16:38:22 -0800178 return status;
179 }
180
181private:
182 std::shared_ptr<C2BlockPool> mBase;
183};
184
Pawin Vongmasa36653902018-11-15 00:10:25 -0800185////////////////////////////////////////////////////////////////////////////////
186
187namespace {
188
189struct DummyReadView : public C2ReadView {
190 DummyReadView() : C2ReadView(C2_NO_INIT) {}
191};
192
193} // namespace
194
195SimpleC2Component::SimpleC2Component(
196 const std::shared_ptr<C2ComponentInterface> &intf)
197 : mDummyReadView(DummyReadView()),
198 mIntf(intf),
199 mLooper(new ALooper),
200 mHandler(new WorkHandler) {
201 mLooper->setName(intf->getName().c_str());
202 (void)mLooper->registerHandler(mHandler);
203 mLooper->start(false, false, ANDROID_PRIORITY_VIDEO);
204}
205
206SimpleC2Component::~SimpleC2Component() {
207 mLooper->unregisterHandler(mHandler->id());
208 (void)mLooper->stop();
209}
210
211c2_status_t SimpleC2Component::setListener_vb(
212 const std::shared_ptr<C2Component::Listener> &listener, c2_blocking_t mayBlock) {
213 mHandler->setComponent(shared_from_this());
214
215 Mutexed<ExecState>::Locked state(mExecState);
216 if (state->mState == RUNNING) {
217 if (listener) {
218 return C2_BAD_STATE;
219 } else if (!mayBlock) {
220 return C2_BLOCKING;
221 }
222 }
223 state->mListener = listener;
224 // TODO: wait for listener change to have taken place before returning
225 // (e.g. if there is an ongoing listener callback)
226 return C2_OK;
227}
228
229c2_status_t SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> * const items) {
230 {
231 Mutexed<ExecState>::Locked state(mExecState);
232 if (state->mState != RUNNING) {
233 return C2_BAD_STATE;
234 }
235 }
236 bool queueWasEmpty = false;
237 {
238 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
239 queueWasEmpty = queue->empty();
240 while (!items->empty()) {
241 queue->push_back(std::move(items->front()));
242 items->pop_front();
243 }
244 }
245 if (queueWasEmpty) {
246 (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
247 }
248 return C2_OK;
249}
250
251c2_status_t SimpleC2Component::announce_nb(const std::vector<C2WorkOutline> &items) {
252 (void)items;
253 return C2_OMITTED;
254}
255
256c2_status_t SimpleC2Component::flush_sm(
257 flush_mode_t flushMode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
258 (void)flushMode;
259 {
260 Mutexed<ExecState>::Locked state(mExecState);
261 if (state->mState != RUNNING) {
262 return C2_BAD_STATE;
263 }
264 }
265 {
266 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
267 queue->incGeneration();
268 // TODO: queue->splicedBy(flushedWork, flushedWork->end());
269 while (!queue->empty()) {
270 std::unique_ptr<C2Work> work = queue->pop_front();
271 if (work) {
272 flushedWork->push_back(std::move(work));
273 }
274 }
275 }
276 {
277 Mutexed<PendingWork>::Locked pending(mPendingWork);
278 while (!pending->empty()) {
279 flushedWork->push_back(std::move(pending->begin()->second));
280 pending->erase(pending->begin());
281 }
282 }
283
284 return C2_OK;
285}
286
287c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainMode) {
288 if (drainMode == DRAIN_CHAIN) {
289 return C2_OMITTED;
290 }
291 {
292 Mutexed<ExecState>::Locked state(mExecState);
293 if (state->mState != RUNNING) {
294 return C2_BAD_STATE;
295 }
296 }
297 bool queueWasEmpty = false;
298 {
299 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
300 queueWasEmpty = queue->empty();
301 queue->markDrain(drainMode);
302 }
303 if (queueWasEmpty) {
304 (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
305 }
306
307 return C2_OK;
308}
309
310c2_status_t SimpleC2Component::start() {
311 Mutexed<ExecState>::Locked state(mExecState);
312 if (state->mState == RUNNING) {
313 return C2_BAD_STATE;
314 }
315 bool needsInit = (state->mState == UNINITIALIZED);
316 state.unlock();
317 if (needsInit) {
318 sp<AMessage> reply;
319 (new AMessage(WorkHandler::kWhatInit, mHandler))->postAndAwaitResponse(&reply);
320 int32_t err;
321 CHECK(reply->findInt32("err", &err));
322 if (err != C2_OK) {
323 return (c2_status_t)err;
324 }
325 } else {
326 (new AMessage(WorkHandler::kWhatStart, mHandler))->post();
327 }
328 state.lock();
329 state->mState = RUNNING;
330 return C2_OK;
331}
332
333c2_status_t SimpleC2Component::stop() {
334 ALOGV("stop");
335 {
336 Mutexed<ExecState>::Locked state(mExecState);
337 if (state->mState != RUNNING) {
338 return C2_BAD_STATE;
339 }
340 state->mState = STOPPED;
341 }
342 {
343 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
344 queue->clear();
345 }
346 {
347 Mutexed<PendingWork>::Locked pending(mPendingWork);
348 pending->clear();
349 }
350 sp<AMessage> reply;
351 (new AMessage(WorkHandler::kWhatStop, mHandler))->postAndAwaitResponse(&reply);
352 int32_t err;
353 CHECK(reply->findInt32("err", &err));
354 if (err != C2_OK) {
355 return (c2_status_t)err;
356 }
357 return C2_OK;
358}
359
360c2_status_t SimpleC2Component::reset() {
361 ALOGV("reset");
362 {
363 Mutexed<ExecState>::Locked state(mExecState);
364 state->mState = UNINITIALIZED;
365 }
366 {
367 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
368 queue->clear();
369 }
370 {
371 Mutexed<PendingWork>::Locked pending(mPendingWork);
372 pending->clear();
373 }
374 sp<AMessage> reply;
375 (new AMessage(WorkHandler::kWhatReset, mHandler))->postAndAwaitResponse(&reply);
376 return C2_OK;
377}
378
379c2_status_t SimpleC2Component::release() {
380 ALOGV("release");
381 sp<AMessage> reply;
382 (new AMessage(WorkHandler::kWhatRelease, mHandler))->postAndAwaitResponse(&reply);
383 return C2_OK;
384}
385
386std::shared_ptr<C2ComponentInterface> SimpleC2Component::intf() {
387 return mIntf;
388}
389
390namespace {
391
392std::list<std::unique_ptr<C2Work>> vec(std::unique_ptr<C2Work> &work) {
393 std::list<std::unique_ptr<C2Work>> ret;
394 ret.push_back(std::move(work));
395 return ret;
396}
397
398} // namespace
399
400void SimpleC2Component::finish(
401 uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
402 std::unique_ptr<C2Work> work;
403 {
404 Mutexed<PendingWork>::Locked pending(mPendingWork);
405 if (pending->count(frameIndex) == 0) {
406 ALOGW("unknown frame index: %" PRIu64, frameIndex);
407 return;
408 }
409 work = std::move(pending->at(frameIndex));
410 pending->erase(frameIndex);
411 }
412 if (work) {
413 fillWork(work);
414 std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
415 listener->onWorkDone_nb(shared_from_this(), vec(work));
416 ALOGV("returning pending work");
417 }
418}
419
420void SimpleC2Component::cloneAndSend(
421 uint64_t frameIndex,
422 const std::unique_ptr<C2Work> &currentWork,
423 std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
424 std::unique_ptr<C2Work> work(new C2Work);
425 if (currentWork->input.ordinal.frameIndex == frameIndex) {
426 work->input.flags = currentWork->input.flags;
427 work->input.ordinal = currentWork->input.ordinal;
428 } else {
429 Mutexed<PendingWork>::Locked pending(mPendingWork);
430 if (pending->count(frameIndex) == 0) {
431 ALOGW("unknown frame index: %" PRIu64, frameIndex);
432 return;
433 }
434 work->input.flags = pending->at(frameIndex)->input.flags;
435 work->input.ordinal = pending->at(frameIndex)->input.ordinal;
436 }
437 work->worklets.emplace_back(new C2Worklet);
438 if (work) {
439 fillWork(work);
440 std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
441 listener->onWorkDone_nb(shared_from_this(), vec(work));
442 ALOGV("cloned and sending work");
443 }
444}
445
446bool SimpleC2Component::processQueue() {
447 std::unique_ptr<C2Work> work;
448 uint64_t generation;
449 int32_t drainMode;
450 bool isFlushPending = false;
451 bool hasQueuedWork = false;
452 {
453 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
454 if (queue->empty()) {
455 return false;
456 }
457
458 generation = queue->generation();
459 drainMode = queue->drainMode();
460 isFlushPending = queue->popPendingFlush();
461 work = queue->pop_front();
462 hasQueuedWork = !queue->empty();
463 }
464 if (isFlushPending) {
465 ALOGV("processing pending flush");
466 c2_status_t err = onFlush_sm();
467 if (err != C2_OK) {
468 ALOGD("flush err: %d", err);
469 // TODO: error
470 }
471 }
472
473 if (!mOutputBlockPool) {
474 c2_status_t err = [this] {
475 // TODO: don't use query_vb
476 C2StreamFormatConfig::output outputFormat(0u);
477 std::vector<std::unique_ptr<C2Param>> params;
478 c2_status_t err = intf()->query_vb(
479 { &outputFormat },
480 { C2PortBlockPoolsTuning::output::PARAM_TYPE },
481 C2_DONT_BLOCK,
482 &params);
483 if (err != C2_OK && err != C2_BAD_INDEX) {
484 ALOGD("query err = %d", err);
485 return err;
486 }
487 C2BlockPool::local_id_t poolId =
488 outputFormat.value == C2FormatVideo
489 ? C2BlockPool::BASIC_GRAPHIC
490 : C2BlockPool::BASIC_LINEAR;
491 if (params.size()) {
492 C2PortBlockPoolsTuning::output *outputPools =
493 C2PortBlockPoolsTuning::output::From(params[0].get());
494 if (outputPools && outputPools->flexCount() >= 1) {
495 poolId = outputPools->m.values[0];
496 }
497 }
498
Wonsik Kim0cb5a092019-01-03 16:38:22 -0800499 std::shared_ptr<C2BlockPool> blockPool;
500 err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
Pawin Vongmasa36653902018-11-15 00:10:25 -0800501 ALOGD("Using output block pool with poolID %llu => got %llu - %d",
502 (unsigned long long)poolId,
503 (unsigned long long)(
Wonsik Kim0cb5a092019-01-03 16:38:22 -0800504 blockPool ? blockPool->getLocalId() : 111000111),
Pawin Vongmasa36653902018-11-15 00:10:25 -0800505 err);
Wonsik Kim0cb5a092019-01-03 16:38:22 -0800506 if (err == C2_OK) {
507 mOutputBlockPool = std::make_shared<BlockingBlockPool>(blockPool);
508 }
Pawin Vongmasa36653902018-11-15 00:10:25 -0800509 return err;
510 }();
511 if (err != C2_OK) {
512 Mutexed<ExecState>::Locked state(mExecState);
513 std::shared_ptr<C2Component::Listener> listener = state->mListener;
514 state.unlock();
515 listener->onError_nb(shared_from_this(), err);
516 return hasQueuedWork;
517 }
518 }
519
520 if (!work) {
521 c2_status_t err = drain(drainMode, mOutputBlockPool);
522 if (err != C2_OK) {
523 Mutexed<ExecState>::Locked state(mExecState);
524 std::shared_ptr<C2Component::Listener> listener = state->mListener;
525 state.unlock();
526 listener->onError_nb(shared_from_this(), err);
527 }
528 return hasQueuedWork;
529 }
530
531 {
532 std::vector<C2Param *> updates;
533 for (const std::unique_ptr<C2Param> &param: work->input.configUpdate) {
534 if (param) {
535 updates.emplace_back(param.get());
536 }
537 }
538 if (!updates.empty()) {
539 std::vector<std::unique_ptr<C2SettingResult>> failures;
540 c2_status_t err = intf()->config_vb(updates, C2_MAY_BLOCK, &failures);
541 ALOGD("applied %zu configUpdates => %s (%d)", updates.size(), asString(err), err);
542 }
543 }
544
545 ALOGV("start processing frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
Pawin Vongmasa8be93112018-12-11 14:01:42 -0800546 // If input buffer list is not empty, it means we have some input to process on.
547 // However, input could be a null buffer. In such case, clear the buffer list
548 // before making call to process().
549 if (!work->input.buffers.empty() && !work->input.buffers[0]) {
550 ALOGD("Encountered null input buffer. Clearing the input buffer");
551 work->input.buffers.clear();
552 }
Pawin Vongmasa36653902018-11-15 00:10:25 -0800553 process(work, mOutputBlockPool);
554 ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
555 {
556 Mutexed<WorkQueue>::Locked queue(mWorkQueue);
557 if (queue->generation() != generation) {
558 ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
559 queue->generation(), generation);
560 work->result = C2_NOT_FOUND;
561 queue.unlock();
562 {
563 Mutexed<ExecState>::Locked state(mExecState);
564 std::shared_ptr<C2Component::Listener> listener = state->mListener;
565 state.unlock();
566 listener->onWorkDone_nb(shared_from_this(), vec(work));
567 }
568 queue.lock();
569 return hasQueuedWork;
570 }
571 }
572 if (work->workletsProcessed != 0u) {
573 Mutexed<ExecState>::Locked state(mExecState);
574 ALOGV("returning this work");
575 std::shared_ptr<C2Component::Listener> listener = state->mListener;
576 state.unlock();
577 listener->onWorkDone_nb(shared_from_this(), vec(work));
578 } else {
579 ALOGV("queue pending work");
580 work->input.buffers.clear();
581 std::unique_ptr<C2Work> unexpected;
582 {
583 Mutexed<PendingWork>::Locked pending(mPendingWork);
584 uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
585 if (pending->count(frameIndex) != 0) {
586 unexpected = std::move(pending->at(frameIndex));
587 pending->erase(frameIndex);
588 }
589 (void)pending->insert({ frameIndex, std::move(work) });
590 }
591 if (unexpected) {
592 ALOGD("unexpected pending work");
593 unexpected->result = C2_CORRUPTED;
594 Mutexed<ExecState>::Locked state(mExecState);
595 std::shared_ptr<C2Component::Listener> listener = state->mListener;
596 state.unlock();
597 listener->onWorkDone_nb(shared_from_this(), vec(unexpected));
598 }
599 }
600 return hasQueuedWork;
601}
602
603std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
604 const std::shared_ptr<C2LinearBlock> &block) {
605 return createLinearBuffer(block, block->offset(), block->size());
606}
607
608std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
609 const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size) {
610 return C2Buffer::CreateLinearBuffer(block->share(offset, size, ::C2Fence()));
611}
612
613std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
614 const std::shared_ptr<C2GraphicBlock> &block) {
615 return createGraphicBuffer(block, C2Rect(block->width(), block->height()));
616}
617
618std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
619 const std::shared_ptr<C2GraphicBlock> &block, const C2Rect &crop) {
620 return C2Buffer::CreateGraphicBuffer(block->share(crop, ::C2Fence()));
621}
622
623} // namespace android