Merge "codec2: Add SimpleC2Component"
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
index 696a062..74609e8 100644
--- a/media/libstagefright/codec2/Android.bp
+++ b/media/libstagefright/codec2/Android.bp
@@ -35,6 +35,48 @@
ldflags: ["-Wl,-Bsymbolic"],
}
+cc_library_shared {
+ name: "libstagefright_simple_c2component",
+
+ tags: [
+ "optional",
+ ],
+
+ srcs: ["SimpleC2Component.cpp"],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/include",
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.mapper@2.0",
+ "libhidlbase",
+ "libion",
+ "liblog",
+ "libstagefright_codec2",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libstagefright_codec2_vndk",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
+
subdirs = [
"tests",
"vndk",
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
new file mode 100644
index 0000000..2ded56b
--- /dev/null
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2017 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 "SimpleC2Component"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <C2PlatformSupport.h>
+
+#include <SimpleC2Component.h>
+
+namespace android {
+
+SimpleC2Component::SimpleC2Component(
+ const std::shared_ptr<C2ComponentInterface> &intf)
+ : mIntf(intf) {
+}
+
+c2_status_t SimpleC2Component::setListener_sm(const std::shared_ptr<C2Component::Listener> &listener) {
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (state->mState == RUNNING) {
+ return C2_BAD_STATE;
+ }
+ state->mListener = listener;
+ return C2_OK;
+}
+
+c2_status_t SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> * const items) {
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (state->mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ while (!items->empty()) {
+ queue->mQueue.push_back(std::move(items->front()));
+ items->pop_front();
+ }
+ queue->mCondition.broadcast();
+ }
+ return C2_OK;
+}
+
+c2_status_t SimpleC2Component::announce_nb(const std::vector<C2WorkOutline> &items) {
+ (void) items;
+ return C2_OMITTED;
+}
+
+c2_status_t SimpleC2Component::flush_sm(
+ flush_mode_t flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+ (void) flushThrough;
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (state->mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ ++queue->mGeneration;
+ while (!queue->mQueue.empty()) {
+ flushedWork->push_back(std::move(queue->mQueue.front()));
+ queue->mQueue.pop_front();
+ }
+ }
+ {
+ Mutexed<PendingWork>::Locked pending(mPendingWork);
+ while (!pending->empty()) {
+ flushedWork->push_back(std::move(pending->begin()->second));
+ pending->erase(pending->begin());
+ }
+ }
+
+ return onFlush_sm();
+}
+
+c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainThrough) {
+ (void) drainThrough;
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (state->mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ if (!queue->mQueue.empty()) {
+ const std::unique_ptr<C2Work> &work = queue->mQueue.back();
+ work->input.flags = (C2BufferPack::flags_t)(work->input.flags | C2BufferPack::FLAG_END_OF_STREAM);
+ return C2_OK;
+ }
+ }
+
+ return onDrain_nb();
+}
+
+c2_status_t SimpleC2Component::start() {
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (state->mState == RUNNING) {
+ return C2_BAD_STATE;
+ }
+ bool needsInit = (state->mState == UNINITIALIZED);
+ if (needsInit) {
+ state.unlock();
+ c2_status_t err = onInit();
+ if (err != C2_OK) {
+ return err;
+ }
+ state.lock();
+ }
+ if (!state->mThread.joinable()) {
+ mExitRequested = false;
+ state->mThread = std::thread(
+ [](std::weak_ptr<SimpleC2Component> wp) {
+ while (true) {
+ std::shared_ptr<SimpleC2Component> thiz = wp.lock();
+ if (!thiz) {
+ return;
+ }
+ if (thiz->exitRequested()) {
+ return;
+ }
+ thiz->processQueue();
+ }
+ },
+ shared_from_this());
+ }
+ state->mState = RUNNING;
+ return C2_OK;
+}
+
+c2_status_t SimpleC2Component::stop() {
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (state->mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ state->mState = STOPPED;
+ }
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ queue->mQueue.clear();
+ }
+ {
+ Mutexed<PendingWork>::Locked pending(mPendingWork);
+ pending->clear();
+ }
+ c2_status_t err = onStop();
+ if (err != C2_OK) {
+ return err;
+ }
+ return C2_OK;
+}
+
+void SimpleC2Component::reset() {
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ state->mState = UNINITIALIZED;
+ }
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ queue->mQueue.clear();
+ }
+ {
+ Mutexed<PendingWork>::Locked pending(mPendingWork);
+ pending->clear();
+ }
+ onReset();
+}
+
+void SimpleC2Component::release() {
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ mExitRequested = true;
+ state->mThread.join();
+ }
+ onRelease();
+}
+
+std::shared_ptr<C2ComponentInterface> SimpleC2Component::intf() {
+ return mIntf;
+}
+
+namespace {
+
+std::vector<std::unique_ptr<C2Work>> vec(std::unique_ptr<C2Work> &work) {
+ std::vector<std::unique_ptr<C2Work>> ret;
+ ret.push_back(std::move(work));
+ return ret;
+}
+
+} // namespace
+
+void SimpleC2Component::finish(
+ uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
+ std::unique_ptr<C2Work> work;
+ {
+ Mutexed<PendingWork>::Locked pending(mPendingWork);
+ if (pending->count(frameIndex) == 0) {
+ return;
+ }
+ work = std::move(pending->at(frameIndex));
+ pending->erase(frameIndex);
+ }
+ if (work) {
+ fillWork(work);
+ Mutexed<ExecState>::Locked state(mExecState);
+ state->mListener->onWorkDone_nb(shared_from_this(), vec(work));
+ }
+}
+
+void SimpleC2Component::processQueue() {
+ std::unique_ptr<C2Work> work;
+ uint64_t generation;
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ nsecs_t deadline = systemTime() + ms2ns(250);
+ while (queue->mQueue.empty()) {
+ status_t err = queue.waitForConditionRelative(
+ queue->mCondition, std::max(deadline - systemTime(), (nsecs_t)0));
+ if (err == TIMED_OUT) {
+ return;
+ }
+ }
+
+ generation = queue->mGeneration;
+ work = std::move(queue->mQueue.front());
+ queue->mQueue.pop_front();
+ }
+ if (!work) {
+ return;
+ }
+
+ // TODO: grab pool ID from intf
+ if (!mOutputBlockPool) {
+ c2_status_t err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, shared_from_this(), &mOutputBlockPool);
+ if (err != C2_OK) {
+ Mutexed<ExecState>::Locked state(mExecState);
+ state->mListener->onError_nb(shared_from_this(), err);
+ return;
+ }
+ }
+
+ bool done = process(work, mOutputBlockPool);
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ if (queue->mGeneration != generation) {
+ work->result = C2_NOT_FOUND;
+ queue.unlock();
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ state->mListener->onWorkDone_nb(shared_from_this(), vec(work));
+ }
+ queue.lock();
+ return;
+ }
+ }
+ if (done) {
+ Mutexed<ExecState>::Locked state(mExecState);
+ state->mListener->onWorkDone_nb(shared_from_this(), vec(work));
+ } else {
+ std::unique_ptr<C2Work> unexpected;
+ {
+ Mutexed<PendingWork>::Locked pending(mPendingWork);
+ uint64_t frameIndex = work->input.ordinal.frame_index;
+ if (pending->count(frameIndex) != 0) {
+ unexpected = std::move(pending->at(frameIndex));
+ pending->erase(frameIndex);
+ }
+ (void) pending->insert({ frameIndex, std::move(work) });
+ }
+ if (unexpected) {
+ unexpected->result = C2_CORRUPTED;
+ Mutexed<ExecState>::Locked state(mExecState);
+ state->mListener->onWorkDone_nb(shared_from_this(), vec(unexpected));
+ }
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/codec2/include/SimpleC2Component.h b/media/libstagefright/codec2/include/SimpleC2Component.h
new file mode 100644
index 0000000..45b4816
--- /dev/null
+++ b/media/libstagefright/codec2/include/SimpleC2Component.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef SIMPLE_C2_COMPONENT_H_
+#define SIMPLE_C2_COMPONENT_H_
+
+#include <list>
+#include <thread>
+#include <unordered_map>
+
+#include <C2Component.h>
+
+#include <media/stagefright/foundation/Mutexed.h>
+
+namespace android {
+
+class SimpleC2Component
+ : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
+public:
+ SimpleC2Component(
+ const std::shared_ptr<C2ComponentInterface> &intf);
+ virtual ~SimpleC2Component() = default;
+
+ // C2Component
+ virtual c2_status_t setListener_sm(const std::shared_ptr<C2Component::Listener> &listener) final;
+ virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>> * const items) final;
+ virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) final;
+ virtual c2_status_t flush_sm(
+ flush_mode_t flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) final;
+ virtual c2_status_t drain_nb(drain_mode_t drainThrough) final;
+ virtual c2_status_t start() final;
+ virtual c2_status_t stop() final;
+ virtual void reset() final;
+ virtual void release() final;
+ virtual std::shared_ptr<C2ComponentInterface> intf() final;
+
+ // for thread
+ inline bool exitRequested() { return mExitRequested; }
+ void processQueue();
+
+protected:
+ /**
+ * Initialize internal states of the component according to the config set
+ * in the interface.
+ *
+ * This method is called during start(), but only at the first invocation or
+ * after reset().
+ */
+ virtual c2_status_t onInit() = 0;
+
+ /**
+ * Stop the component.
+ */
+ virtual c2_status_t onStop() = 0;
+
+ /**
+ * Reset the component.
+ */
+ virtual void onReset() = 0;
+
+ /**
+ * Release the component.
+ */
+ virtual void onRelease() = 0;
+
+ /**
+ * Flush the component.
+ */
+ virtual c2_status_t onFlush_sm() = 0;
+
+ /**
+ * Drain the component.
+ */
+ virtual c2_status_t onDrain_nb() = 0;
+
+ /**
+ * Process the given work and finish pending work using finish().
+ *
+ * \param[in,out] work the work to process
+ * \param[in] pool the pool to use for allocating output blocks.
+ *
+ * \retval true |work| is done and ready for return to client
+ * \retval false more data is needed for the |work| to be done;
+ * mark |work| as pending.
+ */
+ virtual bool process(
+ const std::unique_ptr<C2Work> &work,
+ std::shared_ptr<C2BlockPool> pool) = 0;
+
+ // for derived classes
+ /**
+ * Finish pending work.
+ *
+ * This method will retrieve the pending work according to |frameIndex| and
+ * feed the work into |fillWork| function. |fillWork| must be
+ * "non-blocking". Once |fillWork| returns the filled work will be returned
+ * to the client.
+ *
+ * \param[in] frameIndex the index of the pending work
+ * \param[in] fillWork the function to fill the retrieved work.
+ */
+ void finish(uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork);
+
+private:
+ const std::shared_ptr<C2ComponentInterface> mIntf;
+ std::atomic_bool mExitRequested;
+
+ enum {
+ UNINITIALIZED,
+ STOPPED,
+ RUNNING,
+ };
+
+ struct ExecState {
+ ExecState() : mState(UNINITIALIZED) {}
+
+ int mState;
+ std::thread mThread;
+ std::shared_ptr<C2Component::Listener> mListener;
+ };
+ Mutexed<ExecState> mExecState;
+
+ struct WorkQueue {
+ Condition mCondition;
+ std::list<std::unique_ptr<C2Work>> mQueue;
+ uint64_t mGeneration;
+ };
+ Mutexed<WorkQueue> mWorkQueue;
+
+ typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
+ Mutexed<PendingWork> mPendingWork;
+
+ std::shared_ptr<C2BlockPool> mOutputBlockPool;
+
+ SimpleC2Component() = delete;
+};
+
+} // namespace android
+
+#endif // SIMPLE_C2_COMPONENT_H_
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index d2cfebb..fb469d7 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -15,7 +15,6 @@
include_dirs: [
"frameworks/av/media/libstagefright/codec2/include",
- "frameworks/av/media/libstagefright/codec2/vndk/include",
"frameworks/native/include/media/hardware",
],
diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp
index 5dcffd5..983005f 100644
--- a/media/libstagefright/codecs/avcdec/Android.bp
+++ b/media/libstagefright/codecs/avcdec/Android.bp
@@ -67,6 +67,7 @@
"libmedia",
"libstagefright_codec2",
"libstagefright_foundation",
+ "libstagefright_simple_c2component",
"libutils",
],
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index 2423629..9d88895 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 The Android Open Source Project
+ * Copyright 2017 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "C2SoftAvcDec"
#include <utils/Log.h>
@@ -608,47 +608,10 @@
///////////////////////////////////////////////////////////////////////////////
-class C2SoftAvcDec::QueueProcessThread {
-public:
- QueueProcessThread() : mExitRequested(false), mRunning(false) {}
-
- ~QueueProcessThread() {
- if (mThread && mThread->joinable()) {
- mExitRequested = true;
- mThread->join();
- }
- }
-
- void start(std::weak_ptr<C2SoftAvcDec> component) {
- mThread.reset(new std::thread([this, component] () {
- mRunning = true;
- while (auto comp = component.lock()) {
- if (mExitRequested) break;
- comp->processQueue();
- }
- mRunning = false;
- }));
- }
-
- void requestExit() {
- mExitRequested = true;
- }
-
- bool isRunning() {
- return mRunning;
- }
-
-private:
- std::atomic_bool mExitRequested;
- std::atomic_bool mRunning;
- std::unique_ptr<std::thread> mThread;
-};
-
C2SoftAvcDec::C2SoftAvcDec(
const char *name,
c2_node_id_t id)
- : mIntf(std::make_shared<C2SoftAvcDecIntf>(name, id)),
- mThread(new QueueProcessThread),
+ : SimpleC2Component(std::make_shared<C2SoftAvcDecIntf>(name, id)),
mCodecCtx(NULL),
mFlushOutBuffer(NULL),
mIvColorFormat(IV_YUV_420P),
@@ -670,106 +633,12 @@
CHECK_EQ(deInitDecoder(), (status_t)OK);
}
-c2_status_t C2SoftAvcDec::setListener_sm(
- const std::shared_ptr<C2Component::Listener> &listener) {
- std::unique_lock<std::mutex> lock(mListenerLock);
- // TODO: we really need to lock the running check as well
- if (listener && mThread->isRunning()) {
- return C2_BAD_STATE;
- }
- mListener = listener;
- if (mActiveListener && !listener) {
- // wait until no active listeners are in use
- mActiveListenerChanged.wait(lock, [this]{ return !mActiveListener; });
- }
+c2_status_t C2SoftAvcDec::onInit() {
+ // TODO: initialize using intf
return C2_OK;
}
-c2_status_t C2SoftAvcDec::queue_nb(
- std::list<std::unique_ptr<C2Work>>* const items) {
- if (!mThread->isRunning()) {
- return C2_CORRUPTED;
- }
- std::unique_lock<std::mutex> lock(mQueueLock);
- while (!items->empty()) {
- // TODO: examine item and update width/height?
- mQueue.emplace_back(std::move(items->front()));
- items->pop_front();
- }
- mQueueCond.notify_all();
- return C2_OK;
-}
-
-c2_status_t C2SoftAvcDec::announce_nb(const std::vector<C2WorkOutline> &items) {
- // Tunneling is not supported
- (void) items;
- return C2_OMITTED;
-}
-
-c2_status_t C2SoftAvcDec::flush_sm(
- flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
- // Tunneling is not supported
- (void) mode;
-
- if (!mThread->isRunning()) {
- return C2_CORRUPTED;
- }
- {
- std::unique_lock<std::mutex> lock(mQueueLock);
- while (!mQueue.empty()) {
- flushedWork->emplace_back(std::move(mQueue.front()));
- mQueue.pop_front();
- }
- mQueueCond.notify_all();
- }
- {
- std::unique_lock<std::mutex> lock(mPendingLock);
- for (auto &elem : mPendingWork) {
- flushedWork->emplace_back(std::move(elem.second));
- }
- mPendingWork.clear();
- }
- return C2_OK;
-}
-
-c2_status_t C2SoftAvcDec::drain_nb(drain_mode_t mode) {
- // Tunneling is not supported
- (void) mode;
-
- if (!mThread->isRunning()) {
- return C2_CORRUPTED;
- }
- std::unique_lock<std::mutex> lock(mQueueLock);
- if (!mQueue.empty()) {
- C2BufferPack &lastInput = mQueue.back()->input;
- lastInput.flags = (C2BufferPack::flags_t)(lastInput.flags | C2BufferPack::FLAG_END_OF_STREAM);
- mQueueCond.notify_all();
- }
- return C2_OK;
-}
-
-c2_status_t C2SoftAvcDec::start() {
- if (!mThread->isRunning()) {
- mThread->start(shared_from_this());
- }
- return C2_OK;
-}
-
-c2_status_t C2SoftAvcDec::stop() {
- ALOGV("stop");
- std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
- std::chrono::system_clock::time_point deadline = now + std::chrono::milliseconds(500);
-
- mThread->requestExit();
- while (mThread->isRunning() && (now = std::chrono::system_clock::now()) < deadline) {
- std::this_thread::yield();
- std::unique_lock<std::mutex> lock(mQueueLock);
- mQueueCond.notify_all();
- }
- if (mThread->isRunning()) {
- return C2_TIMED_OUT;
- }
-
+c2_status_t C2SoftAvcDec::onStop() {
mSignalledError = false;
resetDecoder();
resetPlugin();
@@ -777,107 +646,55 @@
return C2_OK;
}
-void C2SoftAvcDec::reset() {
- if (mThread->isRunning()) {
- stop();
+void C2SoftAvcDec::onReset() {
+ (void)onStop();
+}
+
+void C2SoftAvcDec::onRelease() {
+ (void)deInitDecoder();
+}
+
+c2_status_t C2SoftAvcDec::onFlush_sm() {
+ setFlushMode();
+
+ /* Allocate a picture buffer to flushed data */
+ uint32_t displayStride = mWidth;
+ uint32_t displayHeight = mHeight;
+
+ uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
+ mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize);
+ if (NULL == mFlushOutBuffer) {
+ ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
+ return C2_NO_MEMORY;
}
+
+ while (true) {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ IV_API_CALL_STATUS_T status;
+ // size_t sizeY, sizeUV;
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0, 0u);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ if (0 == s_dec_op.u4_output_present) {
+ resetPlugin();
+ break;
+ }
+ }
+
+ if (mFlushOutBuffer) {
+ free(mFlushOutBuffer);
+ mFlushOutBuffer = NULL;
+ }
+ return C2_OK;
+}
+
+c2_status_t C2SoftAvcDec::onDrain_nb() {
// TODO
+ return C2_OK;
}
-void C2SoftAvcDec::release() {
- if (mThread->isRunning()) {
- stop();
- }
- // TODO
-}
-
-std::shared_ptr<C2ComponentInterface> C2SoftAvcDec::intf() {
- return mIntf;
-}
-
-void C2SoftAvcDec::processQueue() {
-#if 0
- if (mIsInFlush) {
- setFlushMode();
-
- /* Allocate a picture buffer to flushed data */
- uint32_t displayStride = mWidth;
- uint32_t displayHeight = mHeight;
-
- uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
- mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize);
- if (NULL == mFlushOutBuffer) {
- ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
- return;
- }
-
- while (true) {
- ivd_video_decode_ip_t s_dec_ip;
- ivd_video_decode_op_t s_dec_op;
- IV_API_CALL_STATUS_T status;
- size_t sizeY, sizeUV;
-
- setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0, 0u);
-
- status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
- if (0 == s_dec_op.u4_output_present) {
- resetPlugin();
- break;
- }
- }
-
- if (mFlushOutBuffer) {
- free(mFlushOutBuffer);
- mFlushOutBuffer = NULL;
- }
- mIsInFlush = false;
- }
-#endif
-
- std::unique_ptr<C2Work> work;
- if (!mIsInFlush) {
- std::unique_lock<std::mutex> lock(mQueueLock);
- if (mQueue.empty()) {
- mQueueCond.wait(lock);
- }
- if (mQueue.empty()) {
- ALOGV("empty queue");
- return;
- }
- work.swap(mQueue.front());
- mQueue.pop_front();
- }
-
- // Process the work
- process(work);
-
- std::vector<std::unique_ptr<C2Work>> done;
- if (work) {
- std::unique_lock<std::mutex> lock(mPendingLock);
- uint32_t index = work->input.ordinal.frame_index;
- mPendingWork[index].swap(work);
-
- if (work) {
- work->result = C2_CORRUPTED;
- done.emplace_back(std::move(work));
- }
- }
-
- if (!done.empty()) {
- std::unique_lock<std::mutex> lock(mListenerLock);
- mActiveListener = mListener;
-
- if (mActiveListener) {
- lock.unlock();
- mActiveListener->onWorkDone_nb(shared_from_this(), std::move(done));
- lock.lock();
- mActiveListener.reset();
- mActiveListenerChanged.notify_all();
- }
- }
-}
-
-
static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
(void) ctxt;
return memalign(alignment, size);
@@ -955,7 +772,6 @@
}
status_t C2SoftAvcDec::resetPlugin() {
- mReceivedEOS = false;
mInputOffset = 0;
/* Initialize both start and end times */
@@ -1028,7 +844,6 @@
s_video_flush_op.u4_error_code);
return UNKNOWN_ERROR;
}
- mIsInFlush = true;
return OK;
}
@@ -1107,7 +922,6 @@
}
}
-
mChangingResolution = false;
return OK;
@@ -1210,78 +1024,76 @@
return true;
}
-void C2SoftAvcDec::process(std::unique_ptr<C2Work> &work) {
- if (mSignalledError) {
- return;
- }
- if (NULL == mCodecCtx) {
- if (OK != initDecoder()) {
- ALOGE("Failed to initialize decoder");
- // TODO: notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
- mSignalledError = true;
- return;
+bool C2SoftAvcDec::process(const std::unique_ptr<C2Work> &work, std::shared_ptr<C2BlockPool> pool) {
+ bool isInFlush = false;
+ bool eos = false;
+
+ bool done = false;
+ work->result = C2_OK;
+
+ const C2ConstLinearBlock &buffer =
+ work->input.buffers[0]->data().linearBlocks().front();
+ auto fillEmptyWork = [](const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if ((work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
+ flags |= C2BufferPack::FLAG_END_OF_STREAM;
}
- }
- if (mWidth != mStride) {
- /* Set the run-time (dynamic) parameters */
- mStride = mWidth;
- setParams(mStride);
- }
-
- uint32_t workIndex = 0;
- std::unique_ptr<C2ReadView> input;
- if (work) {
- work->result = C2_OK;
-
- const C2ConstLinearBlock &buffer =
- work->input.buffers[0]->data().linearBlocks().front();
- if (buffer.capacity() == 0) {
- // TODO: result?
-
- std::vector<std::unique_ptr<C2Work>> done;
- done.emplace_back(std::move(work));
- mListener->onWorkDone_nb(shared_from_this(), std::move(done));
- if (!(work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
- return;
- }
-
- mReceivedEOS = true;
- // TODO: flush
- } else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
- ALOGV("input EOS: %llu", work->input.ordinal.frame_index);
- mReceivedEOS = true;
+ work->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.emplace_back(nullptr);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ };
+ if (buffer.capacity() == 0) {
+ ALOGV("empty input: %llu", work->input.ordinal.frame_index);
+ // TODO: result?
+ fillEmptyWork(work);
+ if ((work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
+ eos = true;
}
-
- input.reset(new C2ReadView(work->input.buffers[0]->data().linearBlocks().front().map().get()));
- workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
+ done = true;
+ } else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
+ ALOGV("input EOS: %llu", work->input.ordinal.frame_index);
+ eos = true;
}
+ std::unique_ptr<C2ReadView> deferred;
+ std::unique_ptr<C2ReadView> input(new C2ReadView(
+ work->input.buffers[0]->data().linearBlocks().front().map().get()));
+ uint32_t workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
size_t inOffset = 0u;
- while (!input || inOffset < input->capacity()) {
- if (!input) {
+
+ while (input || isInFlush) {
+ if (mSignalledError) {
+ return done;
+ }
+ if (NULL == mCodecCtx) {
+ if (OK != initDecoder()) {
+ ALOGE("Failed to initialize decoder");
+ // TODO: notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
+ mSignalledError = true;
+ return done;
+ }
+ }
+ if (mWidth != mStride) {
+ /* Set the run-time (dynamic) parameters */
+ mStride = mWidth;
+ setParams(mStride);
+ }
+
+ if (isInFlush) {
ALOGV("flushing");
}
- // TODO: populate --- assume display order?
+
if (!mAllocatedBlock) {
// TODO: error handling
// TODO: format & usage
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite };
- // TODO: lock access to interface
- C2BlockPool::local_id_t poolId =
- mIntf->mOutputBlockPools->flexCount() ?
- mIntf->mOutputBlockPools->m.mValues[0] : C2BlockPool::BASIC_GRAPHIC;
- if (!mOutputBlockPool || mOutputBlockPool->getLocalId() != poolId) {
- c2_status_t err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
- if (err != C2_OK) {
- // TODO: trip
- }
- }
- ALOGE("using allocator %u", mOutputBlockPool->getAllocatorId());
+ ALOGV("using allocator %u", pool->getAllocatorId());
- (void)mOutputBlockPool->fetchGraphicBlock(
+ (void)pool->fetchGraphicBlock(
mWidth, mHeight, format, usage, &mAllocatedBlock);
- ALOGE("provided (%dx%d) required (%dx%d)", mAllocatedBlock->width(), mAllocatedBlock->height(), mWidth, mHeight);
+ ALOGV("provided (%dx%d) required (%dx%d)", mAllocatedBlock->width(), mAllocatedBlock->height(), mWidth, mHeight);
}
C2GraphicView output = mAllocatedBlock->map().get();
if (output.error() != OK) {
@@ -1291,13 +1103,13 @@
ivd_video_decode_ip_t s_dec_ip;
ivd_video_decode_op_t s_dec_op;
WORD32 timeDelay, timeTaken;
- size_t sizeY, sizeUV;
+ //size_t sizeY, sizeUV;
if (!setDecodeArgs(&s_dec_ip, &s_dec_op, input.get(), &output, workIndex, inOffset)) {
ALOGE("Decoder arg setup failed");
// TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
mSignalledError = true;
- return;
+ return done;
}
ALOGV("Decoder arg setup succeeded");
// If input dump is enabled, then write to file
@@ -1319,7 +1131,7 @@
ALOGE("Unsupported resolution : %dx%d", mWidth, mHeight);
// TODO: notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
mSignalledError = true;
- return;
+ return done;
}
bool allocationFailed = (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & 0xFF));
@@ -1327,7 +1139,7 @@
ALOGE("Allocation failure in decoder");
// TODO: notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
mSignalledError = true;
- return;
+ return done;
}
bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
@@ -1341,23 +1153,18 @@
PRINT_TIME("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
s_dec_op.u4_num_bytes_consumed);
if (input) {
- ALOGI("bytes total=%u", input->capacity());
+ ALOGV("bytes total=%u", input->capacity());
}
if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
mFlushNeeded = true;
}
- if (1 != s_dec_op.u4_frame_decoded_flag && work) {
+ if (1 != s_dec_op.u4_frame_decoded_flag && input) {
/* If the input did not contain picture data, return work without
* buffer */
- ALOGV("no picture data");
- std::vector<std::unique_ptr<C2Work>> done;
- done.push_back(std::move(work));
- done[0]->worklets.front()->output.flags = (C2BufferPack::flags_t)0;
- done[0]->worklets.front()->output.buffers.clear();
- done[0]->worklets.front()->output.buffers.emplace_back(nullptr);
- done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
- mListener->onWorkDone_nb(shared_from_this(), std::move(done));
+ ALOGV("no picture data: %u", workIndex);
+ fillEmptyWork(work);
+ done = true;
}
// If the decoder is in the changing resolution mode and there is no output present,
@@ -1369,7 +1176,7 @@
resetPlugin();
mStride = mWidth;
setParams(mStride);
- return;
+ continue;
}
if (resChanged) {
@@ -1377,8 +1184,10 @@
mChangingResolution = true;
if (mFlushNeeded) {
setFlushMode();
+ isInFlush = true;
+ deferred = std::move(input);
}
- return;
+ continue;
}
// Combine the resolution change and coloraspects change in one PortSettingChange event
@@ -1397,69 +1206,68 @@
// kDescribeColorAspectsIndex, NULL);
ALOGV("update color aspect");
mUpdateColorAspects = false;
- return;
+ continue;
}
if (s_dec_op.u4_output_present) {
ALOGV("output_present: %d", s_dec_op.u4_ts);
- std::vector<std::unique_ptr<C2Work>> done;
- {
- std::unique_lock<std::mutex> lock(mPendingLock);
- done.push_back(std::move(mPendingWork[s_dec_op.u4_ts]));
- mPendingWork.erase(s_dec_op.u4_ts);
+ auto fillWork = [this](const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
+ flags |= C2BufferPack::FLAG_END_OF_STREAM;
+ ALOGV("EOS");
+ }
+ work->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.emplace_back(
+ std::make_shared<GraphicBuffer>(std::move(mAllocatedBlock)));
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ };
+ if (s_dec_op.u4_ts != workIndex) {
+ finish(s_dec_op.u4_ts, fillWork);
+ } else {
+ fillWork(work);
+ done = true;
}
- uint32_t flags = 0;
- if (done[0]->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
- flags |= C2BufferPack::FLAG_END_OF_STREAM;
- ALOGV("EOS");
- }
- done[0]->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
- done[0]->worklets.front()->output.buffers.clear();
- done[0]->worklets.front()->output.buffers.emplace_back(
- std::make_shared<GraphicBuffer>(std::move(mAllocatedBlock)));
- done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
- mListener->onWorkDone_nb(shared_from_this(), std::move(done));
- } else if (mIsInFlush) {
- ALOGV("flush");
+ } else if (isInFlush) {
+ ALOGV("flush complete");
/* If in flush mode and no output is returned by the codec,
* then come out of flush mode */
- mIsInFlush = false;
+ isInFlush = false;
/* If EOS was recieved on input port and there is no output
* from the codec, then signal EOS on output port */
- if (mReceivedEOS) {
- std::vector<std::unique_ptr<C2Work>> done;
- {
- std::unique_lock<std::mutex> lock(mPendingLock);
- if (!mPendingWork.empty()) {
- done.push_back(std::move(mPendingWork.begin()->second));
- mPendingWork.erase(mPendingWork.begin());
- }
- }
- if (!done.empty()) {
- ALOGV("sending empty EOS buffer");
- done[0]->worklets.front()->output.flags = C2BufferPack::FLAG_END_OF_STREAM;
- done[0]->worklets.front()->output.buffers.clear();
- done[0]->worklets.front()->output.buffers.emplace_back(nullptr);
- done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
- mListener->onWorkDone_nb(shared_from_this(), std::move(done));
- }
+ if (eos) {
+ // TODO: It's an error if not done.
resetPlugin();
- return;
+ return done;
+ }
+
+ input = std::move(deferred);
+ }
+
+ if (input) {
+ inOffset += s_dec_op.u4_num_bytes_consumed;
+ if (inOffset >= input->capacity()) {
+ /* If input EOS is seen and decoder is not in flush mode,
+ * set the decoder in flush mode.
+ * There can be a case where EOS is sent along with last picture data
+ * In that case, only after decoding that input data, decoder has to be
+ * put in flush. This case is handled here */
+ if (eos && !isInFlush) {
+ setFlushMode();
+ isInFlush = true;
+ }
+ if (isInFlush) {
+ input.reset();
+ } else {
+ break;
+ }
}
}
- inOffset += s_dec_op.u4_num_bytes_consumed;
}
- /* If input EOS is seen and decoder is not in flush mode,
- * set the decoder in flush mode.
- * There can be a case where EOS is sent along with last picture data
- * In that case, only after decoding that input data, decoder has to be
- * put in flush. This case is handled here */
-
- if (mReceivedEOS && !mIsInFlush) {
- setFlushMode();
- }
+ return done;
}
bool C2SoftAvcDec::colorAspectsDiffer(
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
index 28f1dfd..c7fc1af 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 The Android Open Source Project
+ * Copyright 2017 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.
@@ -29,6 +29,7 @@
#include <C2Component.h>
#include <C2Param.h>
+#include <SimpleC2Component.h>
#include "C2AvcConfig.h"
@@ -140,29 +141,23 @@
friend class C2SoftAvcDec;
};
-class C2SoftAvcDec
- : public C2Component,
- public std::enable_shared_from_this<C2SoftAvcDec> {
+class C2SoftAvcDec : public SimpleC2Component {
public:
C2SoftAvcDec(const char *name, c2_node_id_t id);
virtual ~C2SoftAvcDec();
- // From C2Component
- virtual c2_status_t setListener_sm(const std::shared_ptr<Listener> &listener) override;
- virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
- virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
- virtual c2_status_t flush_sm(
- flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
- virtual c2_status_t drain_nb(drain_mode_t mode) override;
- virtual c2_status_t start() override;
- virtual c2_status_t stop() override;
- virtual void reset() override;
- virtual void release() override;
- virtual std::shared_ptr<C2ComponentInterface> intf() override;
+ // From SimpleC2Component
+ virtual c2_status_t onInit() override;
+ virtual c2_status_t onStop() override;
+ virtual void onReset() override;
+ virtual void onRelease() override;
+ virtual c2_status_t onFlush_sm() override;
+ virtual c2_status_t onDrain_nb() override;
+ virtual bool process(
+ const std::unique_ptr<C2Work> &work,
+ std::shared_ptr<C2BlockPool> pool) override;
private:
- class QueueProcessThread;
-
Mutex mColorAspectsLock;
// color aspects passed from the framework.
ColorAspects mDefaultColorAspects;
@@ -182,30 +177,6 @@
// This function will update the mFinalColorAspects based on codec preference.
status_t handleColorAspectsChange();
- // Number of input and output buffers
- enum {
- kNumBuffers = 8
- };
-
- using IndexType = decltype(C2WorkOrdinalStruct().frame_index);
-
- const std::shared_ptr<C2SoftAvcDecIntf> mIntf;
- std::shared_ptr<Listener> mListener;
- std::shared_ptr<Listener> mActiveListener;
- std::mutex mListenerLock;
- std::condition_variable mActiveListenerChanged;
-
- std::shared_ptr<C2BlockPool> mOutputBlockPool;
-
- std::mutex mQueueLock;
- std::condition_variable mQueueCond;
- std::list<std::unique_ptr<C2Work>> mQueue;
-
- std::mutex mPendingLock;
- std::unordered_map<IndexType, std::unique_ptr<C2Work>> mPendingWork;
-
- std::unique_ptr<QueueProcessThread> mThread;
-
std::shared_ptr<C2GraphicBlock> mAllocatedBlock;
iv_obj_t *mCodecCtx; // Codec context
@@ -237,9 +208,6 @@
uint32_t mStride;
size_t mInputOffset;
- void processQueue();
- void process(std::unique_ptr<C2Work> &work);
-
status_t initDecoder();
status_t deInitDecoder();
status_t setFlushMode();
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 1972a7a..03c1e60 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -197,6 +197,7 @@
}
void SimplePlayer::play(const sp<IMediaSource> &source) {
+ ALOGV("SimplePlayer::play");
sp<AMessage> format;
(void) convertMetaDataToMessage(source->getFormat(), &format);
@@ -353,6 +354,7 @@
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
+ ALOGV("Frame #%ld size = %zu", numFrames, size);
// DO THE DECODING
component->queue_nb(&items);