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.
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
58
include/media/stagefright/TIHardwareRenderer.h
Normal file
58
include/media/stagefright/TIHardwareRenderer.h
Normal file
@@ -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_
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
#include <media/PVPlayer.h>
|
||||
#include "TestPlayerStub.h"
|
||||
|
||||
//#undef USE_STAGEFRIGHT
|
||||
|
||||
#if USE_STAGEFRIGHT
|
||||
#include "StagefrightPlayer.h"
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,7 @@ LOCAL_SRC_FILES:= \
|
||||
SurfaceRenderer.cpp \
|
||||
TimeSource.cpp \
|
||||
TimedEventQueue.cpp \
|
||||
TIHardwareRenderer.cpp \
|
||||
Utils.cpp \
|
||||
AudioPlayer.cpp \
|
||||
ESDS.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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mGroup->acquire_buffer(&mBuffer);
|
||||
if (err != OK) {
|
||||
assert(mBuffer == NULL);
|
||||
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;
|
||||
|
||||
mBuffer->set_range(mBufferOffset - 2, nal_length + 4);
|
||||
} else {
|
||||
mBuffer->set_range(mBufferOffset + 2, nal_length);
|
||||
}
|
||||
|
||||
mBufferOffset += nal_length + 2;
|
||||
mBufferSizeRemaining -= nal_length + 2;
|
||||
|
||||
if (mBufferSizeRemaining > 0) {
|
||||
*out = mBuffer->clone();
|
||||
} else {
|
||||
*out = mBuffer;
|
||||
if (num_bytes_read < (ssize_t)size) {
|
||||
mBuffer->release();
|
||||
mBuffer = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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());
|
||||
PortStatus status = getPortStatus(port_index);
|
||||
|
||||
if (port_index == kPortIndexInput) {
|
||||
postEmptyBufferDone(buffer);
|
||||
} else {
|
||||
postInitialFillBuffer(buffer);
|
||||
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);
|
||||
|
||||
setPortStatus(kPortIndexInput, kPortStatusShutdown);
|
||||
setPortStatus(kPortIndexOutput, kPortStatusShutdown);
|
||||
err = mOMX->send_command(mNode, OMX_CommandFlush, kPortIndexOutput);
|
||||
} else {
|
||||
err = mOMX->send_command(mNode, OMX_CommandFlush, OMX_ALL);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
99
media/libstagefright/TIHardwareRenderer.cpp
Normal file
99
media/libstagefright/TIHardwareRenderer.cpp
Normal file
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user