From 3184abdd4f164cbc66221818db1243399d20cfa0 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 22 Jan 2018 19:30:38 -0800 Subject: [PATCH] CCodec: Episode III --- Video Encoding - Implement createInputSurface() - Handle RGB format in C2AllocatorGralloc - Define picture type mask and handle it - Process onWorkDone_nb() callbacks in a separate thread - Add an unofficial option to take component name directly in screenrecord - Force array mode for allocation latency; to be removed once proper buffer pool is in place. Test: setprop debug.stagefright.ccodec yes Test: stagefright -S -N c2.google.avc.decoder /sdcard/a.mp4 Change-Id: Iba225350e2eb2a1e6f4d45a02628d0f7ba188df3 --- cmds/screenrecord/screenrecord.cpp | 34 +- media/libstagefright/Android.bp | 1 + media/libstagefright/CCodec.cpp | 213 ++++-- media/libstagefright/CCodecBufferChannel.cpp | 653 ++++++++++++++---- .../libstagefright/codec2/include/C2Config.h | 7 + .../codec2/vndk/C2AllocatorGralloc.cpp | 266 ++++--- .../codec2/vndk/include/C2AllocatorGralloc.h | 13 +- .../include/CCodecBufferChannel.h | 165 +---- .../include/media/stagefright/CCodec.h | 19 +- 9 files changed, 944 insertions(+), 427 deletions(-) diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index f9e46390da..46bd8f0f7c 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -73,6 +73,7 @@ static bool gMonotonicTime = false; // use system monotonic time for timesta static enum { FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES } gOutputFormat = FORMAT_MP4; // data format for output +static AString gCodecName = ""; // codec name override static bool gSizeSpecified = false; // was size explicitly requested? static bool gWantInfoScreen = false; // do we want initial info screen? static bool gWantFrameTime = false; // do we want times on each frame? @@ -154,6 +155,7 @@ static status_t prepareEncoder(float displayFps, sp* pCodec, if (gVerbose) { printf("Configuring recorder for %dx%d %s at %.2fMbps\n", gVideoWidth, gVideoHeight, kMimeTypeAvc, gBitRate / 1000000.0); + fflush(stdout); } sp format = new AMessage; @@ -169,11 +171,21 @@ static status_t prepareEncoder(float displayFps, sp* pCodec, looper->setName("screenrecord_looper"); looper->start(); ALOGV("Creating codec"); - sp codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true); - if (codec == NULL) { - fprintf(stderr, "ERROR: unable to create %s codec instance\n", - kMimeTypeAvc); - return UNKNOWN_ERROR; + sp codec; + if (gCodecName.empty()) { + codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true); + if (codec == NULL) { + fprintf(stderr, "ERROR: unable to create %s codec instance\n", + kMimeTypeAvc); + return UNKNOWN_ERROR; + } + } else { + codec = MediaCodec::CreateByComponentName(looper, gCodecName); + if (codec == NULL) { + fprintf(stderr, "ERROR: unable to create %s codec instance\n", + gCodecName.c_str()); + return UNKNOWN_ERROR; + } } err = codec->configure(format, NULL, NULL, @@ -275,9 +287,11 @@ static status_t setDisplayProjection( if (gRotate) { printf("Rotated content area is %ux%u at offset x=%d y=%d\n", outHeight, outWidth, offY, offX); + fflush(stdout); } else { printf("Content area is %ux%u at offset x=%d y=%d\n", outWidth, outHeight, offX, offY); + fflush(stdout); } } @@ -346,6 +360,7 @@ static status_t runEncoder(const sp& encoder, if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) { if (gVerbose) { printf("Time limit reached\n"); + fflush(stdout); } break; } @@ -483,6 +498,7 @@ static status_t runEncoder(const sp& encoder, printf("Encoder stopping; recorded %u frames in %" PRId64 " seconds\n", debugNumFrames, nanoseconds_to_seconds( systemTime(CLOCK_MONOTONIC) - startWhenNsec)); + fflush(stdout); } return NO_ERROR; } @@ -556,6 +572,7 @@ static status_t recordScreen(const char* fileName) { printf("Main display is %dx%d @%.2ffps (orientation=%u)\n", mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps, mainDpyInfo.orientation); + fflush(stdout); } bool rotated = isDeviceRotated(mainDpyInfo.orientation); @@ -623,6 +640,7 @@ static status_t recordScreen(const char* fileName) { } if (gVerbose) { printf("Bugreport overlay created\n"); + fflush(stdout); } } else { // Use the encoder's input surface as the virtual display surface. @@ -715,6 +733,7 @@ static status_t recordScreen(const char* fileName) { if (gVerbose) { printf("Stopping encoder and muxer\n"); + fflush(stdout); } } @@ -761,6 +780,7 @@ static status_t notifyMediaScanner(const char* fileName) { printf(" %s", argv[i]); } putchar('\n'); + fflush(stdout); } pid_t pid = fork(); @@ -898,6 +918,7 @@ int main(int argc, char* const argv[]) { { "show-frame-time", no_argument, NULL, 'f' }, { "rotate", no_argument, NULL, 'r' }, { "output-format", required_argument, NULL, 'o' }, + { "codec-name", required_argument, NULL, 'N' }, { "monotonic-time", no_argument, NULL, 'm' }, { NULL, 0, NULL, 0 } }; @@ -978,6 +999,9 @@ int main(int argc, char* const argv[]) { return 2; } break; + case 'N': + gCodecName = optarg; + break; case 'm': gMonotonicTime = true; break; diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 01f73a14e5..90f6ed7bdd 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -105,6 +105,7 @@ cc_library_shared { "libmedia_helper", "libstagefright_codec2", "libstagefright_foundation", + "libstagefright_gbs", "libstagefright_omx", "libstagefright_omx_utils", "libstagefright_xmlparser", diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp index 0103abd797..b058f37328 100644 --- a/media/libstagefright/CCodec.cpp +++ b/media/libstagefright/CCodec.cpp @@ -23,7 +23,9 @@ #include #include +#include #include +#include #include "include/CCodecBufferChannel.h" @@ -111,33 +113,35 @@ Mutexed> CCodecWatchdog::sInstance; class CCodecListener : public C2Component::Listener { public: - CCodecListener(const std::shared_ptr &channel) - : mChannel(channel) { - } + explicit CCodecListener(const wp &codec) : mCodec(codec) {} virtual void onWorkDone_nb( std::weak_ptr component, std::vector> workItems) override { - (void) component; - mChannel->onWorkDone(std::move(workItems)); + (void)component; + sp codec(mCodec.promote()); + if (!codec) { + return; + } + codec->onWorkDone(workItems); } virtual void onTripped_nb( std::weak_ptr component, std::vector> settingResult) override { // TODO - (void) component; - (void) settingResult; + (void)component; + (void)settingResult; } virtual void onError_nb(std::weak_ptr component, uint32_t errorCode) override { // TODO - (void) component; - (void) errorCode; + (void)component; + (void)errorCode; } private: - std::shared_ptr mChannel; + wp mCodec; }; } // namespace @@ -159,11 +163,11 @@ std::shared_ptr CCodec::getBufferChannel() { void CCodec::initiateAllocateComponent(const sp &msg) { { Mutexed::Locked state(mState); - if (state->mState != RELEASED) { + if (state->get() != RELEASED) { mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL); return; } - state->mState = ALLOCATING; + state->set(ALLOCATING); } AString componentName; @@ -178,14 +182,14 @@ void CCodec::initiateAllocateComponent(const sp &msg) { void CCodec::allocate(const AString &componentName) { // TODO: use C2ComponentStore to create component - mListener.reset(new CCodecListener(mChannel)); + mListener.reset(new CCodecListener(this)); std::shared_ptr comp; c2_status_t err = GetCodec2PlatformComponentStore()->createComponent( componentName.c_str(), &comp); if (err != C2_OK) { Mutexed::Locked state(mState); - state->mState = RELEASED; + state->set(RELEASED); state.unlock(); mCallback->onError(err, ACTION_CODE_FATAL); state.lock(); @@ -194,15 +198,15 @@ void CCodec::allocate(const AString &componentName) { comp->setListener_vb(mListener, C2_MAY_BLOCK); { Mutexed::Locked state(mState); - if (state->mState != ALLOCATING) { - state->mState = RELEASED; + if (state->get() != ALLOCATING) { + state->set(RELEASED); state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - state->mState = ALLOCATED; - state->mComp = comp; + state->set(ALLOCATED); + state->comp = comp; } mChannel->setComponent(comp); mCallback->onComponentAllocated(comp->intf()->getName().c_str()); @@ -211,7 +215,7 @@ void CCodec::allocate(const AString &componentName) { void CCodec::initiateConfigureComponent(const sp &format) { { Mutexed::Locked state(mState); - if (state->mState != ALLOCATED) { + if (state->get() != ALLOCATED) { mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); return; } @@ -252,6 +256,9 @@ void CCodec::configure(const sp &msg) { inputFormat->setInt32("sample-rate", 44100); outputFormat->setInt32("channel-count", 1); outputFormat->setInt32("sample-rate", 44100); + } else { + outputFormat->setInt32("width", 1080); + outputFormat->setInt32("height", 1920); } } else { inputFormat->setString("mime", mime); @@ -272,32 +279,81 @@ void CCodec::configure(const sp &msg) { { Mutexed::Locked formats(mFormats); - formats->mInputFormat = inputFormat; - formats->mOutputFormat = outputFormat; + formats->inputFormat = inputFormat; + formats->outputFormat = outputFormat; } mCallback->onComponentConfigured(inputFormat, outputFormat); } - void CCodec::initiateCreateInputSurface() { - // TODO + (new AMessage(kWhatCreateInputSurface, this))->post(); +} + +void CCodec::createInputSurface() { + sp producer; + sp source(new GraphicBufferSource); + + status_t err = source->initCheck(); + if (err != OK) { + ALOGE("Failed to initialize graphic buffer source: %d", err); + mCallback->onInputSurfaceCreationFailed(err); + return; + } + producer = source->getIGraphicBufferProducer(); + + err = setupInputSurface(source); + if (err != OK) { + ALOGE("Failed to set up input surface: %d", err); + mCallback->onInputSurfaceCreationFailed(err); + return; + } + + sp inputFormat; + sp outputFormat; + { + Mutexed::Locked formats(mFormats); + inputFormat = formats->inputFormat; + outputFormat = formats->outputFormat; + } + mCallback->onInputSurfaceCreated( + inputFormat, + outputFormat, + new BufferProducerWrapper(producer)); +} + +status_t CCodec::setupInputSurface(const sp &source) { + status_t err = mChannel->setGraphicBufferSource(source); + if (err != OK) { + return err; + } + + // TODO: configure |source| with other settings. + return OK; } void CCodec::initiateSetInputSurface(const sp &surface) { + sp msg = new AMessage(kWhatSetInputSurface, this); + msg->setObject("surface", surface); + msg->post(); +} + +void CCodec::setInputSurface(const sp &surface) { // TODO - (void) surface; + (void)surface; + + mCallback->onInputSurfaceDeclined(ERROR_UNSUPPORTED); } void CCodec::initiateStart() { { Mutexed::Locked state(mState); - if (state->mState != ALLOCATED) { + if (state->get() != ALLOCATED) { state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - state->mState = STARTING; + state->set(STARTING); } (new AMessage(kWhatStart, this))->post(); @@ -307,13 +363,13 @@ void CCodec::start() { std::shared_ptr comp; { Mutexed::Locked state(mState); - if (state->mState != STARTING) { + if (state->get() != STARTING) { state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - comp = state->mComp; + comp = state->comp; } c2_status_t err = comp->start(); if (err != C2_OK) { @@ -325,20 +381,20 @@ void CCodec::start() { sp outputFormat; { Mutexed::Locked formats(mFormats); - inputFormat = formats->mInputFormat; - outputFormat = formats->mOutputFormat; + inputFormat = formats->inputFormat; + outputFormat = formats->outputFormat; } mChannel->start(inputFormat, outputFormat); { Mutexed::Locked state(mState); - if (state->mState != STARTING) { + if (state->get() != STARTING) { state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - state->mState = RUNNING; + state->set(RUNNING); } mCallback->onStartCompleted(); } @@ -354,17 +410,17 @@ void CCodec::initiateShutdown(bool keepComponentAllocated) { void CCodec::initiateStop() { { Mutexed::Locked state(mState); - if (state->mState == ALLOCATED - || state->mState == RELEASED - || state->mState == STOPPING - || state->mState == RELEASING) { + if (state->get() == ALLOCATED + || state->get() == RELEASED + || state->get() == STOPPING + || state->get() == RELEASING) { // We're already stopped, released, or doing it right now. state.unlock(); mCallback->onStopCompleted(); state.lock(); return; } - state->mState = STOPPING; + state->set(STOPPING); } (new AMessage(kWhatStop, this))->post(); @@ -374,19 +430,19 @@ void CCodec::stop() { std::shared_ptr comp; { Mutexed::Locked state(mState); - if (state->mState == RELEASING) { + if (state->get() == RELEASING) { state.unlock(); // We're already stopped or release is in progress. mCallback->onStopCompleted(); state.lock(); return; - } else if (state->mState != STOPPING) { + } else if (state->get() != STOPPING) { state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - comp = state->mComp; + comp = state->comp; } mChannel->stop(); status_t err = comp->stop(); @@ -397,8 +453,8 @@ void CCodec::stop() { { Mutexed::Locked state(mState); - if (state->mState == STOPPING) { - state->mState = ALLOCATED; + if (state->get() == STOPPING) { + state->set(ALLOCATED); } } mCallback->onStopCompleted(); @@ -407,7 +463,7 @@ void CCodec::stop() { void CCodec::initiateRelease(bool sendCallback /* = true */) { { Mutexed::Locked state(mState); - if (state->mState == RELEASED || state->mState == RELEASING) { + if (state->get() == RELEASED || state->get() == RELEASING) { // We're already released or doing it right now. if (sendCallback) { state.unlock(); @@ -416,8 +472,8 @@ void CCodec::initiateRelease(bool sendCallback /* = true */) { } return; } - if (state->mState == ALLOCATING) { - state->mState = RELEASING; + if (state->get() == ALLOCATING) { + state->set(RELEASING); // With the altered state allocate() would fail and clean up. if (sendCallback) { state.unlock(); @@ -426,7 +482,7 @@ void CCodec::initiateRelease(bool sendCallback /* = true */) { } return; } - state->mState = RELEASING; + state->set(RELEASING); } std::thread([this, sendCallback] { release(sendCallback); }).detach(); @@ -436,7 +492,7 @@ void CCodec::release(bool sendCallback) { std::shared_ptr comp; { Mutexed::Locked state(mState); - if (state->mState == RELEASED) { + if (state->get() == RELEASED) { if (sendCallback) { state.unlock(); mCallback->onReleaseCompleted(); @@ -444,15 +500,15 @@ void CCodec::release(bool sendCallback) { } return; } - comp = state->mComp; + comp = state->comp; } mChannel->stop(); comp->release(); { Mutexed::Locked state(mState); - state->mState = RELEASED; - state->mComp.reset(); + state->set(RELEASED); + state->comp.reset(); } if (sendCallback) { mCallback->onReleaseCompleted(); @@ -466,11 +522,11 @@ status_t CCodec::setSurface(const sp &surface) { void CCodec::signalFlush() { { Mutexed::Locked state(mState); - if (state->mState != RUNNING) { + if (state->get() != RUNNING) { mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); return; } - state->mState = FLUSHING; + state->set(FLUSHING); } (new AMessage(kWhatFlush, this))->post(); @@ -480,13 +536,13 @@ void CCodec::flush() { std::shared_ptr comp; { Mutexed::Locked state(mState); - if (state->mState != FLUSHING) { + if (state->get() != FLUSHING) { state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - comp = state->mComp; + comp = state->comp; } mChannel->stop(); @@ -502,7 +558,7 @@ void CCodec::flush() { { Mutexed::Locked state(mState); - state->mState = FLUSHED; + state->set(FLUSHED); } mCallback->onFlushCompleted(); } @@ -510,26 +566,26 @@ void CCodec::flush() { void CCodec::signalResume() { { Mutexed::Locked state(mState); - if (state->mState != FLUSHED) { + if (state->get() != FLUSHED) { state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - state->mState = RESUMING; + state->set(RESUMING); } mChannel->start(nullptr, nullptr); { Mutexed::Locked state(mState); - if (state->mState != RESUMING) { + if (state->get() != RESUMING) { state.unlock(); mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); state.lock(); return; } - state->mState = RUNNING; + state->set(RUNNING); } } @@ -545,6 +601,14 @@ void CCodec::signalRequestIDRFrame() { // TODO } +void CCodec::onWorkDone(std::vector> &workItems) { + Mutexed>>::Locked queue(mWorkDoneQueue); + for (std::unique_ptr &item : workItems) { + queue->push_back(std::move(item)); + } + (new AMessage(kWhatWorkDone, this))->post(); +} + void CCodec::onMessageReceived(const sp &msg) { TimePoint now = std::chrono::steady_clock::now(); switch (msg->what()) { @@ -582,6 +646,37 @@ void CCodec::onMessageReceived(const sp &msg) { flush(); break; } + case kWhatCreateInputSurface: { + // Surface operations may be briefly blocking. + setDeadline(now + 100ms); + createInputSurface(); + break; + } + case kWhatSetInputSurface: { + // Surface operations may be briefly blocking. + setDeadline(now + 100ms); + sp obj; + CHECK(msg->findObject("surface", &obj)); + sp surface(static_cast(obj.get())); + setInputSurface(surface); + break; + } + case kWhatWorkDone: { + std::unique_ptr work; + { + Mutexed>>::Locked queue(mWorkDoneQueue); + if (queue->empty()) { + break; + } + work.swap(queue->front()); + queue->pop_front(); + if (!queue->empty()) { + (new AMessage(kWhatWorkDone, this))->post(); + } + } + mChannel->onWorkDone(work); + break; + } default: { ALOGE("unrecognized message"); break; diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp index ebc9b0a36a..d18b372b74 100644 --- a/media/libstagefright/CCodecBufferChannel.cpp +++ b/media/libstagefright/CCodecBufferChannel.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #define LOG_TAG "CCodecBufferChannel" #include @@ -48,10 +48,189 @@ using hardware::hidl_vec; using namespace hardware::cas::V1_0; using namespace hardware::cas::native::V1_0; +/** + * Base class for representation of buffers at one port. + */ +class CCodecBufferChannel::Buffers { +public: + Buffers() = default; + virtual ~Buffers() = default; + + /** + * Set format for MediaCodec-facing buffers. + */ + void setFormat(const sp &format) { mFormat = format; } + + /** + * Returns true if the buffers are operating under array mode. + */ + virtual bool isArrayMode() const { return false; } + + /** + * Fills the vector with MediaCodecBuffer's if in array mode; otherwise, + * no-op. + */ + virtual void getArray(Vector> *) const {} + +protected: + // Format to be used for creating MediaCodec-facing buffers. + sp mFormat; + +private: + DISALLOW_EVIL_CONSTRUCTORS(Buffers); +}; + +class CCodecBufferChannel::InputBuffers : public CCodecBufferChannel::Buffers { +public: + InputBuffers() = default; + virtual ~InputBuffers() = default; + + /** + * Set a block pool to obtain input memory blocks. + */ + void setPool(const std::shared_ptr &pool) { mPool = pool; } + + /** + * Get a new MediaCodecBuffer for input and its corresponding index. + * Returns false if no new buffer can be obtained at the moment. + */ + virtual bool requestNewBuffer(size_t *index, sp *buffer) = 0; + + /** + * Release the buffer obtained from requestNewBuffer() and get the + * associated C2Buffer object back. Returns empty shared_ptr if the + * buffer is not on file. + * + * XXX: this is a quick hack to be removed + */ + virtual std::shared_ptr releaseBufferIndex(size_t /* index */) { return nullptr; } + + /** + * Release the buffer obtained from requestNewBuffer() and get the + * associated C2Buffer object back. Returns empty shared_ptr if the + * buffer is not on file. + */ + virtual std::shared_ptr releaseBuffer(const sp &buffer) = 0; + + /** + * Flush internal state. After this call, no index or buffer previously + * returned from requestNewBuffer() is valid. + */ + virtual void flush() = 0; + + /** + * Return array-backed version of input buffers. The returned object + * shall retain the internal state so that it will honor index and + * buffer from previous calls of requestNewBuffer(). + */ + virtual std::unique_ptr toArrayMode() = 0; + +protected: + // Pool to obtain blocks for input buffers. + std::shared_ptr mPool; + +private: + DISALLOW_EVIL_CONSTRUCTORS(InputBuffers); +}; + +class CCodecBufferChannel::InputBufferClient { +public: + explicit InputBufferClient( + const std::shared_ptr &channel) : mChannel(channel) {} + virtual ~InputBufferClient() = default; + + virtual void onInputBufferAdded(size_t index, const sp &buffer) { + std::shared_ptr channel = mChannel.lock(); + if (!channel) { + return; + } + channel->mCallback->onInputBufferAvailable(index, buffer); + } + + virtual void onStart() { + // no-op + } + + virtual void onStop() { + // no-op + } + + virtual void onRelease() { + // no-op + } + + virtual void onInputBufferAvailable(size_t index, const sp &buffer) { + std::shared_ptr channel = mChannel.lock(); + if (!channel) { + return; + } + channel->mCallback->onInputBufferAvailable(index, buffer); + } + +protected: + InputBufferClient() = default; + std::weak_ptr mChannel; + + DISALLOW_EVIL_CONSTRUCTORS(InputBufferClient); +}; + +class CCodecBufferChannel::OutputBuffers : public CCodecBufferChannel::Buffers { +public: + OutputBuffers() = default; + virtual ~OutputBuffers() = default; + + /** + * Register output C2Buffer from the component and obtain corresponding + * index and MediaCodecBuffer object. Returns false if registration + * fails. + */ + virtual bool registerBuffer( + const std::shared_ptr &buffer, + size_t *index, + sp *codecBuffer) = 0; + + /** + * Register codec specific data as a buffer to be consistent with + * MediaCodec behavior. + */ + virtual bool registerCsd( + const C2StreamCsdInfo::output * /* csd */, + size_t * /* index */, + sp * /* codecBuffer */) { + return false; + } + + /** + * Release the buffer obtained from registerBuffer() and get the + * associated C2Buffer object back. Returns empty shared_ptr if the + * buffer is not on file. + */ + virtual std::shared_ptr releaseBuffer(const sp &buffer) = 0; + + /** + * Flush internal state. After this call, no index or buffer previously + * returned from registerBuffer() is valid. + */ + virtual void flush(const std::list> &flushedWork) = 0; + + /** + * Return array-backed version of output buffers. The returned object + * shall retain the internal state so that it will honor index and + * buffer from previous calls of registerBuffer(). + */ + virtual std::unique_ptr toArrayMode() = 0; + +private: + DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers); +}; + namespace { +constexpr int32_t kMaskI32 = ~0; + // TODO: get this info from component const static size_t kMinBufferArraySize = 16; +const static size_t kLinearBufferSize = 524288; template ssize_t findBufferSlot( @@ -88,9 +267,14 @@ sp allocateLinearBuffer( return Codec2Buffer::allocate(format, block); } -class LinearBuffer : public C2Buffer { +class Buffer1D : public C2Buffer { public: - explicit LinearBuffer(C2ConstLinearBlock block) : C2Buffer({ block }) {} + explicit Buffer1D(C2ConstLinearBlock block) : C2Buffer({ block }) {} +}; + +class Buffer2D : public C2Buffer { +public: + explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {} }; class InputBuffersArray : public CCodecBufferChannel::InputBuffers { @@ -99,36 +283,36 @@ public: void add( size_t index, - const sp &clientBuffer, - const std::shared_ptr &compBuffer, + const sp &clientBuffer, bool available) { - if (mBufferArray.size() < index) { + if (mBufferArray.size() <= index) { + // TODO: make this more efficient mBufferArray.resize(index + 1); } mBufferArray[index].clientBuffer = clientBuffer; - mBufferArray[index].compBuffer = compBuffer; mBufferArray[index].available = available; } - bool isArrayMode() final { return true; } + bool isArrayMode() const final { return true; } std::unique_ptr toArrayMode() final { return nullptr; } - void getArray(Vector> *array) final { + void getArray(Vector> *array) const final { array->clear(); - for (const auto &entry : mBufferArray) { + for (const Entry &entry : mBufferArray) { array->push(entry.clientBuffer); } } bool requestNewBuffer(size_t *index, sp *buffer) override { for (size_t i = 0; i < mBufferArray.size(); ++i) { - if (mBufferArray[i].available) { + if (mBufferArray[i].available && mBufferArray[i].compBuffer.expired()) { mBufferArray[i].available = false; *index = i; *buffer = mBufferArray[i].clientBuffer; + (*buffer)->setRange(0, (*buffer)->capacity()); return true; } } @@ -138,8 +322,10 @@ public: std::shared_ptr releaseBuffer(const sp &buffer) override { for (size_t i = 0; i < mBufferArray.size(); ++i) { if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) { + std::shared_ptr buffer = std::make_shared(mBufferArray[i].clientBuffer->share()); mBufferArray[i].available = true; - return std::move(mBufferArray[i].compBuffer); + mBufferArray[i].compBuffer = buffer; + return buffer; } } return nullptr; @@ -148,14 +334,13 @@ public: void flush() override { for (size_t i = 0; i < mBufferArray.size(); ++i) { mBufferArray[i].available = true; - mBufferArray[i].compBuffer.reset(); } } private: struct Entry { - sp clientBuffer; - std::shared_ptr compBuffer; + sp clientBuffer; + std::weak_ptr compBuffer; bool available; }; @@ -177,7 +362,7 @@ public: // TODO: proper max input size and usage // TODO: read usage from intf C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - sp newBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage); + sp newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage); if (newBuffer == nullptr) { return false; } @@ -195,7 +380,7 @@ public: sp codecBuffer = it->promote(); // We got sp<> reference from the caller so this should never happen.. CHECK(codecBuffer != nullptr); - return std::make_shared(codecBuffer->share()); + return std::make_shared(codecBuffer->share()); } void flush() override { @@ -203,8 +388,10 @@ public: std::unique_ptr toArrayMode() final { std::unique_ptr array(new InputBuffersArray); + array->setFormat(mFormat); // TODO const size_t size = std::max(kMinBufferArraySize, mBuffers.size()); + mBuffers.resize(size); for (size_t i = 0; i < size; ++i) { sp clientBuffer = mBuffers[i].promote(); bool available = false; @@ -212,13 +399,12 @@ public: // TODO: proper max input size // TODO: read usage from intf C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - clientBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage); + clientBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage); available = true; } array->add( i, clientBuffer, - std::make_shared(clientBuffer->share()), available); } return std::move(array); @@ -230,19 +416,30 @@ private: std::vector> mBuffers; }; -// TODO: stub class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers { public: - using CCodecBufferChannel::InputBuffers::InputBuffers; + GraphicInputBuffers() = default; bool requestNewBuffer(size_t *index, sp *buffer) override { - (void)index; - (void)buffer; - return false; + *buffer = nullptr; + for (size_t i = 0; i < mAvailable.size(); ++i) { + if (mAvailable[i]) { + *index = i; + mAvailable[i] = false; + return true; + } + } + *index = mAvailable.size(); + mAvailable.push_back(false); + return true; } - std::shared_ptr releaseBuffer(const sp &buffer) override { - (void)buffer; + std::shared_ptr releaseBufferIndex(size_t index) override { + mAvailable[index] = true; + return nullptr; + } + + std::shared_ptr releaseBuffer(const sp &) override { return nullptr; } @@ -252,6 +449,9 @@ public: std::unique_ptr toArrayMode() final { return nullptr; } + +private: + std::vector mAvailable; }; class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers { @@ -263,7 +463,8 @@ public: const sp &clientBuffer, const std::shared_ptr &compBuffer, bool available) { - if (mBufferArray.size() < index) { + if (mBufferArray.size() <= index) { + // TODO: make this more efficient mBufferArray.resize(index + 1); } mBufferArray[index].clientBuffer = clientBuffer; @@ -271,7 +472,7 @@ public: mBufferArray[index].available = available; } - bool isArrayMode() final { return true; } + bool isArrayMode() const final { return true; } std::unique_ptr toArrayMode() final { return nullptr; @@ -285,6 +486,7 @@ public: if (mBufferArray[i].available && copy(buffer, mBufferArray[i].clientBuffer)) { *index = i; *codecBuffer = mBufferArray[i].clientBuffer; + (*codecBuffer)->setFormat(mFormat); mBufferArray[i].compBuffer = buffer; mBufferArray[i].available = false; return true; @@ -299,10 +501,17 @@ public: sp *codecBuffer) final { for (size_t i = 0; i < mBufferArray.size(); ++i) { if (mBufferArray[i].available - && mBufferArray[i].clientBuffer->capacity() <= csd->flexCount()) { + && mBufferArray[i].clientBuffer->capacity() >= csd->flexCount()) { + // TODO: proper format update + sp csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()); + mFormat = mFormat->dup(); + mFormat->setBuffer("csd-0", csdBuffer); + memcpy(mBufferArray[i].clientBuffer->base(), csd->m.value, csd->flexCount()); + mBufferArray[i].clientBuffer->setRange(0, csd->flexCount()); *index = i; *codecBuffer = mBufferArray[i].clientBuffer; + (*codecBuffer)->setFormat(mFormat); mBufferArray[i].available = false; return true; } @@ -333,9 +542,9 @@ public: const std::shared_ptr &buffer, const sp &clientBuffer) = 0; - void getArray(Vector> *array) final { + void getArray(Vector> *array) const final { array->clear(); - for (const auto &entry : mBufferArray) { + for (const Entry &entry : mBufferArray) { array->push(entry.clientBuffer); } } @@ -363,6 +572,7 @@ public: } C2ReadView view = buffer->data().linearBlocks().front().map().get(); if (clientBuffer->capacity() < view.capacity()) { + ALOGV("view.capacity() = %u", view.capacity()); return false; } clientBuffer->setRange(0u, view.capacity()); @@ -425,9 +635,11 @@ public: if (ret < 0) { return false; } - sp newBuffer = new MediaCodecBuffer( - mFormat, - ABuffer::CreateAsCopy(csd->m.value, csd->flexCount())); + // TODO: proper format update + sp csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()); + mFormat = mFormat->dup(); + mFormat->setBuffer("csd-0", csdBuffer); + sp newBuffer = new MediaCodecBuffer(mFormat, csdBuffer); mBuffers[ret] = { newBuffer, nullptr }; *index = ret; *codecBuffer = newBuffer; @@ -498,15 +710,17 @@ public: std::unique_ptr toArrayMode() override { std::unique_ptr array(new LinearOutputBuffersArray); + array->setFormat(mFormat); const size_t size = std::max(kMinBufferArraySize, mBuffers.size()); + mBuffers.resize(size); for (size_t i = 0; i < size; ++i) { sp clientBuffer = mBuffers[i].clientBuffer.promote(); std::shared_ptr compBuffer = mBuffers[i].bufferRef; bool available = false; if (clientBuffer == nullptr) { // TODO: proper max input size - clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(65536)); + clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(kLinearBufferSize)); available = true; compBuffer.reset(); } @@ -526,8 +740,10 @@ public: std::unique_ptr toArrayMode() override { std::unique_ptr array(new GraphicOutputBuffersArray); + array->setFormat(mFormat); const size_t size = std::max(kMinBufferArraySize, mBuffers.size()); + mBuffers.resize(size); for (size_t i = 0; i < size; ++i) { sp clientBuffer = mBuffers[i].clientBuffer.promote(); std::shared_ptr compBuffer = mBuffers[i].bufferRef; @@ -543,8 +759,163 @@ public: } }; +class BufferQueueClient : public CCodecBufferChannel::InputBufferClient { +public: + explicit BufferQueueClient(const sp &source) : mSource(source) {} + virtual ~BufferQueueClient() = default; + + void onInputBufferAdded(size_t index, const sp &buffer) override { + (void)buffer; + mSource->onInputBufferAdded(index & kMaskI32); + } + + void onStart() override { + mSource->start(); + } + + void onStop() override { + mSource->stop(); + } + + void onRelease() override { + mSource->release(); + } + + void onInputBufferAvailable(size_t index, const sp &buffer) override { + ALOGV("onInputBufferEmptied index = %zu", index); + (void)buffer; + // TODO: can we really ignore fence here? + mSource->onInputBufferEmptied(index & kMaskI32, -1 /* fenceFd */); + } + +private: + sp mSource; +}; + +class GraphicBlock : public C2GraphicBlock { + using C2GraphicBlock::C2GraphicBlock; + friend class ::android::CCodecBufferChannel; +}; + } // namespace +class CCodecBufferChannel::C2ComponentWrapper : public ComponentWrapper { +public: + explicit C2ComponentWrapper( + const std::shared_ptr &channel) + : mChannel(channel), mLastTimestamp(0) {} + + virtual ~C2ComponentWrapper() { + for (const std::pair &entry : mHandles) { + native_handle_delete(entry.second); + } + } + + status_t submitBuffer( + int32_t bufferId, const sp &buffer, + int64_t timestamp, int fenceFd) override { + ALOGV("submitBuffer bufferId = %d", bufferId); + // TODO: Use fd to construct fence + (void)fenceFd; + + std::shared_ptr channel = mChannel.lock(); + if (!channel) { + return NO_INIT; + } + + std::shared_ptr allocator = mAllocator.lock(); + if (!allocator) { + c2_status_t err = GetCodec2PlatformAllocatorStore()->fetchAllocator( + C2AllocatorStore::PLATFORM_START + 1, // GRALLOC + &allocator); + if (err != OK) { + return UNKNOWN_ERROR; + } + mAllocator = allocator; + } + + std::shared_ptr alloc; + C2Handle *handle = WrapNativeCodec2GrallocHandle( + buffer->handle, buffer->width, buffer->height, + buffer->format, buffer->usage, buffer->stride); + c2_status_t err = allocator->priorGraphicAllocation(handle, &alloc); + if (err != OK) { + return UNKNOWN_ERROR; + } + std::shared_ptr block(new GraphicBlock(alloc)); + + std::unique_ptr work(new C2Work); + work->input.flags = (C2FrameData::flags_t)0; + work->input.ordinal.timestamp = timestamp; + work->input.ordinal.frameIndex = channel->mFrameIndex++; + work->input.buffers.clear(); + work->input.buffers.emplace_back(new Buffer2D( + // TODO: fence + block->share(C2Rect(block->width(), block->height()), ::android::C2Fence()))); + work->worklets.clear(); + work->worklets.emplace_back(new C2Worklet); + std::list> items; + items.push_back(std::move(work)); + + err = channel->mComponent->queue_nb(&items); + if (err != OK) { + native_handle_delete(handle); + return UNKNOWN_ERROR; + } + + mLastTimestamp = timestamp; + if (mHandles.count(bufferId) > 0) { + native_handle_delete(mHandles[bufferId]); + } + mHandles[bufferId] = handle; + + Mutexed>::Locked buffers(channel->mInputBuffers); + ALOGV("releaseBufferIndex index = %d", bufferId); + (*buffers)->releaseBufferIndex(bufferId); + + return OK; + } + + status_t submitEos(int32_t bufferId) override { + std::shared_ptr channel = mChannel.lock(); + if (!channel) { + return NO_INIT; + } + + std::unique_ptr work(new C2Work); + work->input.flags = C2FrameData::FLAG_END_OF_STREAM; + work->input.ordinal.timestamp = mLastTimestamp; + work->input.ordinal.frameIndex = channel->mFrameIndex++; + work->input.buffers.clear(); + work->input.buffers.push_back(nullptr); + work->worklets.clear(); + work->worklets.emplace_back(new C2Worklet); + std::list> items; + items.push_back(std::move(work)); + + c2_status_t err = channel->mComponent->queue_nb(&items); + + Mutexed>::Locked buffers(channel->mInputBuffers); + (*buffers)->releaseBufferIndex(bufferId); + + return (err == C2_OK) ? OK : UNKNOWN_ERROR; + } + + void dispatchDataSpaceChanged( + int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override { + // TODO + (void)dataSpace; + (void)aspects; + (void)pixelFormat; + } + +private: + std::weak_ptr mChannel; + std::map mHandles; + int64_t mLastTimestamp; + std::weak_ptr mAllocator; +}; + CCodecBufferChannel::QueueGuard::QueueGuard( CCodecBufferChannel::QueueSync &sync) : mSync(sync) { std::unique_lock l(mSync.mMutex); @@ -601,10 +972,14 @@ CCodecBufferChannel::~CCodecBufferChannel() { if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) { mCrypto->unsetHeap(mHeapSeqNum); } + // TODO: is this the right place? + mInputClient->onRelease(); } void CCodecBufferChannel::setComponent(const std::shared_ptr &component) { mComponent = component; + mInputClient.reset(new InputBufferClient(shared_from_this())); + C2StreamFormatConfig::input inputFormat(0u); C2StreamFormatConfig::output outputFormat(0u); c2_status_t err = mComponent->intf()->query_vb( @@ -638,6 +1013,10 @@ void CCodecBufferChannel::setComponent(const std::shared_ptr &compo } else { // TODO: error } + // TODO: remove once we switch to proper buffer pool. + if (!graphic) { + *buffers = (*buffers)->toArrayMode(); + } } { @@ -652,6 +1031,18 @@ void CCodecBufferChannel::setComponent(const std::shared_ptr &compo } } +status_t CCodecBufferChannel::setGraphicBufferSource( + const sp &source) { + ALOGV("setGraphicBufferSource"); + mInputClient.reset(new BufferQueueClient(source)); + + // TODO: proper color aspect & dataspace + android_dataspace dataSpace = HAL_DATASPACE_BT709; + // TODO: read settings properly from the interface + return source->configure(new C2ComponentWrapper( + shared_from_this()), dataSpace, 16, 1080, 1920, GRALLOC_USAGE_SW_READ_OFTEN); +} + status_t CCodecBufferChannel::queueInputBuffer(const sp &buffer) { QueueGuard guard(mSync); if (!guard.isRunning()) { @@ -708,9 +1099,24 @@ status_t CCodecBufferChannel::queueSecureInputBuffer( return -ENOSYS; } +void CCodecBufferChannel::feedInputBufferIfAvailable() { + sp inBuffer; + size_t index; + { + Mutexed>::Locked buffers(mInputBuffers); + if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) { + ALOGW("no new buffer available"); + inBuffer = nullptr; + } + } + ALOGV("new input index = %zu", index); + mInputClient->onInputBufferAvailable(index, inBuffer); +} + status_t CCodecBufferChannel::renderOutputBuffer( const sp &buffer, int64_t timestampNs) { ALOGV("renderOutputBuffer"); + feedInputBufferIfAvailable(); std::shared_ptr c2Buffer; { @@ -780,7 +1186,9 @@ status_t CCodecBufferChannel::discardBuffer(const sp &buffer) } { Mutexed>::Locked buffers(mOutputBuffers); - (void)(*buffers)->releaseBuffer(buffer); + if((*buffers)->releaseBuffer(buffer)) { + feedInputBufferIfAvailable(); + } } return OK; } @@ -832,13 +1240,15 @@ void CCodecBufferChannel::start(const sp &inputFormat, const sponInputBufferAvailable(index, buffer); + mInputClient->onInputBufferAdded(index, buffer); } + mInputClient->onStart(); } void CCodecBufferChannel::stop() { mSync.stop(); mFirstValidFrameIndex = mFrameIndex.load(); + mInputClient->onStop(); } void CCodecBufferChannel::flush(const std::list> &flushedWork) { @@ -852,106 +1262,103 @@ void CCodecBufferChannel::flush(const std::list> &flushe } } -void CCodecBufferChannel::onWorkDone(std::vector> workItems) { - for (const auto &work : workItems) { - sp inBuffer; - size_t index; - { - Mutexed>::Locked buffers(mInputBuffers); - if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) { - ALOGW("no new buffer available"); - inBuffer = nullptr; - } - } - if (inBuffer != nullptr) { - mCallback->onInputBufferAvailable(index, inBuffer); - } +void CCodecBufferChannel::onWorkDone(const std::unique_ptr &work) { + if (work->result != OK) { + ALOGE("work failed to complete: %d", work->result); + mOnError(work->result, ACTION_CODE_FATAL); + return; + } - if (work->result != OK) { - ALOGE("work failed to complete: %d", work->result); - mOnError(work->result, ACTION_CODE_FATAL); - return; - } + // NOTE: MediaCodec usage supposedly have only one worklet + if (work->worklets.size() != 1u) { + ALOGE("incorrect number of worklets: %zu", work->worklets.size()); + mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); + return; + } - // NOTE: MediaCodec usage supposedly have only one worklet - if (work->worklets.size() != 1u) { - ALOGE("incorrect number of worklets: %zu", work->worklets.size()); - mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - continue; - } + const std::unique_ptr &worklet = work->worklets.front(); + if ((worklet->output.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) { + // Discard frames from previous generation. + return; + } + // NOTE: MediaCodec usage supposedly have only one output stream. + if (worklet->output.buffers.size() != 1u) { + ALOGE("incorrect number of output buffers: %zu", worklet->output.buffers.size()); + mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); + return; + } - const std::unique_ptr &worklet = work->worklets.front(); - if ((worklet->output.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) { - // Discard frames from previous generation. - continue; - } - // NOTE: MediaCodec usage supposedly have only one output stream. - if (worklet->output.buffers.size() != 1u) { - ALOGE("incorrect number of output buffers: %zu", worklet->output.buffers.size()); - mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - continue; + const std::shared_ptr &buffer = worklet->output.buffers[0]; + const C2StreamCsdInfo::output *csdInfo = nullptr; + for (const std::unique_ptr &info : worklet->output.configUpdate) { + if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) { + ALOGV("csd found"); + csdInfo = static_cast(info.get()); } + } - const std::shared_ptr &buffer = worklet->output.buffers[0]; - const C2StreamCsdInfo::output *csdInfo = nullptr; - if (buffer) { - // TODO: transfer infos() into buffer metadata - } - for (const auto &info : worklet->output.configUpdate) { - if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) { - ALOGV("csd found"); - csdInfo = static_cast(info.get()); - } - } + int32_t flags = 0; + if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) { + flags |= MediaCodec::BUFFER_FLAG_EOS; + ALOGV("output EOS"); + } - int32_t flags = 0; - if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) { - flags |= MediaCodec::BUFFER_FLAG_EOS; - ALOGV("output EOS"); + sp outBuffer; + size_t index; + if (csdInfo != nullptr) { + Mutexed>::Locked buffers(mOutputBuffers); + if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) { + outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek()); + outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG); + ALOGV("csd index = %zu", index); + + buffers.unlock(); + mCallback->onOutputBufferAvailable(index, outBuffer); + buffers.lock(); + } else { + ALOGE("unable to register csd"); + buffers.unlock(); + mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); + buffers.lock(); + return; } + } - sp outBuffer; - if (csdInfo != nullptr) { - Mutexed>::Locked buffers(mOutputBuffers); - if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) { - outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek()); - outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG); - ALOGV("csd index = %zu", index); + if (!buffer && !flags) { + ALOGV("Not reporting output buffer"); + return; + } - buffers.unlock(); - mCallback->onOutputBufferAvailable(index, outBuffer); - buffers.lock(); - } else { - ALOGE("unable to register output buffer"); - buffers.unlock(); - mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - buffers.lock(); - continue; + if (buffer) { + for (const std::shared_ptr &info : buffer->info()) { + // TODO: properly translate these to metadata + switch (info->coreIndex().coreIndex()) { + case C2StreamPictureTypeMaskInfo::CORE_INDEX: + if (((C2StreamPictureTypeMaskInfo *)info.get())->value & C2PictureTypeKeyFrame) { + flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME; + } + break; + default: + break; } } + } - if (!buffer && !flags) { - ALOGV("Not reporting output buffer"); - continue; - } - - { - Mutexed>::Locked buffers(mOutputBuffers); - if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) { - ALOGE("unable to register output buffer"); - - buffers.unlock(); - mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - buffers.lock(); - continue; - } + { + Mutexed>::Locked buffers(mOutputBuffers); + if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) { + ALOGE("unable to register output buffer"); + buffers.unlock(); + mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL); + buffers.lock(); + return; } - - outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek()); - outBuffer->meta()->setInt32("flags", flags); - ALOGV("index = %zu", index); - mCallback->onOutputBufferAvailable(index, outBuffer); } + + outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek()); + outBuffer->meta()->setInt32("flags", flags); + ALOGV("out buffer index = %zu", index); + mCallback->onOutputBufferAvailable(index, outBuffer); } status_t CCodecBufferChannel::setSurface(const sp &newSurface) { diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h index 83cb72c402..2a2b9dec62 100644 --- a/media/libstagefright/codec2/include/C2Config.h +++ b/media/libstagefright/codec2/include/C2Config.h @@ -67,6 +67,7 @@ enum C2ParamIndexKind : C2Param::type_index_t { kParamIndexVideoSizeTuning, kParamIndexCsd, + kParamIndexPictureTypeMask, // video info @@ -133,6 +134,12 @@ typedef C2PortParam C2PortBlockP typedef C2StreamParam C2StreamCsdInfo; +C2ENUM(C2PictureTypeMask, uint32_t, + C2PictureTypeKeyFrame = (1u << 0), +) + +typedef C2StreamParam C2StreamPictureTypeMaskInfo; + /* Component description fields: diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp index 18db3e9dbc..a5ea511194 100644 --- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp @@ -38,6 +38,15 @@ using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_vec; +namespace { + +struct BufferDescriptorInfo { + IMapper::BufferDescriptorInfo mapperInfo; + uint32_t stride; +}; + +} + /* ===================================== GRALLOC ALLOCATION ==================================== */ static c2_status_t maperr2error(Error maperr) { switch (maperr) { @@ -73,6 +82,7 @@ private: uint32_t format; uint32_t usage_lo; uint32_t usage_hi; + uint32_t stride; uint32_t magic; }; @@ -109,13 +119,16 @@ public: static C2HandleGralloc* WrapNativeHandle( const native_handle_t *const handle, - uint32_t width, uint32_t height, uint32_t format, uint64_t usage) { + uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride) { //CHECK(handle != nullptr); if (native_handle_is_invalid(handle) || handle->numInts > int((INT_MAX - handle->version) / sizeof(int)) - NUM_INTS - handle->numFds) { return nullptr; } - ExtraData xd = { width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32), MAGIC }; + ExtraData xd = { + width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32), + stride, MAGIC + }; native_handle_t *res = native_handle_create(handle->numFds, handle->numInts + NUM_INTS); if (res != nullptr) { memcpy(&res->data, &handle->data, sizeof(int) * (handle->numFds + handle->numInts)); @@ -138,7 +151,8 @@ public: static const C2HandleGralloc* Import( const C2Handle *const handle, - uint32_t *width, uint32_t *height, uint32_t *format, uint64_t *usage) { + uint32_t *width, uint32_t *height, uint32_t *format, + uint64_t *usage, uint32_t *stride) { const ExtraData *xd = getExtraData(handle); if (xd == nullptr) { return nullptr; @@ -147,15 +161,22 @@ public: *height = xd->height; *format = xd->format; *usage = xd->usage_lo | (uint64_t(xd->usage_hi) << 32); + *stride = xd->stride; return reinterpret_cast(handle); } }; -native_handle_t* UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) { +native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) { return C2HandleGralloc::UnwrapNativeHandle(handle); } +C2Handle *WrapNativeCodec2GrallocHandle( + const native_handle_t *const handle, + uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride) { + return C2HandleGralloc::WrapNativeHandle(handle, width, height, format, usage, stride); +} + class C2AllocationGralloc : public C2GraphicAllocation { public: virtual ~C2AllocationGralloc() override; @@ -171,7 +192,7 @@ public: // internal methods // |handle| will be moved. C2AllocationGralloc( - const IMapper::BufferDescriptorInfo &info, + const BufferDescriptorInfo &info, const sp &mapper, hidl_handle &hidlHandle, const C2HandleGralloc *const handle); @@ -179,7 +200,7 @@ public: c2_status_t status() const; private: - const IMapper::BufferDescriptorInfo mInfo; + const BufferDescriptorInfo mInfo; const sp mMapper; const hidl_handle mHidlHandle; const C2HandleGralloc *mHandle; @@ -189,11 +210,11 @@ private: }; C2AllocationGralloc::C2AllocationGralloc( - const IMapper::BufferDescriptorInfo &info, + const BufferDescriptorInfo &info, const sp &mapper, hidl_handle &hidlHandle, const C2HandleGralloc *const handle) - : C2GraphicAllocation(info.width, info.height), + : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height), mInfo(info), mMapper(mapper), mHidlHandle(std::move(hidlHandle)), @@ -241,83 +262,133 @@ c2_status_t C2AllocationGralloc::map( return C2_CORRUPTED; } mLockedHandle = C2HandleGralloc::WrapNativeHandle( - mBuffer, mInfo.width, mInfo.height, (uint32_t)mInfo.format, mInfo.usage); + mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height, + (uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride); } - if (mInfo.format == PixelFormat::YCBCR_420_888 || mInfo.format == PixelFormat::YV12) { - YCbCrLayout ycbcrLayout; - mMapper->lockYCbCr( - const_cast(mBuffer), - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN, - { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, - // TODO: fence - hidl_handle(), - [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) { - err = maperr2error(maperr); - if (err == C2_OK) { - ycbcrLayout = mapLayout; - } - }); - if (err != C2_OK) { - return err; + switch (mInfo.mapperInfo.format) { + case PixelFormat::YCBCR_420_888: + case PixelFormat::YV12: { + YCbCrLayout ycbcrLayout; + mMapper->lockYCbCr( + const_cast(mBuffer), + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN, + { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) { + err = maperr2error(maperr); + if (err == C2_OK) { + ycbcrLayout = mapLayout; + } + }); + if (err != C2_OK) { + return err; + } + addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y; + addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb; + addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr; + layout->type = C2PlanarLayout::TYPE_YUV; + layout->numPlanes = 3; + layout->planes[C2PlanarLayout::PLANE_Y] = { + C2PlaneInfo::CHANNEL_Y, // channel + 1, // colInc + (int32_t)ycbcrLayout.yStride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + }; + layout->planes[C2PlanarLayout::PLANE_U] = { + C2PlaneInfo::CHANNEL_CB, // channel + (int32_t)ycbcrLayout.chromaStep, // colInc + (int32_t)ycbcrLayout.cStride, // rowInc + 2, // mColSampling + 2, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + }; + layout->planes[C2PlanarLayout::PLANE_V] = { + C2PlaneInfo::CHANNEL_CR, // channel + (int32_t)ycbcrLayout.chromaStep, // colInc + (int32_t)ycbcrLayout.cStride, // rowInc + 2, // mColSampling + 2, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + }; + break; } - addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y; - addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb; - addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr; - layout->type = C2PlanarLayout::TYPE_YUV; - layout->numPlanes = 3; - layout->planes[C2PlanarLayout::PLANE_Y] = { - C2PlaneInfo::CHANNEL_Y, // channel - 1, // colInc - (int32_t)ycbcrLayout.yStride, // rowInc - 1, // mColSampling - 1, // mRowSampling - 8, // allocatedDepth - 8, // bitDepth - 0, // rightShift - C2PlaneInfo::NATIVE, // endianness - }; - layout->planes[C2PlanarLayout::PLANE_U] = { - C2PlaneInfo::CHANNEL_CB, // channel - (int32_t)ycbcrLayout.chromaStep, // colInc - (int32_t)ycbcrLayout.cStride, // rowInc - 2, // mColSampling - 2, // mRowSampling - 8, // allocatedDepth - 8, // bitDepth - 0, // rightShift - C2PlaneInfo::NATIVE, // endianness - }; - layout->planes[C2PlanarLayout::PLANE_V] = { - C2PlaneInfo::CHANNEL_CR, // channel - (int32_t)ycbcrLayout.chromaStep, // colInc - (int32_t)ycbcrLayout.cStride, // rowInc - 2, // mColSampling - 2, // mRowSampling - 8, // allocatedDepth - 8, // bitDepth - 0, // rightShift - C2PlaneInfo::NATIVE, // endianness - }; - } else { - void *pointer = nullptr; - mMapper->lock( - const_cast(mBuffer), - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN, - { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, - // TODO: fence - hidl_handle(), - [&err, &pointer](const auto &maperr, const auto &mapPointer) { - err = maperr2error(maperr); - if (err == C2_OK) { - pointer = mapPointer; - } - }); - if (err != C2_OK) { - return err; + + case PixelFormat::RGBA_8888: + // TODO: alpha channel + // fall-through + case PixelFormat::RGBX_8888: { + void *pointer = nullptr; + mMapper->lock( + const_cast(mBuffer), + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN, + { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &pointer](const auto &maperr, const auto &mapPointer) { + err = maperr2error(maperr); + if (err == C2_OK) { + pointer = mapPointer; + } + }); + if (err != C2_OK) { + return err; + } + addr[C2PlanarLayout::PLANE_R] = (uint8_t *)pointer; + addr[C2PlanarLayout::PLANE_G] = (uint8_t *)pointer + 1; + addr[C2PlanarLayout::PLANE_B] = (uint8_t *)pointer + 2; + layout->type = C2PlanarLayout::TYPE_RGB; + layout->numPlanes = 3; + layout->planes[C2PlanarLayout::PLANE_R] = { + C2PlaneInfo::CHANNEL_R, // channel + 4, // colInc + 4 * (int32_t)mInfo.stride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + }; + layout->planes[C2PlanarLayout::PLANE_G] = { + C2PlaneInfo::CHANNEL_G, // channel + 4, // colInc + 4 * (int32_t)mInfo.stride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + }; + layout->planes[C2PlanarLayout::PLANE_B] = { + C2PlaneInfo::CHANNEL_B, // channel + 4, // colInc + 4 * (int32_t)mInfo.stride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + }; + break; + } + default: { + return C2_OMITTED; } - // TODO - return C2_OMITTED; } mLocked = true; @@ -396,17 +467,20 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation( // TODO: buffer usage should be determined according to |usage| (void) usage; - IMapper::BufferDescriptorInfo info = { - width, - height, - 1u, // layerCount - (PixelFormat)format, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN, + BufferDescriptorInfo info = { + { + width, + height, + 1u, // layerCount + (PixelFormat)format, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN, + }, + 0u, // stride placeholder }; c2_status_t err = C2_OK; BufferDescriptor desc; mMapper->createDescriptor( - info, [&err, &desc](const auto &maperr, const auto &descriptor) { + info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) { err = maperr2error(maperr); if (err == C2_OK) { desc = descriptor; @@ -421,8 +495,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation( mAllocator->allocate( desc, 1u, - [&err, &buffer](const auto &maperr, const auto &stride, auto &buffers) { - (void) stride; + [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) { err = maperr2error(maperr); if (err != C2_OK) { return; @@ -431,6 +504,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation( err = C2_CORRUPTED; return; } + info.stride = stride; buffer = std::move(buffers[0]); }); if (err != C2_OK) { @@ -442,18 +516,20 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation( info, mMapper, buffer, C2HandleGralloc::WrapNativeHandle( buffer.getNativeHandle(), - info.width, info.height, (uint32_t)info.format, info.usage))); + info.mapperInfo.width, info.mapperInfo.height, + (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride))); return C2_OK; } c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation( const C2Handle *handle, std::shared_ptr *allocation) { - IMapper::BufferDescriptorInfo info; - info.layerCount = 1u; + BufferDescriptorInfo info; + info.mapperInfo.layerCount = 1u; const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import( handle, - &info.width, &info.height, (uint32_t *)&info.format, (uint64_t *)&info.usage); + &info.mapperInfo.width, &info.mapperInfo.height, + (uint32_t *)&info.mapperInfo.format, (uint64_t *)&info.mapperInfo.usage, &info.stride); if (grallocHandle == nullptr) { return C2_BAD_VALUE; } @@ -461,7 +537,7 @@ c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation( hidl_handle hidlHandle = C2HandleGralloc::UnwrapNativeHandle(grallocHandle); allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle)); - return C2_OMITTED; + return C2_OK; } C2AllocatorGralloc::C2AllocatorGralloc() : mImpl(new Impl) {} diff --git a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h index 531174713c..56fa317a01 100644 --- a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h +++ b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h @@ -1,4 +1,3 @@ - /* * Copyright (C) 2016 The Android Open Source Project * @@ -32,7 +31,17 @@ namespace android { * * @return a new NON-OWNING native handle that must be deleted using native_handle_delete. */ -native_handle_t*UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle); +native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle); + +/** + * Wrap the gralloc handle and metadata into Codec2 handle recognized by + * C2AllocatorGralloc. + * + * @return a new NON-OWNING C2Handle that must be deleted using native_handle_delete. + */ +C2Handle *WrapNativeCodec2GrallocHandle( + const native_handle_t *const handle, + uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride); class C2AllocatorGralloc : public C2Allocator { public: diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h index c5062d6b1b..57a1374a22 100644 --- a/media/libstagefright/include/CCodecBufferChannel.h +++ b/media/libstagefright/include/CCodecBufferChannel.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -35,134 +36,9 @@ namespace android { /** * BufferChannelBase implementation for CCodec. */ -class CCodecBufferChannel : public BufferChannelBase { +class CCodecBufferChannel + : public BufferChannelBase, public std::enable_shared_from_this { public: - /** - * Base class for representation of buffers at one port. - */ - class Buffers { - public: - Buffers() = default; - virtual ~Buffers() = default; - - /** - * Set format for MediaCodec-facing buffers. - */ - inline void setFormat(const sp &format) { mFormat = format; } - - /** - * Returns true if the buffers are operating under array mode. - */ - virtual bool isArrayMode() { return false; } - - /** - * Fills the vector with MediaCodecBuffer's if in array mode; otherwise, - * no-op. - */ - virtual void getArray(Vector> *) {} - - protected: - // Format to be used for creating MediaCodec-facing buffers. - sp mFormat; - - private: - DISALLOW_EVIL_CONSTRUCTORS(Buffers); - }; - - class InputBuffers : public Buffers { - public: - using Buffers::Buffers; - virtual ~InputBuffers() = default; - - /** - * Set a block pool to obtain input memory blocks. - */ - inline void setPool(const std::shared_ptr &pool) { mPool = pool; } - - /** - * Get a new MediaCodecBuffer for input and its corresponding index. - * Returns false if no new buffer can be obtained at the moment. - */ - virtual bool requestNewBuffer(size_t *index, sp *buffer) = 0; - - /** - * Release the buffer obtained from requestNewBuffer() and get the - * associated C2Buffer object back. Returns empty shared_ptr if the - * buffer is not on file. - */ - virtual std::shared_ptr releaseBuffer(const sp &buffer) = 0; - - /** - * Flush internal state. After this call, no index or buffer previously - * returned from requestNewBuffer() is valid. - */ - virtual void flush() = 0; - - /** - * Return array-backed version of input buffers. The returned object - * shall retain the internal state so that it will honor index and - * buffer from previous calls of requestNewBuffer(). - */ - virtual std::unique_ptr toArrayMode() = 0; - - protected: - // Pool to obtain blocks for input buffers. - std::shared_ptr mPool; - - private: - DISALLOW_EVIL_CONSTRUCTORS(InputBuffers); - }; - - class OutputBuffers : public Buffers { - public: - using Buffers::Buffers; - virtual ~OutputBuffers() = default; - - /** - * Register output C2Buffer from the component and obtain corresponding - * index and MediaCodecBuffer object. Returns false if registration - * fails. - */ - virtual bool registerBuffer( - const std::shared_ptr &buffer, - size_t *index, - sp *codecBuffer) = 0; - - /** - * Register codec specific data as a buffer to be consistent with - * MediaCodec behavior. - */ - virtual bool registerCsd( - const C2StreamCsdInfo::output * /* csd */, - size_t * /* index */, - sp * /* codecBuffer */) { - return false; - } - - /** - * Release the buffer obtained from registerBuffer() and get the - * associated C2Buffer object back. Returns empty shared_ptr if the - * buffer is not on file. - */ - virtual std::shared_ptr releaseBuffer(const sp &buffer) = 0; - - /** - * Flush internal state. After this call, no index or buffer previously - * returned from registerBuffer() is valid. - */ - virtual void flush(const std::list> &flushedWork) = 0; - - /** - * Return array-backed version of output buffers. The returned object - * shall retain the internal state so that it will honor index and - * buffer from previous calls of registerBuffer(). - */ - virtual std::unique_ptr toArrayMode() = 0; - - private: - DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers); - }; - CCodecBufferChannel(const std::function &onError); virtual ~CCodecBufferChannel(); @@ -186,23 +62,21 @@ public: // Methods below are interface for CCodec to use. + /** + * Set the component object for buffer processing. + */ void setComponent(const std::shared_ptr &component); - status_t setSurface(const sp &surface); /** - * Set C2BlockPool for input buffers. - * - * TODO: start timestamp? + * Set output graphic surface for rendering. */ - void setInputBufferAllocator(const sp &inAlloc); + status_t setSurface(const sp &surface); /** - * Set C2BlockPool for output buffers. This object shall never use the - * allocator itself; it's just passed - * - * TODO: start timestamp? + * Set GraphicBufferSource object from which the component extracts input + * buffers. */ - void setOutputBufferAllocator(const sp &outAlloc); + status_t setGraphicBufferSource(const sp &source); /** * Start queueing buffers to the component. This object should never queue @@ -219,11 +93,17 @@ public: void flush(const std::list> &flushedWork); /** - * Notify MediaCodec about work done. + * Notify input client about work done. * - * @param workItems finished work items. + * @param workItems finished work item. */ - void onWorkDone(std::vector> workItems); + void onWorkDone(const std::unique_ptr &work); + + // Internal classes + class Buffers; + class InputBuffers; + class OutputBuffers; + class InputBufferClient; private: class QueueGuard; @@ -276,12 +156,17 @@ private: bool mRunning; }; + class C2ComponentWrapper; + + void feedInputBufferIfAvailable(); + QueueSync mSync; sp mDealer; sp mDecryptDestination; int32_t mHeapSeqNum; std::shared_ptr mComponent; + std::shared_ptr mInputClient; std::function mOnError; std::shared_ptr mInputAllocator; QueueSync mQueueSync; diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h index 3e24bbe727..c689761c71 100644 --- a/media/libstagefright/include/media/stagefright/CCodec.h +++ b/media/libstagefright/include/media/stagefright/CCodec.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ public: virtual void signalRequestIDRFrame() override; void initiateReleaseIfStuck(); + void onWorkDone(std::vector> &workItems); protected: virtual ~CCodec(); @@ -77,6 +79,10 @@ private: void flush(); void release(bool sendCallback); + void createInputSurface(); + void setInputSurface(const sp &surface); + status_t setupInputSurface(const sp &source); + void setDeadline(const TimePoint &deadline); enum { @@ -86,6 +92,9 @@ private: kWhatFlush, kWhatStop, kWhatRelease, + kWhatCreateInputSurface, + kWhatSetInputSurface, + kWhatWorkDone, }; enum { @@ -104,14 +113,17 @@ private: struct State { inline State() : mState(RELEASED) {} + inline int get() const { return mState; } + inline void set(int newState) { mState = newState; } + std::shared_ptr comp; + private: int mState; - std::shared_ptr mComp; }; struct Formats { - sp mInputFormat; - sp mOutputFormat; + sp inputFormat; + sp outputFormat; }; Mutexed mState; @@ -119,6 +131,7 @@ private: std::shared_ptr mListener; Mutexed mDeadline; Mutexed mFormats; + Mutexed>> mWorkDoneQueue; DISALLOW_EVIL_CONSTRUCTORS(CCodec); };