summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2009-07-28 10:03:13 -0700
committerAndreas Huber <andih@google.com>2009-07-28 11:28:14 -0700
commit0024245e134467d120b40099da16c467dc365e76 (patch)
treeb4023ffb3f2167dc3303cd048031ad7278643fd9
parent9a92037bd6477533062d635b676a6c9833aab96e (diff)
downloadframeworks_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.
-rw-r--r--cmds/stagefright/record.cpp3
-rw-r--r--cmds/stagefright/stagefright.cpp11
-rw-r--r--include/media/stagefright/OMXDecoder.h42
-rw-r--r--include/media/stagefright/TIHardwareRenderer.h58
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp2
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/AudioPlayer.cpp9
-rw-r--r--media/libstagefright/MP3Extractor.cpp2
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp121
-rw-r--r--media/libstagefright/MediaPlayerImpl.cpp21
-rw-r--r--media/libstagefright/OMXDecoder.cpp573
-rw-r--r--media/libstagefright/TIHardwareRenderer.cpp99
12 files changed, 773 insertions, 169 deletions
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 12bdead..d8db8b3 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -119,7 +119,8 @@ int main(int argc, char **argv) {
enc_meta->setInt32(kKeyWidth, width);
enc_meta->setInt32(kKeyHeight, height);
- OMXDecoder *encoder = OMXDecoder::CreateEncoder(&client, enc_meta);
+ OMXDecoder *encoder =
+ OMXDecoder::Create(&client, enc_meta, true /* createEncoder */);
encoder->setSource(decoder);
// encoder->setSource(meta, new DummySource(width, height));
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 961942a..7e23574 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -102,6 +102,7 @@ static int64_t getNowUs() {
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
+ bool audioOnly = false;
if (argc > 1 && !strcmp(argv[1], "--list")) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
@@ -121,6 +122,10 @@ int main(int argc, char **argv) {
}
return 0;
+ } else if (argc > 1 && !strcmp(argv[1], "--audio")) {
+ audioOnly = true;
+ ++argv;
+ --argc;
}
#if 0
@@ -149,7 +154,11 @@ int main(int argc, char **argv) {
const char *mime;
meta->findCString(kKeyMIMEType, &mime);
- if (!strncasecmp(mime, "video/", 6)) {
+ if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+ break;
+ }
+
+ if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
break;
}
}
diff --git a/include/media/stagefright/OMXDecoder.h b/include/media/stagefright/OMXDecoder.h
index 0859457..54a8047 100644
--- a/include/media/stagefright/OMXDecoder.h
+++ b/include/media/stagefright/OMXDecoder.h
@@ -26,6 +26,8 @@
#include <utils/List.h>
#include <utils/threads.h>
+#include <OMX_Video.h>
+
namespace android {
class OMXMediaBuffer;
@@ -35,10 +37,8 @@ class OMXDecoder : public MediaSource,
public MediaBufferObserver {
public:
static OMXDecoder *Create(
- OMXClient *client, const sp<MetaData> &data);
-
- static OMXDecoder *CreateEncoder(
- OMXClient *client, const sp<MetaData> &data);
+ OMXClient *client, const sp<MetaData> &data,
+ bool createEncoder = false);
virtual ~OMXDecoder();
@@ -68,10 +68,22 @@ private:
};
enum PortStatus {
- kPortStatusActive = 0,
- kPortStatusDisabled = 1,
- kPortStatusShutdown = 2,
- kPortStatusFlushing = 3
+ kPortStatusActive = 0,
+ kPortStatusDisabled = 1,
+ kPortStatusShutdown = 2,
+ kPortStatusFlushing = 3,
+ kPortStatusFlushingToDisabled = 4,
+ kPortStatusFlushingToShutdown = 5,
+ };
+
+ enum Quirks {
+ kWantsRawNALFrames = 1,
+ kDoesntReturnBuffersOnDisable = 2,
+ kDoesntFlushOnExecutingToIdle = 4,
+ kDoesntProperlyFlushAllPortsAtOnce = 8,
+ kRequiresAllocateBufferOnInputPorts = 16,
+ kRequiresAllocateBufferOnOutputPorts = 32,
+ kRequiresLoadedToIdleAfterAllocation = 64
};
OMXClient *mClient;
@@ -79,6 +91,8 @@ private:
IOMX::node_id mNode;
char *mComponentName;
bool mIsMP3;
+ bool mIsAVC;
+ uint32_t mQuirks;
MediaSource *mSource;
sp<MetaData> mOutputFormat;
@@ -116,7 +130,8 @@ private:
bool mReachedEndOfInput;
OMXDecoder(OMXClient *client, IOMX::node_id node,
- const char *mime, const char *codec);
+ const char *mime, const char *codec,
+ uint32_t quirks);
void setPortStatus(OMX_U32 port_index, PortStatus status);
PortStatus getPortStatus(OMX_U32 port_index) const;
@@ -125,7 +140,13 @@ private:
void setAMRFormat();
void setAACFormat();
- void setVideoOutputFormat(OMX_U32 width, OMX_U32 height);
+
+ status_t setVideoPortFormatType(
+ OMX_U32 portIndex,
+ OMX_VIDEO_CODINGTYPE compressionFormat,
+ OMX_COLOR_FORMATTYPE colorFormat);
+
+ void setVideoOutputFormat(const char *mime, OMX_U32 width, OMX_U32 height);
void setup();
void dumpPortDefinition(OMX_U32 port_index);
@@ -144,6 +165,7 @@ private:
void freeInputBuffer(IOMX::buffer_id buffer);
void freeOutputBuffer(IOMX::buffer_id buffer);
+ void freePortBuffers(OMX_U32 port_index);
void postStart();
void postEmptyBufferDone(IOMX::buffer_id buffer);
diff --git a/include/media/stagefright/TIHardwareRenderer.h b/include/media/stagefright/TIHardwareRenderer.h
new file mode 100644
index 0000000..f7fa81b
--- /dev/null
+++ b/include/media/stagefright/TIHardwareRenderer.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef TI_HARDWARE_RENDERER_H_
+
+#define TI_HARDWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class ISurface;
+class Overlay;
+
+class TIHardwareRenderer : public VideoRenderer {
+public:
+ TIHardwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~TIHardwareRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ sp<ISurface> mISurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+ size_t mFrameSize;
+ sp<Overlay> mOverlay;
+ Vector<void *> mOverlayAddresses;
+ size_t mIndex;
+
+ TIHardwareRenderer(const TIHardwareRenderer &);
+ TIHardwareRenderer &operator=(const TIHardwareRenderer &);
+};
+
+} // namespace android
+
+#endif // TI_HARDWARE_RENDERER_H_
+
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 = &param->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
+