diff options
author | Andreas Huber <andih@google.com> | 2009-07-28 10:03:13 -0700 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2009-07-28 11:28:14 -0700 |
commit | 0024245e134467d120b40099da16c467dc365e76 (patch) | |
tree | b4023ffb3f2167dc3303cd048031ad7278643fd9 /media | |
parent | 9a92037bd6477533062d635b676a6c9833aab96e (diff) | |
download | frameworks_av-0024245e134467d120b40099da16c467dc365e76.zip frameworks_av-0024245e134467d120b40099da16c467dc365e76.tar.gz frameworks_av-0024245e134467d120b40099da16c467dc365e76.tar.bz2 |
Squashed commit of the following:
commit 374ea382ee3a9e3ce17e4c6357fc40d02e362810
Author: Andreas Huber <andih@google.com>
Date: Tue Jul 28 09:54:13 2009 -0700
PV's OMX implementation now uses (spec-compliant) microseconds instead of milliseconds in buffer timestamps.
commit 8d02f8ab5d7b022ad4ad34db2a9bdeea6ce2acfe
Author: Andreas Huber <andih@google.com>
Date: Mon Jul 27 14:24:26 2009 -0700
Support for using an overlay for video playback on TI hardware.
Appears to be currently constrained to CbYCrY 16-bit colorspace.
commit d17f321cb4b15c1fea378f33a7ef5998f23dd0fc
Author: Andreas Huber <andih@google.com>
Date: Mon Jul 27 09:45:38 2009 -0700
Added '--audio-only' commandline option to stagefright tool.
commit d8beef6be5c668c46451446d87e622933371cd75
Author: Andreas Huber <andih@google.com>
Date: Fri Jul 24 13:35:00 2009 -0700
Generalize the various workarounds for OMX nodes with their own unique interpretation of the spec.
commit c7dfd53eeadf8ed5a39bf2b19b014dcd62f3324d
Author: Andreas Huber <andih@google.com>
Date: Thu Jul 23 16:06:36 2009 -0700
Fixed erroneous TI AAC decoder behaviour wrt shutdown.
The AAC decoder appears to not return out buffers on an Executing->Idle transition, implemented a workaround that does a flush on all ports followed by the Executing->Idle. Oh, and flush with OMX_ALL doesn't properly work either. Fun.
commit d6377282e75060881336578f166f9c7feacf3f8f
Author: Andreas Huber <andih@google.com>
Date: Thu Jul 23 14:06:50 2009 -0700
Apparently the "channels" parameter in AudioTrack's constructor no longer counts channels but is a bitmask of enabled destinations, update the code accordingly.
commit ff698c79e851a2e57d362e9c3a09828af4048087
Author: Andreas Huber <andih@google.com>
Date: Wed Jul 22 16:54:44 2009 -0700
Fix MPEG4 decoding using TI's hardware decoder that I broke earlier...
commit 2ef78bb87cd856eb7f0b3d7dd68782a8650c12bf
Author: Andreas Huber <andih@google.com>
Date: Wed Jul 22 15:43:18 2009 -0700
Now that the qcom decoder properly advertises its own custom colorspace, update dependent code to reflect this fact.
commit bbaec96910727080fd7c8a6907c04facb9f5220f
Author: Andreas Huber <andih@google.com>
Date: Wed Jul 22 14:32:03 2009 -0700
Finishing up previous, incomplete change.
commit 76f14a1ae816b6f434771f8d12bdad81196f351e
Author: Andreas Huber <andih@google.com>
Date: Wed Jul 22 14:25:17 2009 -0700
The TI video decoder now properly decoder AVC/H.264 content.
commit e106130d8c100d5c94603e43864a7a93cca10252
Author: Andreas Huber <andih@google.com>
Date: Wed Jul 22 08:56:04 2009 -0700
Experimental support for the TI H.264 decoder, various modifications to OMXDecoder, non-functional still.
commit 241c3062dec3447db1a1ee74558cb4b9098fc404
Author: Andreas Huber <andih@google.com>
Date: Tue Jul 21 12:13:09 2009 -0700
Enable TI hardware OMX decoders (except for AVC/H.264 which still has issues).
This particular set of OMX nodes does not appear to properly return our buffers when sending the "disable" command on a port. Rather it reqires manually flushing that port and _then_ disabling it instead.
commit 1c34506a46e32ce25f2a86f3b4250dcfc037356a
Author: Andreas Huber <andih@google.com>
Date: Tue Jul 21 08:51:35 2009 -0700
Make it simpler to switch between the stagefright player and PVPlayer.
commit 249c6de05671d403f8dd51f095d49bf190430c9c
Author: Andreas Huber <andih@google.com>
Date: Mon Jul 20 14:38:15 2009 -0700
Prepare to use soon-to-be-available hardware decoders in the OMX decoder.
Diffstat (limited to 'media')
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/Android.mk | 1 | ||||
-rw-r--r-- | media/libstagefright/AudioPlayer.cpp | 9 | ||||
-rw-r--r-- | media/libstagefright/MP3Extractor.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 121 | ||||
-rw-r--r-- | media/libstagefright/MediaPlayerImpl.cpp | 21 | ||||
-rw-r--r-- | media/libstagefright/OMXDecoder.cpp | 573 | ||||
-rw-r--r-- | media/libstagefright/TIHardwareRenderer.cpp | 99 |
8 files changed, 671 insertions, 157 deletions
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 1d960c5..95d61cd 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -59,6 +59,8 @@ #include <media/PVPlayer.h> #include "TestPlayerStub.h" +//#undef USE_STAGEFRIGHT + #if USE_STAGEFRIGHT #include "StagefrightPlayer.h" #endif diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 5944d9c..5be9224 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -26,6 +26,7 @@ LOCAL_SRC_FILES:= \ SurfaceRenderer.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ + TIHardwareRenderer.cpp \ Utils.cpp \ AudioPlayer.cpp \ ESDS.cpp \ diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 17c72b9..d547556 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -87,7 +87,10 @@ void AudioPlayer::start() { } else { mAudioTrack = new AudioTrack( AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT, - numChannels, 8192, 0, &AudioCallback, this, 0); + (numChannels == 2) + ? AudioSystem::CHANNEL_OUT_STEREO + : AudioSystem::CHANNEL_OUT_MONO, + 8192, 0, &AudioCallback, this, 0); assert(mAudioTrack->initCheck() == OK); @@ -217,8 +220,10 @@ void AudioPlayer::fillBuffer(void *data, size_t size) { Mutex::Autolock autoLock(mLock); mPositionTimeMediaUs = (int64_t)units * 1000000 / scale; + mPositionTimeRealUs = - ((mNumFramesPlayed + size_done / 4) * 1000000) / mSampleRate; // XXX + ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) + / mSampleRate; } if (mInputBuffer->range_length() == 0) { diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 6b47a38..01cb2d9 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -73,8 +73,6 @@ static bool get_mp3_frame_size( if (bitrate_index == 0 || bitrate_index == 0x0f) { // Disallow "free" bitrate. - - LOGE("We disallow 'free' bitrate for now."); return false; } diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index caaec06..4c883c6 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -73,6 +73,8 @@ private: bool mNeedsNALFraming; + uint8_t *mSrcBuffer; + MPEG4Source(const MPEG4Source &); MPEG4Source &operator=(const MPEG4Source &); }; @@ -743,7 +745,8 @@ MPEG4Source::MPEG4Source( mBuffer(NULL), mBufferOffset(0), mBufferSizeRemaining(0), - mNeedsNALFraming(false) { + mNeedsNALFraming(false), + mSrcBuffer(NULL) { const char *mime; bool success = mFormat->findCString(kKeyMIMEType, &mime); assert(success); @@ -777,8 +780,13 @@ status_t MPEG4Source::start(MetaData *params) { status_t err = mSampleTable->getMaxSampleSize(&max_size); assert(err == OK); - // Add padding for de-framing of AVC content just in case. - mGroup->add_buffer(new MediaBuffer(max_size + 2)); + // Assume that a given buffer only contains at most 10 fragments, + // each fragment originally prefixed with a 2 byte length will + // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion, + // and thus will grow by 2 bytes per fragment. + mGroup->add_buffer(new MediaBuffer(max_size + 10 * 2)); + + mSrcBuffer = new uint8_t[max_size]; mStarted = true; @@ -793,6 +801,9 @@ status_t MPEG4Source::stop() { mBuffer = NULL; } + delete[] mSrcBuffer; + mSrcBuffer = NULL; + delete mGroup; mGroup = NULL; @@ -832,33 +843,31 @@ status_t MPEG4Source::read( // fall through } - if (mBuffer == NULL) { - off_t offset; - size_t size; - status_t err = mSampleTable->getSampleOffsetAndSize( - mCurrentSampleIndex, &offset, &size); + off_t offset; + size_t size; + status_t err = mSampleTable->getSampleOffsetAndSize( + mCurrentSampleIndex, &offset, &size); - if (err != OK) { - return err; - } + if (err != OK) { + return err; + } - uint32_t dts; - err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts); + uint32_t dts; + err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts); - if (err != OK) { - return err; - } - - err = mGroup->acquire_buffer(&mBuffer); - if (err != OK) { - assert(mBuffer == NULL); - return err; - } + if (err != OK) { + return err; + } - assert(mBuffer->size() + 2 >= size); + err = mGroup->acquire_buffer(&mBuffer); + if (err != OK) { + assert(mBuffer == NULL); + return err; + } + if (!mIsAVC || !mNeedsNALFraming) { ssize_t num_bytes_read = - mDataSource->read_at(offset, (uint8_t *)mBuffer->data() + 2, size); + mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size); if (num_bytes_read < (ssize_t)size) { mBuffer->release(); @@ -867,50 +876,62 @@ status_t MPEG4Source::read( return err; } - mBuffer->set_range(2, size); + mBuffer->set_range(0, size); mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts); mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale); - ++mCurrentSampleIndex; - mBufferOffset = 2; - mBufferSizeRemaining = size; - } - - if (!mIsAVC) { *out = mBuffer; mBuffer = NULL; return OK; } - uint8_t *data = (uint8_t *)mBuffer->data() + mBufferOffset; - assert(mBufferSizeRemaining >= 2); + ssize_t num_bytes_read = + mDataSource->read_at(offset, mSrcBuffer, size); - size_t nal_length = (data[0] << 8) | data[1]; - assert(mBufferSizeRemaining >= 2 + nal_length); - - if (mNeedsNALFraming) { - // Insert marker. - data[-2] = data[-1] = data[0] = 0; - data[1] = 1; + if (num_bytes_read < (ssize_t)size) { + mBuffer->release(); + mBuffer = NULL; - mBuffer->set_range(mBufferOffset - 2, nal_length + 4); - } else { - mBuffer->set_range(mBufferOffset + 2, nal_length); + return err; } - mBufferOffset += nal_length + 2; - mBufferSizeRemaining -= nal_length + 2; + uint8_t *dstData = (uint8_t *)mBuffer->data(); + size_t srcOffset = 0; + size_t dstOffset = 0; + while (srcOffset < size) { + assert(srcOffset + 1 < size); + size_t nalLength = + (mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1]; + assert(srcOffset + 1 + nalLength < size); + srcOffset += 2; + + if (nalLength == 0) { + continue; + } - if (mBufferSizeRemaining > 0) { - *out = mBuffer->clone(); - } else { - *out = mBuffer; - mBuffer = NULL; + assert(dstOffset + 4 <= mBuffer->size()); + + dstData[dstOffset++] = 0; + dstData[dstOffset++] = 0; + dstData[dstOffset++] = 0; + dstData[dstOffset++] = 1; + memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength); + srcOffset += nalLength; + dstOffset += nalLength; } + mBuffer->set_range(0, dstOffset); + mBuffer->meta_data()->clear(); + mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts); + mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale); + ++mCurrentSampleIndex; + + *out = mBuffer; + mBuffer = NULL; + return OK; } diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp index 78fcdee..04c9a11 100644 --- a/media/libstagefright/MediaPlayerImpl.cpp +++ b/media/libstagefright/MediaPlayerImpl.cpp @@ -40,6 +40,7 @@ #include <media/stagefright/SoftwareRenderer.h> #include <media/stagefright/SurfaceRenderer.h> #include <media/stagefright/TimeSource.h> +#include <media/stagefright/TIHardwareRenderer.h> #include <ui/PixelFormat.h> #include <ui/Surface.h> @@ -311,6 +312,9 @@ void MediaPlayerImpl::videoEntry() { { Mutex::Autolock autoLock(mLock); mVideoPosition = pts_us; + + LOGV("now_video = %.2f secs (%lld ms)", + pts_us / 1E6, (pts_us + 500) / 1000); } if (seeking && mAudioPlayer != NULL) { @@ -344,6 +348,7 @@ void MediaPlayerImpl::displayOrDiscardFrame( if (mAudioPlayer != NULL && mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) { mTimeSourceDeltaUs = realtime_us - mediatime_us; + LOGV("mTimeSourceDeltaUs = %.2f secs", mTimeSourceDeltaUs / 1E6); } int64_t now_us = mTimeSource->getRealTimeUs(); @@ -436,6 +441,7 @@ void MediaPlayerImpl::init() { } void MediaPlayerImpl::setAudioSource(MediaSource *source) { + LOGI("setAudioSource"); mAudioSource = source; sp<MetaData> meta = source->getFormat(); @@ -646,17 +652,28 @@ void MediaPlayerImpl::populateISurface() { success = success && meta->findInt32(kKeyHeight, &decodedHeight); assert(success); + static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; + if (mSurface.get() != NULL) { + LOGW("Using SurfaceRenderer."); mRenderer = new SurfaceRenderer( mSurface, mVideoWidth, mVideoHeight, decodedWidth, decodedHeight); - } else if (format == OMX_COLOR_FormatYUV420Planar - && !strncasecmp(component, "OMX.qcom.video.decoder.", 23)) { + } else if (format == OMX_QCOM_COLOR_FormatYVU420SemiPlanar + && !strncmp(component, "OMX.qcom.video.decoder.", 23)) { + LOGW("Using QComHardwareRenderer."); mRenderer = new QComHardwareRenderer( mISurface, mVideoWidth, mVideoHeight, decodedWidth, decodedHeight); + } else if (format == OMX_COLOR_FormatCbYCrY + && !strcmp(component, "OMX.TI.Video.Decoder")) { + LOGW("Using TIHardwareRenderer."); + mRenderer = + new TIHardwareRenderer( + mISurface, mVideoWidth, mVideoHeight, + decodedWidth, decodedHeight); } else { LOGW("Using software renderer."); mRenderer = new SoftwareRenderer( diff --git a/media/libstagefright/OMXDecoder.cpp b/media/libstagefright/OMXDecoder.cpp index c059a9d..1fc2ba0 100644 --- a/media/libstagefright/OMXDecoder.cpp +++ b/media/libstagefright/OMXDecoder.cpp @@ -20,6 +20,7 @@ #undef NDEBUG #include <assert.h> +#include <ctype.h> #include <OMX_Component.h> @@ -54,14 +55,20 @@ struct CodecInfo { }; static const CodecInfo kDecoderInfo[] = { + { "audio/mpeg", "OMX.TI.MP3.decode" }, { "audio/mpeg", "OMX.PV.mp3dec" }, + { "audio/3gpp", "OMX.TI.AMR.decode" }, { "audio/3gpp", "OMX.PV.amrdec" }, + { "audio/mp4a-latm", "OMX.TI.AAC.decode" }, { "audio/mp4a-latm", "OMX.PV.aacdec" }, { "video/mp4v-es", "OMX.qcom.video.decoder.mpeg4" }, + { "video/mp4v-es", "OMX.TI.Video.Decoder" }, { "video/mp4v-es", "OMX.PV.mpeg4dec" }, { "video/3gpp", "OMX.qcom.video.decoder.h263" }, + { "video/3gpp", "OMX.TI.Video.Decoder" }, { "video/3gpp", "OMX.PV.h263dec" }, { "video/avc", "OMX.qcom.video.decoder.avc" }, + { "video/avc", "OMX.TI.Video.Decoder" }, { "video/avc", "OMX.PV.avcdec" }, }; @@ -92,7 +99,9 @@ static const char *GetCodec(const CodecInfo *info, size_t numInfos, } // static -OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) { +OMXDecoder *OMXDecoder::Create( + OMXClient *client, const sp<MetaData> &meta, + bool createEncoder) { const char *mime; bool success = meta->findCString(kKeyMIMEType, &mime); assert(success); @@ -102,9 +111,15 @@ OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) { const char *codec = NULL; IOMX::node_id node = 0; for (int index = 0;; ++index) { - codec = GetCodec( - kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]), - mime, index); + if (createEncoder) { + codec = GetCodec( + kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]), + mime, index); + } else { + codec = GetCodec( + kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]), + mime, index); + } if (!codec) { return NULL; @@ -118,7 +133,29 @@ OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) { } } - OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec); + uint32_t quirks = 0; + if (!strcmp(codec, "OMX.PV.avcdec")) { + quirks |= kWantsRawNALFrames; + } + if (!strcmp(codec, "OMX.TI.AAC.decode") + || !strcmp(codec, "OMX.TI.MP3.decode")) { + quirks |= kDoesntReturnBuffersOnDisable; + } + if (!strcmp(codec, "OMX.TI.AAC.decode")) { + quirks |= kDoesntFlushOnExecutingToIdle; + quirks |= kDoesntProperlyFlushAllPortsAtOnce; + } + if (!strncmp(codec, "OMX.qcom.video.encoder.", 23)) { + quirks |= kRequiresAllocateBufferOnInputPorts; + } + if (!strncmp(codec, "OMX.qcom.video.decoder.", 23)) { + quirks |= kRequiresAllocateBufferOnOutputPorts; + } + if (!strncmp(codec, "OMX.qcom.video.", 15)) { + quirks |= kRequiresLoadedToIdleAfterAllocation; + } + + OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec, quirks); uint32_t type; const void *data; @@ -169,52 +206,22 @@ OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) { return decoder; } -// static -OMXDecoder *OMXDecoder::CreateEncoder( - OMXClient *client, const sp<MetaData> &meta) { - const char *mime; - bool success = meta->findCString(kKeyMIMEType, &mime); - assert(success); - - sp<IOMX> omx = client->interface(); - - const char *codec = NULL; - IOMX::node_id node = 0; - for (int index = 0;; ++index) { - codec = GetCodec( - kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]), - mime, index); - - if (!codec) { - return NULL; - } - - LOGI("Attempting to allocate OMX node '%s'", codec); - - status_t err = omx->allocate_node(codec, &node); - if (err == OK) { - break; - } - } - - OMXDecoder *encoder = new OMXDecoder(client, node, mime, codec); - - return encoder; -} - OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node, - const char *mime, const char *codec) + const char *mime, const char *codec, + uint32_t quirks) : mClient(client), mOMX(mClient->interface()), mNode(node), mComponentName(strdup(codec)), mIsMP3(!strcasecmp(mime, "audio/mpeg")), + mIsAVC(!strcasecmp(mime, "video/avc")), + mQuirks(quirks), mSource(NULL), mCodecSpecificDataIterator(mCodecSpecificData.begin()), mState(OMX_StateLoaded), mPortStatusMask(kPortStatusActive << 2 | kPortStatusActive), mShutdownInitiated(false), - mDealer(new MemoryDealer(3 * 1024 * 1024)), + mDealer(new MemoryDealer(5 * 1024 * 1024)), mSeeking(false), mStarted(false), mErrorCondition(OK), @@ -261,7 +268,7 @@ status_t OMXDecoder::start(MetaData *) { // mDealer->dump("Decoder Dealer"); sp<MetaData> params = new MetaData; - if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) { + if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) { params->setInt32(kKeyNeedsNALFraming, true); } @@ -297,7 +304,7 @@ status_t OMXDecoder::stop() { } int attempt = 1; - while (mState != OMX_StateLoaded && attempt < 10) { + while (mState != OMX_StateLoaded && attempt < 20) { usleep(100000); ++attempt; @@ -366,7 +373,11 @@ status_t OMXDecoder::read( mOutputBuffers.erase(mOutputBuffers.begin()); } - status_t err = mOMX->send_command(mNode, OMX_CommandFlush, -1); + // XXX One of TI's decoders appears to ignore a flush if it doesn't + // currently hold on to any buffers on the port in question and + // never sends the completion event... FIXME + + status_t err = mOMX->send_command(mNode, OMX_CommandFlush, OMX_ALL); assert(err == OK); // Once flushing is completed buffers will again be scheduled to be @@ -472,9 +483,121 @@ void OMXDecoder::setAACFormat() { assert(err == NO_ERROR); } -void OMXDecoder::setVideoOutputFormat(OMX_U32 width, OMX_U32 height) { +status_t OMXDecoder::setVideoPortFormatType( + OMX_U32 portIndex, + OMX_VIDEO_CODINGTYPE compressionFormat, + OMX_COLOR_FORMATTYPE colorFormat) { + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + format.nSize = sizeof(format); + format.nVersion.s.nVersionMajor = 1; + format.nVersion.s.nVersionMinor = 1; + format.nPortIndex = portIndex; + format.nIndex = 0; + bool found = false; + + OMX_U32 index = 0; + for (;;) { + format.nIndex = index; + status_t err = mOMX->get_parameter( + mNode, OMX_IndexParamVideoPortFormat, + &format, sizeof(format)); + + if (err != OK) { + return err; + } + + // The following assertion is violated by TI's video decoder. + // assert(format.nIndex == index); + + if (format.eCompressionFormat == compressionFormat + && format.eColorFormat == colorFormat) { + found = true; + break; + } + + ++index; + } + + if (!found) { + return UNKNOWN_ERROR; + } + + status_t err = mOMX->set_parameter( + mNode, OMX_IndexParamVideoPortFormat, + &format, sizeof(format)); + + return err; +} + +#if 1 +void OMXDecoder::setVideoOutputFormat( + const char *mime, OMX_U32 width, OMX_U32 height) { LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height); +#if 1 + // Enabling this code appears to be the right thing(tm), but,... + // the TI decoder then loses the ability to output YUV420 and only outputs + // YCbYCr (16bit) + if (!strcasecmp("video/avc", mime)) { + OMX_PARAM_COMPONENTROLETYPE role; + role.nSize = sizeof(role); + role.nVersion.s.nVersionMajor = 1; + role.nVersion.s.nVersionMinor = 1; + strncpy((char *)role.cRole, "video_decoder.avc", + OMX_MAX_STRINGNAME_SIZE - 1); + role.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; + + status_t err = mOMX->set_parameter( + mNode, OMX_IndexParamStandardComponentRole, + &role, sizeof(role)); + assert(err == OK); + } +#endif + + OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; + if (!strcasecmp("video/avc", mime)) { + compressionFormat = OMX_VIDEO_CodingAVC; + } else if (!strcasecmp("video/mp4v-es", mime)) { + compressionFormat = OMX_VIDEO_CodingMPEG4; + } else if (!strcasecmp("video/3gpp", mime)) { + compressionFormat = OMX_VIDEO_CodingH263; + } else { + assert(!"Should not be here. Not a supported video mime type."); + } + + setVideoPortFormatType( + kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused); + +#if 1 + { + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + format.nSize = sizeof(format); + format.nVersion.s.nVersionMajor = 1; + format.nVersion.s.nVersionMinor = 1; + format.nPortIndex = kPortIndexOutput; + format.nIndex = 0; + + status_t err = mOMX->get_parameter( + mNode, OMX_IndexParamVideoPortFormat, + &format, sizeof(format)); + assert(err == OK); + + assert(format.eCompressionFormat == OMX_VIDEO_CodingUnused); + + static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; + + assert(format.eColorFormat == OMX_COLOR_FormatYUV420Planar + || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar + || format.eColorFormat == OMX_COLOR_FormatCbYCrY + || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar); + + err = mOMX->set_parameter( + mNode, OMX_IndexParamVideoPortFormat, + &format, sizeof(format)); + assert(err == OK); + } +#endif + OMX_PARAM_PORTDEFINITIONTYPE def; OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; @@ -502,7 +625,7 @@ void OMXDecoder::setVideoOutputFormat(OMX_U32 width, OMX_U32 height) { video_def->nFrameWidth = width; video_def->nFrameHeight = height; - // video_def.eCompressionFormat = OMX_VIDEO_CodingAVC; + video_def->eColorFormat = OMX_COLOR_FormatUnused; err = mOMX->set_parameter( @@ -522,21 +645,189 @@ void OMXDecoder::setVideoOutputFormat(OMX_U32 width, OMX_U32 height) { assert(def.eDomain == OMX_PortDomainVideo); +#if 0 def.nBufferSize = (((width + 15) & -16) * ((height + 15) & -16) * 3) / 2; // YUV420 +#endif video_def->nFrameWidth = width; video_def->nFrameHeight = height; - video_def->nStride = width; - // video_def->nSliceHeight = height; - video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; -// video_def->eColorFormat = OMX_COLOR_FormatYUV420Planar; err = mOMX->set_parameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); assert(err == NO_ERROR); } +#else +static void hexdump(const void *_data, size_t size) { + char line[256]; + char tmp[16]; + + const uint8_t *data = (const uint8_t *)_data; + size_t offset = 0; + while (offset < size) { + sprintf(line, "0x%04x ", offset); + + size_t n = size - offset; + if (n > 16) { + n = 16; + } + + for (size_t i = 0; i < 16; ++i) { + if (i == 8) { + strcat(line, " "); + } + + if (offset + i < size) { + sprintf(tmp, "%02x ", data[offset + i]); + strcat(line, tmp); + } else { + strcat(line, " "); + } + } + + strcat(line, " "); + + for (size_t i = 0; i < n; ++i) { + if (isprint(data[offset + i])) { + sprintf(tmp, "%c", data[offset + i]); + strcat(line, tmp); + } else { + strcat(line, "."); + } + } + + LOGI(line); + + offset += 16; + } +} + +static void DumpPortDefinitionType(const void *_param) { + OMX_PARAM_PORTDEFINITIONTYPE *param = (OMX_PARAM_PORTDEFINITIONTYPE *)_param; + + LOGI("nPortIndex=%ld eDir=%s nBufferCountActual=%ld nBufferCountMin=%ld nBufferSize=%ld", param->nPortIndex, param->eDir == OMX_DirInput ? "input" : "output", + param->nBufferCountActual, param->nBufferCountMin, param->nBufferSize); + + if (param->eDomain == OMX_PortDomainVideo) { + OMX_VIDEO_PORTDEFINITIONTYPE *video = ¶m->format.video; + LOGI("nFrameWidth=%ld nFrameHeight=%ld nStride=%ld nSliceHeight=%ld nBitrate=%ld xFramerate=%ld eCompressionFormat=%d eColorFormat=%d", + video->nFrameWidth, video->nFrameHeight, video->nStride, video->nSliceHeight, video->nBitrate, video->xFramerate, video->eCompressionFormat, video->eColorFormat); + } else { + hexdump(param, param->nSize); + } +} + +void OMXDecoder::setVideoOutputFormat( + const char *mime, OMX_U32 width, OMX_U32 height) { + LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height); + +#if 0 + // Enabling this code appears to be the right thing(tm), but,... + // the decoder then loses the ability to output YUV420 and only outputs + // YCbYCr (16bit) + { + OMX_PARAM_COMPONENTROLETYPE role; + role.nSize = sizeof(role); + role.nVersion.s.nVersionMajor = 1; + role.nVersion.s.nVersionMinor = 1; + strncpy((char *)role.cRole, "video_decoder.avc", + OMX_MAX_STRINGNAME_SIZE - 1); + role.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; + + status_t err = mOMX->set_parameter( + mNode, OMX_IndexParamStandardComponentRole, + &role, sizeof(role)); + assert(err == OK); + } +#endif + + setVideoPortFormatType( + kPortIndexInput, OMX_VIDEO_CodingAVC, OMX_COLOR_FormatUnused); + +#if 1 + { + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + format.nSize = sizeof(format); + format.nVersion.s.nVersionMajor = 1; + format.nVersion.s.nVersionMinor = 1; + format.nPortIndex = kPortIndexOutput; + format.nIndex = 0; + + status_t err = mOMX->get_parameter( + mNode, OMX_IndexParamVideoPortFormat, + &format, sizeof(format)); + assert(err == OK); + + LOGI("XXX MyOMX_GetParameter OMX_IndexParamVideoPortFormat"); + hexdump(&format, format.nSize); + + assert(format.eCompressionFormat == OMX_VIDEO_CodingUnused); + assert(format.eColorFormat == OMX_COLOR_FormatYUV420Planar + || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar + || format.eColorFormat == OMX_COLOR_FormatCbYCrY); + + err = mOMX->set_parameter( + mNode, OMX_IndexParamVideoPortFormat, + &format, sizeof(format)); + assert(err == OK); + } +#endif + + OMX_PORT_PARAM_TYPE ptype; + ptype.nSize = sizeof(ptype); + ptype.nVersion.s.nVersionMajor = 1; + ptype.nVersion.s.nVersionMinor = 1; + + status_t err = mOMX->get_parameter( + mNode, OMX_IndexParamVideoInit, &ptype, sizeof(ptype)); + assert(err == OK); + + LOGI("XXX MyOMX_GetParameter OMX_IndexParamVideoInit"); + hexdump(&ptype, ptype.nSize); + + OMX_PARAM_PORTDEFINITIONTYPE def; + def.nSize = sizeof(def); + def.nVersion.s.nVersionMajor = 1; + def.nVersion.s.nVersionMinor = 1; + def.nPortIndex = kPortIndexInput; + + err = mOMX->get_parameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + assert(err == OK); + + LOGI("XXX MyOMX_GetParameter OMX_IndexParamPortDefinition"); + DumpPortDefinitionType(&def); + + OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; + video_def->nFrameWidth = width; + video_def->nFrameHeight = height; + + err = mOMX->set_parameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + assert(err == OK); + + //////////////////////////////////////////////////////////////////////////// + + def.nPortIndex = kPortIndexOutput; + + err = mOMX->get_parameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + assert(err == OK); + + LOGI("XXX MyOMX_GetParameter OMX_IndexParamPortDefinition"); + DumpPortDefinitionType(&def); + + video_def->nFrameWidth = width; + video_def->nFrameHeight = height; + + err = mOMX->set_parameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + assert(err == OK); +} + +#endif + void OMXDecoder::setup() { const sp<MetaData> &meta = mSource->getFormat(); @@ -554,7 +845,7 @@ void OMXDecoder::setup() { success = success && meta->findInt32(kKeyHeight, &height); assert(success); - setVideoOutputFormat(width, height); + setVideoOutputFormat(mime, width, height); } // dumpPortDefinition(0); @@ -644,10 +935,7 @@ void OMXDecoder::setup() { } void OMXDecoder::onStart() { - bool needs_qcom_hack = - !strncmp(mComponentName, "OMX.qcom.video.", 15); - - if (!needs_qcom_hack) { + if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) { status_t err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle); assert(err == NO_ERROR); @@ -656,7 +944,7 @@ void OMXDecoder::onStart() { allocateBuffers(kPortIndexInput); allocateBuffers(kPortIndexOutput); - if (needs_qcom_hack) { + if (mQuirks & kRequiresLoadedToIdleAfterAllocation) { // XXX this should happen before AllocateBuffers, but qcom's // h264 vdec disagrees. status_t err = @@ -691,13 +979,17 @@ void OMXDecoder::allocateBuffers(OMX_U32 port_index) { for (OMX_U32 i = 0; i < num_buffers; ++i) { sp<IMemory> mem = mDealer->allocate(buffer_size); + if (mem.get() == NULL) { + LOGE("[%s] allocating IMemory of size %ld FAILED.", + mComponentName, buffer_size); + } assert(mem.get() != NULL); IOMX::buffer_id buffer; status_t err; if (port_index == kPortIndexInput - && !strncmp(mComponentName, "OMX.qcom.video.encoder.", 23)) { + && (mQuirks & kRequiresAllocateBufferOnInputPorts)) { // qcom's H.263 encoder appears to want to allocate its own input // buffers. err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer); @@ -706,7 +998,7 @@ void OMXDecoder::allocateBuffers(OMX_U32 port_index) { mComponentName, err); } } else if (port_index == kPortIndexOutput - && !strncmp(mComponentName, "OMX.qcom.video.decoder.", 23)) { + && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) { #if 1 err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer); #else @@ -817,18 +1109,59 @@ void OMXDecoder::onEventCmdComplete(OMX_COMMANDTYPE type, OMX_U32 data) { case OMX_CommandFlush: { OMX_U32 port_index = data; LOGV("Port %ld flush complete.", port_index); - assert(getPortStatus(port_index) == kPortStatusFlushing); - setPortStatus(port_index, kPortStatusActive); - BufferList *buffers = &mBuffers.editItemAt(port_index); - while (!buffers->empty()) { - IOMX::buffer_id buffer = *buffers->begin(); - buffers->erase(buffers->begin()); - - if (port_index == kPortIndexInput) { - postEmptyBufferDone(buffer); - } else { - postInitialFillBuffer(buffer); + PortStatus status = getPortStatus(port_index); + + assert(status == kPortStatusFlushing + || status == kPortStatusFlushingToDisabled + || status == kPortStatusFlushingToShutdown); + + switch (status) { + case kPortStatusFlushing: + { + // This happens when we're flushing before a seek. + setPortStatus(port_index, kPortStatusActive); + BufferList *buffers = &mBuffers.editItemAt(port_index); + while (!buffers->empty()) { + IOMX::buffer_id buffer = *buffers->begin(); + buffers->erase(buffers->begin()); + + if (port_index == kPortIndexInput) { + postEmptyBufferDone(buffer); + } else { + postInitialFillBuffer(buffer); + } + } + break; + } + + case kPortStatusFlushingToDisabled: + { + // Port settings have changed and the (buggy) OMX component + // does not properly return buffers on disabling, we need to + // do a flush first and _then_ disable the port in question. + + setPortStatus(port_index, kPortStatusDisabled); + status_t err = mOMX->send_command( + mNode, OMX_CommandPortDisable, port_index); + assert(err == OK); + + freePortBuffers(port_index); + break; + } + + default: + { + assert(status == kPortStatusFlushingToShutdown); + + setPortStatus(port_index, kPortStatusShutdown); + if (getPortStatus(kPortIndexInput) == kPortStatusShutdown + && getPortStatus(kPortIndexOutput) == kPortStatusShutdown) { + status_t err = mOMX->send_command( + mNode, OMX_CommandStateSet, OMX_StateIdle); + assert(err == OK); + } + break; } } break; @@ -841,10 +1174,22 @@ void OMXDecoder::onEventCmdComplete(OMX_COMMANDTYPE type, OMX_U32 data) { void OMXDecoder::onEventPortSettingsChanged(OMX_U32 port_index) { assert(getPortStatus(port_index) == kPortStatusActive); - setPortStatus(port_index, kPortStatusDisabled); - status_t err = - mOMX->send_command(mNode, OMX_CommandPortDisable, port_index); + status_t err; + + if (mQuirks & kDoesntReturnBuffersOnDisable) { + // Decoder does not properly return our buffers when disabled... + // Need to flush port instead and _then_ disable. + + setPortStatus(port_index, kPortStatusFlushingToDisabled); + + err = mOMX->send_command(mNode, OMX_CommandFlush, port_index); + } else { + setPortStatus(port_index, kPortStatusDisabled); + + err = mOMX->send_command(mNode, OMX_CommandPortDisable, port_index); + } + assert(err == NO_ERROR); } @@ -894,19 +1239,8 @@ void OMXDecoder::onStateChanged(OMX_STATETYPE to) { mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateLoaded); assert(err == NO_ERROR); - BufferList *ibuffers = &mBuffers.editItemAt(kPortIndexInput); - for (BufferList::iterator it = ibuffers->begin(); - it != ibuffers->end(); ++it) { - freeInputBuffer(*it); - } - ibuffers->clear(); - - BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput); - for (BufferList::iterator it = obuffers->begin(); - it != obuffers->end(); ++it) { - freeOutputBuffer(*it); - } - obuffers->clear(); + freePortBuffers(kPortIndexInput); + freePortBuffers(kPortIndexOutput); } } @@ -925,26 +1259,41 @@ void OMXDecoder::initiateShutdown() { mShutdownInitiated = true; - status_t err = - mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle); - assert(err == NO_ERROR); + status_t err; + if (mQuirks & kDoesntFlushOnExecutingToIdle) { + if (mQuirks & kDoesntProperlyFlushAllPortsAtOnce) { + err = mOMX->send_command(mNode, OMX_CommandFlush, kPortIndexInput); + assert(err == OK); + + err = mOMX->send_command(mNode, OMX_CommandFlush, kPortIndexOutput); + } else { + err = mOMX->send_command(mNode, OMX_CommandFlush, OMX_ALL); + } - setPortStatus(kPortIndexInput, kPortStatusShutdown); - setPortStatus(kPortIndexOutput, kPortStatusShutdown); + setPortStatus(kPortIndexInput, kPortStatusFlushingToShutdown); + setPortStatus(kPortIndexOutput, kPortStatusFlushingToShutdown); + } else { + err = mClient->send_command( + mNode, OMX_CommandStateSet, OMX_StateIdle); + + setPortStatus(kPortIndexInput, kPortStatusShutdown); + setPortStatus(kPortIndexOutput, kPortStatusShutdown); + } + assert(err == OK); } void OMXDecoder::setPortStatus(OMX_U32 port_index, PortStatus status) { - int shift = 2 * port_index; + int shift = 3 * port_index; - mPortStatusMask &= ~(3 << shift); + mPortStatusMask &= ~(7 << shift); mPortStatusMask |= status << shift; } OMXDecoder::PortStatus OMXDecoder::getPortStatus( OMX_U32 port_index) const { - int shift = 2 * port_index; + int shift = 3 * port_index; - return static_cast<PortStatus>((mPortStatusMask >> shift) & 3); + return static_cast<PortStatus>((mPortStatusMask >> shift) & 7); } void OMXDecoder::onEmptyBufferDone(IOMX::buffer_id buffer) { @@ -964,6 +1313,8 @@ void OMXDecoder::onEmptyBufferDone(IOMX::buffer_id buffer) { break; case kPortStatusFlushing: + case kPortStatusFlushingToDisabled: + case kPortStatusFlushingToShutdown: LOGV("We're currently flushing, enqueue INPUT buffer %p.", buffer); mBuffers.editItemAt(kPortIndexInput).push_back(buffer); err = NO_ERROR; @@ -980,7 +1331,9 @@ void OMXDecoder::onEmptyBufferDone(IOMX::buffer_id buffer) { void OMXDecoder::onFillBufferDone(const omx_message &msg) { IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer; - LOGV("[%s] onFillBufferDone (%p)", mComponentName, buffer); + LOGV("[%s] on%sFillBufferDone (%p, size:%ld)", mComponentName, + msg.type == omx_message::INITIAL_FILL_BUFFER ? "Initial" : "", + buffer, msg.u.extended_buffer_data.range_length); status_t err; switch (getPortStatus(kPortIndexOutput)) { @@ -995,6 +1348,8 @@ void OMXDecoder::onFillBufferDone(const omx_message &msg) { break; case kPortStatusFlushing: + case kPortStatusFlushingToDisabled: + case kPortStatusFlushingToShutdown: LOGV("We're currently flushing, enqueue OUTPUT buffer %p.", buffer); mBuffers.editItemAt(kPortIndexOutput).push_back(buffer); err = NO_ERROR; @@ -1035,7 +1390,7 @@ void OMXDecoder::onRealEmptyBufferDone(IOMX::buffer_id buffer) { size_t range_length = 0; - if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) { + if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) { assert((*mCodecSpecificDataIterator).size + 4 <= mem->size()); memcpy(mem->pointer(), kNALStartCode, 4); @@ -1142,15 +1497,14 @@ void OMXDecoder::onRealEmptyBufferDone(IOMX::buffer_id buffer) { OMX_TICKS timestamp = 0; if (success) { - // XXX units should be microseconds but PV treats them as milliseconds. - timestamp = ((OMX_S64)units * 1000) / scale; + timestamp = ((OMX_S64)units * 1000000) / scale; } input_buffer->release(); input_buffer = NULL; - LOGV("[%s] Calling EmptyBuffer on buffer %p", - mComponentName, buffer); + LOGV("[%s] Calling EmptyBuffer on buffer %p size:%d flags:0x%08lx", + mComponentName, buffer, src_length, flags); status_t err2 = mClient->emptyBuffer( mNode, buffer, 0, src_length, flags, timestamp); @@ -1170,7 +1524,8 @@ void OMXDecoder::onRealFillBufferDone(const omx_message &msg) { media_buffer->meta_data()->clear(); media_buffer->meta_data()->setInt32( - kKeyTimeUnits, msg.u.extended_buffer_data.timestamp); + kKeyTimeUnits, + (msg.u.extended_buffer_data.timestamp + 500) / 1000); media_buffer->meta_data()->setInt32(kKeyTimeScale, 1000); if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) { @@ -1198,7 +1553,9 @@ void OMXDecoder::signalBufferReturned(MediaBuffer *_buffer) { PortStatus outputStatus = getPortStatus(kPortIndexOutput); if (outputStatus == kPortStatusShutdown - || outputStatus == kPortStatusFlushing) { + || outputStatus == kPortStatusFlushing + || outputStatus == kPortStatusFlushingToDisabled + || outputStatus == kPortStatusFlushingToShutdown) { mBuffers.editItemAt(kPortIndexOutput).push_back(buffer); } else { LOGV("[%s] Calling FillBuffer on buffer %p.", mComponentName, buffer); @@ -1326,4 +1683,18 @@ void OMXDecoder::postInitialFillBuffer(IOMX::buffer_id buffer) { postMessage(msg); } +void OMXDecoder::freePortBuffers(OMX_U32 port_index) { + BufferList *buffers = &mBuffers.editItemAt(port_index); + while (!buffers->empty()) { + IOMX::buffer_id buffer = *buffers->begin(); + buffers->erase(buffers->begin()); + + if (port_index == kPortIndexInput) { + freeInputBuffer(buffer); + } else { + freeOutputBuffer(buffer); + } + } +} + } // namespace android diff --git a/media/libstagefright/TIHardwareRenderer.cpp b/media/libstagefright/TIHardwareRenderer.cpp new file mode 100644 index 0000000..ba42ef4 --- /dev/null +++ b/media/libstagefright/TIHardwareRenderer.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "TIHardwareRenderer" +#include <utils/Log.h> + +#undef NDEBUG +#include <assert.h> + +#include <media/stagefright/TIHardwareRenderer.h> +#include <ui/ISurface.h> +#include <ui/Overlay.h> + +namespace android { + +//////////////////////////////////////////////////////////////////////////////// + +TIHardwareRenderer::TIHardwareRenderer( + const sp<ISurface> &surface, + size_t displayWidth, size_t displayHeight, + size_t decodedWidth, size_t decodedHeight) + : mISurface(surface), + mDisplayWidth(displayWidth), + mDisplayHeight(displayHeight), + mDecodedWidth(decodedWidth), + mDecodedHeight(decodedHeight), + mFrameSize((mDecodedWidth * mDecodedHeight * 3) / 2) { + assert(mISurface.get() != NULL); + assert(mDecodedWidth > 0); + assert(mDecodedHeight > 0); + + sp<OverlayRef> ref = mISurface->createOverlay( + mDisplayWidth, mDisplayHeight, OVERLAY_FORMAT_CbYCrY_422_I); + + if (ref.get() == NULL) { + LOGE("Unable to create the overlay!"); + return; + } + + mOverlay = new Overlay(ref); + + for (size_t i = 0; i < mOverlay->getBufferCount(); ++i) { + mOverlayAddresses.push(mOverlay->getBufferAddress((void *)i)); + } + mIndex = mOverlayAddresses.size() - 1; +} + +TIHardwareRenderer::~TIHardwareRenderer() { + if (mOverlay.get() != NULL) { + mOverlay->destroy(); + mOverlay.clear(); + + // XXX apparently destroying an overlay is an asynchronous process... + sleep(1); + } +} + +void TIHardwareRenderer::render( + const void *data, size_t size, void *platformPrivate) { + // assert(size == mFrameSize); + + if (mOverlay.get() == NULL) { + return; + } + +#if 0 + overlay_buffer_t buffer; + if (mOverlay->dequeueBuffer(&buffer) == OK) { + void *addr = mOverlay->getBufferAddress(buffer); + + memcpy(addr, data, size); + + mOverlay->queueBuffer(buffer); + } +#else + memcpy(mOverlayAddresses[mIndex], data, size); + mOverlay->queueBuffer((void *)mIndex); + + if (mIndex-- == 0) { + mIndex = mOverlayAddresses.size() - 1; + } +#endif +} + +} // namespace android + |