Squashed commit of the following:
commit 5bb012f0065f7ffaaeb4f569d71f0e3a8d6b19c3
Author: Andreas Huber <andih@google.com>
Date: Fri Aug 14 10:40:08 2009 -0700
An attempt at fixing export using the qcom encoders. More quirks.
commit 0690e76bfa48118a68287ccf1bbfa82febaa620c
Author: Andreas Huber <andih@google.com>
Date: Fri Aug 14 09:08:28 2009 -0700
Callbacks are now dispatched from a separate thread in OMX.
commit c6571a039526df29b6343f9a1971dbc019088c61
Author: Andreas Huber <andih@google.com>
Date: Thu Aug 13 15:42:25 2009 -0700
Massive API changes throughout stagefright, smart pointers everywhere.
commit 900612af6a0555664d9ba195112cd859491265f4
Author: Andreas Huber <andih@google.com>
Date: Thu Aug 13 13:33:12 2009 -0700
OMXCodecs now properly shutdown.
commit 96732f05e1b0603dcd1b11f16a23512592eeb4f5
Author: Andreas Huber <andih@google.com>
Date: Thu Aug 13 12:04:04 2009 -0700
More work on JPEG decoding using the hardware OMX component.
commit 63839a073ac393e3a130434ba467969053b694ad
Author: Andreas Huber <andih@google.com>
Date: Wed Aug 12 13:13:31 2009 -0700
An attempt to drive the JPEG decoder OMX node.
commit 3ac2fe5ab2926eda81b2123610b2434c645294ff
Author: Andreas Huber <andih@google.com>
Date: Tue Aug 11 16:38:21 2009 -0700
Renamed StateMachine to OMXCodec and put it in its proper place.
commit 247da75a96bf8881956413023dd49a84d5b4f5b2
Author: Andreas Huber <andih@google.com>
Date: Tue Aug 11 16:06:19 2009 -0700
Statemachine is now a full-fledged MediaSource.
commit 045244f6771fa0b9b329495c953afda900a84b71
Author: Andreas Huber <andih@google.com>
Date: Fri Aug 7 09:16:54 2009 -0700
Properly setup the input format when exporting to AMR audio.
commit 271b984cb32c5cd9e46e3f90ae121f334e4b8da9
Author: Andreas Huber <andih@google.com>
Date: Thu Aug 6 09:59:38 2009 -0700
Added some code to test audio encoding to the OMX harness.
commit 79af4748e4af33bd66d3fbac606e332a69741cf4
Author: Andreas Huber <andih@google.com>
Date: Wed Aug 5 14:36:22 2009 -0700
Merge the old OMXDecoder and the new, shiny, StateMachine code.
commit 91cf5dd77a8762bc10a0b2ffce35e3bbeb262231
Author: Andreas Huber <andih@google.com>
Date: Tue Aug 4 17:41:43 2009 -0700
A new harness to test OMX node compliance (and quirks).
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index e6d0503..4576b8e 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -10,8 +10,7 @@
LOCAL_C_INCLUDES:= \
frameworks/base/media/libstagefright \
- $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
- $(TOP)/external/opencore/android
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
LOCAL_CFLAGS += -Wno-multichar
@@ -31,8 +30,7 @@
LOCAL_C_INCLUDES:= \
frameworks/base/media/libstagefright \
- $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
- $(TOP)/external/opencore/android
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
LOCAL_CFLAGS += -Wno-multichar
@@ -52,8 +50,7 @@
#
# LOCAL_C_INCLUDES:= \
# frameworks/base/media/libstagefright \
-# $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
-# $(TOP)/external/opencore/android
+# $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
#
# LOCAL_CFLAGS += -Wno-multichar
#
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index cd54958..cf2962b 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -25,6 +25,7 @@
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/OMXDecoder.h>
using namespace android;
@@ -32,18 +33,38 @@
class DummySource : public MediaSource {
public:
DummySource(int width, int height)
- : mSize((width * height * 3) / 2) {
+ : mWidth(width),
+ mHeight(height),
+ mSize((width * height * 3) / 2) {
mGroup.add_buffer(new MediaBuffer(mSize));
}
- virtual ::status_t getMaxSampleSize(size_t *max_size) {
- *max_size = mSize;
- return ::OK;
+ virtual sp<MetaData> getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyWidth, mWidth);
+ meta->setInt32(kKeyHeight, mHeight);
+ meta->setCString(kKeyMIMEType, "video/raw");
+
+ return meta;
}
- virtual ::status_t read(MediaBuffer **buffer) {
- ::status_t err = mGroup.acquire_buffer(buffer);
- if (err != ::OK) {
+ virtual status_t getMaxSampleSize(size_t *max_size) {
+ *max_size = mSize;
+ return OK;
+ }
+
+ virtual status_t start(MetaData *params) {
+ return OK;
+ }
+
+ virtual status_t stop() {
+ return OK;
+ }
+
+ virtual status_t read(
+ MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+ status_t err = mGroup.acquire_buffer(buffer);
+ if (err != OK) {
return err;
}
@@ -51,34 +72,34 @@
memset((*buffer)->data(), x, mSize);
(*buffer)->set_range(0, mSize);
- return ::OK;
+ return OK;
}
+protected:
+ virtual ~DummySource() {}
+
private:
MediaBufferGroup mGroup;
+ int mWidth, mHeight;
size_t mSize;
DummySource(const DummySource &);
DummySource &operator=(const DummySource &);
};
-int main(int argc, char **argv) {
- android::ProcessState::self()->startThreadPool();
+#define USE_OMX_CODEC 1
-#if 1
- if (argc != 2) {
- fprintf(stderr, "usage: %s filename\n", argv[0]);
- return 1;
- }
+sp<MediaSource> createSource(const char *filename) {
+ sp<MediaSource> source;
- MPEG4Extractor extractor(new MmapSource(argv[1]));
- int num_tracks;
- assert(extractor.countTracks(&num_tracks) == ::OK);
+ sp<MPEG4Extractor> extractor =
+ new MPEG4Extractor(new MmapSource(filename));
- MediaSource *source = NULL;
+ size_t num_tracks = extractor->countTracks();
+
sp<MetaData> meta;
- for (int i = 0; i < num_tracks; ++i) {
- meta = extractor.getTrackMetaData(i);
+ for (size_t i = 0; i < num_tracks; ++i) {
+ meta = extractor->getTrackMetaData(i);
assert(meta.get() != NULL);
const char *mime;
@@ -90,48 +111,75 @@
continue;
}
- if (extractor.getTrack(i, &source) != ::OK) {
- source = NULL;
- continue;
- }
+ source = extractor->getTrack(i);
break;
}
- if (source == NULL) {
- fprintf(stderr, "Unable to find a suitable video track.\n");
+ return source;
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+#if 1
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s filename\n", argv[0]);
return 1;
}
OMXClient client;
assert(client.connect() == android::OK);
- OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
- decoder->setSource(source);
+#if 0
+ sp<MediaSource> source = createSource(argv[1]);
+
+ if (source == NULL) {
+ fprintf(stderr, "Unable to find a suitable video track.\n");
+ return 1;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+#if USE_OMX_CODEC
+ sp<OMXCodec> decoder = OMXCodec::Create(
+ client.interface(), meta, false /* createEncoder */, source);
+#else
+ sp<OMXDecoder> decoder = OMXDecoder::Create(
+ &client, meta, false /* createEncoder */, source);
+#endif
int width, height;
bool success = meta->findInt32(kKeyWidth, &width);
success = success && meta->findInt32(kKeyHeight, &height);
assert(success);
+#else
+ int width = 320;
+ int height = 240;
+ sp<MediaSource> decoder = new DummySource(width, height);
+#endif
sp<MetaData> enc_meta = new MetaData;
- enc_meta->setCString(kKeyMIMEType, "video/3gpp");
- // enc_meta->setCString(kKeyMIMEType, "video/mp4v-es");
+ // enc_meta->setCString(kKeyMIMEType, "video/3gpp");
+ enc_meta->setCString(kKeyMIMEType, "video/mp4v-es");
enc_meta->setInt32(kKeyWidth, width);
enc_meta->setInt32(kKeyHeight, height);
- OMXDecoder *encoder =
- OMXDecoder::Create(&client, enc_meta, true /* createEncoder */);
-
- encoder->setSource(decoder);
- // encoder->setSource(meta, new DummySource(width, height));
+#if USE_OMX_CODEC
+ sp<OMXCodec> encoder =
+ OMXCodec::Create(
+ client.interface(), enc_meta, true /* createEncoder */, decoder);
+#else
+ sp<OMXDecoder> encoder = OMXDecoder::Create(
+ &client, enc_meta, true /* createEncoder */, decoder);
+#endif
#if 1
- MPEG4Writer writer("/sdcard/output.mp4");
- writer.addSource(enc_meta, encoder);
- writer.start();
+ sp<MPEG4Writer> writer = new MPEG4Writer("/sdcard/output.mp4");
+ writer->addSource(enc_meta, encoder);
+ writer->start();
sleep(20);
printf("stopping now.\n");
- writer.stop();
+ writer->stop();
#else
encoder->start();
@@ -146,16 +194,7 @@
encoder->stop();
#endif
- delete encoder;
- encoder = NULL;
-
- delete decoder;
- decoder = NULL;
-
client.disconnect();
-
- delete source;
- source = NULL;
#endif
#if 0
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 7e23574..b2de67a 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -16,9 +16,6 @@
#include <sys/time.h>
-#undef NDEBUG
-#include <assert.h>
-
#include <pthread.h>
#include <stdlib.h>
@@ -30,12 +27,15 @@
#include <media/stagefright/ESDS.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaPlayerImpl.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/OMXDecoder.h>
#include "WaveWriter.h"
@@ -44,50 +44,236 @@
////////////////////////////////////////////////////////////////////////////////
-static bool convertToWav(
- OMXClient *client, const sp<MetaData> &meta, MediaSource *source) {
- printf("convertToWav\n");
+struct JPEGSource : public MediaSource {
+ // Assumes ownership of "source".
+ JPEGSource(const sp<DataSource> &source);
- OMXDecoder *decoder = OMXDecoder::Create(client, meta);
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
- int32_t sampleRate;
- bool success = meta->findInt32(kKeySampleRate, &sampleRate);
- assert(success);
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
- int32_t numChannels;
- success = meta->findInt32(kKeyChannelCount, &numChannels);
- assert(success);
+protected:
+ virtual ~JPEGSource();
- const char *mime;
- success = meta->findCString(kKeyMIMEType, &mime);
- assert(success);
+private:
+ sp<DataSource> mSource;
+ MediaBufferGroup *mGroup;
+ bool mStarted;
+ off_t mSize;
+ int32_t mWidth, mHeight;
+ off_t mOffset;
- if (!strcasecmp("audio/3gpp", mime)) {
- numChannels = 1; // XXX
+ status_t parseJPEG();
+
+ JPEGSource(const JPEGSource &);
+ JPEGSource &operator=(const JPEGSource &);
+};
+
+JPEGSource::JPEGSource(const sp<DataSource> &source)
+ : mSource(source),
+ mGroup(NULL),
+ mStarted(false),
+ mSize(0),
+ mWidth(0),
+ mHeight(0),
+ mOffset(0) {
+ CHECK_EQ(parseJPEG(), OK);
+}
+
+JPEGSource::~JPEGSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t JPEGSource::start(MetaData *) {
+ if (mStarted) {
+ return UNKNOWN_ERROR;
}
- WaveWriter writer("/sdcard/Music/shoutcast.wav", numChannels, sampleRate);
+ if (mSource->getSize(&mSize) != OK) {
+ return UNKNOWN_ERROR;
+ }
- decoder->setSource(source);
- for (int i = 0; i < 100; ++i) {
- MediaBuffer *buffer;
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(mSize));
- ::status_t err = decoder->read(&buffer);
- if (err != ::OK) {
- break;
- }
+ mOffset = 0;
- writer.Append((const char *)buffer->data() + buffer->range_offset(),
- buffer->range_length());
+ mStarted = true;
+
+ return OK;
+}
+status_t JPEGSource::stop() {
+ if (!mStarted) {
+ return UNKNOWN_ERROR;
+ }
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> JPEGSource::getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, "image/jpeg");
+ meta->setInt32(kKeyWidth, mWidth);
+ meta->setInt32(kKeyHeight, mHeight);
+
+ return meta;
+}
+
+status_t JPEGSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options != NULL && options->getSeekTo(&seekTimeUs)) {
+ return UNKNOWN_ERROR;
+ }
+
+ MediaBuffer *buffer;
+ mGroup->acquire_buffer(&buffer);
+
+ ssize_t n = mSource->read_at(mOffset, buffer->data(), mSize - mOffset);
+
+ if (n <= 0) {
buffer->release();
buffer = NULL;
+
+ return UNKNOWN_ERROR;
}
- delete decoder;
- decoder = NULL;
+ buffer->set_range(0, n);
- return true;
+ mOffset += n;
+
+ *out = buffer;
+
+ return OK;
+}
+
+#define JPEG_SOF0 0xC0 /* nStart Of Frame N*/
+#define JPEG_SOF1 0xC1 /* N indicates which compression process*/
+#define JPEG_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use*/
+#define JPEG_SOF3 0xC3
+#define JPEG_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers*/
+#define JPEG_SOF6 0xC6
+#define JPEG_SOF7 0xC7
+#define JPEG_SOF9 0xC9
+#define JPEG_SOF10 0xCA
+#define JPEG_SOF11 0xCB
+#define JPEG_SOF13 0xCD
+#define JPEG_SOF14 0xCE
+#define JPEG_SOF15 0xCF
+#define JPEG_SOI 0xD8 /* nStart Of Image (beginning of datastream)*/
+#define JPEG_EOI 0xD9 /* End Of Image (end of datastream)*/
+#define JPEG_SOS 0xDA /* nStart Of Scan (begins compressed data)*/
+#define JPEG_JFIF 0xE0 /* Jfif marker*/
+#define JPEG_EXIF 0xE1 /* Exif marker*/
+#define JPEG_COM 0xFE /* COMment */
+#define JPEG_DQT 0xDB
+#define JPEG_DHT 0xC4
+#define JPEG_DRI 0xDD
+
+status_t JPEGSource::parseJPEG() {
+ mWidth = 0;
+ mHeight = 0;
+
+ off_t i = 0;
+
+ uint16_t soi;
+ if (!mSource->getUInt16(i, &soi)) {
+ return ERROR_IO;
+ }
+
+ i += 2;
+
+ if (soi != 0xffd8) {
+ return UNKNOWN_ERROR;
+ }
+
+ for (;;) {
+ uint8_t marker;
+ if (mSource->read_at(i++, &marker, 1) != 1) {
+ return ERROR_IO;
+ }
+
+ CHECK_EQ(marker, 0xff);
+
+ if (mSource->read_at(i++, &marker, 1) != 1) {
+ return ERROR_IO;
+ }
+
+ CHECK(marker != 0xff);
+
+ uint16_t chunkSize;
+ if (!mSource->getUInt16(i, &chunkSize)) {
+ return ERROR_IO;
+ }
+
+ i += 2;
+
+ if (chunkSize < 2) {
+ return UNKNOWN_ERROR;
+ }
+
+ switch (marker) {
+ case JPEG_SOS:
+ {
+ return (mWidth > 0 && mHeight > 0) ? OK : UNKNOWN_ERROR;
+ }
+
+ case JPEG_EOI:
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ case JPEG_SOF0:
+ case JPEG_SOF1:
+ case JPEG_SOF3:
+ case JPEG_SOF5:
+ case JPEG_SOF6:
+ case JPEG_SOF7:
+ case JPEG_SOF9:
+ case JPEG_SOF10:
+ case JPEG_SOF11:
+ case JPEG_SOF13:
+ case JPEG_SOF14:
+ case JPEG_SOF15:
+ {
+ uint16_t width, height;
+ if (!mSource->getUInt16(i + 1, &height)
+ || !mSource->getUInt16(i + 3, &width)) {
+ return ERROR_IO;
+ }
+
+ mWidth = width;
+ mHeight = height;
+
+ i += chunkSize - 2;
+ break;
+ }
+
+ default:
+ {
+ // Skip chunk
+
+ i += chunkSize - 2;
+
+ break;
+ }
+ }
+ }
+
+ return OK;
}
////////////////////////////////////////////////////////////////////////////////
@@ -99,6 +285,48 @@
return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
}
+#define USE_OMX_CODEC 1
+
+static void playSource(OMXClient *client, const sp<MediaSource> &source) {
+ sp<MetaData> meta = source->getFormat();
+
+#if !USE_OMX_CODEC
+ sp<OMXDecoder> decoder = OMXDecoder::Create(
+ client, meta, false /* createEncoder */, source);
+#else
+ sp<OMXCodec> decoder = OMXCodec::Create(
+ client->interface(), meta, false /* createEncoder */, source);
+#endif
+
+ if (decoder == NULL) {
+ return;
+ }
+
+ decoder->start();
+
+ int64_t startTime = getNowUs();
+
+ int n = 0;
+ MediaBuffer *buffer;
+ status_t err;
+ while ((err = decoder->read(&buffer)) == OK) {
+ if ((++n % 16) == 0) {
+ printf(".");
+ fflush(stdout);
+ }
+
+ buffer->release();
+ buffer = NULL;
+ }
+ decoder->stop();
+ printf("\n");
+
+ int64_t delay = getNowUs() - startTime;
+ printf("avg. %.2f fps\n", n * 1E6 / delay);
+
+ printf("decoded a total of %d frame(s).\n", n);
+}
+
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
@@ -108,10 +336,10 @@
sp<IBinder> binder = sm->getService(String16("media.player"));
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
- assert(service.get() != NULL);
+ CHECK(service.get() != NULL);
sp<IOMX> omx = service->createOMX();
- assert(omx.get() != NULL);
+ CHECK(omx.get() != NULL);
List<String8> list;
omx->list_nodes(&list);
@@ -128,82 +356,52 @@
--argc;
}
-#if 0
- MediaPlayerImpl player(argv[1]);
- player.play();
-
- sleep(10000);
-#else
DataSource::RegisterDefaultSniffers();
OMXClient client;
status_t err = client.connect();
- MmapSource *dataSource = new MmapSource(argv[1]);
- MediaExtractor *extractor = MediaExtractor::Create(dataSource);
- dataSource = NULL;
+ sp<MmapSource> dataSource = new MmapSource(argv[1]);
- int numTracks;
- err = extractor->countTracks(&numTracks);
+ bool isJPEG = false;
- sp<MetaData> meta;
- int i;
- for (i = 0; i < numTracks; ++i) {
- meta = extractor->getTrackMetaData(i);
-
- const char *mime;
- meta->findCString(kKeyMIMEType, &mime);
-
- if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
- break;
- }
-
- if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
- break;
- }
+ size_t len = strlen(argv[1]);
+ if (len >= 4 && !strcasecmp(argv[1] + len - 4, ".jpg")) {
+ isJPEG = true;
}
- OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
+ sp<MediaSource> mediaSource;
- if (decoder != NULL) {
- MediaSource *source;
- err = extractor->getTrack(i, &source);
+ if (isJPEG) {
+ mediaSource = new JPEGSource(dataSource);
+ } else {
+ sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
- decoder->setSource(source);
+ size_t numTracks = extractor->countTracks();
- decoder->start();
+ sp<MetaData> meta;
+ size_t i;
+ for (i = 0; i < numTracks; ++i) {
+ meta = extractor->getTrackMetaData(i);
- int64_t startTime = getNowUs();
+ const char *mime;
+ meta->findCString(kKeyMIMEType, &mime);
- int n = 0;
- MediaBuffer *buffer;
- while ((err = decoder->read(&buffer)) == OK) {
- if ((++n % 16) == 0) {
- printf(".");
- fflush(stdout);
+ if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+ break;
}
- buffer->release();
- buffer = NULL;
+ if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
+ break;
+ }
}
- decoder->stop();
- printf("\n");
- int64_t delay = getNowUs() - startTime;
- printf("avg. %.2f fps\n", n * 1E6 / delay);
-
- delete decoder;
- decoder = NULL;
-
- delete source;
- source = NULL;
+ mediaSource = extractor->getTrack(i);
}
- delete extractor;
- extractor = NULL;
+ playSource(&client, mediaSource);
client.disconnect();
-#endif
return 0;
}
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 7e5ff61..58a74c7 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -56,6 +56,14 @@
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) = 0;
+ virtual status_t get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) = 0;
+
+ virtual status_t set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) = 0;
+
virtual status_t use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) = 0;
@@ -82,6 +90,11 @@
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp) = 0;
+ virtual status_t get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) = 0;
+
virtual sp<IOMXRenderer> createRenderer(
const sp<ISurface> &surface,
const char *componentName,
@@ -114,10 +127,11 @@
QUIT_OBSERVER,
} type;
+ IOMX::node_id node;
+
union {
// if type == EVENT
struct {
- IOMX::node_id node;
OMX_EVENTTYPE event;
OMX_U32 data1;
OMX_U32 data2;
@@ -126,13 +140,11 @@
// if type == EMPTY_BUFFER_DONE || type == FILL_BUFFER
// || type == INITIAL_FILL_BUFFER
struct {
- IOMX::node_id node;
IOMX::buffer_id buffer;
} buffer_data;
// if type == EMPTY_BUFFER || type == FILL_BUFFER_DONE
struct {
- IOMX::node_id node;
IOMX::buffer_id buffer;
OMX_U32 range_offset;
OMX_U32 range_length;
@@ -143,7 +155,6 @@
// if type == SEND_COMMAND
struct {
- IOMX::node_id node;
OMX_COMMANDTYPE cmd;
OMX_S32 param;
} send_command_data;
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 0f2e528..960eda3 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -31,10 +31,10 @@
class AudioPlayer : public TimeSource {
public:
AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
- ~AudioPlayer();
+ virtual ~AudioPlayer();
// Caller retains ownership of "source".
- void setSource(MediaSource *source);
+ void setSource(const sp<MediaSource> &source);
// Return time in us.
virtual int64_t getRealTimeUs();
@@ -56,7 +56,7 @@
status_t seekTo(int64_t time_us);
private:
- MediaSource *mSource;
+ sp<MediaSource> mSource;
AudioTrack *mAudioTrack;
MediaBuffer *mInputBuffer;
diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h
index e275cb4..e35e19e 100644
--- a/include/media/stagefright/CachingDataSource.h
+++ b/include/media/stagefright/CachingDataSource.h
@@ -26,14 +26,16 @@
class CachingDataSource : public DataSource {
public:
- // Assumes ownership of "source".
- CachingDataSource(DataSource *source, size_t pageSize, int numPages);
- virtual ~CachingDataSource();
+ CachingDataSource(
+ const sp<DataSource> &source, size_t pageSize, int numPages);
status_t InitCheck() const;
virtual ssize_t read_at(off_t offset, void *data, size_t size);
+protected:
+ virtual ~CachingDataSource();
+
private:
struct Page {
Page *mPrev, *mNext;
@@ -42,7 +44,7 @@
void *mData;
};
- DataSource *mSource;
+ sp<DataSource> mSource;
void *mData;
size_t mPageSize;
Page *mFirst, *mLast;
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 31eea27..f46f0af 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -22,19 +22,22 @@
#include <utils/Errors.h>
#include <utils/List.h>
+#include <utils/RefBase.h>
#include <utils/threads.h>
namespace android {
class String8;
-class DataSource {
+class DataSource : public RefBase {
public:
DataSource() {}
- virtual ~DataSource() {}
virtual ssize_t read_at(off_t offset, void *data, size_t size) = 0;
+ // Convenience methods:
+ bool getUInt16(off_t offset, uint16_t *x);
+
// May return ERROR_UNSUPPORTED.
virtual status_t getSize(off_t *size);
@@ -43,11 +46,14 @@
bool sniff(String8 *mimeType, float *confidence);
typedef bool (*SnifferFunc)(
- DataSource *source, String8 *mimeType, float *confidence);
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
static void RegisterSniffer(SnifferFunc func);
static void RegisterDefaultSniffers();
+protected:
+ virtual ~DataSource() {}
+
private:
static Mutex gSnifferMutex;
static List<SnifferFunc> gSniffers;
diff --git a/include/media/stagefright/MP3Extractor.h b/include/media/stagefright/MP3Extractor.h
index 09cfb70..4e1f3c3 100644
--- a/include/media/stagefright/MP3Extractor.h
+++ b/include/media/stagefright/MP3Extractor.h
@@ -28,16 +28,17 @@
class MP3Extractor : public MediaExtractor {
public:
// Extractor assumes ownership of "source".
- MP3Extractor(DataSource *source);
+ MP3Extractor(const sp<DataSource> &source);
- ~MP3Extractor();
+ size_t countTracks();
+ sp<MediaSource> getTrack(size_t index);
+ sp<MetaData> getTrackMetaData(size_t index);
- status_t countTracks(int *num_tracks);
- status_t getTrack(int index, MediaSource **source);
- sp<MetaData> getTrackMetaData(int index);
+protected:
+ virtual ~MP3Extractor();
private:
- DataSource *mDataSource;
+ sp<DataSource> mDataSource;
off_t mFirstFramePos;
sp<MetaData> mMeta;
uint32_t mFixedHeader;
@@ -46,7 +47,8 @@
MP3Extractor &operator=(const MP3Extractor &);
};
-bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence);
+bool SniffMP3(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
} // namespace android
diff --git a/include/media/stagefright/MPEG4Extractor.h b/include/media/stagefright/MPEG4Extractor.h
index 51a7e82..932e30f 100644
--- a/include/media/stagefright/MPEG4Extractor.h
+++ b/include/media/stagefright/MPEG4Extractor.h
@@ -29,22 +29,24 @@
class MPEG4Extractor : public MediaExtractor {
public:
// Extractor assumes ownership of "source".
- MPEG4Extractor(DataSource *source);
- ~MPEG4Extractor();
+ MPEG4Extractor(const sp<DataSource> &source);
- status_t countTracks(int *num_tracks);
- status_t getTrack(int index, MediaSource **source);
- sp<MetaData> getTrackMetaData(int index);
+ size_t countTracks();
+ sp<MediaSource> getTrack(size_t index);
+ sp<MetaData> getTrackMetaData(size_t index);
+
+protected:
+ virtual ~MPEG4Extractor();
private:
struct Track {
Track *next;
sp<MetaData> meta;
uint32_t timescale;
- SampleTable *sampleTable;
+ sp<SampleTable> sampleTable;
};
- DataSource *mDataSource;
+ sp<DataSource> mDataSource;
bool mHaveMetadata;
Track *mFirstTrack, *mLastTrack;
@@ -58,7 +60,8 @@
MPEG4Extractor &operator=(const MPEG4Extractor &);
};
-bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence);
+bool SniffMPEG4(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
} // namespace android
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 40d6127..5147de9 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -30,13 +30,12 @@
class MediaSource;
class MetaData;
-class MPEG4Writer {
+class MPEG4Writer : public RefBase {
public:
MPEG4Writer(const char *filename);
- ~MPEG4Writer();
// Caller retains ownership of both meta and source.
- void addSource(const sp<MetaData> &meta, MediaSource *source);
+ void addSource(const sp<MetaData> &meta, const sp<MediaSource> &source);
void start();
void stop();
@@ -50,6 +49,9 @@
void write(const void *data, size_t size);
void endBox();
+protected:
+ virtual ~MPEG4Writer();
+
private:
class Track;
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index c72ed66..339e6fb 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -75,6 +75,8 @@
// MetaData.
MediaBuffer *clone();
+ int refcount() const;
+
protected:
virtual ~MediaBuffer();
@@ -102,8 +104,6 @@
void setNextBuffer(MediaBuffer *buffer);
MediaBuffer *nextBuffer();
- int refcount() const;
-
MediaBuffer(const MediaBuffer &);
MediaBuffer &operator=(const MediaBuffer &);
};
diff --git a/include/media/stagefright/MediaDebug.h b/include/media/stagefright/MediaDebug.h
new file mode 100644
index 0000000..83acd77
--- /dev/null
+++ b/include/media/stagefright/MediaDebug.h
@@ -0,0 +1,18 @@
+#ifndef MEDIA_DEBUG_H_
+
+#define MEDIA_DEBUG_H_
+
+#define LITERAL_TO_STRING_INTERNAL(x) #x
+#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
+
+#define CHECK_EQ(x,y) \
+ LOG_ALWAYS_FATAL_IF( \
+ (x) != (y), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x " != " #y)
+
+#define CHECK(x) \
+ LOG_ALWAYS_FATAL_IF( \
+ !(x), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x)
+
+#endif // MEDIA_DEBUG_H_
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index 38f8e5b..67e45bd 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -26,18 +26,18 @@
class MediaSource;
class MetaData;
-class MediaExtractor {
+class MediaExtractor : public RefBase {
public:
- static MediaExtractor *Create(DataSource *source, const char *mime = NULL);
+ static sp<MediaExtractor> Create(
+ const sp<DataSource> &source, const char *mime = NULL);
- virtual ~MediaExtractor() {}
-
- virtual status_t countTracks(int *num_tracks) = 0;
- virtual status_t getTrack(int index, MediaSource **source) = 0;
- virtual sp<MetaData> getTrackMetaData(int index) = 0;
+ virtual size_t countTracks() = 0;
+ virtual sp<MediaSource> getTrack(size_t index) = 0;
+ virtual sp<MetaData> getTrackMetaData(size_t index) = 0;
protected:
MediaExtractor() {}
+ virtual ~MediaExtractor() {}
private:
MediaExtractor(const MediaExtractor &);
diff --git a/include/media/stagefright/MediaPlayerImpl.h b/include/media/stagefright/MediaPlayerImpl.h
index e96e5e8..53a2088 100644
--- a/include/media/stagefright/MediaPlayerImpl.h
+++ b/include/media/stagefright/MediaPlayerImpl.h
@@ -35,7 +35,6 @@
class MediaSource;
class MemoryHeapPmem;
class MetaData;
-class OMXDecoder;
class Surface;
class TimeSource;
@@ -71,16 +70,16 @@
OMXClient mClient;
- MediaExtractor *mExtractor;
+ sp<MediaExtractor> mExtractor;
TimeSource *mTimeSource;
- MediaSource *mAudioSource;
- OMXDecoder *mAudioDecoder;
+ sp<MediaSource> mAudioSource;
+ sp<MediaSource> mAudioDecoder;
AudioPlayer *mAudioPlayer;
- MediaSource *mVideoSource;
- MediaSource *mVideoDecoder;
+ sp<MediaSource> mVideoSource;
+ sp<MediaSource> mVideoDecoder;
int32_t mVideoWidth, mVideoHeight;
int64_t mVideoPosition;
@@ -103,16 +102,13 @@
bool mSeeking;
int64_t mSeekTimeUs;
- size_t mFrameSize;
- bool mUseSoftwareColorConversion;
-
void init();
static void *VideoWrapper(void *me);
void videoEntry();
- void setAudioSource(MediaSource *source);
- void setVideoSource(MediaSource *source);
+ void setAudioSource(const sp<MediaSource> &source);
+ void setVideoSource(const sp<MediaSource> &source);
MediaSource *makeShoutcastSource(const char *path);
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
index eb07f68..d1fa114 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -27,9 +27,8 @@
class MediaBuffer;
class MetaData;
-struct MediaSource {
+struct MediaSource : public RefBase {
MediaSource();
- virtual ~MediaSource();
// To be called before any other methods on this object, except
// getFormat().
@@ -81,6 +80,9 @@
int64_t mLatenessUs;
};
+protected:
+ virtual ~MediaSource();
+
private:
MediaSource(const MediaSource &);
MediaSource &operator=(const MediaSource &);
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
new file mode 100644
index 0000000..d4ae349
--- /dev/null
+++ b/include/media/stagefright/OMXCodec.h
@@ -0,0 +1,190 @@
+/*
+ * 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 OMX_CODEC_H_
+
+#define OMX_CODEC_H_
+
+#include <media/IOMX.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MemoryDealer;
+struct OMXCodecObserver;
+
+struct OMXCodec : public MediaSource,
+ public MediaBufferObserver {
+ static sp<OMXCodec> Create(
+ const sp<IOMX> &omx,
+ const sp<MetaData> &meta, bool createEncoder,
+ const sp<MediaSource> &source);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+ void on_message(const omx_message &msg);
+
+ // from MediaBufferObserver
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+protected:
+ virtual ~OMXCodec();
+
+private:
+ enum State {
+ DEAD,
+ LOADED,
+ LOADED_TO_IDLE,
+ IDLE_TO_EXECUTING,
+ EXECUTING,
+ EXECUTING_TO_IDLE,
+ IDLE_TO_LOADED,
+ RECONFIGURING,
+ ERROR
+ };
+
+ enum {
+ kPortIndexInput = 0,
+ kPortIndexOutput = 1
+ };
+
+ enum PortStatus {
+ ENABLED,
+ DISABLING,
+ DISABLED,
+ ENABLING,
+ SHUTTING_DOWN,
+ };
+
+ enum Quirks {
+ kNeedsFlushBeforeDisable = 1,
+ kWantsRawNALFrames = 2,
+ kRequiresLoadedToIdleAfterAllocation = 4,
+ kRequiresAllocateBufferOnInputPorts = 8,
+ };
+
+ struct BufferInfo {
+ IOMX::buffer_id mBuffer;
+ bool mOwnedByComponent;
+ sp<IMemory> mMem;
+ MediaBuffer *mMediaBuffer;
+ };
+
+ struct CodecSpecificData {
+ size_t mSize;
+ uint8_t mData[1];
+ };
+
+ sp<IOMX> mOMX;
+ IOMX::node_id mNode;
+ sp<OMXCodecObserver> mObserver;
+ uint32_t mQuirks;
+ bool mIsEncoder;
+ char *mMIME;
+ char *mComponentName;
+ sp<MetaData> mOutputFormat;
+ sp<MediaSource> mSource;
+ Vector<CodecSpecificData *> mCodecSpecificData;
+ size_t mCodecSpecificDataIndex;
+
+ sp<MemoryDealer> mDealer;
+
+ State mState;
+ Vector<BufferInfo> mPortBuffers[2];
+ PortStatus mPortStatus[2];
+ bool mSignalledEOS;
+ bool mNoMoreOutputData;
+ int64_t mSeekTimeUs;
+
+ Mutex mLock;
+ Condition mAsyncCompletion;
+
+ // A list of indices into mPortStatus[kPortIndexOutput] filled with data.
+ List<size_t> mFilledBuffers;
+ Condition mBufferFilled;
+
+ OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
+ bool isEncoder, const char *mime, const char *componentName,
+ const sp<MediaSource> &source);
+
+ void addCodecSpecificData(const void *data, size_t size);
+ void clearCodecSpecificData();
+
+ void setAMRFormat();
+ void setAACFormat();
+
+ status_t setVideoPortFormatType(
+ OMX_U32 portIndex,
+ OMX_VIDEO_CODINGTYPE compressionFormat,
+ OMX_COLOR_FORMATTYPE colorFormat);
+
+ void setVideoInputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height);
+
+ void setVideoOutputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height);
+
+ void setImageOutputFormat(
+ OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height);
+
+ status_t allocateBuffers();
+ status_t allocateBuffersOnPort(OMX_U32 portIndex);
+
+ status_t freeBuffersOnPort(
+ OMX_U32 portIndex, bool onlyThoseWeOwn = false);
+
+ void drainInputBuffer(IOMX::buffer_id buffer);
+ void fillOutputBuffer(IOMX::buffer_id buffer);
+ void drainInputBuffer(BufferInfo *info);
+ void fillOutputBuffer(BufferInfo *info);
+
+ void drainInputBuffers();
+ void fillOutputBuffers();
+
+ void flushPortAsync(OMX_U32 portIndex);
+ void disablePortAsync(OMX_U32 portIndex);
+ void enablePortAsync(OMX_U32 portIndex);
+
+ static size_t countBuffersWeOwn(const Vector<BufferInfo> &buffers);
+ static bool isIntermediateState(State state);
+
+ void onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+ void onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data);
+ void onStateChange(OMX_STATETYPE newState);
+ void onPortSettingsChanged(OMX_U32 portIndex);
+
+ void setState(State newState);
+
+ status_t init();
+ void initOutputFormat(const sp<MetaData> &inputFormat);
+
+ void dumpPortStatus(OMX_U32 portIndex);
+
+ OMXCodec(const OMXCodec &);
+ OMXCodec &operator=(const OMXCodec &);
+};
+
+} // namespace android
+
+#endif // OMX_CODEC_H_
diff --git a/include/media/stagefright/OMXDecoder.h b/include/media/stagefright/OMXDecoder.h
index c6b7cb3..0abc5a6 100644
--- a/include/media/stagefright/OMXDecoder.h
+++ b/include/media/stagefright/OMXDecoder.h
@@ -36,14 +36,10 @@
public OMXObserver,
public MediaBufferObserver {
public:
- static OMXDecoder *Create(
+ static sp<OMXDecoder> Create(
OMXClient *client, const sp<MetaData> &data,
- bool createEncoder = false);
-
- virtual ~OMXDecoder();
-
- // Caller retains ownership of "source".
- void setSource(MediaSource *source);
+ bool createEncoder,
+ const sp<MediaSource> &source);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -61,6 +57,9 @@
// from MediaBufferObserver
virtual void signalBufferReturned(MediaBuffer *buffer);
+protected:
+ virtual ~OMXDecoder();
+
private:
enum {
kPortIndexInput = 0,
@@ -97,7 +96,7 @@
bool mIsEncoder;
uint32_t mQuirks;
- MediaSource *mSource;
+ sp<MediaSource> mSource;
sp<MetaData> mOutputFormat;
Mutex mLock;
@@ -135,7 +134,8 @@
OMXDecoder(OMXClient *client, IOMX::node_id node,
const char *mime, const char *codec,
bool is_encoder,
- uint32_t quirks);
+ uint32_t quirks,
+ const sp<MediaSource> &source);
void setPortStatus(OMX_U32 port_index, PortStatus status);
PortStatus getPortStatus(OMX_U32 port_index) const;
diff --git a/include/media/stagefright/SampleTable.h b/include/media/stagefright/SampleTable.h
index 712da10..808d142 100644
--- a/include/media/stagefright/SampleTable.h
+++ b/include/media/stagefright/SampleTable.h
@@ -22,17 +22,16 @@
#include <stdint.h>
#include <media/stagefright/MediaErrors.h>
+#include <utils/RefBase.h>
#include <utils/threads.h>
namespace android {
class DataSource;
-class SampleTable {
+class SampleTable : public RefBase {
public:
- // Caller retains ownership of "source".
- SampleTable(DataSource *source);
- ~SampleTable();
+ SampleTable(const sp<DataSource> &source);
// type can be 'stco' or 'co64'.
status_t setChunkOffsetParams(
@@ -76,8 +75,11 @@
status_t findClosestSyncSample(
uint32_t start_sample_index, uint32_t *sample_index);
+protected:
+ ~SampleTable();
+
private:
- DataSource *mDataSource;
+ sp<DataSource> mDataSource;
Mutex mLock;
off_t mChunkOffsetOffset;
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index d1dbc5c..ec3241c 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -18,6 +18,8 @@
SEND_COMMAND,
GET_PARAMETER,
SET_PARAMETER,
+ GET_CONFIG,
+ SET_CONFIG,
USE_BUFFER,
ALLOC_BUFFER,
ALLOC_BUFFER_WITH_BACKUP,
@@ -25,6 +27,7 @@
OBSERVE_NODE,
FILL_BUFFER,
EMPTY_BUFFER,
+ GET_EXTENSION_INDEX,
CREATE_RENDERER,
OBSERVER_ON_MSG,
RENDERER_RENDER,
@@ -147,6 +150,41 @@
return reply.readInt32();
}
+ virtual status_t get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(GET_CONFIG, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ return err;
+ }
+
+ reply.read(params, size);
+
+ return OK;
+ }
+
+ virtual status_t set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(SET_CONFIG, data, &reply);
+
+ return reply.readInt32();
+ }
+
virtual status_t use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
@@ -260,6 +298,27 @@
remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
}
+ virtual status_t get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeCString(parameter_name);
+
+ remote()->transact(GET_EXTENSION_INDEX, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ *index = static_cast<OMX_INDEXTYPE>(reply.readInt32());
+ } else {
+ *index = OMX_IndexComponentStartUnused;
+ }
+
+ return err;
+ }
+
virtual sp<IOMXRenderer> createRenderer(
const sp<ISurface> &surface,
const char *componentName,
@@ -394,6 +453,48 @@
return NO_ERROR;
}
+ case GET_CONFIG:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+
+ // XXX I am not happy with this but Parcel::readInplace didn't work.
+ void *params = malloc(size);
+ data.read(params, size);
+
+ status_t err = get_config(node, index, params, size);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->write(params, size);
+ }
+
+ free(params);
+ params = NULL;
+
+ return NO_ERROR;
+ }
+
+ case SET_CONFIG:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+ void *params = const_cast<void *>(data.readInplace(size));
+
+ reply->writeInt32(set_config(node, index, params, size));
+
+ return NO_ERROR;
+ }
+
case USE_BUFFER:
{
CHECK_INTERFACE(IOMX, data, reply);
@@ -508,6 +609,25 @@
return NO_ERROR;
}
+ case GET_EXTENSION_INDEX:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ const char *parameter_name = data.readCString();
+
+ OMX_INDEXTYPE index;
+ status_t err = get_extension_index(node, parameter_name, &index);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->writeInt32(index);
+ }
+
+ return OK;
+ }
+
case CREATE_RENDERER:
{
CHECK_INTERFACE(IOMX, data, reply);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 00ba1ac..0c40b91 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -17,6 +17,7 @@
MediaSource.cpp \
MetaData.cpp \
MmapSource.cpp \
+ OMXCodec.cpp \
SampleTable.cpp \
ShoutcastSource.cpp \
TimeSource.cpp \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index d547556..e8571b5 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -28,8 +28,7 @@
namespace android {
AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
- : mSource(NULL),
- mAudioTrack(NULL),
+ : mAudioTrack(NULL),
mInputBuffer(NULL),
mSampleRate(0),
mLatencyUs(0),
@@ -48,7 +47,7 @@
}
}
-void AudioPlayer::setSource(MediaSource *source) {
+void AudioPlayer::setSource(const sp<MediaSource> &source) {
assert(mSource == NULL);
mSource = source;
}
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
index 0fd71d5..d599cd5 100644
--- a/media/libstagefright/CachingDataSource.cpp
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -25,7 +25,7 @@
namespace android {
CachingDataSource::CachingDataSource(
- DataSource *source, size_t pageSize, int numPages)
+ const sp<DataSource> &source, size_t pageSize, int numPages)
: mSource(source),
mData(malloc(pageSize * numPages)),
mPageSize(pageSize),
@@ -61,9 +61,6 @@
free(mData);
mData = NULL;
-
- delete mSource;
- mSource = NULL;
}
status_t CachingDataSource::InitCheck() const {
@@ -78,7 +75,7 @@
Page *page = mFirst;
while (page != NULL) {
if (page->mOffset >= 0 && offset >= page->mOffset
- && offset < page->mOffset + page->mLength) {
+ && offset < page->mOffset + (off_t)page->mLength) {
break;
}
page = page->mNext;
@@ -102,7 +99,7 @@
return n;
}
- if (offset >= page->mOffset + page->mLength) {
+ if (offset >= page->mOffset + (off_t)page->mLength) {
break;
}
} else {
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 6e6b43d..02a276b 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -22,6 +22,19 @@
namespace android {
+bool DataSource::getUInt16(off_t offset, uint16_t *x) {
+ *x = 0;
+
+ uint8_t byte[2];
+ if (read_at(offset, byte, 2) != 2) {
+ return false;
+ }
+
+ *x = (byte[0] << 8) | byte[1];
+
+ return true;
+}
+
status_t DataSource::getSize(off_t *size) {
*size = 0;
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 01cb2d9..44258ba 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -165,7 +165,7 @@
}
static bool Resync(
- DataSource *source, uint32_t match_header,
+ const sp<DataSource> &source, uint32_t match_header,
off_t *inout_pos, uint32_t *out_header) {
// Everything must match except for
// protection, bitrate, padding, private bits and mode extension.
@@ -281,11 +281,9 @@
class MP3Source : public MediaSource {
public:
MP3Source(
- const sp<MetaData> &meta, DataSource *source,
+ const sp<MetaData> &meta, const sp<DataSource> &source,
off_t first_frame_pos, uint32_t fixed_header);
- virtual ~MP3Source();
-
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -294,9 +292,12 @@
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
+protected:
+ virtual ~MP3Source();
+
private:
sp<MetaData> mMeta;
- DataSource *mDataSource;
+ sp<DataSource> mDataSource;
off_t mFirstFramePos;
uint32_t mFixedHeader;
off_t mCurrentPos;
@@ -309,7 +310,7 @@
MP3Source &operator=(const MP3Source &);
};
-MP3Extractor::MP3Extractor(DataSource *source)
+MP3Extractor::MP3Extractor(const sp<DataSource> &source)
: mDataSource(source),
mFirstFramePos(-1),
mFixedHeader(0) {
@@ -347,28 +348,22 @@
}
MP3Extractor::~MP3Extractor() {
- delete mDataSource;
- mDataSource = NULL;
}
-status_t MP3Extractor::countTracks(int *num_tracks) {
- *num_tracks = mFirstFramePos < 0 ? 0 : 1;
-
- return OK;
+size_t MP3Extractor::countTracks() {
+ return (mFirstFramePos < 0) ? 0 : 1;
}
-status_t MP3Extractor::getTrack(int index, MediaSource **source) {
+sp<MediaSource> MP3Extractor::getTrack(size_t index) {
if (mFirstFramePos < 0 || index != 0) {
- return ERROR_OUT_OF_RANGE;
+ return NULL;
}
- *source = new MP3Source(
+ return new MP3Source(
mMeta, mDataSource, mFirstFramePos, mFixedHeader);
-
- return OK;
}
-sp<MetaData> MP3Extractor::getTrackMetaData(int index) {
+sp<MetaData> MP3Extractor::getTrackMetaData(size_t index) {
if (mFirstFramePos < 0 || index != 0) {
return NULL;
}
@@ -379,7 +374,7 @@
////////////////////////////////////////////////////////////////////////////////
MP3Source::MP3Source(
- const sp<MetaData> &meta, DataSource *source,
+ const sp<MetaData> &meta, const sp<DataSource> &source,
off_t first_frame_pos, uint32_t fixed_header)
: mMeta(meta),
mDataSource(source),
@@ -509,7 +504,8 @@
return OK;
}
-bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence) {
+bool SniffMP3(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
off_t pos = 0;
uint32_t header;
if (!Resync(source, 0, &pos, &header)) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 4c883c6..662d5fb 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -42,10 +42,9 @@
class MPEG4Source : public MediaSource {
public:
// Caller retains ownership of both "dataSource" and "sampleTable".
- MPEG4Source(const sp<MetaData> &format, DataSource *dataSource,
- SampleTable *sampleTable);
-
- virtual ~MPEG4Source();
+ MPEG4Source(const sp<MetaData> &format,
+ const sp<DataSource> &dataSource,
+ const sp<SampleTable> &sampleTable);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -55,11 +54,14 @@
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
+protected:
+ virtual ~MPEG4Source();
+
private:
sp<MetaData> mFormat;
- DataSource *mDataSource;
+ sp<DataSource> mDataSource;
int32_t mTimescale;
- SampleTable *mSampleTable;
+ sp<SampleTable> mSampleTable;
uint32_t mCurrentSampleIndex;
bool mIsAVC;
@@ -141,7 +143,7 @@
}
}
-MPEG4Extractor::MPEG4Extractor(DataSource *source)
+MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
: mDataSource(source),
mHaveMetadata(false),
mFirstTrack(NULL),
@@ -153,39 +155,29 @@
while (track) {
Track *next = track->next;
- delete track->sampleTable;
- track->sampleTable = NULL;
-
delete track;
track = next;
}
mFirstTrack = mLastTrack = NULL;
-
- delete mDataSource;
- mDataSource = NULL;
}
-status_t MPEG4Extractor::countTracks(int *num_tracks) {
+size_t MPEG4Extractor::countTracks() {
status_t err;
if ((err = readMetaData()) != OK) {
- return err;
+ return 0;
}
- *num_tracks = 0;
+ size_t n = 0;
Track *track = mFirstTrack;
while (track) {
- ++*num_tracks;
+ ++n;
track = track->next;
}
- return OK;
+ return n;
}
-sp<MetaData> MPEG4Extractor::getTrackMetaData(int index) {
- if (index < 0) {
- return NULL;
- }
-
+sp<MetaData> MPEG4Extractor::getTrackMetaData(size_t index) {
status_t err;
if ((err = readMetaData()) != OK) {
return NULL;
@@ -701,39 +693,32 @@
return OK;
}
-status_t MPEG4Extractor::getTrack(int index, MediaSource **source) {
- *source = NULL;
-
- if (index < 0) {
- return ERROR_OUT_OF_RANGE;
- }
-
+sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
status_t err;
if ((err = readMetaData()) != OK) {
- return err;
+ return NULL;
}
Track *track = mFirstTrack;
while (index > 0) {
if (track == NULL) {
- return ERROR_OUT_OF_RANGE;
+ return NULL;
}
track = track->next;
--index;
}
- *source = new MPEG4Source(
+ return new MPEG4Source(
track->meta, mDataSource, track->sampleTable);
-
- return OK;
}
////////////////////////////////////////////////////////////////////////////////
MPEG4Source::MPEG4Source(
const sp<MetaData> &format,
- DataSource *dataSource, SampleTable *sampleTable)
+ const sp<DataSource> &dataSource,
+ const sp<SampleTable> &sampleTable)
: mFormat(format),
mDataSource(dataSource),
mTimescale(0),
@@ -935,7 +920,8 @@
return OK;
}
-bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence) {
+bool SniffMPEG4(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
uint8_t header[8];
ssize_t n = source->read_at(4, header, sizeof(header));
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index b53bb29..10c4629 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -32,7 +32,8 @@
class MPEG4Writer::Track {
public:
- Track(MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source);
+ Track(MPEG4Writer *owner,
+ const sp<MetaData> &meta, const sp<MediaSource> &source);
~Track();
void start();
@@ -44,7 +45,7 @@
private:
MPEG4Writer *mOwner;
sp<MetaData> mMeta;
- MediaSource *mSource;
+ sp<MediaSource> mSource;
volatile bool mDone;
pthread_t mThread;
@@ -83,7 +84,8 @@
mTracks.clear();
}
-void MPEG4Writer::addSource(const sp<MetaData> &meta, MediaSource *source) {
+void MPEG4Writer::addSource(
+ const sp<MetaData> &meta, const sp<MediaSource> &source) {
Track *track = new Track(this, meta, source);
mTracks.push_back(track);
}
@@ -255,7 +257,8 @@
////////////////////////////////////////////////////////////////////////////////
MPEG4Writer::Track::Track(
- MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source)
+ MPEG4Writer *owner,
+ const sp<MetaData> &meta, const sp<MediaSource> &source)
: mOwner(owner),
mMeta(meta),
mSource(source),
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index bc66794..5f78e12 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -27,7 +27,8 @@
namespace android {
// static
-MediaExtractor *MediaExtractor::Create(DataSource *source, const char *mime) {
+sp<MediaExtractor> MediaExtractor::Create(
+ const sp<DataSource> &source, const char *mime) {
String8 tmp;
if (mime == NULL) {
float confidence;
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
index f2e62f5..2d7b62837b 100644
--- a/media/libstagefright/MediaPlayerImpl.cpp
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -34,32 +34,28 @@
#include <media/stagefright/MediaPlayerImpl.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/OMXDecoder.h>
#include <media/stagefright/ShoutcastSource.h>
#include <media/stagefright/TimeSource.h>
#include <ui/PixelFormat.h>
#include <ui/Surface.h>
+#define USE_OMX_CODEC 1
+
namespace android {
MediaPlayerImpl::MediaPlayerImpl(const char *uri)
: mInitCheck(NO_INIT),
- mExtractor(NULL),
mTimeSource(NULL),
- mAudioSource(NULL),
- mAudioDecoder(NULL),
mAudioPlayer(NULL),
- mVideoSource(NULL),
- mVideoDecoder(NULL),
mVideoWidth(0),
mVideoHeight(0),
mVideoPosition(0),
mDuration(0),
mPlaying(false),
mPaused(false),
- mSeeking(false),
- mFrameSize(0),
- mUseSoftwareColorConversion(false) {
+ mSeeking(false) {
LOGI("MediaPlayerImpl(%s)", uri);
DataSource::RegisterDefaultSniffers();
@@ -78,7 +74,7 @@
mVideoDecoder = CameraSource::Create();
#endif
} else {
- DataSource *source = NULL;
+ sp<DataSource> source;
if (!strncasecmp("file://", uri, 7)) {
source = new MmapSource(uri + 7);
} else if (!strncasecmp("http://", uri, 7)) {
@@ -103,22 +99,15 @@
MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length)
: mInitCheck(NO_INIT),
- mExtractor(NULL),
mTimeSource(NULL),
- mAudioSource(NULL),
- mAudioDecoder(NULL),
mAudioPlayer(NULL),
- mVideoSource(NULL),
- mVideoDecoder(NULL),
mVideoWidth(0),
mVideoHeight(0),
mVideoPosition(0),
mDuration(0),
mPlaying(false),
mPaused(false),
- mSeeking(false),
- mFrameSize(0),
- mUseSoftwareColorConversion(false) {
+ mSeeking(false) {
LOGI("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length);
DataSource::RegisterDefaultSniffers();
@@ -148,23 +137,6 @@
stop();
setSurface(NULL);
- LOGV("Shutting down audio.");
- delete mAudioDecoder;
- mAudioDecoder = NULL;
-
- delete mAudioSource;
- mAudioSource = NULL;
-
- LOGV("Shutting down video.");
- delete mVideoDecoder;
- mVideoDecoder = NULL;
-
- delete mVideoSource;
- mVideoSource = NULL;
-
- delete mExtractor;
- mExtractor = NULL;
-
if (mInitCheck == OK) {
mClient.disconnect();
}
@@ -384,12 +356,11 @@
void MediaPlayerImpl::init() {
if (mExtractor != NULL) {
- int num_tracks;
- assert(mExtractor->countTracks(&num_tracks) == OK);
+ size_t num_tracks = mExtractor->countTracks();
mDuration = 0;
- for (int i = 0; i < num_tracks; ++i) {
+ for (size_t i = 0; i < num_tracks; ++i) {
const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
assert(meta != NULL);
@@ -411,10 +382,7 @@
continue;
}
- MediaSource *source;
- if (mExtractor->getTrack(i, &source) != OK) {
- continue;
- }
+ sp<MediaSource> source = mExtractor->getTrack(i);
int32_t units, scale;
if (meta->findInt32(kKeyDuration, &units)
@@ -434,17 +402,22 @@
}
}
-void MediaPlayerImpl::setAudioSource(MediaSource *source) {
+void MediaPlayerImpl::setAudioSource(const sp<MediaSource> &source) {
LOGI("setAudioSource");
mAudioSource = source;
sp<MetaData> meta = source->getFormat();
- mAudioDecoder = OMXDecoder::Create(&mClient, meta);
- mAudioDecoder->setSource(source);
+#if !USE_OMX_CODEC
+ mAudioDecoder = OMXDecoder::Create(
+ &mClient, meta, false /* createEncoder */, source);
+#else
+ mAudioDecoder = OMXCodec::Create(
+ mClient.interface(), meta, false /* createEncoder */, source);
+#endif
}
-void MediaPlayerImpl::setVideoSource(MediaSource *source) {
+void MediaPlayerImpl::setVideoSource(const sp<MediaSource> &source) {
LOGI("setVideoSource");
mVideoSource = source;
@@ -456,8 +429,13 @@
success = meta->findInt32(kKeyHeight, &mVideoHeight);
assert(success);
- mVideoDecoder = OMXDecoder::Create(&mClient, meta);
- ((OMXDecoder *)mVideoDecoder)->setSource(source);
+#if !USE_OMX_CODEC
+ mVideoDecoder = OMXDecoder::Create(
+ &mClient, meta, false /* createEncoder */, source);
+#else
+ mVideoDecoder = OMXCodec::Create(
+ mClient.interface(), meta, false /* createEncoder */, source);
+#endif
if (mISurface.get() != NULL || mSurface.get() != NULL) {
depopulateISurface();
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 3e7cf3c..5423ffa 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -132,7 +132,7 @@
}
Mutex::Autolock autoLock(mLock);
- ssize_t index = mObservers.indexOfKey(msg.u.buffer_data.node);
+ ssize_t index = mObservers.indexOfKey(msg.node);
if (index >= 0) {
mObservers.editValueAt(index)->postMessage(msg);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
new file mode 100644
index 0000000..35d599c
--- /dev/null
+++ b/media/libstagefright/OMXCodec.cpp
@@ -0,0 +1,1962 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "OMXCodec"
+#include <utils/Log.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <binder/ProcessState.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/ESDS.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXCodec.h>
+#include <utils/Vector.h>
+
+#include <OMX_Audio.h>
+#include <OMX_Component.h>
+
+namespace android {
+
+struct CodecInfo {
+ const char *mime;
+ const char *codec;
+};
+
+static const CodecInfo kDecoderInfo[] = {
+ { "image/jpeg", "OMX.TI.JPEG.decode" },
+ { "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" },
+};
+
+static const CodecInfo kEncoderInfo[] = {
+ { "audio/3gpp", "OMX.TI.AMR.encode" },
+ { "audio/3gpp", "OMX.PV.amrencnb" },
+ { "audio/mp4a-latm", "OMX.TI.AAC.encode" },
+ { "audio/mp4a-latm", "OMX.PV.aacenc" },
+ { "video/mp4v-es", "OMX.qcom.video.encoder.mpeg4" },
+ { "video/mp4v-es", "OMX.TI.Video.encoder" },
+ { "video/mp4v-es", "OMX.PV.mpeg4enc" },
+ { "video/3gpp", "OMX.qcom.video.encoder.h263" },
+ { "video/3gpp", "OMX.TI.Video.encoder" },
+ { "video/3gpp", "OMX.PV.h263enc" },
+ { "video/avc", "OMX.TI.Video.encoder" },
+ { "video/avc", "OMX.PV.avcenc" },
+};
+
+struct OMXCodecObserver : public BnOMXObserver {
+ OMXCodecObserver(const wp<OMXCodec> &target)
+ : mTarget(target) {
+ }
+
+ // from IOMXObserver
+ virtual void on_message(const omx_message &msg) {
+ sp<OMXCodec> codec = mTarget.promote();
+
+ if (codec.get() != NULL) {
+ codec->on_message(msg);
+ }
+ }
+
+protected:
+ virtual ~OMXCodecObserver() {}
+
+private:
+ wp<OMXCodec> mTarget;
+
+ OMXCodecObserver(const OMXCodecObserver &);
+ OMXCodecObserver &operator=(const OMXCodecObserver &);
+};
+
+static const char *GetCodec(const CodecInfo *info, size_t numInfos,
+ const char *mime, int index) {
+ CHECK(index >= 0);
+ for(size_t i = 0; i < numInfos; ++i) {
+ if (!strcasecmp(mime, info[i].mime)) {
+ if (index == 0) {
+ return info[i].codec;
+ }
+
+ --index;
+ }
+ }
+
+ return NULL;
+}
+
+// static
+sp<OMXCodec> OMXCodec::Create(
+ const sp<IOMX> &omx,
+ const sp<MetaData> &meta, bool createEncoder,
+ const sp<MediaSource> &source) {
+ const char *mime;
+ bool success = meta->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+
+ const char *componentName = NULL;
+ IOMX::node_id node = 0;
+ for (int index = 0;; ++index) {
+ if (createEncoder) {
+ componentName = GetCodec(
+ kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+ mime, index);
+ } else {
+ componentName = GetCodec(
+ kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+ mime, index);
+ }
+
+ if (!componentName) {
+ return NULL;
+ }
+
+ LOGV("Attempting to allocate OMX node '%s'", componentName);
+
+ status_t err = omx->allocate_node(componentName, &node);
+ if (err == OK) {
+ break;
+ }
+ }
+
+ uint32_t quirks = 0;
+ if (!strcmp(componentName, "OMX.PV.avcdec")) {
+ quirks |= kWantsRawNALFrames;
+ }
+ if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
+ quirks |= kNeedsFlushBeforeDisable;
+ }
+ if (!strcmp(componentName, "OMX.TI.AAC.decode")) {
+ quirks |= kNeedsFlushBeforeDisable;
+ }
+ if (!strncmp(componentName, "OMX.qcom.video.encoder.", 23)) {
+ quirks |= kRequiresLoadedToIdleAfterAllocation;
+ quirks |= kRequiresAllocateBufferOnInputPorts;
+ }
+
+ sp<OMXCodec> codec = new OMXCodec(
+ omx, node, quirks, createEncoder, mime, componentName,
+ source);
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyESDS, &type, &data, &size)) {
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ printf("found codec-specific data of size %d\n",
+ codec_specific_data_size);
+
+ codec->addCodecSpecificData(
+ codec_specific_data, codec_specific_data_size);
+ } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ printf("found avcc of size %d\n", size);
+
+ const uint8_t *ptr = (const uint8_t *)data + 6;
+ size -= 6;
+ while (size >= 2) {
+ size_t length = ptr[0] << 8 | ptr[1];
+
+ ptr += 2;
+ size -= 2;
+
+ // printf("length = %d, size = %d\n", length, size);
+
+ CHECK(size >= length);
+
+ codec->addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+
+ if (size <= 1) {
+ break;
+ }
+
+ ptr++; // XXX skip trailing 0x01 byte???
+ --size;
+ }
+ }
+
+ if (!strcasecmp("audio/3gpp", mime)) {
+ codec->setAMRFormat();
+ }
+ if (!createEncoder && !strcasecmp("audio/mp4a-latm", mime)) {
+ codec->setAACFormat();
+ }
+ if (!strncasecmp(mime, "video/", 6)) {
+ int32_t width, height;
+ bool success = meta->findInt32(kKeyWidth, &width);
+ success = success && meta->findInt32(kKeyHeight, &height);
+ assert(success);
+
+ if (createEncoder) {
+ codec->setVideoInputFormat(mime, width, height);
+ } else {
+ codec->setVideoOutputFormat(mime, width, height);
+ }
+ }
+ if (!strcasecmp(mime, "image/jpeg")
+ && !strcmp(componentName, "OMX.TI.JPEG.decode")) {
+ OMX_COLOR_FORMATTYPE format =
+ OMX_COLOR_Format32bitARGB8888;
+ // OMX_COLOR_FormatYUV420PackedPlanar;
+ // OMX_COLOR_FormatCbYCrY;
+ // OMX_COLOR_FormatYUV411Planar;
+
+ int32_t width, height;
+ bool success = meta->findInt32(kKeyWidth, &width);
+ success = success && meta->findInt32(kKeyHeight, &height);
+ assert(success);
+
+ codec->setImageOutputFormat(format, width, height);
+ }
+
+ codec->initOutputFormat(meta);
+
+ return codec;
+}
+
+status_t OMXCodec::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 1
+ LOGI("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
+ portIndex,
+ index, format.eCompressionFormat, format.eColorFormat);
+#endif
+
+ if (!strcmp("OMX.TI.Video.encoder", mComponentName)) {
+ if (portIndex == kPortIndexInput
+ && colorFormat == format.eColorFormat) {
+ // eCompressionFormat does not seem right.
+ found = true;
+ break;
+ }
+ if (portIndex == kPortIndexOutput
+ && compressionFormat == format.eCompressionFormat) {
+ // eColorFormat does not seem right.
+ found = true;
+ break;
+ }
+ }
+
+ if (format.eCompressionFormat == compressionFormat
+ && format.eColorFormat == colorFormat) {
+ found = true;
+ break;
+ }
+
+ ++index;
+ }
+
+ if (!found) {
+ return UNKNOWN_ERROR;
+ }
+
+ LOGI("found a match.");
+ status_t err = mOMX->set_parameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+
+ return err;
+}
+
+void OMXCodec::setVideoInputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height) {
+ LOGI("setVideoInputFormat width=%ld, height=%ld", width, height);
+
+ 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 {
+ LOGE("Not a supported video mime type: %s", mime);
+ CHECK(!"Should not be here. Not a supported video mime type.");
+ }
+
+ OMX_COLOR_FORMATTYPE colorFormat =
+ 0 ? OMX_COLOR_FormatYCbYCr : OMX_COLOR_FormatCbYCrY;
+
+ if (!strncmp("OMX.qcom.video.encoder.", mComponentName, 23)) {
+ colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+ }
+
+ setVideoPortFormatType(
+ kPortIndexInput, OMX_VIDEO_CodingUnused,
+ colorFormat);
+
+ setVideoPortFormatType(
+ kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ CHECK_EQ(err, OK);
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ video_def->eCompressionFormat = compressionFormat;
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ 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));
+ CHECK_EQ(err, OK);
+
+ def.nBufferSize = (width * height * 2); // (width * height * 3) / 2;
+ LOGI("setting nBufferSize = %ld", def.nBufferSize);
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+ video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ video_def->eColorFormat = colorFormat;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::setVideoOutputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height) {
+ LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+
+ // 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 (!strcmp("OMX.TI.Video.Decoder", mComponentName)
+ && !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));
+ CHECK_EQ(err, OK);
+ }
+
+ 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 {
+ LOGE("Not a supported video mime type: %s", mime);
+ CHECK(!"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));
+ CHECK_EQ(err, OK);
+ CHECK_EQ(format.eCompressionFormat, OMX_VIDEO_CodingUnused);
+
+ static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ CHECK(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));
+ CHECK_EQ(err, OK);
+ }
+#endif
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ CHECK_EQ(err, OK);
+
+#if 1
+ // XXX Need a (much) better heuristic to compute input buffer sizes.
+ const size_t X = 64 * 1024;
+ if (def.nBufferSize < X) {
+ def.nBufferSize = X;
+ }
+#endif
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+ CHECK_EQ(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;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+
+OMXCodec::OMXCodec(
+ const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
+ bool isEncoder,
+ const char *mime,
+ const char *componentName,
+ const sp<MediaSource> &source)
+ : mOMX(omx),
+ mNode(node),
+ mQuirks(quirks),
+ mIsEncoder(isEncoder),
+ mMIME(strdup(mime)),
+ mComponentName(strdup(componentName)),
+ mSource(source),
+ mCodecSpecificDataIndex(0),
+ mDealer(new MemoryDealer(5 * 1024 * 1024)),
+ mState(LOADED),
+ mSignalledEOS(false),
+ mNoMoreOutputData(false),
+ mSeekTimeUs(-1) {
+ mPortStatus[kPortIndexInput] = ENABLED;
+ mPortStatus[kPortIndexOutput] = ENABLED;
+
+ mObserver = new OMXCodecObserver(this);
+ mOMX->observe_node(mNode, mObserver);
+}
+
+OMXCodec::~OMXCodec() {
+ CHECK_EQ(mState, LOADED);
+
+ status_t err = mOMX->observe_node(mNode, NULL);
+ CHECK_EQ(err, OK);
+
+ err = mOMX->free_node(mNode);
+ CHECK_EQ(err, OK);
+
+ mNode = NULL;
+ setState(DEAD);
+
+ clearCodecSpecificData();
+
+ free(mComponentName);
+ mComponentName = NULL;
+
+ free(mMIME);
+ mMIME = NULL;
+}
+
+status_t OMXCodec::init() {
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK_EQ(mState, LOADED);
+
+ status_t err;
+ if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
+ err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ CHECK_EQ(err, OK);
+
+ setState(LOADED_TO_IDLE);
+ }
+
+ err = allocateBuffers();
+ CHECK_EQ(err, OK);
+
+ if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
+ err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ CHECK_EQ(err, OK);
+
+ setState(LOADED_TO_IDLE);
+ }
+
+ while (mState != EXECUTING && mState != ERROR) {
+ mAsyncCompletion.wait(mLock);
+ }
+
+ return mState == ERROR ? UNKNOWN_ERROR : OK;
+}
+
+// static
+bool OMXCodec::isIntermediateState(State state) {
+ return state == LOADED_TO_IDLE
+ || state == IDLE_TO_EXECUTING
+ || state == EXECUTING_TO_IDLE
+ || state == IDLE_TO_LOADED
+ || state == RECONFIGURING;
+}
+
+status_t OMXCodec::allocateBuffers() {
+ status_t err = allocateBuffersOnPort(kPortIndexInput);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return allocateBuffersOnPort(kPortIndexOutput);
+}
+
+status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nVersion.s.nRevision = 0;
+ def.nVersion.s.nStep = 0;
+ def.nPortIndex = portIndex;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ sp<IMemory> mem = mDealer->allocate(def.nBufferSize);
+ CHECK(mem.get() != NULL);
+
+ IOMX::buffer_id buffer;
+ if (portIndex == kPortIndexInput
+ && (mQuirks & kRequiresAllocateBufferOnInputPorts)) {
+ err = mOMX->allocate_buffer_with_backup(
+ mNode, portIndex, mem, &buffer);
+ } else {
+ err = mOMX->use_buffer(mNode, portIndex, mem, &buffer);
+ }
+
+ if (err != OK) {
+ LOGE("allocate_buffer_with_backup failed");
+ return err;
+ }
+
+ BufferInfo info;
+ info.mBuffer = buffer;
+ info.mOwnedByComponent = false;
+ info.mMem = mem;
+ info.mMediaBuffer = NULL;
+
+ if (portIndex == kPortIndexOutput) {
+ info.mMediaBuffer = new MediaBuffer(mem->pointer(), mem->size());
+ info.mMediaBuffer->setObserver(this);
+ }
+
+ mPortBuffers[portIndex].push(info);
+
+ LOGV("allocated buffer %p on %s port", buffer,
+ portIndex == kPortIndexInput ? "input" : "output");
+ }
+
+ dumpPortStatus(portIndex);
+
+ return OK;
+}
+
+void OMXCodec::on_message(const omx_message &msg) {
+ Mutex::Autolock autoLock(mLock);
+
+ switch (msg.type) {
+ case omx_message::EVENT:
+ {
+ onEvent(
+ msg.u.event_data.event, msg.u.event_data.data1,
+ msg.u.event_data.data2);
+
+ break;
+ }
+
+ case omx_message::EMPTY_BUFFER_DONE:
+ {
+ IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
+
+ LOGV("EMPTY_BUFFER_DONE(buffer: %p)", buffer);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+ size_t i = 0;
+ while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) {
+ ++i;
+ }
+
+ CHECK(i < buffers->size());
+ if (!(*buffers)[i].mOwnedByComponent) {
+ LOGW("We already own input buffer %p, yet received "
+ "an EMPTY_BUFFER_DONE.", buffer);
+ }
+
+ buffers->editItemAt(i).mOwnedByComponent = false;
+
+ if (mPortStatus[kPortIndexInput] == DISABLING) {
+ LOGV("Port is disabled, freeing buffer %p", buffer);
+
+ status_t err =
+ mOMX->free_buffer(mNode, kPortIndexInput, buffer);
+ CHECK_EQ(err, OK);
+
+ buffers->removeAt(i);
+ } else if (mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
+ CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
+ drainInputBuffer(&buffers->editItemAt(i));
+ }
+
+ break;
+ }
+
+ case omx_message::FILL_BUFFER_DONE:
+ {
+ IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
+ OMX_U32 flags = msg.u.extended_buffer_data.flags;
+
+ LOGV("FILL_BUFFER_DONE(buffer: %p, size: %ld, flags: 0x%08lx)",
+ buffer,
+ msg.u.extended_buffer_data.range_length,
+ flags);
+
+ LOGV("FILL_BUFFER_DONE(timestamp: %lld us (%.2f secs))",
+ msg.u.extended_buffer_data.timestamp,
+ msg.u.extended_buffer_data.timestamp / 1E6);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ size_t i = 0;
+ while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) {
+ ++i;
+ }
+
+ CHECK(i < buffers->size());
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (!info->mOwnedByComponent) {
+ LOGW("We already own output buffer %p, yet received "
+ "a FILL_BUFFER_DONE.", buffer);
+ }
+
+ info->mOwnedByComponent = false;
+
+ if (mPortStatus[kPortIndexOutput] == DISABLING) {
+ LOGV("Port is disabled, freeing buffer %p", buffer);
+
+ status_t err =
+ mOMX->free_buffer(mNode, kPortIndexOutput, buffer);
+ CHECK_EQ(err, OK);
+
+ buffers->removeAt(i);
+ } else if (flags & OMX_BUFFERFLAG_EOS) {
+ LOGV("No more output data.");
+ mNoMoreOutputData = true;
+ mBufferFilled.signal();
+ } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) {
+ CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+
+ MediaBuffer *buffer = info->mMediaBuffer;
+
+ buffer->set_range(
+ msg.u.extended_buffer_data.range_offset,
+ msg.u.extended_buffer_data.range_length);
+
+ buffer->meta_data()->clear();
+
+ buffer->meta_data()->setInt32(
+ kKeyTimeUnits,
+ (msg.u.extended_buffer_data.timestamp + 500) / 1000);
+
+ buffer->meta_data()->setInt32(
+ kKeyTimeScale, 1000);
+
+ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
+ }
+
+ buffer->meta_data()->setPointer(
+ kKeyPlatformPrivate,
+ msg.u.extended_buffer_data.platform_private);
+
+ buffer->meta_data()->setPointer(
+ kKeyBufferID,
+ msg.u.extended_buffer_data.buffer);
+
+ mFilledBuffers.push_back(i);
+ mBufferFilled.signal();
+ }
+
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here.");
+ break;
+ }
+ }
+}
+
+void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ onCmdComplete((OMX_COMMANDTYPE)data1, data2);
+ break;
+ }
+
+ case OMX_EventError:
+ {
+ LOGE("ERROR(%ld, %ld)", data1, data2);
+
+ setState(ERROR);
+ break;
+ }
+
+ case OMX_EventPortSettingsChanged:
+ {
+ onPortSettingsChanged(data1);
+ break;
+ }
+
+ case OMX_EventBufferFlag:
+ {
+ LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
+
+ if (data1 == kPortIndexOutput) {
+ mNoMoreOutputData = true;
+ }
+ break;
+ }
+
+ default:
+ {
+ LOGV("EVENT(%d, %ld, %ld)", event, data1, data2);
+ break;
+ }
+ }
+}
+
+void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
+ switch (cmd) {
+ case OMX_CommandStateSet:
+ {
+ onStateChange((OMX_STATETYPE)data);
+ break;
+ }
+
+ case OMX_CommandPortDisable:
+ {
+ OMX_U32 portIndex = data;
+ LOGV("PORT_DISABLED(%ld)", portIndex);
+
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+ CHECK_EQ(mPortStatus[portIndex], DISABLING);
+ CHECK_EQ(mPortBuffers[portIndex].size(), 0);
+
+ mPortStatus[portIndex] = DISABLED;
+
+ if (mState == RECONFIGURING) {
+ CHECK_EQ(portIndex, kPortIndexOutput);
+
+ enablePortAsync(portIndex);
+
+ status_t err = allocateBuffersOnPort(portIndex);
+ CHECK_EQ(err, OK);
+ }
+ break;
+ }
+
+ case OMX_CommandPortEnable:
+ {
+ OMX_U32 portIndex = data;
+ LOGV("PORT_ENABLED(%ld)", portIndex);
+
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+ CHECK_EQ(mPortStatus[portIndex], ENABLING);
+
+ mPortStatus[portIndex] = ENABLED;
+
+ if (mState == RECONFIGURING) {
+ CHECK_EQ(portIndex, kPortIndexOutput);
+
+ setState(EXECUTING);
+
+ fillOutputBuffers();
+ }
+ break;
+ }
+
+ case OMX_CommandFlush:
+ {
+ OMX_U32 portIndex = data;
+
+ LOGV("FLUSH_DONE(%ld)", portIndex);
+
+ CHECK_EQ(mPortStatus[portIndex], SHUTTING_DOWN);
+ mPortStatus[portIndex] = ENABLED;
+
+ CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]),
+ mPortBuffers[portIndex].size());
+
+ if (mState == RECONFIGURING) {
+ CHECK_EQ(portIndex, kPortIndexOutput);
+
+ disablePortAsync(portIndex);
+ } else {
+ // We're flushing both ports in preparation for seeking.
+
+ if (mPortStatus[kPortIndexInput] == ENABLED
+ && mPortStatus[kPortIndexOutput] == ENABLED) {
+ LOGV("Finished flushing both ports, now continuing from"
+ " seek-time.");
+
+ drainInputBuffers();
+ fillOutputBuffers();
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ LOGV("CMD_COMPLETE(%d, %ld)", cmd, data);
+ break;
+ }
+ }
+}
+
+void OMXCodec::onStateChange(OMX_STATETYPE newState) {
+ switch (newState) {
+ case OMX_StateIdle:
+ {
+ LOGV("Now Idle.");
+ if (mState == LOADED_TO_IDLE) {
+ status_t err = mOMX->send_command(
+ mNode, OMX_CommandStateSet, OMX_StateExecuting);
+
+ CHECK_EQ(err, OK);
+
+ setState(IDLE_TO_EXECUTING);
+ } else {
+ CHECK_EQ(mState, EXECUTING_TO_IDLE);
+
+ CHECK_EQ(
+ countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
+ mPortBuffers[kPortIndexInput].size());
+
+ CHECK_EQ(
+ countBuffersWeOwn(mPortBuffers[kPortIndexOutput]),
+ mPortBuffers[kPortIndexOutput].size());
+
+ status_t err = mOMX->send_command(
+ mNode, OMX_CommandStateSet, OMX_StateLoaded);
+
+ CHECK_EQ(err, OK);
+
+ err = freeBuffersOnPort(kPortIndexInput);
+ CHECK_EQ(err, OK);
+
+ err = freeBuffersOnPort(kPortIndexOutput);
+ CHECK_EQ(err, OK);
+
+ mPortStatus[kPortIndexInput] = ENABLED;
+ mPortStatus[kPortIndexOutput] = ENABLED;
+
+ setState(IDLE_TO_LOADED);
+ }
+ break;
+ }
+
+ case OMX_StateExecuting:
+ {
+ CHECK_EQ(mState, IDLE_TO_EXECUTING);
+
+ LOGV("Now Executing.");
+
+ setState(EXECUTING);
+
+ drainInputBuffers();
+ fillOutputBuffers();
+ break;
+ }
+
+ case OMX_StateLoaded:
+ {
+ CHECK_EQ(mState, IDLE_TO_LOADED);
+
+ LOGV("Now Loaded.");
+
+ setState(LOADED);
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here.");
+ break;
+ }
+ }
+}
+
+// static
+size_t OMXCodec::countBuffersWeOwn(const Vector<BufferInfo> &buffers) {
+ size_t n = 0;
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ if (!buffers[i].mOwnedByComponent) {
+ ++n;
+ }
+ }
+
+ return n;
+}
+
+status_t OMXCodec::freeBuffersOnPort(
+ OMX_U32 portIndex, bool onlyThoseWeOwn) {
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+ status_t stickyErr = OK;
+
+ for (size_t i = buffers->size(); i-- > 0;) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (onlyThoseWeOwn && info->mOwnedByComponent) {
+ continue;
+ }
+
+ CHECK_EQ(info->mOwnedByComponent, false);
+
+ status_t err =
+ mOMX->free_buffer(mNode, portIndex, info->mBuffer);
+
+ if (err != OK) {
+ stickyErr = err;
+ }
+
+ if (info->mMediaBuffer != NULL) {
+ info->mMediaBuffer->setObserver(NULL);
+
+ // Make sure nobody but us owns this buffer at this point.
+ CHECK_EQ(info->mMediaBuffer->refcount(), 0);
+
+ info->mMediaBuffer->release();
+ }
+
+ buffers->removeAt(i);
+ }
+
+ CHECK(onlyThoseWeOwn || buffers->isEmpty());
+
+ return stickyErr;
+}
+
+void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
+ LOGV("PORT_SETTINGS_CHANGED(%ld)", portIndex);
+
+ CHECK_EQ(mState, EXECUTING);
+ CHECK_EQ(portIndex, kPortIndexOutput);
+ setState(RECONFIGURING);
+
+ if (mQuirks & kNeedsFlushBeforeDisable) {
+ flushPortAsync(portIndex);
+ } else {
+ disablePortAsync(portIndex);
+ }
+}
+
+void OMXCodec::flushPortAsync(OMX_U32 portIndex) {
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+
+ CHECK_EQ(mPortStatus[portIndex], ENABLED);
+ mPortStatus[portIndex] = SHUTTING_DOWN;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandFlush, portIndex);
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::disablePortAsync(OMX_U32 portIndex) {
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+
+ CHECK_EQ(mPortStatus[portIndex], ENABLED);
+ mPortStatus[portIndex] = DISABLING;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandPortDisable, portIndex);
+ CHECK_EQ(err, OK);
+
+ freeBuffersOnPort(portIndex, true);
+}
+
+void OMXCodec::enablePortAsync(OMX_U32 portIndex) {
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+
+ CHECK_EQ(mPortStatus[portIndex], DISABLED);
+ mPortStatus[portIndex] = ENABLING;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandPortEnable, portIndex);
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::fillOutputBuffers() {
+ CHECK_EQ(mState, EXECUTING);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ fillOutputBuffer(&buffers->editItemAt(i));
+ }
+}
+
+void OMXCodec::drainInputBuffers() {
+ CHECK_EQ(mState, EXECUTING);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ drainInputBuffer(&buffers->editItemAt(i));
+ }
+}
+
+void OMXCodec::drainInputBuffer(BufferInfo *info) {
+ CHECK_EQ(info->mOwnedByComponent, false);
+
+ if (mSignalledEOS) {
+ return;
+ }
+
+ if (mCodecSpecificDataIndex < mCodecSpecificData.size()) {
+ const CodecSpecificData *specific =
+ mCodecSpecificData[mCodecSpecificDataIndex];
+
+ size_t size = specific->mSize;
+
+ if (!strcasecmp(mMIME, "video/avc")
+ && !(mQuirks & kWantsRawNALFrames)) {
+ static const uint8_t kNALStartCode[4] =
+ { 0x00, 0x00, 0x00, 0x01 };
+
+ CHECK(info->mMem->size() >= specific->mSize + 4);
+
+ size += 4;
+
+ memcpy(info->mMem->pointer(), kNALStartCode, 4);
+ memcpy((uint8_t *)info->mMem->pointer() + 4,
+ specific->mData, specific->mSize);
+ } else {
+ CHECK(info->mMem->size() >= specific->mSize);
+ memcpy(info->mMem->pointer(), specific->mData, specific->mSize);
+ }
+
+ mOMX->empty_buffer(
+ mNode, info->mBuffer, 0, size,
+ OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
+ 0);
+
+ info->mOwnedByComponent = true;
+
+ ++mCodecSpecificDataIndex;
+ return;
+ }
+
+ MediaBuffer *srcBuffer;
+ status_t err;
+ if (mSeekTimeUs >= 0) {
+ MediaSource::ReadOptions options;
+ options.setSeekTo(mSeekTimeUs);
+ mSeekTimeUs = -1;
+
+ err = mSource->read(&srcBuffer, &options);
+ } else {
+ err = mSource->read(&srcBuffer);
+ }
+
+ OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
+ OMX_TICKS timestamp = 0;
+ size_t srcLength = 0;
+
+ if (err != OK) {
+ LOGV("signalling end of input stream.");
+ flags |= OMX_BUFFERFLAG_EOS;
+
+ mSignalledEOS = true;
+ } else {
+ srcLength = srcBuffer->range_length();
+
+ if (info->mMem->size() < srcLength) {
+ LOGE("info->mMem->size() = %d, srcLength = %d",
+ info->mMem->size(), srcLength);
+ }
+ CHECK(info->mMem->size() >= srcLength);
+ memcpy(info->mMem->pointer(),
+ (const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(),
+ srcLength);
+
+ int32_t units, scale;
+ if (srcBuffer->meta_data()->findInt32(kKeyTimeUnits, &units)
+ && srcBuffer->meta_data()->findInt32(kKeyTimeScale, &scale)) {
+ timestamp = ((OMX_TICKS)units * 1000000) / scale;
+
+ LOGV("Calling empty_buffer on buffer %p (length %d)",
+ info->mBuffer, srcLength);
+ LOGV("Calling empty_buffer with timestamp %lld us (%.2f secs)",
+ timestamp, timestamp / 1E6);
+ }
+ }
+
+ mOMX->empty_buffer(
+ mNode, info->mBuffer, 0, srcLength,
+ flags, timestamp);
+
+ info->mOwnedByComponent = true;
+
+ if (srcBuffer != NULL) {
+ srcBuffer->release();
+ srcBuffer = NULL;
+ }
+}
+
+void OMXCodec::fillOutputBuffer(BufferInfo *info) {
+ CHECK_EQ(info->mOwnedByComponent, false);
+
+ LOGV("Calling fill_buffer on buffer %p", info->mBuffer);
+ mOMX->fill_buffer(mNode, info->mBuffer);
+
+ info->mOwnedByComponent = true;
+}
+
+void OMXCodec::drainInputBuffer(IOMX::buffer_id buffer) {
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ if ((*buffers)[i].mBuffer == buffer) {
+ drainInputBuffer(&buffers->editItemAt(i));
+ return;
+ }
+ }
+
+ CHECK(!"should not be here.");
+}
+
+void OMXCodec::fillOutputBuffer(IOMX::buffer_id buffer) {
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ if ((*buffers)[i].mBuffer == buffer) {
+ fillOutputBuffer(&buffers->editItemAt(i));
+ return;
+ }
+ }
+
+ CHECK(!"should not be here.");
+}
+
+void OMXCodec::setState(State newState) {
+ mState = newState;
+ mAsyncCompletion.signal();
+
+ // This may cause some spurious wakeups but is necessary to
+ // unblock the reader if we enter ERROR state.
+ mBufferFilled.signal();
+}
+
+void OMXCodec::setAMRFormat() {
+ if (!mIsEncoder) {
+ OMX_AUDIO_PARAM_AMRTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err =
+ mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+
+ CHECK_EQ(err, OK);
+
+ def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+ def.eAMRBandMode = OMX_AUDIO_AMRBandModeNB0;
+
+ err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+ }
+
+ ////////////////////////
+
+ if (mIsEncoder) {
+ sp<MetaData> format = mSource->getFormat();
+ int32_t sampleRate;
+ int32_t numChannels;
+ CHECK(format->findInt32(kKeySampleRate, &sampleRate));
+ CHECK(format->findInt32(kKeyChannelCount, &numChannels));
+
+ OMX_AUDIO_PARAM_PCMMODETYPE pcmParams;
+ pcmParams.nSize = sizeof(pcmParams);
+ pcmParams.nVersion.s.nVersionMajor = 1;
+ pcmParams.nVersion.s.nVersionMinor = 1;
+ pcmParams.nPortIndex = kPortIndexInput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
+
+ CHECK_EQ(err, OK);
+
+ pcmParams.nChannels = numChannels;
+ pcmParams.eNumData = OMX_NumericalDataSigned;
+ pcmParams.bInterleaved = OMX_TRUE;
+ pcmParams.nBitPerSample = 16;
+ pcmParams.nSamplingRate = sampleRate;
+ pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear;
+
+ if (numChannels == 1) {
+ pcmParams.eChannelMapping[0] = OMX_AUDIO_ChannelCF;
+ } else {
+ CHECK_EQ(numChannels, 2);
+
+ pcmParams.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+ }
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
+
+ CHECK_EQ(err, OK);
+ }
+}
+
+void OMXCodec::setAACFormat() {
+ OMX_AUDIO_PARAM_AACPROFILETYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err =
+ mOMX->get_parameter(mNode, OMX_IndexParamAudioAac, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ def.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+ err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAac, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::setImageOutputFormat(
+ OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) {
+ LOGV("setImageOutputFormat(%ld, %ld)", width, height);
+
+#if 0
+ OMX_INDEXTYPE index;
+ status_t err = mOMX->get_extension_index(
+ mNode, "OMX.TI.JPEG.decode.Config.OutputColorFormat", &index);
+ CHECK_EQ(err, OK);
+
+ err = mOMX->set_config(mNode, index, &format, sizeof(format));
+ CHECK_EQ(err, OK);
+#endif
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+
+ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+
+ CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+ imageDef->eColorFormat = format;
+ imageDef->nFrameWidth = width;
+ imageDef->nFrameHeight = height;
+
+ switch (format) {
+ case OMX_COLOR_FormatYUV420PackedPlanar:
+ case OMX_COLOR_FormatYUV411Planar:
+ {
+ def.nBufferSize = (width * height * 3) / 2;
+ break;
+ }
+
+ case OMX_COLOR_FormatCbYCrY:
+ {
+ def.nBufferSize = width * height * 2;
+ break;
+ }
+
+ case OMX_COLOR_Format32bitARGB8888:
+ {
+ def.nBufferSize = width * height * 4;
+ break;
+ }
+
+ default:
+ CHECK(!"Should not be here. Unknown color format.");
+ break;
+ }
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ ////
+
+ def.nPortIndex = kPortIndexInput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingJPEG);
+ imageDef->nFrameWidth = width;
+ imageDef->nFrameHeight = height;
+
+ def.nBufferSize = 128 * 1024;
+ def.nBufferCountActual = def.nBufferCountMin;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::addCodecSpecificData(const void *data, size_t size) {
+ CodecSpecificData *specific =
+ (CodecSpecificData *)malloc(sizeof(CodecSpecificData) + size - 1);
+
+ specific->mSize = size;
+ memcpy(specific->mData, data, size);
+
+ mCodecSpecificData.push(specific);
+}
+
+void OMXCodec::clearCodecSpecificData() {
+ for (size_t i = 0; i < mCodecSpecificData.size(); ++i) {
+ free(mCodecSpecificData.editItemAt(i));
+ }
+ mCodecSpecificData.clear();
+ mCodecSpecificDataIndex = 0;
+}
+
+status_t OMXCodec::start(MetaData *) {
+ if (mState != LOADED) {
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MetaData> params = new MetaData;
+ if (!strcasecmp(mMIME, "video/avc") && !(mQuirks & kWantsRawNALFrames)) {
+ params->setInt32(kKeyNeedsNALFraming, true);
+ }
+ status_t err = mSource->start(params.get());
+
+ if (err != OK) {
+ return err;
+ }
+
+ mCodecSpecificDataIndex = 0;
+ mSignalledEOS = false;
+ mNoMoreOutputData = false;
+ mSeekTimeUs = -1;
+ mFilledBuffers.clear();
+
+ return init();
+}
+
+status_t OMXCodec::stop() {
+ LOGI("stop");
+
+ Mutex::Autolock autoLock(mLock);
+
+ while (isIntermediateState(mState)) {
+ mAsyncCompletion.wait(mLock);
+ }
+
+ switch (mState) {
+ case LOADED:
+ case ERROR:
+ break;
+
+ case EXECUTING:
+ {
+ setState(EXECUTING_TO_IDLE);
+
+ mPortStatus[kPortIndexInput] = SHUTTING_DOWN;
+ mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ CHECK_EQ(err, OK);
+
+ while (mState != LOADED && mState != ERROR) {
+ mAsyncCompletion.wait(mLock);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here.");
+ break;
+ }
+ }
+
+ mSource->stop();
+
+ return OK;
+}
+
+sp<MetaData> OMXCodec::getFormat() {
+ return mOutputFormat;
+}
+
+status_t OMXCodec::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ *buffer = NULL;
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != EXECUTING && mState != RECONFIGURING) {
+ return UNKNOWN_ERROR;
+ }
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
+
+ mSignalledEOS = false;
+ mNoMoreOutputData = false;
+
+ CHECK(seekTimeUs >= 0);
+ mSeekTimeUs = seekTimeUs;
+
+ mFilledBuffers.clear();
+
+ CHECK_EQ(mState, EXECUTING);
+
+ flushPortAsync(kPortIndexInput);
+ flushPortAsync(kPortIndexOutput);
+ }
+
+ while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
+ mBufferFilled.wait(mLock);
+ }
+
+ if (mState == ERROR) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mFilledBuffers.empty()) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ size_t index = *mFilledBuffers.begin();
+ mFilledBuffers.erase(mFilledBuffers.begin());
+
+ BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
+ info->mMediaBuffer->add_ref();
+ *buffer = info->mMediaBuffer;
+
+ return OK;
+}
+
+void OMXCodec::signalBufferReturned(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (info->mMediaBuffer == buffer) {
+ CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+ fillOutputBuffer(info);
+ return;
+ }
+ }
+
+ CHECK(!"should not be here.");
+}
+
+static const char *imageCompressionFormatString(OMX_IMAGE_CODINGTYPE type) {
+ static const char *kNames[] = {
+ "OMX_IMAGE_CodingUnused",
+ "OMX_IMAGE_CodingAutoDetect",
+ "OMX_IMAGE_CodingJPEG",
+ "OMX_IMAGE_CodingJPEG2K",
+ "OMX_IMAGE_CodingEXIF",
+ "OMX_IMAGE_CodingTIFF",
+ "OMX_IMAGE_CodingGIF",
+ "OMX_IMAGE_CodingPNG",
+ "OMX_IMAGE_CodingLZW",
+ "OMX_IMAGE_CodingBMP",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *colorFormatString(OMX_COLOR_FORMATTYPE type) {
+ static const char *kNames[] = {
+ "OMX_COLOR_FormatUnused",
+ "OMX_COLOR_FormatMonochrome",
+ "OMX_COLOR_Format8bitRGB332",
+ "OMX_COLOR_Format12bitRGB444",
+ "OMX_COLOR_Format16bitARGB4444",
+ "OMX_COLOR_Format16bitARGB1555",
+ "OMX_COLOR_Format16bitRGB565",
+ "OMX_COLOR_Format16bitBGR565",
+ "OMX_COLOR_Format18bitRGB666",
+ "OMX_COLOR_Format18bitARGB1665",
+ "OMX_COLOR_Format19bitARGB1666",
+ "OMX_COLOR_Format24bitRGB888",
+ "OMX_COLOR_Format24bitBGR888",
+ "OMX_COLOR_Format24bitARGB1887",
+ "OMX_COLOR_Format25bitARGB1888",
+ "OMX_COLOR_Format32bitBGRA8888",
+ "OMX_COLOR_Format32bitARGB8888",
+ "OMX_COLOR_FormatYUV411Planar",
+ "OMX_COLOR_FormatYUV411PackedPlanar",
+ "OMX_COLOR_FormatYUV420Planar",
+ "OMX_COLOR_FormatYUV420PackedPlanar",
+ "OMX_COLOR_FormatYUV420SemiPlanar",
+ "OMX_COLOR_FormatYUV422Planar",
+ "OMX_COLOR_FormatYUV422PackedPlanar",
+ "OMX_COLOR_FormatYUV422SemiPlanar",
+ "OMX_COLOR_FormatYCbYCr",
+ "OMX_COLOR_FormatYCrYCb",
+ "OMX_COLOR_FormatCbYCrY",
+ "OMX_COLOR_FormatCrYCbY",
+ "OMX_COLOR_FormatYUV444Interleaved",
+ "OMX_COLOR_FormatRawBayer8bit",
+ "OMX_COLOR_FormatRawBayer10bit",
+ "OMX_COLOR_FormatRawBayer8bitcompressed",
+ "OMX_COLOR_FormatL2",
+ "OMX_COLOR_FormatL4",
+ "OMX_COLOR_FormatL8",
+ "OMX_COLOR_FormatL16",
+ "OMX_COLOR_FormatL24",
+ "OMX_COLOR_FormatL32",
+ "OMX_COLOR_FormatYUV420PackedSemiPlanar",
+ "OMX_COLOR_FormatYUV422PackedSemiPlanar",
+ "OMX_COLOR_Format18BitBGR666",
+ "OMX_COLOR_Format24BitARGB6666",
+ "OMX_COLOR_Format24BitABGR6666",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ if (type == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) {
+ return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar";
+ } else if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *videoCompressionFormatString(OMX_VIDEO_CODINGTYPE type) {
+ static const char *kNames[] = {
+ "OMX_VIDEO_CodingUnused",
+ "OMX_VIDEO_CodingAutoDetect",
+ "OMX_VIDEO_CodingMPEG2",
+ "OMX_VIDEO_CodingH263",
+ "OMX_VIDEO_CodingMPEG4",
+ "OMX_VIDEO_CodingWMV",
+ "OMX_VIDEO_CodingRV",
+ "OMX_VIDEO_CodingAVC",
+ "OMX_VIDEO_CodingMJPEG",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *audioCodingTypeString(OMX_AUDIO_CODINGTYPE type) {
+ static const char *kNames[] = {
+ "OMX_AUDIO_CodingUnused",
+ "OMX_AUDIO_CodingAutoDetect",
+ "OMX_AUDIO_CodingPCM",
+ "OMX_AUDIO_CodingADPCM",
+ "OMX_AUDIO_CodingAMR",
+ "OMX_AUDIO_CodingGSMFR",
+ "OMX_AUDIO_CodingGSMEFR",
+ "OMX_AUDIO_CodingGSMHR",
+ "OMX_AUDIO_CodingPDCFR",
+ "OMX_AUDIO_CodingPDCEFR",
+ "OMX_AUDIO_CodingPDCHR",
+ "OMX_AUDIO_CodingTDMAFR",
+ "OMX_AUDIO_CodingTDMAEFR",
+ "OMX_AUDIO_CodingQCELP8",
+ "OMX_AUDIO_CodingQCELP13",
+ "OMX_AUDIO_CodingEVRC",
+ "OMX_AUDIO_CodingSMV",
+ "OMX_AUDIO_CodingG711",
+ "OMX_AUDIO_CodingG723",
+ "OMX_AUDIO_CodingG726",
+ "OMX_AUDIO_CodingG729",
+ "OMX_AUDIO_CodingAAC",
+ "OMX_AUDIO_CodingMP3",
+ "OMX_AUDIO_CodingSBC",
+ "OMX_AUDIO_CodingVORBIS",
+ "OMX_AUDIO_CodingWMA",
+ "OMX_AUDIO_CodingRA",
+ "OMX_AUDIO_CodingMIDI",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *audioPCMModeString(OMX_AUDIO_PCMMODETYPE type) {
+ static const char *kNames[] = {
+ "OMX_AUDIO_PCMModeLinear",
+ "OMX_AUDIO_PCMModeALaw",
+ "OMX_AUDIO_PCMModeMULaw",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+
+void OMXCodec::dumpPortStatus(OMX_U32 portIndex) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = portIndex;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ printf("%s Port = {\n", portIndex == kPortIndexInput ? "Input" : "Output");
+
+ CHECK((portIndex == kPortIndexInput && def.eDir == OMX_DirInput)
+ || (portIndex == kPortIndexOutput && def.eDir == OMX_DirOutput));
+
+ printf(" nBufferCountActual = %ld\n", def.nBufferCountActual);
+ printf(" nBufferCountMin = %ld\n", def.nBufferCountMin);
+ printf(" nBufferSize = %ld\n", def.nBufferSize);
+
+ switch (def.eDomain) {
+ case OMX_PortDomainImage:
+ {
+ const OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+
+ printf("\n");
+ printf(" // Image\n");
+ printf(" nFrameWidth = %ld\n", imageDef->nFrameWidth);
+ printf(" nFrameHeight = %ld\n", imageDef->nFrameHeight);
+ printf(" nStride = %ld\n", imageDef->nStride);
+
+ printf(" eCompressionFormat = %s\n",
+ imageCompressionFormatString(imageDef->eCompressionFormat));
+
+ printf(" eColorFormat = %s\n",
+ colorFormatString(imageDef->eColorFormat));
+
+ break;
+ }
+
+ case OMX_PortDomainVideo:
+ {
+ OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;
+
+ printf("\n");
+ printf(" // Video\n");
+ printf(" nFrameWidth = %ld\n", videoDef->nFrameWidth);
+ printf(" nFrameHeight = %ld\n", videoDef->nFrameHeight);
+ printf(" nStride = %ld\n", videoDef->nStride);
+
+ printf(" eCompressionFormat = %s\n",
+ videoCompressionFormatString(videoDef->eCompressionFormat));
+
+ printf(" eColorFormat = %s\n",
+ colorFormatString(videoDef->eColorFormat));
+
+ break;
+ }
+
+ case OMX_PortDomainAudio:
+ {
+ OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
+
+ printf("\n");
+ printf(" // Audio\n");
+ printf(" eEncoding = %s\n",
+ audioCodingTypeString(audioDef->eEncoding));
+
+ if (audioDef->eEncoding == OMX_AUDIO_CodingPCM) {
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ params.nSize = sizeof(params);
+ params.nVersion.s.nVersionMajor = 1;
+ params.nVersion.s.nVersionMinor = 1;
+ params.nPortIndex = portIndex;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
+ CHECK_EQ(err, OK);
+
+ printf(" nSamplingRate = %ld\n", params.nSamplingRate);
+ printf(" nChannels = %ld\n", params.nChannels);
+ printf(" bInterleaved = %d\n", params.bInterleaved);
+ printf(" nBitPerSample = %ld\n", params.nBitPerSample);
+
+ printf(" eNumData = %s\n",
+ params.eNumData == OMX_NumericalDataSigned
+ ? "signed" : "unsigned");
+
+ printf(" ePCMMode = %s\n", audioPCMModeString(params.ePCMMode));
+ }
+
+ break;
+ }
+
+ default:
+ {
+ printf(" // Unknown\n");
+ break;
+ }
+ }
+
+ printf("}\n");
+}
+
+void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
+ mOutputFormat = new MetaData;
+ mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ switch (def.eDomain) {
+ case OMX_PortDomainImage:
+ {
+ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+ CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+
+ mOutputFormat->setCString(kKeyMIMEType, "image/raw");
+ mOutputFormat->setInt32(kKeyColorFormat, imageDef->eColorFormat);
+ mOutputFormat->setInt32(kKeyWidth, imageDef->nFrameWidth);
+ mOutputFormat->setInt32(kKeyHeight, imageDef->nFrameHeight);
+ break;
+ }
+
+ case OMX_PortDomainAudio:
+ {
+ OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio;
+
+ CHECK_EQ(audio_def->eEncoding, OMX_AUDIO_CodingPCM);
+
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ params.nSize = sizeof(params);
+ params.nVersion.s.nVersionMajor = 1;
+ params.nVersion.s.nVersionMinor = 1;
+ params.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
+ CHECK_EQ(err, OK);
+
+ CHECK_EQ(params.eNumData, OMX_NumericalDataSigned);
+ CHECK_EQ(params.nBitPerSample, 16);
+ CHECK_EQ(params.ePCMMode, OMX_AUDIO_PCMModeLinear);
+
+ int32_t numChannels, sampleRate;
+ inputFormat->findInt32(kKeyChannelCount, &numChannels);
+ inputFormat->findInt32(kKeySampleRate, &sampleRate);
+
+ mOutputFormat->setCString(kKeyMIMEType, "audio/raw");
+ mOutputFormat->setInt32(kKeyChannelCount, numChannels);
+ mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+ break;
+ }
+
+ case OMX_PortDomainVideo:
+ {
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ if (video_def->eCompressionFormat == OMX_VIDEO_CodingUnused) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/raw");
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingMPEG4) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/mp4v-es");
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingH263) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/3gpp");
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingAVC) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/avc");
+ } else {
+ CHECK(!"Unknown compression format.");
+ }
+
+ if (!strcmp(mComponentName, "OMX.PV.avcdec")) {
+ // This component appears to be lying to me.
+ mOutputFormat->setInt32(
+ kKeyWidth, (video_def->nFrameWidth + 15) & -16);
+ mOutputFormat->setInt32(
+ kKeyHeight, (video_def->nFrameHeight + 15) & -16);
+ } else {
+ mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
+ mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
+ }
+
+ mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here, neither audio nor video.");
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/OMXDecoder.cpp b/media/libstagefright/OMXDecoder.cpp
index a00872f..94cca43 100644
--- a/media/libstagefright/OMXDecoder.cpp
+++ b/media/libstagefright/OMXDecoder.cpp
@@ -102,9 +102,10 @@
}
// static
-OMXDecoder *OMXDecoder::Create(
+sp<OMXDecoder> OMXDecoder::Create(
OMXClient *client, const sp<MetaData> &meta,
- bool createEncoder) {
+ bool createEncoder,
+ const sp<MediaSource> &source) {
const char *mime;
bool success = meta->findCString(kKeyMIMEType, &mime);
assert(success);
@@ -158,8 +159,9 @@
quirks |= kRequiresLoadedToIdleAfterAllocation;
}
- OMXDecoder *decoder = new OMXDecoder(
- client, node, mime, codec, createEncoder, quirks);
+ sp<OMXDecoder> decoder = new OMXDecoder(
+ client, node, mime, codec, createEncoder, quirks,
+ source);
uint32_t type;
const void *data;
@@ -213,7 +215,8 @@
OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node,
const char *mime, const char *codec,
bool is_encoder,
- uint32_t quirks)
+ uint32_t quirks,
+ const sp<MediaSource> &source)
: mClient(client),
mOMX(mClient->interface()),
mNode(node),
@@ -223,7 +226,7 @@
mIsAVC(!strcasecmp(mime, "video/avc")),
mIsEncoder(is_encoder),
mQuirks(quirks),
- mSource(NULL),
+ mSource(source),
mCodecSpecificDataIterator(mCodecSpecificData.begin()),
mState(OMX_StateLoaded),
mPortStatusMask(kPortStatusActive << 2 | kPortStatusActive),
@@ -237,6 +240,8 @@
mBuffers.push(); // input buffers
mBuffers.push(); // output buffers
+
+ setup();
}
OMXDecoder::~OMXDecoder() {
@@ -263,15 +268,6 @@
mComponentName = NULL;
}
-void OMXDecoder::setSource(MediaSource *source) {
- Mutex::Autolock autoLock(mLock);
-
- assert(mSource == NULL);
-
- mSource = source;
- setup();
-}
-
status_t OMXDecoder::start(MetaData *) {
assert(!mStarted);
@@ -580,6 +576,10 @@
OMX_COLOR_FORMATTYPE colorFormat =
0 ? OMX_COLOR_FormatYCbYCr : OMX_COLOR_FormatCbYCrY;
+ if (!strncmp("OMX.qcom.video.encoder.", mComponentName, 23)) {
+ colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+ }
+
setVideoPortFormatType(
kPortIndexInput, OMX_VIDEO_CodingUnused,
colorFormat);
@@ -1621,7 +1621,7 @@
void OMXDecoder::postEmptyBufferDone(IOMX::buffer_id buffer) {
omx_message msg;
msg.type = omx_message::EMPTY_BUFFER_DONE;
- msg.u.buffer_data.node = mNode;
+ msg.node = mNode;
msg.u.buffer_data.buffer = buffer;
postMessage(msg);
}
@@ -1629,7 +1629,7 @@
void OMXDecoder::postInitialFillBuffer(IOMX::buffer_id buffer) {
omx_message msg;
msg.type = omx_message::INITIAL_FILL_BUFFER;
- msg.u.buffer_data.node = mNode;
+ msg.node = mNode;
msg.u.buffer_data.buffer = buffer;
postMessage(msg);
}
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 8f1fa67..75bfde3 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -31,7 +31,7 @@
static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
-SampleTable::SampleTable(DataSource *source)
+SampleTable::SampleTable(const sp<DataSource> &source)
: mDataSource(source),
mChunkOffsetOffset(-1),
mChunkOffsetType(0),
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index d44e3a3..39fa27e 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -75,6 +75,102 @@
NodeMeta &operator=(const NodeMeta &);
};
+////////////////////////////////////////////////////////////////////////////////
+
+struct OMX::CallbackDispatcher : public RefBase {
+ CallbackDispatcher();
+
+ void post(const omx_message &msg);
+
+protected:
+ virtual ~CallbackDispatcher();
+
+private:
+ Mutex mLock;
+ bool mDone;
+ Condition mQueueChanged;
+ List<omx_message> mQueue;
+
+ pthread_t mThread;
+
+ void dispatch(const omx_message &msg);
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ CallbackDispatcher(const CallbackDispatcher &);
+ CallbackDispatcher &operator=(const CallbackDispatcher &);
+};
+
+OMX::CallbackDispatcher::CallbackDispatcher()
+ : mDone(false) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+
+ pthread_attr_destroy(&attr);
+}
+
+OMX::CallbackDispatcher::~CallbackDispatcher() {
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ mDone = true;
+ mQueueChanged.signal();
+ }
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+}
+
+void OMX::CallbackDispatcher::post(const omx_message &msg) {
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(msg);
+ mQueueChanged.signal();
+}
+
+void OMX::CallbackDispatcher::dispatch(const omx_message &msg) {
+ NodeMeta *meta = static_cast<NodeMeta *>(msg.node);
+
+ sp<IOMXObserver> observer = meta->observer();
+ if (observer.get() != NULL) {
+ observer->on_message(msg);
+ }
+}
+
+// static
+void *OMX::CallbackDispatcher::ThreadWrapper(void *me) {
+ static_cast<CallbackDispatcher *>(me)->threadEntry();
+
+ return NULL;
+}
+
+void OMX::CallbackDispatcher::threadEntry() {
+ for (;;) {
+ omx_message msg;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ while (!mDone && mQueue.empty()) {
+ mQueueChanged.wait(mLock);
+ }
+
+ if (mDone) {
+ break;
+ }
+
+ msg = *mQueue.begin();
+ mQueue.erase(mQueue.begin());
+ }
+
+ dispatch(msg);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
class BufferMeta {
public:
BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
@@ -154,7 +250,8 @@
return meta->owner()->OnFillBufferDone(meta, pBuffer);
}
-OMX::OMX() {
+OMX::OMX()
+ : mDispatcher(new CallbackDispatcher) {
}
status_t OMX::list_nodes(List<String8> *list) {
@@ -249,6 +346,29 @@
return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
}
+status_t OMX::get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err = OMX_GetConfig(meta->handle(), index, params);
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_SetConfig(meta->handle(), index, const_cast<void *>(params));
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
status_t OMX::use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer) {
@@ -357,15 +477,12 @@
omx_message msg;
msg.type = omx_message::EVENT;
- msg.u.event_data.node = meta;
+ msg.node = meta;
msg.u.event_data.event = eEvent;
msg.u.event_data.data1 = nData1;
msg.u.event_data.data2 = nData2;
- sp<IOMXObserver> observer = meta->observer();
- if (observer.get() != NULL) {
- observer->on_message(msg);
- }
+ mDispatcher->post(msg);
return OMX_ErrorNone;
}
@@ -376,13 +493,10 @@
omx_message msg;
msg.type = omx_message::EMPTY_BUFFER_DONE;
- msg.u.buffer_data.node = meta;
+ msg.node = meta;
msg.u.buffer_data.buffer = pBuffer;
- sp<IOMXObserver> observer = meta->observer();
- if (observer.get() != NULL) {
- observer->on_message(msg);
- }
+ mDispatcher->post(msg);
return OMX_ErrorNone;
}
@@ -395,7 +509,7 @@
omx_message msg;
msg.type = omx_message::FILL_BUFFER_DONE;
- msg.u.extended_buffer_data.node = meta;
+ msg.node = meta;
msg.u.extended_buffer_data.buffer = pBuffer;
msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
@@ -403,10 +517,7 @@
msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;
- sp<IOMXObserver> observer = meta->observer();
- if (observer.get() != NULL) {
- observer->on_message(msg);
- }
+ mDispatcher->post(msg);
return OMX_ErrorNone;
}
@@ -455,6 +566,20 @@
assert(err == OMX_ErrorNone);
}
+status_t OMX::get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) {
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err =
+ OMX_GetExtensionIndex(
+ node_meta->handle(),
+ const_cast<char *>(parameter_name), index);
+
+ return err == OMX_ErrorNone ? OK : UNKNOWN_ERROR;
+}
+
////////////////////////////////////////////////////////////////////////////////
sp<IOMXRenderer> OMX::createRenderer(
diff --git a/media/libstagefright/omx/OMX.h b/media/libstagefright/omx/OMX.h
index 8ac311c..6325f79 100644
--- a/media/libstagefright/omx/OMX.h
+++ b/media/libstagefright/omx/OMX.h
@@ -44,6 +44,14 @@
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size);
+ virtual status_t get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
virtual status_t use_buffer(
node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
buffer_id *buffer);
@@ -70,6 +78,11 @@
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp);
+ virtual status_t get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index);
+
virtual sp<IOMXRenderer> createRenderer(
const sp<ISurface> &surface,
const char *componentName,
@@ -82,6 +95,9 @@
Mutex mLock;
+ struct CallbackDispatcher;
+ sp<CallbackDispatcher> mDispatcher;
+
static OMX_ERRORTYPE OnEvent(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,