diff options
Diffstat (limited to 'media/libstagefright/omx')
-rw-r--r-- | media/libstagefright/omx/Android.mk | 6 | ||||
-rw-r--r-- | media/libstagefright/omx/GraphicBufferSource.cpp | 197 | ||||
-rw-r--r-- | media/libstagefright/omx/GraphicBufferSource.h | 41 | ||||
-rw-r--r-- | media/libstagefright/omx/OMX.cpp | 11 | ||||
-rw-r--r-- | media/libstagefright/omx/OMXMaster.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/omx/OMXNodeInstance.cpp | 97 | ||||
-rw-r--r-- | media/libstagefright/omx/SoftOMXPlugin.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp | 189 | ||||
-rw-r--r-- | media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp | 311 | ||||
-rw-r--r-- | media/libstagefright/omx/tests/Android.mk | 2 | ||||
-rw-r--r-- | media/libstagefright/omx/tests/OMXHarness.cpp | 6 |
11 files changed, 784 insertions, 80 deletions
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index cd912e7..aaa8334 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -1,6 +1,10 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +ifeq ($(TARGET_DEVICE), manta) + LOCAL_CFLAGS += -DSURFACE_IS_BGR32 +endif + LOCAL_SRC_FILES:= \ GraphicBufferSource.cpp \ OMX.cpp \ @@ -10,6 +14,7 @@ LOCAL_SRC_FILES:= \ SoftOMXComponent.cpp \ SoftOMXPlugin.cpp \ SoftVideoDecoderOMXComponent.cpp \ + SoftVideoEncoderOMXComponent.cpp \ LOCAL_C_INCLUDES += \ $(TOP)/frameworks/av/media/libstagefright \ @@ -18,6 +23,7 @@ LOCAL_C_INCLUDES += \ LOCAL_SHARED_LIBRARIES := \ libbinder \ + libhardware \ libmedia \ libutils \ liblog \ diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index 3fe9c23..3e70956 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -29,13 +29,16 @@ #include <media/hardware/MetadataBufferType.h> #include <ui/GraphicBuffer.h> +#include <inttypes.h> + namespace android { static const bool EXTRA_CHECK = true; GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, - uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount) : + uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount, + bool useGraphicBufferInMeta) : mInitCheck(UNKNOWN_ERROR), mNodeInstance(nodeInstance), mExecuting(false), @@ -43,16 +46,22 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, mNumFramesAvailable(0), mEndOfStream(false), mEndOfStreamSent(false), - mRepeatAfterUs(-1ll), mMaxTimestampGapUs(-1ll), mPrevOriginalTimeUs(-1ll), mPrevModifiedTimeUs(-1ll), + mSkipFramesBeforeNs(-1ll), + mRepeatAfterUs(-1ll), mRepeatLastFrameGeneration(0), mRepeatLastFrameTimestamp(-1ll), mLatestSubmittedBufferId(-1), mLatestSubmittedBufferFrameNum(0), mLatestSubmittedBufferUseCount(0), - mRepeatBufferDeferred(false) { + mRepeatBufferDeferred(false), + mTimePerCaptureUs(-1ll), + mTimePerFrameUs(-1ll), + mPrevCaptureUs(-1ll), + mPrevFrameUs(-1ll), + mUseGraphicBufferInMeta(useGraphicBufferInMeta) { ALOGV("GraphicBufferSource w=%u h=%u c=%u", bufferWidth, bufferHeight, bufferCount); @@ -65,13 +74,12 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, String8 name("GraphicBufferSource"); - mBufferQueue = new BufferQueue(); - mBufferQueue->setConsumerName(name); - mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight); - mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | - GRALLOC_USAGE_HW_TEXTURE); + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mConsumer->setConsumerName(name); + mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight); + mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER); - mInitCheck = mBufferQueue->setMaxAcquiredBufferCount(bufferCount); + mInitCheck = mConsumer->setMaxAcquiredBufferCount(bufferCount); if (mInitCheck != NO_ERROR) { ALOGE("Unable to set BQ max acquired buffer count to %u: %d", bufferCount, mInitCheck); @@ -85,7 +93,7 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, wp<BufferQueue::ConsumerListener> listener = static_cast<BufferQueue::ConsumerListener*>(this); sp<BufferQueue::ProxyConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener); - mInitCheck = mBufferQueue->consumerConnect(proxy, false); + mInitCheck = mConsumer->consumerConnect(proxy, false); if (mInitCheck != NO_ERROR) { ALOGE("Error connecting to BufferQueue: %s (%d)", strerror(-mInitCheck), mInitCheck); @@ -97,8 +105,8 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, GraphicBufferSource::~GraphicBufferSource() { ALOGV("~GraphicBufferSource"); - if (mBufferQueue != NULL) { - status_t err = mBufferQueue->consumerDisconnect(); + if (mConsumer != NULL) { + status_t err = mConsumer->consumerDisconnect(); if (err != NO_ERROR) { ALOGW("consumerDisconnect failed: %d", err); } @@ -107,7 +115,7 @@ GraphicBufferSource::~GraphicBufferSource() { void GraphicBufferSource::omxExecuting() { Mutex::Autolock autoLock(mMutex); - ALOGV("--> executing; avail=%d, codec vec size=%zd", + ALOGV("--> executing; avail=%zu, codec vec size=%zd", mNumFramesAvailable, mCodecBuffers.size()); CHECK(!mExecuting); mExecuting = true; @@ -129,7 +137,7 @@ void GraphicBufferSource::omxExecuting() { } } - ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable); + ALOGV("done loading initial frames, avail=%zu", mNumFramesAvailable); // If EOS has already been signaled, and there are no more frames to // submit, try to send EOS now as well. @@ -181,7 +189,7 @@ void GraphicBufferSource::omxLoaded(){ mLooper.clear(); } - ALOGV("--> loaded; avail=%d eos=%d eosSent=%d", + ALOGV("--> loaded; avail=%zu eos=%d eosSent=%d", mNumFramesAvailable, mEndOfStream, mEndOfStreamSent); // Codec is no longer executing. Discard all codec-related state. @@ -248,13 +256,25 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // Pull the graphic buffer handle back out of the buffer, and confirm // that it matches expectations. OMX_U8* data = header->pBuffer; - buffer_handle_t bufferHandle; - memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t)); - if (bufferHandle != codecBuffer.mGraphicBuffer->handle) { - // should never happen - ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", - bufferHandle, codecBuffer.mGraphicBuffer->handle); - CHECK(!"codecBufferEmptied: mismatched buffer"); + MetadataBufferType type = *(MetadataBufferType *)data; + if (type == kMetadataBufferTypeGrallocSource) { + buffer_handle_t bufferHandle; + memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t)); + if (bufferHandle != codecBuffer.mGraphicBuffer->handle) { + // should never happen + ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", + bufferHandle, codecBuffer.mGraphicBuffer->handle); + CHECK(!"codecBufferEmptied: mismatched buffer"); + } + } else if (type == kMetadataBufferTypeGraphicBuffer) { + GraphicBuffer *buffer; + memcpy(&buffer, data + 4, sizeof(buffer)); + if (buffer != codecBuffer.mGraphicBuffer.get()) { + // should never happen + ALOGE("codecBufferEmptied: buffer is %p, expected %p", + buffer, codecBuffer.mGraphicBuffer.get()); + CHECK(!"codecBufferEmptied: mismatched buffer"); + } } } @@ -270,7 +290,7 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { if (id == mLatestSubmittedBufferId) { CHECK_GT(mLatestSubmittedBufferUseCount--, 0); } else { - mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber, + mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); } } else { @@ -284,7 +304,7 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { if (mNumFramesAvailable) { // Fill this codec buffer. CHECK(!mEndOfStreamSent); - ALOGV("buffer freed, %d frames avail (eos=%d)", + ALOGV("buffer freed, %zu frames avail (eos=%d)", mNumFramesAvailable, mEndOfStream); fillCodecBuffer_l(); } else if (mEndOfStream) { @@ -313,7 +333,8 @@ void GraphicBufferSource::codecBufferFilled(OMX_BUFFERHEADERTYPE* header) { ssize_t index = mOriginalTimeUs.indexOfKey(header->nTimeStamp); if (index >= 0) { ALOGV("OUT timestamp: %lld -> %lld", - header->nTimeStamp, mOriginalTimeUs[index]); + static_cast<long long>(header->nTimeStamp), + static_cast<long long>(mOriginalTimeUs[index])); header->nTimeStamp = mOriginalTimeUs[index]; mOriginalTimeUs.removeItemsAt(index); } else { @@ -324,7 +345,7 @@ void GraphicBufferSource::codecBufferFilled(OMX_BUFFERHEADERTYPE* header) { } if (mOriginalTimeUs.size() > BufferQueue::NUM_BUFFER_SLOTS) { // something terribly wrong must have happened, giving up... - ALOGE("mOriginalTimeUs has too many entries (%d)", + ALOGE("mOriginalTimeUs has too many entries (%zu)", mOriginalTimeUs.size()); mMaxTimestampGapUs = -1ll; } @@ -339,7 +360,7 @@ void GraphicBufferSource::suspend(bool suspend) { while (mNumFramesAvailable > 0) { BufferQueue::BufferItem item; - status_t err = mBufferQueue->acquireBuffer(&item, 0); + status_t err = mConsumer->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // shouldn't happen. @@ -352,7 +373,7 @@ void GraphicBufferSource::suspend(bool suspend) { --mNumFramesAvailable; - mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, + mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } return; @@ -381,15 +402,15 @@ bool GraphicBufferSource::fillCodecBuffer_l() { int cbi = findAvailableCodecBuffer_l(); if (cbi < 0) { // No buffers available, bail. - ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d", + ALOGV("fillCodecBuffer_l: no codec buffers, avail now %zu", mNumFramesAvailable); return false; } - ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", + ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%zu", mNumFramesAvailable); BufferQueue::BufferItem item; - status_t err = mBufferQueue->acquireBuffer(&item, 0); + status_t err = mConsumer->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // shouldn't happen ALOGW("fillCodecBuffer_l: frame was not available"); @@ -416,10 +437,21 @@ bool GraphicBufferSource::fillCodecBuffer_l() { mBufferSlot[item.mBuf] = item.mGraphicBuffer; } - err = submitBuffer_l(item, cbi); + err = UNKNOWN_ERROR; + + // only submit sample if start time is unspecified, or sample + // is queued after the specified start time + if (mSkipFramesBeforeNs < 0ll || item.mTimestamp >= mSkipFramesBeforeNs) { + // if start time is set, offset time stamp by start time + if (mSkipFramesBeforeNs > 0) { + item.mTimestamp -= mSkipFramesBeforeNs; + } + err = submitBuffer_l(item, cbi); + } + if (err != OK) { ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); - mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, + mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); } else { ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); @@ -442,7 +474,7 @@ bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() { // // To be on the safe side we try to release the buffer. ALOGD("repeatLatestSubmittedBuffer_l: slot was NULL"); - mBufferQueue->releaseBuffer( + mConsumer->releaseBuffer( mLatestSubmittedBufferId, mLatestSubmittedBufferFrameNum, EGL_NO_DISPLAY, @@ -496,7 +528,7 @@ void GraphicBufferSource::setLatestSubmittedBuffer_l( if (mLatestSubmittedBufferId >= 0) { if (mLatestSubmittedBufferUseCount == 0) { - mBufferQueue->releaseBuffer( + mConsumer->releaseBuffer( mLatestSubmittedBufferId, mLatestSubmittedBufferFrameNum, EGL_NO_DISPLAY, @@ -522,7 +554,7 @@ void GraphicBufferSource::setLatestSubmittedBuffer_l( status_t GraphicBufferSource::signalEndOfInputStream() { Mutex::Autolock autoLock(mMutex); - ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d", + ALOGV("signalEndOfInputStream: exec=%d avail=%zu eos=%d", mExecuting, mNumFramesAvailable, mEndOfStream); if (mEndOfStream) { @@ -550,7 +582,32 @@ status_t GraphicBufferSource::signalEndOfInputStream() { int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) { int64_t timeUs = item.mTimestamp / 1000; - if (mMaxTimestampGapUs > 0ll) { + if (mTimePerCaptureUs > 0ll) { + // Time lapse or slow motion mode + if (mPrevCaptureUs < 0ll) { + // first capture + mPrevCaptureUs = timeUs; + mPrevFrameUs = timeUs; + } else { + // snap to nearest capture point + int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs) + / mTimePerCaptureUs; + if (nFrames <= 0) { + // skip this frame as it's too close to previous capture + ALOGV("skipping frame, timeUs %lld", static_cast<long long>(timeUs)); + return -1; + } + mPrevCaptureUs = mPrevCaptureUs + nFrames * mTimePerCaptureUs; + mPrevFrameUs += mTimePerFrameUs * nFrames; + } + + ALOGV("timeUs %lld, captureUs %lld, frameUs %lld", + static_cast<long long>(timeUs), + static_cast<long long>(mPrevCaptureUs), + static_cast<long long>(mPrevFrameUs)); + + return mPrevFrameUs; + } else if (mMaxTimestampGapUs > 0ll) { /* Cap timestamp gap between adjacent frames to specified max * * In the scenario of cast mirroring, encoding could be suspended for @@ -574,7 +631,9 @@ int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) { mPrevOriginalTimeUs = originalTimeUs; mPrevModifiedTimeUs = timeUs; mOriginalTimeUs.add(timeUs, originalTimeUs); - ALOGV("IN timestamp: %lld -> %lld", originalTimeUs, timeUs); + ALOGV("IN timestamp: %lld -> %lld", + static_cast<long long>(originalTimeUs), + static_cast<long long>(timeUs)); } return timeUs; @@ -597,10 +656,22 @@ status_t GraphicBufferSource::submitBuffer_l( OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t)); OMX_U8* data = header->pBuffer; - const OMX_U32 type = kMetadataBufferTypeGrallocSource; - buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle; - memcpy(data, &type, 4); - memcpy(data + 4, &handle, sizeof(buffer_handle_t)); + buffer_handle_t handle; + if (!mUseGraphicBufferInMeta) { + const OMX_U32 type = kMetadataBufferTypeGrallocSource; + handle = codecBuffer.mGraphicBuffer->handle; + memcpy(data, &type, 4); + memcpy(data + 4, &handle, sizeof(buffer_handle_t)); + } else { + // codecBuffer holds a reference to the GraphicBuffer, so + // it is valid while it is with the OMX component + const OMX_U32 type = kMetadataBufferTypeGraphicBuffer; + memcpy(data, &type, 4); + // passing a non-reference-counted graphicBuffer + GraphicBuffer *buffer = codecBuffer.mGraphicBuffer.get(); + handle = buffer->handle; + memcpy(data + 4, &buffer, sizeof(buffer)); + } status_t err = mNodeInstance->emptyDirectBuffer(header, 0, 4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME, @@ -682,7 +753,7 @@ int GraphicBufferSource::findMatchingCodecBuffer_l( void GraphicBufferSource::onFrameAvailable() { Mutex::Autolock autoLock(mMutex); - ALOGV("onFrameAvailable exec=%d avail=%d", + ALOGV("onFrameAvailable exec=%d avail=%zu", mExecuting, mNumFramesAvailable); if (mEndOfStream || mSuspended) { @@ -696,15 +767,15 @@ void GraphicBufferSource::onFrameAvailable() { } BufferQueue::BufferItem item; - status_t err = mBufferQueue->acquireBuffer(&item, 0); + status_t err = mConsumer->acquireBuffer(&item, 0); if (err == OK) { // If this is the first time we're seeing this buffer, add it to our // slot table. if (item.mGraphicBuffer != NULL) { - ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); + ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mBuf); mBufferSlot[item.mBuf] = item.mGraphicBuffer; } - mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, + mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } return; @@ -724,13 +795,13 @@ void GraphicBufferSource::onFrameAvailable() { void GraphicBufferSource::onBuffersReleased() { Mutex::Autolock lock(mMutex); - uint32_t slotMask; - if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) { + uint64_t slotMask; + if (mConsumer->getReleasedBuffers(&slotMask) != NO_ERROR) { ALOGW("onBuffersReleased: unable to get released buffer set"); - slotMask = 0xffffffff; + slotMask = 0xffffffffffffffffULL; } - ALOGV("onBuffersReleased: 0x%08x", slotMask); + ALOGV("onBuffersReleased: 0x%016" PRIx64, slotMask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if ((slotMask & 0x01) != 0) { @@ -740,6 +811,11 @@ void GraphicBufferSource::onBuffersReleased() { } } +// BufferQueue::ConsumerListener callback +void GraphicBufferSource::onSidebandStreamChanged() { + ALOG_ASSERT(false, "GraphicBufferSource can't consume sideband streams"); +} + status_t GraphicBufferSource::setRepeatPreviousFrameDelayUs( int64_t repeatAfterUs) { Mutex::Autolock autoLock(mMutex); @@ -764,6 +840,27 @@ status_t GraphicBufferSource::setMaxTimestampGapUs(int64_t maxGapUs) { return OK; } + +void GraphicBufferSource::setSkipFramesBeforeUs(int64_t skipFramesBeforeUs) { + Mutex::Autolock autoLock(mMutex); + + mSkipFramesBeforeNs = + (skipFramesBeforeUs > 0) ? (skipFramesBeforeUs * 1000) : -1ll; +} + +status_t GraphicBufferSource::setTimeLapseUs(int64_t* data) { + Mutex::Autolock autoLock(mMutex); + + if (mExecuting || data[0] <= 0ll || data[1] <= 0ll) { + return INVALID_OPERATION; + } + + mTimePerFrameUs = data[0]; + mTimePerCaptureUs = data[1]; + + return OK; +} + void GraphicBufferSource::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatRepeatLastFrame: diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 3b0e454..c0860ab 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -49,7 +49,8 @@ namespace android { class GraphicBufferSource : public BufferQueue::ConsumerListener { public: GraphicBufferSource(OMXNodeInstance* nodeInstance, - uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount); + uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount, + bool useGraphicBufferInMeta = false); virtual ~GraphicBufferSource(); // We can't throw an exception if the constructor fails, so we just set @@ -61,7 +62,7 @@ public: // Returns the handle to the producer side of the BufferQueue. Buffers // queued on this will be received by GraphicBufferSource. sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { - return mBufferQueue; + return mProducer; } // This is called when OMX transitions to OMX_StateExecuting, which means @@ -118,6 +119,17 @@ public: // of suspension on input. status_t setMaxTimestampGapUs(int64_t maxGapUs); + // Sets the time lapse (or slow motion) parameters. + // data[0] is the time (us) between two frames for playback + // data[1] is the time (us) between two frames for capture + // When set, the sample's timestamp will be modified to playback framerate, + // and capture timestamp will be modified to capture rate. + status_t setTimeLapseUs(int64_t* data); + + // Sets the start time us (in system time), samples before which should + // be dropped and not submitted to encoder + void setSkipFramesBeforeUs(int64_t startTimeUs); + protected: // BufferQueue::ConsumerListener interface, called when a new frame of // data is available. If we're executing and a codec buffer is @@ -132,6 +144,11 @@ protected: // set of mBufferSlot entries. virtual void onBuffersReleased(); + // BufferQueue::ConsumerListener interface, called when the client has + // changed the sideband stream. GraphicBufferSource doesn't handle sideband + // streams so this is a no-op (and should never be called). + virtual void onSidebandStreamChanged(); + private: // Keep track of codec input buffers. They may either be available // (mGraphicBuffer == NULL) or in use by the codec. @@ -194,8 +211,11 @@ private: bool mSuspended; - // We consume graphic buffers from this. - sp<BufferQueue> mBufferQueue; + // Our BufferQueue interfaces. mProducer is passed to the producer through + // getIGraphicBufferProducer, and mConsumer is used internally to retrieve + // the buffers queued by the producer. + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; // Number of frames pending in BufferQueue that haven't yet been // forwarded to the codec. @@ -223,16 +243,17 @@ private: enum { kRepeatLastFrameCount = 10, }; - int64_t mRepeatAfterUs; - int64_t mMaxTimestampGapUs; KeyedVector<int64_t, int64_t> mOriginalTimeUs; + int64_t mMaxTimestampGapUs; int64_t mPrevOriginalTimeUs; int64_t mPrevModifiedTimeUs; + int64_t mSkipFramesBeforeNs; sp<ALooper> mLooper; sp<AHandlerReflector<GraphicBufferSource> > mReflector; + int64_t mRepeatAfterUs; int32_t mRepeatLastFrameGeneration; int64_t mRepeatLastFrameTimestamp; int32_t mRepeatLastFrameCount; @@ -245,6 +266,14 @@ private: // no codec buffer was available at the time. bool mRepeatBufferDeferred; + // Time lapse / slow motion configuration + int64_t mTimePerCaptureUs; + int64_t mTimePerFrameUs; + int64_t mPrevCaptureUs; + int64_t mPrevFrameUs; + + bool mUseGraphicBufferInMeta; + void onMessageReceived(const sp<AMessage> &msg); DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource); diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 7819fc3..41407e4 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -233,7 +233,7 @@ status_t OMX::allocateNode( instance, &handle); if (err != OMX_ErrorNone) { - ALOGV("FAILED to allocate omx component '%s'", name); + ALOGE("FAILED to allocate omx component '%s'", name); instance->onGetHandleFailed(); @@ -342,6 +342,13 @@ status_t OMX::prepareForAdaptivePlayback( portIndex, enable, maxFrameWidth, maxFrameHeight); } +status_t OMX::configureVideoTunnelMode( + node_id node, OMX_U32 portIndex, OMX_BOOL tunneled, + OMX_U32 audioHwSync, native_handle_t **sidebandHandle) { + return findInstance(node)->configureVideoTunnelMode( + portIndex, tunneled, audioHwSync, sidebandHandle); +} + status_t OMX::useBuffer( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, buffer_id *buffer) { @@ -472,8 +479,6 @@ OMX_ERRORTYPE OMX::OnFillBufferDone( msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen; msg.u.extended_buffer_data.flags = pBuffer->nFlags; msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp; - msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate; - msg.u.extended_buffer_data.data_ptr = pBuffer->pBuffer; findDispatcher(node)->post(msg); diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp index 6b6d0ab..ae3cb33 100644 --- a/media/libstagefright/omx/OMXMaster.cpp +++ b/media/libstagefright/omx/OMXMaster.cpp @@ -91,7 +91,7 @@ void OMXMaster::addPlugin(OMXPluginBase *plugin) { } if (err != OMX_ErrorNoMore) { - ALOGE("OMX plugin failed w/ error 0x%08x after registering %d " + ALOGE("OMX plugin failed w/ error 0x%08x after registering %zu " "components", err, mPluginByComponentName.size()); } } diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index c64dcf0..d07ec14 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -393,20 +393,39 @@ status_t OMXNodeInstance::storeMetaDataInBuffers( OMX_U32 portIndex, OMX_BOOL enable) { Mutex::Autolock autolock(mLock); - return storeMetaDataInBuffers_l(portIndex, enable); + return storeMetaDataInBuffers_l( + portIndex, enable, + OMX_FALSE /* useGraphicBuffer */, NULL /* usingGraphicBufferInMetadata */); } status_t OMXNodeInstance::storeMetaDataInBuffers_l( OMX_U32 portIndex, - OMX_BOOL enable) { + OMX_BOOL enable, + OMX_BOOL useGraphicBuffer, + OMX_BOOL *usingGraphicBufferInMetadata) { OMX_INDEXTYPE index; OMX_STRING name = const_cast<OMX_STRING>( "OMX.google.android.index.storeMetaDataInBuffers"); - OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); + OMX_STRING graphicBufferName = const_cast<OMX_STRING>( + "OMX.google.android.index.storeGraphicBufferInMetaData"); + if (usingGraphicBufferInMetadata == NULL) { + usingGraphicBufferInMetadata = &useGraphicBuffer; + } + + OMX_ERRORTYPE err = + (useGraphicBuffer && portIndex == kPortIndexInput) + ? OMX_GetExtensionIndex(mHandle, graphicBufferName, &index) + : OMX_ErrorBadParameter; + if (err == OMX_ErrorNone) { + *usingGraphicBufferInMetadata = OMX_TRUE; + } else { + *usingGraphicBufferInMetadata = OMX_FALSE; + err = OMX_GetExtensionIndex(mHandle, name, &index); + } + if (err != OMX_ErrorNone) { ALOGE("OMX_GetExtensionIndex %s failed", name); - return StatusFromOMXError(err); } @@ -421,6 +440,7 @@ status_t OMXNodeInstance::storeMetaDataInBuffers_l( params.bStoreMetaData = enable; if ((err = OMX_SetParameter(mHandle, index, ¶ms)) != OMX_ErrorNone) { ALOGE("OMX_SetParameter() failed for StoreMetaDataInBuffers: 0x%08x", err); + *usingGraphicBufferInMetadata = OMX_FALSE; return UNKNOWN_ERROR; } return err; @@ -460,6 +480,49 @@ status_t OMXNodeInstance::prepareForAdaptivePlayback( return err; } +status_t OMXNodeInstance::configureVideoTunnelMode( + OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync, + native_handle_t **sidebandHandle) { + Mutex::Autolock autolock(mLock); + + OMX_INDEXTYPE index; + OMX_STRING name = const_cast<OMX_STRING>( + "OMX.google.android.index.configureVideoTunnelMode"); + + OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); + if (err != OMX_ErrorNone) { + ALOGE("configureVideoTunnelMode extension is missing!"); + return StatusFromOMXError(err); + } + + ConfigureVideoTunnelModeParams tunnelParams; + tunnelParams.nSize = sizeof(tunnelParams); + tunnelParams.nVersion.s.nVersionMajor = 1; + tunnelParams.nVersion.s.nVersionMinor = 0; + tunnelParams.nVersion.s.nRevision = 0; + tunnelParams.nVersion.s.nStep = 0; + + tunnelParams.nPortIndex = portIndex; + tunnelParams.bTunneled = tunneled; + tunnelParams.nAudioHwSync = audioHwSync; + err = OMX_SetParameter(mHandle, index, &tunnelParams); + if (err != OMX_ErrorNone) { + ALOGE("configureVideoTunnelMode failed! (err %d).", err); + return UNKNOWN_ERROR; + } + + err = OMX_GetParameter(mHandle, index, &tunnelParams); + if (err != OMX_ErrorNone) { + ALOGE("GetVideoTunnelWindow failed! (err %d).", err); + return UNKNOWN_ERROR; + } + if (sidebandHandle) { + *sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow; + } + + return err; +} + status_t OMXNodeInstance::useBuffer( OMX_U32 portIndex, const sp<IMemory> ¶ms, OMX::buffer_id *buffer) { @@ -640,7 +703,10 @@ status_t OMXNodeInstance::createInputSurface( } // Input buffers will hold meta-data (gralloc references). - err = storeMetaDataInBuffers_l(portIndex, OMX_TRUE); + OMX_BOOL usingGraphicBuffer = OMX_FALSE; + err = storeMetaDataInBuffers_l( + portIndex, OMX_TRUE, + OMX_TRUE /* useGraphicBuffer */, &usingGraphicBuffer); if (err != OK) { return err; } @@ -666,7 +732,7 @@ status_t OMXNodeInstance::createInputSurface( GraphicBufferSource* bufferSource = new GraphicBufferSource( this, def.format.video.nFrameWidth, def.format.video.nFrameHeight, - def.nBufferCountActual); + def.nBufferCountActual, usingGraphicBuffer); if ((err = bufferSource->initCheck()) != OK) { delete bufferSource; return err; @@ -855,6 +921,8 @@ status_t OMXNodeInstance::setInternalOption( case IOMX::INTERNAL_OPTION_SUSPEND: case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY: case IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP: + case IOMX::INTERNAL_OPTION_START_TIME: + case IOMX::INTERNAL_OPTION_TIME_LAPSE: { const sp<GraphicBufferSource> &bufferSource = getGraphicBufferSource(); @@ -879,7 +947,8 @@ status_t OMXNodeInstance::setInternalOption( int64_t delayUs = *(int64_t *)data; return bufferSource->setRepeatPreviousFrameDelayUs(delayUs); - } else { + } else if (type == + IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP){ if (size != sizeof(int64_t)) { return INVALID_OPERATION; } @@ -887,6 +956,20 @@ status_t OMXNodeInstance::setInternalOption( int64_t maxGapUs = *(int64_t *)data; return bufferSource->setMaxTimestampGapUs(maxGapUs); + } else if (type == IOMX::INTERNAL_OPTION_START_TIME) { + if (size != sizeof(int64_t)) { + return INVALID_OPERATION; + } + + int64_t skipFramesBeforeUs = *(int64_t *)data; + + bufferSource->setSkipFramesBeforeUs(skipFramesBeforeUs); + } else { // IOMX::INTERNAL_OPTION_TIME_LAPSE + if (size != sizeof(int64_t) * 2) { + return INVALID_OPERATION; + } + + bufferSource->setTimeLapseUs((int64_t *)data); } return OK; diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index d49e50b..9b6958a 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -42,6 +42,7 @@ static const struct { { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" }, { "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" }, { "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" }, + { "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" }, { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" }, { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" }, { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" }, @@ -50,6 +51,7 @@ static const struct { { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" }, { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" }, { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" }, + { "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" }, { "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" }, { "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" }, { "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" }, diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp index 20a7076..2f83610 100644 --- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp @@ -22,6 +22,7 @@ #include "include/SoftVideoDecoderOMXComponent.h" +#include <media/hardware/HardwareAPI.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> @@ -50,6 +51,9 @@ SoftVideoDecoderOMXComponent::SoftVideoDecoderOMXComponent( OMX_PTR appData, OMX_COMPONENTTYPE **component) : SimpleSoftOMXComponent(name, callbacks, appData, component), + mIsAdaptive(false), + mAdaptiveMaxWidth(0), + mAdaptiveMaxHeight(0), mWidth(width), mHeight(height), mCropLeft(0), @@ -119,16 +123,18 @@ void SoftVideoDecoderOMXComponent::initPorts( updatePortDefinitions(); } -void SoftVideoDecoderOMXComponent::updatePortDefinitions() { +void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop) { OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; def->format.video.nFrameWidth = mWidth; def->format.video.nFrameHeight = mHeight; def->format.video.nStride = def->format.video.nFrameWidth; def->format.video.nSliceHeight = def->format.video.nFrameHeight; + def->nBufferSize = def->format.video.nFrameWidth * def->format.video.nFrameHeight * 3 / 2; + def = &editPortInfo(kOutputPortIndex)->mDef; - def->format.video.nFrameWidth = mWidth; - def->format.video.nFrameHeight = mHeight; + def->format.video.nFrameWidth = outputBufferWidth(); + def->format.video.nFrameHeight = outputBufferHeight(); def->format.video.nStride = def->format.video.nFrameWidth; def->format.video.nSliceHeight = def->format.video.nFrameHeight; @@ -136,10 +142,105 @@ void SoftVideoDecoderOMXComponent::updatePortDefinitions() { (def->format.video.nFrameWidth * def->format.video.nFrameHeight * 3) / 2; - mCropLeft = 0; - mCropTop = 0; - mCropWidth = mWidth; - mCropHeight = mHeight; + if (updateCrop) { + mCropLeft = 0; + mCropTop = 0; + mCropWidth = mWidth; + mCropHeight = mHeight; + } +} + + +uint32_t SoftVideoDecoderOMXComponent::outputBufferWidth() { + return mIsAdaptive ? mAdaptiveMaxWidth : mWidth; +} + +uint32_t SoftVideoDecoderOMXComponent::outputBufferHeight() { + return mIsAdaptive ? mAdaptiveMaxHeight : mHeight; +} + +void SoftVideoDecoderOMXComponent::handlePortSettingsChange( + bool *portWillReset, uint32_t width, uint32_t height, + CropSettingsMode cropSettingsMode, bool fakeStride) { + *portWillReset = false; + bool sizeChanged = (width != mWidth || height != mHeight); + bool updateCrop = (cropSettingsMode == kCropUnSet); + bool cropChanged = (cropSettingsMode == kCropChanged); + bool strideChanged = false; + if (fakeStride) { + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; + if (def->format.video.nStride != width || def->format.video.nSliceHeight != height) { + strideChanged = true; + } + } + + if (sizeChanged || cropChanged || strideChanged) { + mWidth = width; + mHeight = height; + + if ((sizeChanged && !mIsAdaptive) + || width > mAdaptiveMaxWidth + || height > mAdaptiveMaxHeight) { + if (mIsAdaptive) { + if (width > mAdaptiveMaxWidth) { + mAdaptiveMaxWidth = width; + } + if (height > mAdaptiveMaxHeight) { + mAdaptiveMaxHeight = height; + } + } + updatePortDefinitions(updateCrop); + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + *portWillReset = true; + } else { + updatePortDefinitions(updateCrop); + + if (fakeStride) { + // MAJOR HACK that is not pretty, it's just to fool the renderer to read the correct + // data. + // Some software decoders (e.g. SoftMPEG4) fill decoded frame directly to output + // buffer without considering the output buffer stride and slice height. So this is + // used to signal how the buffer is arranged. The alternative is to re-arrange the + // output buffer in SoftMPEG4, but that results in memcopies. + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; + def->format.video.nStride = mWidth; + def->format.video.nSliceHeight = mHeight; + } + + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, + OMX_IndexConfigCommonOutputCrop, NULL); + } + } +} + +void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer( + uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, + size_t srcYStride, size_t srcUStride, size_t srcVStride) { + size_t dstYStride = outputBufferWidth(); + size_t dstUVStride = dstYStride / 2; + size_t dstHeight = outputBufferHeight(); + uint8_t *dstStart = dst; + + for (size_t i = 0; i < mHeight; ++i) { + memcpy(dst, srcY, mWidth); + srcY += srcYStride; + dst += dstYStride; + } + + dst = dstStart + dstYStride * dstHeight; + for (size_t i = 0; i < mHeight / 2; ++i) { + memcpy(dst, srcU, mWidth / 2); + srcU += srcUStride; + dst += dstUVStride; + } + + dst = dstStart + (5 * dstYStride * dstHeight) / 4; + for (size_t i = 0; i < mHeight / 2; ++i) { + memcpy(dst, srcV, mWidth / 2); + srcV += srcVStride; + dst += dstUVStride; + } } OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( @@ -177,19 +278,18 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( { OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; - OMX_U32 profileIndex = profileLevel->nProfileIndex; if (profileLevel->nPortIndex != kInputPortIndex) { ALOGE("Invalid port index: %" PRIu32, profileLevel->nPortIndex); return OMX_ErrorUnsupportedIndex; } - if (profileIndex >= mNumProfileLevels) { + if (profileLevel->nProfileIndex >= mNumProfileLevels) { return OMX_ErrorNoMore; } - profileLevel->eProfile = mProfileLevels[profileIndex].mProfile; - profileLevel->eLevel = mProfileLevels[profileIndex].mLevel; + profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile; + profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel; return OMX_ErrorNone; } @@ -200,7 +300,10 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter( OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( OMX_INDEXTYPE index, const OMX_PTR params) { - switch (index) { + // Include extension index OMX_INDEXEXTTYPE. + const int32_t indexFull = index; + + switch (indexFull) { case OMX_IndexParamStandardComponentRole: { const OMX_PARAM_COMPONENTROLETYPE *roleParams = @@ -231,6 +334,58 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetParameter( return OMX_ErrorNone; } + case kPrepareForAdaptivePlaybackIndex: + { + const PrepareForAdaptivePlaybackParams* adaptivePlaybackParams = + (const PrepareForAdaptivePlaybackParams *)params; + mIsAdaptive = adaptivePlaybackParams->bEnable; + if (mIsAdaptive) { + mAdaptiveMaxWidth = adaptivePlaybackParams->nMaxFrameWidth; + mAdaptiveMaxHeight = adaptivePlaybackParams->nMaxFrameHeight; + mWidth = mAdaptiveMaxWidth; + mHeight = mAdaptiveMaxHeight; + } else { + mAdaptiveMaxWidth = 0; + mAdaptiveMaxHeight = 0; + } + updatePortDefinitions(); + return OMX_ErrorNone; + } + + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *newParams = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &newParams->format.video; + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(newParams->nPortIndex)->mDef; + + uint32_t oldWidth = def->format.video.nFrameWidth; + uint32_t oldHeight = def->format.video.nFrameHeight; + uint32_t newWidth = video_def->nFrameWidth; + uint32_t newHeight = video_def->nFrameHeight; + if (newWidth != oldWidth || newHeight != oldHeight) { + bool outputPort = (newParams->nPortIndex == kOutputPortIndex); + def->format.video.nFrameWidth = + (mIsAdaptive && outputPort) ? mAdaptiveMaxWidth : newWidth; + def->format.video.nFrameHeight = + (mIsAdaptive && outputPort) ? mAdaptiveMaxHeight : newHeight; + def->format.video.nStride = def->format.video.nFrameWidth; + def->format.video.nSliceHeight = def->format.video.nFrameHeight; + def->nBufferSize = + def->format.video.nFrameWidth * def->format.video.nFrameHeight * 3 / 2; + if (outputPort) { + mWidth = newWidth; + mHeight = newHeight; + mCropLeft = 0; + mCropTop = 0; + mCropWidth = newWidth; + mCropHeight = newHeight; + } + newParams->nBufferSize = def->nBufferSize; + } + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } + default: return SimpleSoftOMXComponent::internalSetParameter(index, params); } @@ -260,6 +415,16 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig( } } +OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex( + const char *name, OMX_INDEXTYPE *index) { + if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) { + *(int32_t*)index = kPrepareForAdaptivePlaybackIndex; + return OMX_ErrorNone; + } + + return SimpleSoftOMXComponent::getExtensionIndex(name, index); +} + void SoftVideoDecoderOMXComponent::onReset() { mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp new file mode 100644 index 0000000..70ec6e4 --- /dev/null +++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp @@ -0,0 +1,311 @@ +/* + * Copyright 2014 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. + */ + +#include <inttypes.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftVideoEncoderOMXComponent" +#include <utils/Log.h> + +#include "include/SoftVideoEncoderOMXComponent.h" + +#include <hardware/gralloc.h> +#include <media/hardware/HardwareAPI.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaDefs.h> + +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferMapper.h> + +namespace android { + +SoftVideoEncoderOMXComponent::SoftVideoEncoderOMXComponent( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mGrallocModule(NULL) { +} + +// static +void SoftVideoEncoderOMXComponent::ConvertFlexYUVToPlanar( + uint8_t *dst, size_t dstStride, size_t dstVStride, + struct android_ycbcr *ycbcr, int32_t width, int32_t height) { + const uint8_t *src = (const uint8_t *)ycbcr->y; + const uint8_t *srcU = (const uint8_t *)ycbcr->cb; + const uint8_t *srcV = (const uint8_t *)ycbcr->cr; + uint8_t *dstU = dst + dstVStride * dstStride; + uint8_t *dstV = dstU + (dstVStride >> 1) * (dstStride >> 1); + + for (size_t y = height; y > 0; --y) { + memcpy(dst, src, width); + dst += dstStride; + src += ycbcr->ystride; + } + if (ycbcr->cstride == ycbcr->ystride >> 1 && ycbcr->chroma_step == 1) { + // planar + for (size_t y = height >> 1; y > 0; --y) { + memcpy(dstU, srcU, width >> 1); + dstU += dstStride >> 1; + srcU += ycbcr->cstride; + memcpy(dstV, srcV, width >> 1); + dstV += dstStride >> 1; + srcV += ycbcr->cstride; + } + } else { + // arbitrary + for (size_t y = height >> 1; y > 0; --y) { + for (size_t x = width >> 1; x > 0; --x) { + *dstU++ = *srcU; + *dstV++ = *srcV; + srcU += ycbcr->chroma_step; + srcV += ycbcr->chroma_step; + } + dstU += (dstStride >> 1) - (width >> 1); + dstV += (dstStride >> 1) - (width >> 1); + srcU += ycbcr->cstride - (width >> 1) * ycbcr->chroma_step; + srcV += ycbcr->cstride - (width >> 1) * ycbcr->chroma_step; + } + } +} + +// static +void SoftVideoEncoderOMXComponent::ConvertYUV420SemiPlanarToYUV420Planar( + const uint8_t *inYVU, uint8_t* outYUV, int32_t width, int32_t height) { + // TODO: add support for stride + int32_t outYsize = width * height; + uint32_t *outY = (uint32_t *) outYUV; + uint16_t *outCb = (uint16_t *) (outYUV + outYsize); + uint16_t *outCr = (uint16_t *) (outYUV + outYsize + (outYsize >> 2)); + + /* Y copying */ + memcpy(outY, inYVU, outYsize); + + /* U & V copying */ + // FIXME this only works if width is multiple of 4 + uint32_t *inYVU_4 = (uint32_t *) (inYVU + outYsize); + for (int32_t i = height >> 1; i > 0; --i) { + for (int32_t j = width >> 2; j > 0; --j) { + uint32_t temp = *inYVU_4++; + uint32_t tempU = temp & 0xFF; + tempU = tempU | ((temp >> 8) & 0xFF00); + + uint32_t tempV = (temp >> 8) & 0xFF; + tempV = tempV | ((temp >> 16) & 0xFF00); + + *outCb++ = tempU; + *outCr++ = tempV; + } + } +} + +// static +void SoftVideoEncoderOMXComponent::ConvertRGB32ToPlanar( + uint8_t *dstY, size_t dstStride, size_t dstVStride, + const uint8_t *src, size_t width, size_t height, size_t srcStride, + bool bgr) { + CHECK((width & 1) == 0); + CHECK((height & 1) == 0); + + uint8_t *dstU = dstY + dstStride * dstVStride; + uint8_t *dstV = dstU + (dstStride >> 1) * (dstVStride >> 1); + +#ifdef SURFACE_IS_BGR32 + bgr = !bgr; +#endif + + const size_t redOffset = bgr ? 2 : 0; + const size_t greenOffset = 1; + const size_t blueOffset = bgr ? 0 : 2; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + unsigned red = src[redOffset]; + unsigned green = src[greenOffset]; + unsigned blue = src[blueOffset]; + + // using ITU-R BT.601 conversion matrix + unsigned luma = + ((red * 66 + green * 129 + blue * 25) >> 8) + 16; + + dstY[x] = luma; + + if ((x & 1) == 0 && (y & 1) == 0) { + unsigned U = + ((-red * 38 - green * 74 + blue * 112) >> 8) + 128; + + unsigned V = + ((red * 112 - green * 94 - blue * 18) >> 8) + 128; + + dstU[x >> 1] = U; + dstV[x >> 1] = V; + } + src += 4; + } + + if ((y & 1) == 0) { + dstU += dstStride >> 1; + dstV += dstStride >> 1; + } + + src += srcStride - 4 * width; + dstY += dstStride; + } +} + +const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( + uint8_t *dst, size_t dstSize, + const uint8_t *src, size_t srcSize, + size_t width, size_t height) const { + size_t dstStride = width; + size_t dstVStride = height; + + MetadataBufferType bufferType = *(MetadataBufferType *)src; + bool usingGraphicBuffer = bufferType == kMetadataBufferTypeGraphicBuffer; + if (!usingGraphicBuffer && bufferType != kMetadataBufferTypeGrallocSource) { + ALOGE("Unsupported metadata type (%d)", bufferType); + return NULL; + } + + if (mGrallocModule == NULL) { + CHECK_EQ(0, hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &mGrallocModule)); + } + + const gralloc_module_t *grmodule = + (const gralloc_module_t *)mGrallocModule; + + buffer_handle_t handle; + int format; + size_t srcStride; + size_t srcVStride; + if (usingGraphicBuffer) { + if (srcSize < sizeof(OMX_U32) + sizeof(GraphicBuffer *)) { + ALOGE("Metadata is too small (%zu vs %zu)", srcSize, sizeof(OMX_U32) + sizeof(GraphicBuffer *)); + return NULL; + } + + GraphicBuffer *buffer = *(GraphicBuffer **)(src + sizeof(OMX_U32)); + handle = buffer->handle; + format = buffer->format; + srcStride = buffer->stride; + srcVStride = buffer->height; + // convert stride from pixels to bytes + if (format != HAL_PIXEL_FORMAT_YV12 && + format != HAL_PIXEL_FORMAT_YCbCr_420_888) { + // TODO do we need to support other formats? + srcStride *= 4; + } + } else { + // TODO: remove this part. Check if anyone uses this. + + if (srcSize < sizeof(OMX_U32) + sizeof(buffer_handle_t)) { + ALOGE("Metadata is too small (%zu vs %zu)", srcSize, sizeof(OMX_U32) + sizeof(buffer_handle_t)); + return NULL; + } + + handle = *(buffer_handle_t *)(src + sizeof(OMX_U32)); + // assume HAL_PIXEL_FORMAT_RGBA_8888 + // there is no way to get the src stride without the graphic buffer + format = HAL_PIXEL_FORMAT_RGBA_8888; + srcStride = width * 4; + srcVStride = height; + } + + size_t neededSize = + dstStride * dstVStride + (width >> 1) + + (dstStride >> 1) * ((dstVStride >> 1) + (height >> 1) - 1); + if (dstSize < neededSize) { + ALOGE("destination buffer is too small (%zu vs %zu)", dstSize, neededSize); + return NULL; + } + + void *bits = NULL; + struct android_ycbcr ycbcr; + status_t res; + if (format == HAL_PIXEL_FORMAT_YCbCr_420_888) { + res = grmodule->lock_ycbcr( + grmodule, handle, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, + 0, 0, width, height, &ycbcr); + } else { + res = grmodule->lock( + grmodule, handle, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, + 0, 0, width, height, &bits); + } + if (res != OK) { + ALOGE("Unable to lock image buffer %p for access", handle); + return NULL; + } + + switch (format) { + case HAL_PIXEL_FORMAT_YV12: // YCrCb / YVU planar + // convert to flex YUV + ycbcr.y = bits; + ycbcr.cr = (uint8_t *)bits + srcStride * srcVStride; + ycbcr.cb = (uint8_t *)ycbcr.cr + (srcStride >> 1) * (srcVStride >> 1); + ycbcr.chroma_step = 1; + ycbcr.cstride = srcVStride >> 1; + ycbcr.ystride = srcVStride; + ConvertFlexYUVToPlanar(dst, dstStride, dstVStride, &ycbcr, width, height); + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: // YCrCb / YVU semiplanar, NV21 + // convert to flex YUV + ycbcr.y = bits; + ycbcr.cr = (uint8_t *)bits + srcStride * srcVStride; + ycbcr.cb = (uint8_t *)ycbcr.cr + 1; + ycbcr.chroma_step = 2; + ycbcr.cstride = srcVStride; + ycbcr.ystride = srcVStride; + ConvertFlexYUVToPlanar(dst, dstStride, dstVStride, &ycbcr, width, height); + break; + case HAL_PIXEL_FORMAT_YCbCr_420_888: + ConvertFlexYUVToPlanar(dst, dstStride, dstVStride, &ycbcr, width, height); + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + ConvertRGB32ToPlanar( + dst, dstStride, dstVStride, + (const uint8_t *)bits, width, height, srcStride, + format == HAL_PIXEL_FORMAT_BGRA_8888); + break; + default: + ALOGE("Unsupported pixel format %#x", format); + dst = NULL; + break; + } + + if (grmodule->unlock(grmodule, handle) != OK) { + ALOGE("Unable to unlock image buffer %p for access", handle); + } + + return dst; +} + +OMX_ERRORTYPE SoftVideoEncoderOMXComponent::getExtensionIndex( + const char *name, OMX_INDEXTYPE *index) { + if (!strcmp(name, "OMX.google.android.index.storeMetaDataInBuffers") || + !strcmp(name, "OMX.google.android.index.storeGraphicBufferInMetaData")) { + *(int32_t*)index = kStoreMetaDataExtensionIndex; + return OMX_ErrorNone; + } + return SimpleSoftOMXComponent::getExtensionIndex(name, index); +} + +} // namespace android diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk index e368134..447b29e 100644 --- a/media/libstagefright/omx/tests/Android.mk +++ b/media/libstagefright/omx/tests/Android.mk @@ -11,6 +11,8 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax +LOCAL_CFLAGS += -Werror + LOCAL_MODULE := omx_tests LOCAL_MODULE_TAGS := tests diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index 44e4f9d..f4dfd6b 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -26,6 +26,7 @@ #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <binder/MemoryDealer.h> +#include <media/IMediaHTTPService.h> #include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> @@ -242,7 +243,8 @@ private: }; static sp<MediaExtractor> CreateExtractorFromURI(const char *uri) { - sp<DataSource> source = DataSource::CreateFromURI(uri); + sp<DataSource> source = + DataSource::CreateFromURI(NULL /* httpService */, uri); if (source == NULL) { return NULL; @@ -461,6 +463,7 @@ static const char *GetMimeFromComponentRole(const char *componentRole) { { "audio_decoder.aac", "audio/mp4a-latm" }, { "audio_decoder.mp3", "audio/mpeg" }, { "audio_decoder.vorbis", "audio/vorbis" }, + { "audio_decoder.opus", "audio/opus" }, { "audio_decoder.g711alaw", MEDIA_MIMETYPE_AUDIO_G711_ALAW }, { "audio_decoder.g711mlaw", MEDIA_MIMETYPE_AUDIO_G711_MLAW }, }; @@ -493,6 +496,7 @@ static const char *GetURLForMime(const char *mime) { { "audio/mpeg", "file:///sdcard/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3" }, { "audio/vorbis", NULL }, + { "audio/opus", NULL }, { "video/x-vnd.on2.vp8", "file:///sdcard/media_api/video/big-buck-bunny_trailer.webm" }, { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "file:///sdcard/M1F1-Alaw-AFsp.wav" }, |