From be06d26cdc70070654f1eedcd08c1c68cd587ad6 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 14 Aug 2009 14:37:10 -0700 Subject: Squashed commit of the following: commit 5bb012f0065f7ffaaeb4f569d71f0e3a8d6b19c3 Author: Andreas Huber 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 Date: Fri Aug 14 09:08:28 2009 -0700 Callbacks are now dispatched from a separate thread in OMX. commit c6571a039526df29b6343f9a1971dbc019088c61 Author: Andreas Huber Date: Thu Aug 13 15:42:25 2009 -0700 Massive API changes throughout stagefright, smart pointers everywhere. commit 900612af6a0555664d9ba195112cd859491265f4 Author: Andreas Huber Date: Thu Aug 13 13:33:12 2009 -0700 OMXCodecs now properly shutdown. commit 96732f05e1b0603dcd1b11f16a23512592eeb4f5 Author: Andreas Huber Date: Thu Aug 13 12:04:04 2009 -0700 More work on JPEG decoding using the hardware OMX component. commit 63839a073ac393e3a130434ba467969053b694ad Author: Andreas Huber Date: Wed Aug 12 13:13:31 2009 -0700 An attempt to drive the JPEG decoder OMX node. commit 3ac2fe5ab2926eda81b2123610b2434c645294ff Author: Andreas Huber 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 Date: Tue Aug 11 16:06:19 2009 -0700 Statemachine is now a full-fledged MediaSource. commit 045244f6771fa0b9b329495c953afda900a84b71 Author: Andreas Huber Date: Fri Aug 7 09:16:54 2009 -0700 Properly setup the input format when exporting to AMR audio. commit 271b984cb32c5cd9e46e3f90ae121f334e4b8da9 Author: Andreas Huber 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 Date: Wed Aug 5 14:36:22 2009 -0700 Merge the old OMXDecoder and the new, shiny, StateMachine code. commit 91cf5dd77a8762bc10a0b2ffce35e3bbeb262231 Author: Andreas Huber Date: Tue Aug 4 17:41:43 2009 -0700 A new harness to test OMX node compliance (and quirks). --- cmds/stagefright/Android.mk | 9 +- cmds/stagefright/record.cpp | 135 +- cmds/stagefright/stagefright.cpp | 372 +++-- include/media/IOMX.h | 19 +- include/media/stagefright/AudioPlayer.h | 6 +- include/media/stagefright/CachingDataSource.h | 10 +- include/media/stagefright/DataSource.h | 12 +- include/media/stagefright/MP3Extractor.h | 16 +- include/media/stagefright/MPEG4Extractor.h | 19 +- include/media/stagefright/MPEG4Writer.h | 8 +- include/media/stagefright/MediaBuffer.h | 4 +- include/media/stagefright/MediaDebug.h | 18 + include/media/stagefright/MediaExtractor.h | 14 +- include/media/stagefright/MediaPlayerImpl.h | 18 +- include/media/stagefright/MediaSource.h | 6 +- include/media/stagefright/OMXCodec.h | 190 +++ include/media/stagefright/OMXDecoder.h | 18 +- include/media/stagefright/SampleTable.h | 12 +- media/libmedia/IOMX.cpp | 120 ++ media/libstagefright/Android.mk | 1 + media/libstagefright/AudioPlayer.cpp | 5 +- media/libstagefright/CachingDataSource.cpp | 9 +- media/libstagefright/DataSource.cpp | 13 + media/libstagefright/MP3Extractor.cpp | 36 +- media/libstagefright/MPEG4Extractor.cpp | 60 +- media/libstagefright/MPEG4Writer.cpp | 11 +- media/libstagefright/MediaExtractor.cpp | 3 +- media/libstagefright/MediaPlayerImpl.cpp | 72 +- media/libstagefright/OMXClient.cpp | 2 +- media/libstagefright/OMXCodec.cpp | 1962 +++++++++++++++++++++++++ media/libstagefright/OMXDecoder.cpp | 34 +- media/libstagefright/SampleTable.cpp | 2 +- media/libstagefright/omx/OMX.cpp | 157 +- media/libstagefright/omx/OMX.h | 16 + 34 files changed, 3027 insertions(+), 362 deletions(-) create mode 100644 include/media/stagefright/MediaDebug.h create mode 100644 include/media/stagefright/OMXCodec.h create mode 100644 media/libstagefright/OMXCodec.cpp 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_SHARED_LIBRARIES := \ 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_SHARED_LIBRARIES := \ 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 @@ include $(BUILD_EXECUTABLE) # # 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 #include #include +#include #include using namespace android; @@ -32,18 +33,38 @@ using namespace android; 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) { + virtual sp getFormat() { + sp meta = new MetaData; + meta->setInt32(kKeyWidth, mWidth); + meta->setInt32(kKeyHeight, mHeight); + meta->setCString(kKeyMIMEType, "video/raw"); + + return meta; + } + + virtual status_t getMaxSampleSize(size_t *max_size) { *max_size = mSize; - return ::OK; + return OK; + } + + virtual status_t start(MetaData *params) { + return OK; + } + + virtual status_t stop() { + return OK; } - virtual ::status_t read(MediaBuffer **buffer) { - ::status_t err = mGroup.acquire_buffer(buffer); - if (err != ::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 @@ public: 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 createSource(const char *filename) { + sp source; - MPEG4Extractor extractor(new MmapSource(argv[1])); - int num_tracks; - assert(extractor.countTracks(&num_tracks) == ::OK); + sp extractor = + new MPEG4Extractor(new MmapSource(filename)); + + size_t num_tracks = extractor->countTracks(); - MediaSource *source = NULL; sp 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 @@ int main(int argc, char **argv) { 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 source = createSource(argv[1]); + + if (source == NULL) { + fprintf(stderr, "Unable to find a suitable video track.\n"); + return 1; + } + + sp meta = source->getFormat(); + +#if USE_OMX_CODEC + sp decoder = OMXCodec::Create( + client.interface(), meta, false /* createEncoder */, source); +#else + sp 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 decoder = new DummySource(width, height); +#endif sp 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 encoder = + OMXCodec::Create( + client.interface(), enc_meta, true /* createEncoder */, decoder); +#else + sp 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 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 @@ int main(int argc, char **argv) { 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 -#undef NDEBUG -#include - #include #include @@ -30,12 +27,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include "WaveWriter.h" @@ -44,50 +44,236 @@ using namespace android; //////////////////////////////////////////////////////////////////////////////// -static bool convertToWav( - OMXClient *client, const sp &meta, MediaSource *source) { - printf("convertToWav\n"); +struct JPEGSource : public MediaSource { + // Assumes ownership of "source". + JPEGSource(const sp &source); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~JPEGSource(); + +private: + sp mSource; + MediaBufferGroup *mGroup; + bool mStarted; + off_t mSize; + int32_t mWidth, mHeight; + off_t mOffset; + + status_t parseJPEG(); + + JPEGSource(const JPEGSource &); + JPEGSource &operator=(const JPEGSource &); +}; + +JPEGSource::JPEGSource(const sp &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; + } - OMXDecoder *decoder = OMXDecoder::Create(client, meta); + if (mSource->getSize(&mSize) != OK) { + return UNKNOWN_ERROR; + } - int32_t sampleRate; - bool success = meta->findInt32(kKeySampleRate, &sampleRate); - assert(success); + mGroup = new MediaBufferGroup; + mGroup->add_buffer(new MediaBuffer(mSize)); - int32_t numChannels; - success = meta->findInt32(kKeyChannelCount, &numChannels); - assert(success); + mOffset = 0; - const char *mime; - success = meta->findCString(kKeyMIMEType, &mime); - assert(success); + mStarted = true; + + return OK; +} - if (!strcasecmp("audio/3gpp", mime)) { - numChannels = 1; // XXX +status_t JPEGSource::stop() { + if (!mStarted) { + return UNKNOWN_ERROR; } - WaveWriter writer("/sdcard/Music/shoutcast.wav", numChannels, sampleRate); + delete mGroup; + mGroup = NULL; - decoder->setSource(source); - for (int i = 0; i < 100; ++i) { - MediaBuffer *buffer; + mStarted = false; - ::status_t err = decoder->read(&buffer); - if (err != ::OK) { - break; - } + return OK; +} + +sp JPEGSource::getFormat() { + sp 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; - writer.Append((const char *)buffer->data() + buffer->range_offset(), - buffer->range_length()); + 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; + } + + buffer->set_range(0, n); + + 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; } - delete decoder; - decoder = NULL; + 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 true; + return OK; } //////////////////////////////////////////////////////////////////////////////// @@ -99,6 +285,48 @@ static int64_t getNowUs() { return (int64_t)tv.tv_usec + tv.tv_sec * 1000000; } +#define USE_OMX_CODEC 1 + +static void playSource(OMXClient *client, const sp &source) { + sp meta = source->getFormat(); + +#if !USE_OMX_CODEC + sp decoder = OMXDecoder::Create( + client, meta, false /* createEncoder */, source); +#else + sp 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 @@ int main(int argc, char **argv) { sp binder = sm->getService(String16("media.player")); sp service = interface_cast(binder); - assert(service.get() != NULL); + CHECK(service.get() != NULL); sp omx = service->createOMX(); - assert(omx.get() != NULL); + CHECK(omx.get() != NULL); List list; omx->list_nodes(&list); @@ -128,82 +356,52 @@ int main(int argc, char **argv) { --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; - - int numTracks; - err = extractor->countTracks(&numTracks); - - sp meta; - int i; - for (i = 0; i < numTracks; ++i) { - meta = extractor->getTrackMetaData(i); + sp dataSource = new MmapSource(argv[1]); - const char *mime; - meta->findCString(kKeyMIMEType, &mime); - - if (audioOnly && !strncasecmp(mime, "audio/", 6)) { - break; - } + bool isJPEG = false; - 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; - if (decoder != NULL) { - MediaSource *source; - err = extractor->getTrack(i, &source); + if (isJPEG) { + mediaSource = new JPEGSource(dataSource); + } else { + sp extractor = MediaExtractor::Create(dataSource); - decoder->setSource(source); + size_t numTracks = extractor->countTracks(); - decoder->start(); + sp 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 @@ public: 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 ¶ms, buffer_id *buffer) = 0; @@ -82,6 +90,11 @@ public: 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 createRenderer( const sp &surface, const char *componentName, @@ -114,10 +127,11 @@ struct omx_message { 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 @@ struct omx_message { // 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 @@ struct omx_message { // 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 AudioTrack; class AudioPlayer : public TimeSource { public: AudioPlayer(const sp &audioSink); - ~AudioPlayer(); + virtual ~AudioPlayer(); // Caller retains ownership of "source". - void setSource(MediaSource *source); + void setSource(const sp &source); // Return time in us. virtual int64_t getRealTimeUs(); @@ -56,7 +56,7 @@ public: status_t seekTo(int64_t time_us); private: - MediaSource *mSource; + sp 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 @@ namespace android { class CachingDataSource : public DataSource { public: - // Assumes ownership of "source". - CachingDataSource(DataSource *source, size_t pageSize, int numPages); - virtual ~CachingDataSource(); + CachingDataSource( + const sp &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 @@ private: void *mData; }; - DataSource *mSource; + sp 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 #include +#include #include 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 @@ public: bool sniff(String8 *mimeType, float *confidence); typedef bool (*SnifferFunc)( - DataSource *source, String8 *mimeType, float *confidence); + const sp &source, String8 *mimeType, float *confidence); static void RegisterSniffer(SnifferFunc func); static void RegisterDefaultSniffers(); +protected: + virtual ~DataSource() {} + private: static Mutex gSnifferMutex; static List 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 String8; class MP3Extractor : public MediaExtractor { public: // Extractor assumes ownership of "source". - MP3Extractor(DataSource *source); + MP3Extractor(const sp &source); - ~MP3Extractor(); + size_t countTracks(); + sp getTrack(size_t index); + sp getTrackMetaData(size_t index); - status_t countTracks(int *num_tracks); - status_t getTrack(int index, MediaSource **source); - sp getTrackMetaData(int index); +protected: + virtual ~MP3Extractor(); private: - DataSource *mDataSource; + sp mDataSource; off_t mFirstFramePos; sp mMeta; uint32_t mFixedHeader; @@ -46,7 +47,8 @@ private: MP3Extractor &operator=(const MP3Extractor &); }; -bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence); +bool SniffMP3( + const sp &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 String8; class MPEG4Extractor : public MediaExtractor { public: // Extractor assumes ownership of "source". - MPEG4Extractor(DataSource *source); - ~MPEG4Extractor(); + MPEG4Extractor(const sp &source); - status_t countTracks(int *num_tracks); - status_t getTrack(int index, MediaSource **source); - sp getTrackMetaData(int index); + size_t countTracks(); + sp getTrack(size_t index); + sp getTrackMetaData(size_t index); + +protected: + virtual ~MPEG4Extractor(); private: struct Track { Track *next; sp meta; uint32_t timescale; - SampleTable *sampleTable; + sp sampleTable; }; - DataSource *mDataSource; + sp mDataSource; bool mHaveMetadata; Track *mFirstTrack, *mLastTrack; @@ -58,7 +60,8 @@ private: MPEG4Extractor &operator=(const MPEG4Extractor &); }; -bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence); +bool SniffMPEG4( + const sp &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 MediaBuffer; 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 &meta, MediaSource *source); + void addSource(const sp &meta, const sp &source); void start(); void stop(); @@ -50,6 +49,9 @@ public: 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 @@ public: // MetaData. MediaBuffer *clone(); + int refcount() const; + protected: virtual ~MediaBuffer(); @@ -102,8 +104,6 @@ private: 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 DataSource; class MediaSource; class MetaData; -class MediaExtractor { +class MediaExtractor : public RefBase { public: - static MediaExtractor *Create(DataSource *source, const char *mime = NULL); + static sp Create( + const sp &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 getTrackMetaData(int index) = 0; + virtual size_t countTracks() = 0; + virtual sp getTrack(size_t index) = 0; + virtual sp 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 MediaBuffer; class MediaSource; class MemoryHeapPmem; class MetaData; -class OMXDecoder; class Surface; class TimeSource; @@ -71,16 +70,16 @@ private: OMXClient mClient; - MediaExtractor *mExtractor; + sp mExtractor; TimeSource *mTimeSource; - MediaSource *mAudioSource; - OMXDecoder *mAudioDecoder; + sp mAudioSource; + sp mAudioDecoder; AudioPlayer *mAudioPlayer; - MediaSource *mVideoSource; - MediaSource *mVideoDecoder; + sp mVideoSource; + sp mVideoDecoder; int32_t mVideoWidth, mVideoHeight; int64_t mVideoPosition; @@ -103,16 +102,13 @@ private: 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 &source); + void setVideoSource(const sp &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 @@ namespace android { 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 @@ struct MediaSource { 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 +#include +#include +#include + +namespace android { + +class MemoryDealer; +struct OMXCodecObserver; + +struct OMXCodec : public MediaSource, + public MediaBufferObserver { + static sp Create( + const sp &omx, + const sp &meta, bool createEncoder, + const sp &source); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + + virtual sp 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 mMem; + MediaBuffer *mMediaBuffer; + }; + + struct CodecSpecificData { + size_t mSize; + uint8_t mData[1]; + }; + + sp mOMX; + IOMX::node_id mNode; + sp mObserver; + uint32_t mQuirks; + bool mIsEncoder; + char *mMIME; + char *mComponentName; + sp mOutputFormat; + sp mSource; + Vector mCodecSpecificData; + size_t mCodecSpecificDataIndex; + + sp mDealer; + + State mState; + Vector 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 mFilledBuffers; + Condition mBufferFilled; + + OMXCodec(const sp &omx, IOMX::node_id node, uint32_t quirks, + bool isEncoder, const char *mime, const char *componentName, + const sp &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 &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 &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 @@ class OMXDecoder : public MediaSource, public OMXObserver, public MediaBufferObserver { public: - static OMXDecoder *Create( + static sp Create( OMXClient *client, const sp &data, - bool createEncoder = false); - - virtual ~OMXDecoder(); - - // Caller retains ownership of "source". - void setSource(MediaSource *source); + bool createEncoder, + const sp &source); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); @@ -61,6 +57,9 @@ public: // from MediaBufferObserver virtual void signalBufferReturned(MediaBuffer *buffer); +protected: + virtual ~OMXDecoder(); + private: enum { kPortIndexInput = 0, @@ -97,7 +96,7 @@ private: bool mIsEncoder; uint32_t mQuirks; - MediaSource *mSource; + sp mSource; sp mOutputFormat; Mutex mLock; @@ -135,7 +134,8 @@ private: OMXDecoder(OMXClient *client, IOMX::node_id node, const char *mime, const char *codec, bool is_encoder, - uint32_t quirks); + uint32_t quirks, + const sp &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 #include +#include #include namespace android { class DataSource; -class SampleTable { +class SampleTable : public RefBase { public: - // Caller retains ownership of "source". - SampleTable(DataSource *source); - ~SampleTable(); + SampleTable(const sp &source); // type can be 'stco' or 'co64'. status_t setChunkOffsetParams( @@ -76,8 +75,11 @@ public: status_t findClosestSyncSample( uint32_t start_sample_index, uint32_t *sample_index); +protected: + ~SampleTable(); + private: - DataSource *mDataSource; + sp 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 @@ enum { SEND_COMMAND, GET_PARAMETER, SET_PARAMETER, + GET_CONFIG, + SET_CONFIG, USE_BUFFER, ALLOC_BUFFER, ALLOC_BUFFER_WITH_BACKUP, @@ -25,6 +27,7 @@ enum { OBSERVE_NODE, FILL_BUFFER, EMPTY_BUFFER, + GET_EXTENSION_INDEX, CREATE_RENDERER, OBSERVER_ON_MSG, RENDERER_RENDER, @@ -147,6 +150,41 @@ public: 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 ¶ms, buffer_id *buffer) { @@ -260,6 +298,27 @@ public: 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(reply.readInt32()); + } else { + *index = OMX_IndexComponentStartUnused; + } + + return err; + } + virtual sp createRenderer( const sp &surface, const char *componentName, @@ -394,6 +453,48 @@ status_t BnOMX::onTransact( return NO_ERROR; } + case GET_CONFIG: + { + CHECK_INTERFACE(IOMX, data, reply); + + node_id node = readVoidStar(&data); + OMX_INDEXTYPE index = static_cast(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(data.readInt32()); + + size_t size = data.readInt32(); + void *params = const_cast(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 @@ status_t BnOMX::onTransact( 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 @@ LOCAL_SRC_FILES:= \ 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 &audioSink) - : mSource(NULL), - mAudioTrack(NULL), + : mAudioTrack(NULL), mInputBuffer(NULL), mSampleRate(0), mLatencyUs(0), @@ -48,7 +47,7 @@ AudioPlayer::~AudioPlayer() { } } -void AudioPlayer::setSource(MediaSource *source) { +void AudioPlayer::setSource(const sp &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 &source, size_t pageSize, int numPages) : mSource(source), mData(malloc(pageSize * numPages)), mPageSize(pageSize), @@ -61,9 +61,6 @@ CachingDataSource::~CachingDataSource() { free(mData); mData = NULL; - - delete mSource; - mSource = NULL; } status_t CachingDataSource::InitCheck() const { @@ -78,7 +75,7 @@ ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) { 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 @@ ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) { 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 get_mp3_frame_size( } static bool Resync( - DataSource *source, uint32_t match_header, + const sp &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 @@ static bool Resync( class MP3Source : public MediaSource { public: MP3Source( - const sp &meta, DataSource *source, + const sp &meta, const sp &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 @@ public: virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); +protected: + virtual ~MP3Source(); + private: sp mMeta; - DataSource *mDataSource; + sp mDataSource; off_t mFirstFramePos; uint32_t mFixedHeader; off_t mCurrentPos; @@ -309,7 +310,7 @@ private: MP3Source &operator=(const MP3Source &); }; -MP3Extractor::MP3Extractor(DataSource *source) +MP3Extractor::MP3Extractor(const sp &source) : mDataSource(source), mFirstFramePos(-1), mFixedHeader(0) { @@ -347,28 +348,22 @@ MP3Extractor::MP3Extractor(DataSource *source) } 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 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 MP3Extractor::getTrackMetaData(int index) { +sp MP3Extractor::getTrackMetaData(size_t index) { if (mFirstFramePos < 0 || index != 0) { return NULL; } @@ -379,7 +374,7 @@ sp MP3Extractor::getTrackMetaData(int index) { //////////////////////////////////////////////////////////////////////////////// MP3Source::MP3Source( - const sp &meta, DataSource *source, + const sp &meta, const sp &source, off_t first_frame_pos, uint32_t fixed_header) : mMeta(meta), mDataSource(source), @@ -509,7 +504,8 @@ status_t MP3Source::read( return OK; } -bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence) { +bool SniffMP3( + const sp &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 @@ namespace android { class MPEG4Source : public MediaSource { public: // Caller retains ownership of both "dataSource" and "sampleTable". - MPEG4Source(const sp &format, DataSource *dataSource, - SampleTable *sampleTable); - - virtual ~MPEG4Source(); + MPEG4Source(const sp &format, + const sp &dataSource, + const sp &sampleTable); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); @@ -55,11 +54,14 @@ public: virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); +protected: + virtual ~MPEG4Source(); + private: sp mFormat; - DataSource *mDataSource; + sp mDataSource; int32_t mTimescale; - SampleTable *mSampleTable; + sp mSampleTable; uint32_t mCurrentSampleIndex; bool mIsAVC; @@ -141,7 +143,7 @@ static const char *const FourCC2MIME(uint32_t fourcc) { } } -MPEG4Extractor::MPEG4Extractor(DataSource *source) +MPEG4Extractor::MPEG4Extractor(const sp &source) : mDataSource(source), mHaveMetadata(false), mFirstTrack(NULL), @@ -153,39 +155,29 @@ MPEG4Extractor::~MPEG4Extractor() { 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 MPEG4Extractor::getTrackMetaData(int index) { - if (index < 0) { - return NULL; - } - +sp MPEG4Extractor::getTrackMetaData(size_t index) { status_t err; if ((err = readMetaData()) != OK) { return NULL; @@ -701,39 +693,32 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return OK; } -status_t MPEG4Extractor::getTrack(int index, MediaSource **source) { - *source = NULL; - - if (index < 0) { - return ERROR_OUT_OF_RANGE; - } - +sp 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 &format, - DataSource *dataSource, SampleTable *sampleTable) + const sp &dataSource, + const sp &sampleTable) : mFormat(format), mDataSource(dataSource), mTimescale(0), @@ -935,7 +920,8 @@ status_t MPEG4Source::read( return OK; } -bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence) { +bool SniffMPEG4( + const sp &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 @@ namespace android { class MPEG4Writer::Track { public: - Track(MPEG4Writer *owner, const sp &meta, MediaSource *source); + Track(MPEG4Writer *owner, + const sp &meta, const sp &source); ~Track(); void start(); @@ -44,7 +45,7 @@ public: private: MPEG4Writer *mOwner; sp mMeta; - MediaSource *mSource; + sp mSource; volatile bool mDone; pthread_t mThread; @@ -83,7 +84,8 @@ MPEG4Writer::~MPEG4Writer() { mTracks.clear(); } -void MPEG4Writer::addSource(const sp &meta, MediaSource *source) { +void MPEG4Writer::addSource( + const sp &meta, const sp &source) { Track *track = new Track(this, meta, source); mTracks.push_back(track); } @@ -255,7 +257,8 @@ void MPEG4Writer::write(const void *data, size_t size) { //////////////////////////////////////////////////////////////////////////////// MPEG4Writer::Track::Track( - MPEG4Writer *owner, const sp &meta, MediaSource *source) + MPEG4Writer *owner, + const sp &meta, const sp &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::Create( + const sp &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..2d7b628 100644 --- a/media/libstagefright/MediaPlayerImpl.cpp +++ b/media/libstagefright/MediaPlayerImpl.cpp @@ -34,32 +34,28 @@ #include #include #include +#include #include #include #include #include #include +#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 @@ MediaPlayerImpl::MediaPlayerImpl(const char *uri) mVideoDecoder = CameraSource::Create(); #endif } else { - DataSource *source = NULL; + sp source; if (!strncasecmp("file://", uri, 7)) { source = new MmapSource(uri + 7); } else if (!strncasecmp("http://", uri, 7)) { @@ -103,22 +99,15 @@ MediaPlayerImpl::MediaPlayerImpl(const char *uri) 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 @@ MediaPlayerImpl::~MediaPlayerImpl() { 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::displayOrDiscardFrame( 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 meta = mExtractor->getTrackMetaData(i); assert(meta != NULL); @@ -411,10 +382,7 @@ void MediaPlayerImpl::init() { continue; } - MediaSource *source; - if (mExtractor->getTrack(i, &source) != OK) { - continue; - } + sp source = mExtractor->getTrack(i); int32_t units, scale; if (meta->findInt32(kKeyDuration, &units) @@ -434,17 +402,22 @@ void MediaPlayerImpl::init() { } } -void MediaPlayerImpl::setAudioSource(MediaSource *source) { +void MediaPlayerImpl::setAudioSource(const sp &source) { LOGI("setAudioSource"); mAudioSource = source; sp 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 &source) { LOGI("setVideoSource"); mVideoSource = source; @@ -456,8 +429,13 @@ void MediaPlayerImpl::setVideoSource(MediaSource *source) { 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 @@ bool OMXClient::onOMXMessage(const omx_message &msg) { } 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 &target) + : mTarget(target) { + } + + // from IOMXObserver + virtual void on_message(const omx_message &msg) { + sp codec = mTarget.promote(); + + if (codec.get() != NULL) { + codec->on_message(msg); + } + } + +protected: + virtual ~OMXCodecObserver() {} + +private: + wp 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::Create( + const sp &omx, + const sp &meta, bool createEncoder, + const sp &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 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 &omx, IOMX::node_id node, uint32_t quirks, + bool isEncoder, + const char *mime, + const char *componentName, + const sp &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 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 *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 *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 &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 *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 *buffers = &mPortBuffers[kPortIndexOutput]; + for (size_t i = 0; i < buffers->size(); ++i) { + fillOutputBuffer(&buffers->editItemAt(i)); + } +} + +void OMXCodec::drainInputBuffers() { + CHECK_EQ(mState, EXECUTING); + + Vector *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 *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 *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 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 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 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 *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 &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 const char *GetCodec(const CodecInfo *info, size_t numInfos, } // static -OMXDecoder *OMXDecoder::Create( +sp OMXDecoder::Create( OMXClient *client, const sp &meta, - bool createEncoder) { + bool createEncoder, + const sp &source) { const char *mime; bool success = meta->findCString(kKeyMIMEType, &mime); assert(success); @@ -158,8 +159,9 @@ OMXDecoder *OMXDecoder::Create( quirks |= kRequiresLoadedToIdleAfterAllocation; } - OMXDecoder *decoder = new OMXDecoder( - client, node, mime, codec, createEncoder, quirks); + sp decoder = new OMXDecoder( + client, node, mime, codec, createEncoder, quirks, + source); uint32_t type; const void *data; @@ -213,7 +215,8 @@ OMXDecoder *OMXDecoder::Create( 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 &source) : mClient(client), mOMX(mClient->interface()), mNode(node), @@ -223,7 +226,7 @@ OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node, 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 @@ OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node, mBuffers.push(); // input buffers mBuffers.push(); // output buffers + + setup(); } OMXDecoder::~OMXDecoder() { @@ -263,15 +268,6 @@ OMXDecoder::~OMXDecoder() { 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 @@ void OMXDecoder::setVideoInputFormat( 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::postStart() { 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::postEmptyBufferDone(IOMX::buffer_id buffer) { 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 kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); 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 &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 @@ private: 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 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(msg.node); + + sp observer = meta->observer(); + if (observer.get() != NULL) { + observer->on_message(msg); + } +} + +// static +void *OMX::CallbackDispatcher::ThreadWrapper(void *me) { + static_cast(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 &mem, bool is_backup = false) @@ -154,7 +250,8 @@ OMX_ERRORTYPE OMX::OnFillBufferDone( return meta->owner()->OnFillBufferDone(meta, pBuffer); } -OMX::OMX() { +OMX::OMX() + : mDispatcher(new CallbackDispatcher) { } status_t OMX::list_nodes(List *list) { @@ -249,6 +346,29 @@ status_t OMX::set_parameter( 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(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(node); + OMX_ERRORTYPE err = + OMX_SetConfig(meta->handle(), index, const_cast(params)); + + return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; +} + status_t OMX::use_buffer( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer) { @@ -357,15 +477,12 @@ OMX_ERRORTYPE OMX::OnEvent( 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 observer = meta->observer(); - if (observer.get() != NULL) { - observer->on_message(msg); - } + mDispatcher->post(msg); return OMX_ErrorNone; } @@ -376,13 +493,10 @@ OMX_ERRORTYPE OMX::OnEmptyBufferDone( 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 observer = meta->observer(); - if (observer.get() != NULL) { - observer->on_message(msg); - } + mDispatcher->post(msg); return OMX_ErrorNone; } @@ -395,7 +509,7 @@ OMX_ERRORTYPE OMX::OnFillBufferDone( 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 @@ OMX_ERRORTYPE OMX::OnFillBufferDone( msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp; msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate; - sp observer = meta->observer(); - if (observer.get() != NULL) { - observer->on_message(msg); - } + mDispatcher->post(msg); return OMX_ErrorNone; } @@ -455,6 +566,20 @@ void OMX::empty_buffer( 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(node); + + OMX_ERRORTYPE err = + OMX_GetExtensionIndex( + node_meta->handle(), + const_cast(parameter_name), index); + + return err == OMX_ErrorNone ? OK : UNKNOWN_ERROR; +} + //////////////////////////////////////////////////////////////////////////////// sp 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 @@ public: 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 ¶ms, buffer_id *buffer); @@ -70,6 +78,11 @@ public: 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 createRenderer( const sp &surface, const char *componentName, @@ -82,6 +95,9 @@ private: Mutex mLock; + struct CallbackDispatcher; + sp mDispatcher; + static OMX_ERRORTYPE OnEvent( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, -- cgit v1.1