diff options
65 files changed, 2007 insertions, 741 deletions
diff --git a/cmds/stagefright/SineSource.cpp b/cmds/stagefright/SineSource.cpp index e5a6ccb..e272a65 100644 --- a/cmds/stagefright/SineSource.cpp +++ b/cmds/stagefright/SineSource.cpp @@ -86,8 +86,8 @@ status_t SineSource::read( x += k; } - buffer->meta_data()->setInt32(kKeyTimeUnits, mPhase); - buffer->meta_data()->setInt32(kKeyTimeScale, mSampleRate); + buffer->meta_data()->setInt64( + kKeyTime, ((int64_t)mPhase * 1000000) / mSampleRate); mPhase += numFramesPerBuffer; diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp index 323d448..8c25d85 100644 --- a/cmds/stagefright/record.cpp +++ b/cmds/stagefright/record.cpp @@ -23,7 +23,7 @@ #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaData.h> -#include <media/stagefright/MPEG4Extractor.h> +#include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MPEG4Writer.h> #include <media/stagefright/MmapSource.h> #include <media/stagefright/OMXClient.h> @@ -32,8 +32,10 @@ using namespace android; -#if 0 +#if 1 class DummySource : public MediaSource { + static const int32_t kFramerate = 24; // fps + public: DummySource(int width, int height) : mWidth(width), @@ -52,6 +54,7 @@ public: } virtual status_t start(MetaData *params) { + mNumFramesOutput = 0; return OK; } @@ -61,6 +64,12 @@ public: virtual status_t read( MediaBuffer **buffer, const MediaSource::ReadOptions *options) { + if (mNumFramesOutput == kFramerate * 10) { + // Stop returning data after 10 secs. + return ERROR_END_OF_STREAM; + } + + // printf("DummySource::read\n"); status_t err = mGroup.acquire_buffer(buffer); if (err != OK) { return err; @@ -69,7 +78,13 @@ public: char x = (char)((double)rand() / RAND_MAX * 255); memset((*buffer)->data(), x, mSize); (*buffer)->set_range(0, mSize); + (*buffer)->meta_data()->clear(); + (*buffer)->meta_data()->setInt64( + kKeyTime, (mNumFramesOutput * 1000000) / kFramerate); + ++mNumFramesOutput; + // printf("DummySource::read - returning buffer\n"); + // LOGI("DummySource::read - returning buffer"); return OK; } @@ -80,6 +95,7 @@ private: MediaBufferGroup mGroup; int mWidth, mHeight; size_t mSize; + int64_t mNumFramesOutput;; DummySource(const DummySource &); DummySource &operator=(const DummySource &); @@ -88,8 +104,8 @@ private: sp<MediaSource> createSource(const char *filename) { sp<MediaSource> source; - sp<MPEG4Extractor> extractor = - new MPEG4Extractor(new MmapSource(filename)); + sp<MediaExtractor> extractor = + MediaExtractor::Create(new MmapSource(filename)); size_t num_tracks = extractor->countTracks(); @@ -117,6 +133,8 @@ sp<MediaSource> createSource(const char *filename) { int main(int argc, char **argv) { android::ProcessState::self()->startThreadPool(); + DataSource::RegisterDefaultSniffers(); + #if 1 if (argc != 2) { fprintf(stderr, "usage: %s filename\n", argv[0]); @@ -126,7 +144,7 @@ int main(int argc, char **argv) { OMXClient client; CHECK_EQ(client.connect(), OK); -#if 0 +#if 1 sp<MediaSource> source = createSource(argv[1]); if (source == NULL) { @@ -144,8 +162,8 @@ int main(int argc, char **argv) { success = success && meta->findInt32(kKeyHeight, &height); CHECK(success); #else - int width = 320; - int height = 240; + int width = 800; + int height = 480; sp<MediaSource> decoder = new DummySource(width, height); #endif @@ -159,19 +177,26 @@ int main(int argc, char **argv) { OMXCodec::Create( client.interface(), enc_meta, true /* createEncoder */, decoder); -#if 0 +#if 1 sp<MPEG4Writer> writer = new MPEG4Writer("/sdcard/output.mp4"); - writer->addSource(enc_meta, encoder); + writer->addSource(encoder); writer->start(); - sleep(20); - printf("stopping now.\n"); + while (!writer->reachedEOS()) { + usleep(100000); + } writer->stop(); #else encoder->start(); MediaBuffer *buffer; while (encoder->read(&buffer) == OK) { - printf("got an output frame of size %d\n", buffer->range_length()); + int32_t isSync; + if (!buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)) { + isSync = false; + } + + printf("got an output frame of size %d%s\n", buffer->range_length(), + isSync ? " (SYNC)" : ""); buffer->release(); buffer = NULL; diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 4ffc8e4..d26e558 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -52,21 +52,26 @@ static int64_t getNowUs() { static void playSource(OMXClient *client, const sp<MediaSource> &source) { sp<MetaData> meta = source->getFormat(); - int32_t durationUnits; - int32_t timeScale; - CHECK(meta->findInt32(kKeyDuration, &durationUnits)); - CHECK(meta->findInt32(kKeyTimeScale, &timeScale)); + int64_t durationUs; + CHECK(meta->findInt64(kKeyDuration, &durationUs)); - int64_t durationUs = ((int64_t)durationUnits * 1000000) / timeScale; + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); - sp<OMXCodec> decoder = OMXCodec::Create( + sp<MediaSource> rawSource; + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) { + rawSource = source; + } else { + rawSource = OMXCodec::Create( client->interface(), meta, false /* createEncoder */, source); - if (decoder == NULL) { - return; + if (rawSource == NULL) { + fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime); + return; + } } - decoder->start(); + rawSource->start(); if (gReproduceBug >= 3 && gReproduceBug <= 5) { status_t err; @@ -74,24 +79,28 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) { MediaSource::ReadOptions options; int64_t seekTimeUs = -1; for (;;) { - err = decoder->read(&buffer, &options); + err = rawSource->read(&buffer, &options); options.clearSeekTo(); bool shouldSeek = false; - if (err != OK) { + if (err == INFO_FORMAT_CHANGED) { + CHECK_EQ(buffer, NULL); + + printf("format changed.\n"); + continue; + } else if (err != OK) { printf("reached EOF.\n"); shouldSeek = true; } else { - int32_t timestampUnits; - CHECK(buffer->meta_data()->findInt32(kKeyTimeUnits, ×tampUnits)); - - int64_t timestampUs = ((int64_t)timestampUnits * 1000000) / timeScale; + int64_t timestampUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); bool failed = false; if (seekTimeUs >= 0) { int64_t diff = timestampUs - seekTimeUs; + if (diff < 0) { diff = -diff; } @@ -134,7 +143,7 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) { } } - decoder->stop(); + rawSource->stop(); return; } @@ -151,12 +160,17 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) { MediaBuffer *buffer; for (;;) { - status_t err = decoder->read(&buffer, &options); + status_t err = rawSource->read(&buffer, &options); options.clearSeekTo(); if (err != OK) { CHECK_EQ(buffer, NULL); + if (err == INFO_FORMAT_CHANGED) { + printf("format changed.\n"); + continue; + } + break; } @@ -188,7 +202,7 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) { options.setSeekTo(0); } - decoder->stop(); + rawSource->stop(); printf("\n"); int64_t delay = getNowUs() - startTime; diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h index e35e19e..b0fc4b2 100644 --- a/include/media/stagefright/CachingDataSource.h +++ b/include/media/stagefright/CachingDataSource.h @@ -29,9 +29,9 @@ public: CachingDataSource( const sp<DataSource> &source, size_t pageSize, int numPages); - status_t InitCheck() const; + virtual status_t initCheck() const; - virtual ssize_t read_at(off_t offset, void *data, size_t size); + virtual ssize_t readAt(off_t offset, void *data, size_t size); protected: virtual ~CachingDataSource(); diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h index 7042e1a..ff3ea05 100644 --- a/include/media/stagefright/CameraSource.h +++ b/include/media/stagefright/CameraSource.h @@ -26,12 +26,11 @@ namespace android { -class ICamera; -class ICameraClient; class IMemory; +class ISurface; +class Camera; -class CameraSource : public MediaSource, - public MediaBufferObserver { +class CameraSource : public MediaSource { public: static CameraSource *Create(); @@ -45,24 +44,25 @@ public: virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); - virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2); - virtual void dataCallback(int32_t msgType, const sp<IMemory>& data); - - virtual void signalBufferReturned(MediaBuffer *buffer); - private: - CameraSource(const sp<ICamera> &camera, const sp<ICameraClient> &client); + friend class CameraSourceListener; - sp<ICamera> mCamera; - sp<ICameraClient> mCameraClient; + sp<Camera> mCamera; Mutex mLock; Condition mFrameAvailableCondition; List<sp<IMemory> > mFrames; + List<int64_t> mFrameTimes; - int mNumFrames; + int mWidth, mHeight; + int64_t mFirstFrameTimeUs; + int32_t mNumFrames; bool mStarted; + CameraSource(const sp<Camera> &camera); + + void dataCallback(int32_t msgType, const sp<IMemory> &data); + CameraSource(const CameraSource &); CameraSource &operator=(const CameraSource &); }; diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h new file mode 100644 index 0000000..1e341b9 --- /dev/null +++ b/include/media/stagefright/ColorConverter.h @@ -0,0 +1,67 @@ +/* + * 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 COLOR_CONVERTER_H_ + +#define COLOR_CONVERTER_H_ + +#include <sys/types.h> + +#include <stdint.h> + +#include <OMX_Video.h> + +namespace android { + +struct ColorConverter { + ColorConverter(OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to); + ~ColorConverter(); + + bool isValid() const; + + void convert( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip); + +private: + OMX_COLOR_FORMATTYPE mSrcFormat, mDstFormat; + uint8_t *mClip; + + uint8_t *initClip(); + + void convertCbYCrY( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip); + + void convertYUV420Planar( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip); + + void convertQCOMYUV420SemiPlanar( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip); + + ColorConverter(const ColorConverter &); + ColorConverter &operator=(const ColorConverter &); +}; + +} // namespace android + +#endif // COLOR_CONVERTER_H_ diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index f46f0af..b843cd9 100644 --- a/include/media/stagefright/DataSource.h +++ b/include/media/stagefright/DataSource.h @@ -33,7 +33,9 @@ class DataSource : public RefBase { public: DataSource() {} - virtual ssize_t read_at(off_t offset, void *data, size_t size) = 0; + virtual status_t initCheck() const = 0; + + virtual ssize_t readAt(off_t offset, void *data, size_t size) = 0; // Convenience methods: bool getUInt16(off_t offset, uint16_t *x); diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h index ccbe0ef..d7b42c3 100644 --- a/include/media/stagefright/FileSource.h +++ b/include/media/stagefright/FileSource.h @@ -29,11 +29,13 @@ namespace android { class FileSource : public DataSource { public: FileSource(const char *filename); - virtual ~FileSource(); - status_t InitCheck() const; + virtual status_t initCheck() const; + + virtual ssize_t readAt(off_t offset, void *data, size_t size); - virtual ssize_t read_at(off_t offset, void *data, size_t size); +protected: + virtual ~FileSource(); private: FILE *mFile; diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h index 0587c7c..d5dc9e6 100644 --- a/include/media/stagefright/HTTPDataSource.h +++ b/include/media/stagefright/HTTPDataSource.h @@ -19,28 +19,29 @@ #define HTTP_DATASOURCE_H_ #include <media/stagefright/DataSource.h> -#include <media/stagefright/HTTPStream.h> namespace android { +class HTTPStream; + class HTTPDataSource : public DataSource { public: HTTPDataSource(const char *host, int port, const char *path); HTTPDataSource(const char *uri); - virtual ~HTTPDataSource(); + virtual status_t initCheck() const; - // XXXandih - status_t InitCheck() const { return OK; } + virtual ssize_t readAt(off_t offset, void *data, size_t size); - virtual ssize_t read_at(off_t offset, void *data, size_t size); +protected: + virtual ~HTTPDataSource(); private: enum { kBufferSize = 64 * 1024 }; - HTTPStream mHttp; + HTTPStream *mHttp; char *mHost; int mPort; char *mPath; @@ -49,6 +50,8 @@ private: size_t mBufferLength; off_t mBufferOffset; + status_t mInitCheck; + HTTPDataSource(const HTTPDataSource &); HTTPDataSource &operator=(const HTTPDataSource &); }; diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h index feb66e3..1efeb92 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -34,6 +34,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_AAC; extern const char *MEDIA_MIMETYPE_AUDIO_RAW; extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; +extern const char *MEDIA_MIMETYPE_CONTAINER_WAV; } // namespace android diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h index 2bb0ed6..73d0f77 100644 --- a/include/media/stagefright/MediaErrors.h +++ b/include/media/stagefright/MediaErrors.h @@ -36,6 +36,9 @@ enum { ERROR_BUFFER_TOO_SMALL = MEDIA_ERROR_BASE - 9, ERROR_UNSUPPORTED = MEDIA_ERROR_BASE - 10, ERROR_END_OF_STREAM = MEDIA_ERROR_BASE - 11, + + // Not technically an error. + INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12, }; } // namespace android diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h index 67e45bd..d56d4b3 100644 --- a/include/media/stagefright/MediaExtractor.h +++ b/include/media/stagefright/MediaExtractor.h @@ -31,9 +31,17 @@ public: static sp<MediaExtractor> Create( const sp<DataSource> &source, const char *mime = NULL); + static sp<MediaExtractor> CreateFromURI( + const char *uri, const char *mime = NULL); + virtual size_t countTracks() = 0; virtual sp<MediaSource> getTrack(size_t index) = 0; - virtual sp<MetaData> getTrackMetaData(size_t index) = 0; + + enum GetTrackMetaDataFlags { + kIncludeExtensiveMetaData = 1 + }; + virtual sp<MetaData> getTrackMetaData( + size_t index, uint32_t flags = 0) = 0; protected: MediaExtractor() {} diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h index d1fa114..96d57e7 100644 --- a/include/media/stagefright/MediaSource.h +++ b/include/media/stagefright/MediaSource.h @@ -51,6 +51,9 @@ struct MediaSource : public RefBase { // buffer is available, an error is encountered of the end of the stream // is reached. // End of stream is signalled by a result of ERROR_END_OF_STREAM. + // A result of INFO_FORMAT_CHANGED indicates that the format of this + // MediaSource has changed mid-stream, the client can continue reading + // but should be prepared for buffers of the new configuration. virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL) = 0; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index abb45a9..c2d8f98 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -27,25 +27,26 @@ namespace android { +// The following keys map to int32_t data unless indicated otherwise. enum { - kKeyMIMEType = 'mime', + kKeyMIMEType = 'mime', // cstring kKeyWidth = 'widt', kKeyHeight = 'heig', kKeyChannelCount = '#chn', kKeySampleRate = 'srte', kKeyBitRate = 'brte', - kKeyESDS = 'esds', - kKeyAVCC = 'avcc', - kKeyTimeUnits = '#tim', - kKeyTimeScale = 'scal', + kKeyESDS = 'esds', // raw data + kKeyAVCC = 'avcc', // raw data kKeyWantsNALFragments = 'NALf', kKeyIsSyncFrame = 'sync', - kKeyDuration = 'dura', + kKeyTime = 'time', // int64_t (usecs) + kKeyDuration = 'dura', // int64_t (usecs) kKeyColorFormat = 'colf', - kKeyPlatformPrivate = 'priv', - kKeyDecoderComponent = 'decC', + kKeyPlatformPrivate = 'priv', // pointer + kKeyDecoderComponent = 'decC', // cstring kKeyBufferID = 'bfID', kKeyMaxInputSize = 'inpS', + kKeyThumbnailTime = 'thbT', // int64_t (usecs) }; enum { @@ -62,6 +63,7 @@ public: TYPE_NONE = 'none', TYPE_C_STRING = 'cstr', TYPE_INT32 = 'in32', + TYPE_INT64 = 'in64', TYPE_FLOAT = 'floa', TYPE_POINTER = 'ptr ', }; @@ -71,11 +73,13 @@ public: bool setCString(uint32_t key, const char *value); bool setInt32(uint32_t key, int32_t value); + bool setInt64(uint32_t key, int64_t value); bool setFloat(uint32_t key, float value); bool setPointer(uint32_t key, void *value); bool findCString(uint32_t key, const char **value); bool findInt32(uint32_t key, int32_t *value); + bool findInt64(uint32_t key, int64_t *value); bool findFloat(uint32_t key, float *value); bool findPointer(uint32_t key, void **value); diff --git a/include/media/stagefright/MmapSource.h b/include/media/stagefright/MmapSource.h index a8bd57f..1b39d53 100644 --- a/include/media/stagefright/MmapSource.h +++ b/include/media/stagefright/MmapSource.h @@ -30,13 +30,14 @@ public: // Assumes ownership of "fd". MmapSource(int fd, int64_t offset, int64_t length); - virtual ~MmapSource(); - - status_t InitCheck() const; + virtual status_t initCheck() const; - virtual ssize_t read_at(off_t offset, void *data, size_t size); + virtual ssize_t readAt(off_t offset, void *data, size_t size); virtual status_t getSize(off_t *size); +protected: + virtual ~MmapSource(); + private: int mFd; void *mBase; diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 3f3dcf9..7890883 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -30,11 +30,15 @@ struct OMXCodecObserver; struct OMXCodec : public MediaSource, public MediaBufferObserver { + enum CreationFlags { + kPreferSoftwareCodecs = 1, + }; static sp<OMXCodec> Create( const sp<IOMX> &omx, const sp<MetaData> &meta, bool createEncoder, const sp<MediaSource> &source, - const char *matchComponentName = NULL); + const char *matchComponentName = NULL, + uint32_t flags = 0); static void setComponentRole( const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder, @@ -90,7 +94,6 @@ private: kRequiresFlushCompleteEmulation = 16, kRequiresAllocateBufferOnOutputPorts = 32, kRequiresFlushBeforeShutdown = 64, - kOutputDimensionsAre16Aligned = 128, }; struct BufferInfo { @@ -107,7 +110,6 @@ private: sp<IOMX> mOMX; IOMX::node_id mNode; - sp<OMXCodecObserver> mObserver; uint32_t mQuirks; bool mIsEncoder; char *mMIME; @@ -125,6 +127,7 @@ private: bool mInitialBufferSubmit; bool mSignalledEOS; bool mNoMoreOutputData; + bool mOutputPortSettingsHaveChanged; int64_t mSeekTimeUs; Mutex mLock; @@ -155,6 +158,8 @@ private: void setVideoInputFormat( const char *mime, OMX_U32 width, OMX_U32 height); + status_t setupMPEG4EncoderParameters(); + void setVideoOutputFormat( const char *mime, OMX_U32 width, OMX_U32 height); @@ -208,6 +213,14 @@ private: void dumpPortStatus(OMX_U32 portIndex); + static uint32_t getComponentQuirks(const char *componentName); + + static void findMatchingCodecs( + const char *mime, + bool createEncoder, const char *matchComponentName, + uint32_t flags, + Vector<String8> *matchingCodecs); + OMXCodec(const OMXCodec &); OMXCodec &operator=(const OMXCodec &); }; diff --git a/include/media/stagefright/ShoutcastSource.h b/include/media/stagefright/ShoutcastSource.h index 352857a..bc67156 100644 --- a/include/media/stagefright/ShoutcastSource.h +++ b/include/media/stagefright/ShoutcastSource.h @@ -31,7 +31,6 @@ class ShoutcastSource : public MediaSource { public: // Assumes ownership of "http". ShoutcastSource(HTTPStream *http); - virtual ~ShoutcastSource(); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); @@ -41,6 +40,9 @@ public: virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); +protected: + virtual ~ShoutcastSource(); + private: HTTPStream *mHttp; size_t mMetaDataOffset; diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 88a7064..76a9e7d 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -284,7 +284,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); data.writeIntPtr((intptr_t)buffer); - remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); + remote()->transact(FILL_BUFFER, data, &reply); return reply.readInt32(); } @@ -302,7 +302,7 @@ public: data.writeInt32(range_length); data.writeInt32(flags); data.writeInt64(timestamp); - remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); + remote()->transact(EMPTY_BUFFER, data, &reply); return reply.readInt32(); } diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index fb569da..6fc9fa2 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -18,8 +18,9 @@ LOCAL_SRC_FILES:= \ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true) -LOCAL_SRC_FILES += \ - StagefrightPlayer.cpp +LOCAL_SRC_FILES += \ + StagefrightPlayer.cpp \ + StagefrightMetadataRetriever.cpp LOCAL_CFLAGS += -DBUILD_WITH_FULL_STAGEFRIGHT=1 diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 0a6c365..b81684b 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -366,11 +366,44 @@ extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); extern "C" void free_malloc_leak_info(uint8_t* info); +// Use the String-class below instead of String8 to allocate all memory +// beforehand and not reenter the heap while we are examining it... +struct MyString8 { + static const size_t MAX_SIZE = 256 * 1024; + + MyString8() + : mPtr((char *)malloc(MAX_SIZE)) { + *mPtr = '\0'; + } + + ~MyString8() { + free(mPtr); + } + + void append(const char *s) { + strcat(mPtr, s); + } + + const char *string() const { + return mPtr; + } + + size_t size() const { + return strlen(mPtr); + } + +private: + char *mPtr; + + MyString8(const MyString8 &); + MyString8 &operator=(const MyString8 &); +}; + void memStatus(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; - String8 result; + MyString8 result; typedef struct { size_t size; diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 2cdc351..866c7bd 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -38,6 +38,7 @@ #include "VorbisMetadataRetriever.h" #include "MidiMetadataRetriever.h" #include "MetadataRetrieverClient.h" +#include "StagefrightMetadataRetriever.h" /* desktop Linux needs a little help with gettid() */ #if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS) @@ -118,9 +119,15 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) LOGV("create midi metadata retriever"); p = new MidiMetadataRetriever(); break; +#if BUILD_WITH_FULL_STAGEFRIGHT + case STAGEFRIGHT_PLAYER: + LOGV("create StagefrightMetadataRetriever"); + p = new StagefrightMetadataRetriever; + break; +#endif default: // TODO: - // support for STAGEFRIGHT_PLAYER and TEST_PLAYER + // support for TEST_PLAYER LOGE("player type %d is not supported", playerType); break; } @@ -138,12 +145,6 @@ status_t MetadataRetrieverClient::setDataSource(const char *url) return UNKNOWN_ERROR; } player_type playerType = getPlayerType(url); -#if !defined(NO_OPENCORE) && defined(BUILD_WITH_FULL_STAGEFRIGHT) - if (playerType == STAGEFRIGHT_PLAYER) { - // Stagefright doesn't support metadata in this branch yet. - playerType = PV_PLAYER; - } -#endif LOGV("player type = %d", playerType); sp<MediaMetadataRetrieverBase> p = createRetriever(playerType); if (p == NULL) return NO_INIT; @@ -182,12 +183,6 @@ status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t } player_type playerType = getPlayerType(fd, offset, length); -#if !defined(NO_OPENCORE) && defined(BUILD_WITH_FULL_STAGEFRIGHT) - if (playerType == STAGEFRIGHT_PLAYER) { - // Stagefright doesn't support metadata in this branch yet. - playerType = PV_PLAYER; - } -#endif LOGV("player type = %d", playerType); sp<MediaMetadataRetrieverBase> p = createRetriever(playerType); if (p == NULL) { diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp new file mode 100644 index 0000000..7a3aee8 --- /dev/null +++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp @@ -0,0 +1,197 @@ +/* +** +** Copyright 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 "StagefrightMetadataRetriever" +#include <utils/Log.h> + +#include "StagefrightMetadataRetriever.h" + +#include <media/stagefright/CachingDataSource.h> +#include <media/stagefright/ColorConverter.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/HTTPDataSource.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MmapSource.h> +#include <media/stagefright/OMXCodec.h> + +namespace android { + +StagefrightMetadataRetriever::StagefrightMetadataRetriever() { + LOGV("StagefrightMetadataRetriever()"); + + DataSource::RegisterDefaultSniffers(); + CHECK_EQ(mClient.connect(), OK); +} + +StagefrightMetadataRetriever::~StagefrightMetadataRetriever() { + LOGV("~StagefrightMetadataRetriever()"); + mClient.disconnect(); +} + +status_t StagefrightMetadataRetriever::setDataSource(const char *uri) { + LOGV("setDataSource(%s)", uri); + + mExtractor = MediaExtractor::CreateFromURI(uri); + + return mExtractor.get() != NULL ? OK : UNKNOWN_ERROR; +} + +status_t StagefrightMetadataRetriever::setDataSource( + int fd, int64_t offset, int64_t length) { + LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + + mExtractor = MediaExtractor::Create( + new MmapSource(fd, offset, length)); + + return OK; +} + +VideoFrame *StagefrightMetadataRetriever::captureFrame() { + LOGV("captureFrame"); + + if (mExtractor.get() == NULL) { + LOGV("no extractor."); + return NULL; + } + + size_t n = mExtractor->countTracks(); + size_t i; + for (i = 0; i < n; ++i) { + sp<MetaData> meta = mExtractor->getTrackMetaData(i); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!strncasecmp(mime, "video/", 6)) { + break; + } + } + + if (i == n) { + LOGV("no video track found."); + return NULL; + } + + sp<MetaData> trackMeta = mExtractor->getTrackMetaData( + i, MediaExtractor::kIncludeExtensiveMetaData); + + sp<MediaSource> source = mExtractor->getTrack(i); + + if (source.get() == NULL) { + LOGV("unable to instantiate video track."); + return NULL; + } + + sp<MetaData> meta = source->getFormat(); + + sp<MediaSource> decoder = + OMXCodec::Create( + mClient.interface(), meta, false, source, + NULL, OMXCodec::kPreferSoftwareCodecs); + + if (decoder.get() == NULL) { + LOGV("unable to instantiate video decoder."); + + return NULL; + } + + decoder->start(); + + // Read one output buffer, ignore format change notifications + // and spurious empty buffers. + + MediaSource::ReadOptions options; + int64_t thumbNailTime; + if (trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) { + options.setSeekTo(thumbNailTime); + } + + MediaBuffer *buffer = NULL; + status_t err; + do { + if (buffer != NULL) { + buffer->release(); + buffer = NULL; + } + err = decoder->read(&buffer, &options); + options.clearSeekTo(); + } while (err == INFO_FORMAT_CHANGED + || (buffer != NULL && buffer->range_length() == 0)); + + if (err != OK) { + CHECK_EQ(buffer, NULL); + + LOGV("decoding frame failed."); + decoder->stop(); + + return NULL; + } + + LOGV("successfully decoded video frame."); + + meta = decoder->getFormat(); + + int32_t width, height; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + + VideoFrame *frame = new VideoFrame; + frame->mWidth = width; + frame->mHeight = height; + frame->mDisplayWidth = width; + frame->mDisplayHeight = height; + frame->mSize = width * height * 2; + frame->mData = new uint8_t[frame->mSize]; + + int32_t srcFormat; + CHECK(meta->findInt32(kKeyColorFormat, &srcFormat)); + + ColorConverter converter( + (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565); + CHECK(converter.isValid()); + + converter.convert( + width, height, + (const uint8_t *)buffer->data() + buffer->range_offset(), + 0, + frame->mData, width * 2); + + buffer->release(); + buffer = NULL; + + decoder->stop(); + + return frame; +} + +MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() { + LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO"); + + return NULL; +} + +const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) { + LOGV("extractMetadata %d (extractor: %s)", + keyCode, mExtractor.get() != NULL ? "YES" : "NO"); + + return NULL; +} + +} // namespace android diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.h b/media/libmediaplayerservice/StagefrightMetadataRetriever.h new file mode 100644 index 0000000..16127d7 --- /dev/null +++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.h @@ -0,0 +1,53 @@ +/* +** +** Copyright 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 STAGEFRIGHT_METADATA_RETRIEVER_H_ + +#define STAGEFRIGHT_METADATA_RETRIEVER_H_ + +#include <media/MediaMetadataRetrieverInterface.h> + +#include <media/stagefright/OMXClient.h> + +namespace android { + +class MediaExtractor; + +struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface { + StagefrightMetadataRetriever(); + virtual ~StagefrightMetadataRetriever(); + + virtual status_t setDataSource(const char *url); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + + virtual VideoFrame *captureFrame(); + virtual MediaAlbumArt *extractAlbumArt(); + virtual const char *extractMetadata(int keyCode); + +private: + OMXClient mClient; + sp<MediaExtractor> mExtractor; + + StagefrightMetadataRetriever(const StagefrightMetadataRetriever &); + + StagefrightMetadataRetriever &operator=( + const StagefrightMetadataRetriever &); +}; + +} // namespace android + +#endif // STAGEFRIGHT_METADATA_RETRIEVER_H_ diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp index 8627708..aa49429 100644 --- a/media/libmediaplayerservice/TestPlayerStub.cpp +++ b/media/libmediaplayerservice/TestPlayerStub.cpp @@ -176,7 +176,7 @@ status_t TestPlayerStub::resetInternal() mContentUrl = NULL; if (mPlayer) { - LOG_ASSERT(mDeletePlayer != NULL); + LOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null"); (*mDeletePlayer)(mPlayer); mPlayer = NULL; } diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp index 8d85ce2..1e3c5a4 100644 --- a/media/libstagefright/AMRExtractor.cpp +++ b/media/libstagefright/AMRExtractor.cpp @@ -18,7 +18,8 @@ #define LOG_TAG "AMRExtractor" #include <utils/Log.h> -#include <media/stagefright/AMRExtractor.h> +#include "include/AMRExtractor.h" + #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDebug.h> @@ -86,7 +87,7 @@ sp<MediaSource> AMRExtractor::getTrack(size_t index) { return new AMRSource(mDataSource, mIsWide); } -sp<MetaData> AMRExtractor::getTrackMetaData(size_t index) { +sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) { if (mInitCheck != OK || index != 0) { return NULL; } @@ -155,7 +156,7 @@ status_t AMRSource::read( *out = NULL; uint8_t header; - ssize_t n = mDataSource->read_at(mOffset, &header, 1); + ssize_t n = mDataSource->readAt(mOffset, &header, 1); if (n < 1) { return ERROR_IO; @@ -191,7 +192,7 @@ status_t AMRSource::read( // Round up bits to bytes and add 1 for the header byte. frameSize = (frameSize + 7) / 8 + 1; - n = mDataSource->read_at(mOffset, buffer->data(), frameSize); + n = mDataSource->readAt(mOffset, buffer->data(), frameSize); if (n != (ssize_t)frameSize) { buffer->release(); @@ -201,10 +202,7 @@ status_t AMRSource::read( } buffer->set_range(0, frameSize); - buffer->meta_data()->setInt32( - kKeyTimeUnits, (mCurrentTimeUs + 500) / 1000); - buffer->meta_data()->setInt32( - kKeyTimeScale, 1000); + buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs); mOffset += frameSize; mCurrentTimeUs += 20000; // Each frame is 20ms @@ -220,7 +218,7 @@ bool SniffAMR( const sp<DataSource> &source, String8 *mimeType, float *confidence) { char header[9]; - if (source->read_at(0, header, sizeof(header)) != sizeof(header)) { + if (source->readAt(0, header, sizeof(header)) != sizeof(header)) { return false; } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 9f71dae..c36e769 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -16,24 +16,26 @@ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true) LOCAL_SRC_FILES += \ AMRExtractor.cpp \ + AudioPlayer.cpp \ CachingDataSource.cpp \ + CameraSource.cpp \ DataSource.cpp \ FileSource.cpp \ HTTPDataSource.cpp \ HTTPStream.cpp \ JPEGSource.cpp \ - MediaExtractor.cpp \ MP3Extractor.cpp \ MPEG4Extractor.cpp \ MPEG4Writer.cpp \ + MediaExtractor.cpp \ MediaPlayerImpl.cpp \ MmapSource.cpp \ SampleTable.cpp \ ShoutcastSource.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ - AudioPlayer.cpp \ - stagefright_string.cpp + WAVExtractor.cpp \ + string.cpp endif diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 538facb..d7e3f66 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -210,15 +210,9 @@ void AudioPlayer::fillBuffer(void *data, size_t size) { break; } - int32_t units, scale; - bool success = - mInputBuffer->meta_data()->findInt32(kKeyTimeUnits, &units); - success = success && - mInputBuffer->meta_data()->findInt32(kKeyTimeScale, &scale); - CHECK(success); - Mutex::Autolock autoLock(mLock); - mPositionTimeMediaUs = (int64_t)units * 1000000 / scale; + CHECK(mInputBuffer->meta_data()->findInt64( + kKeyTime, &mPositionTimeMediaUs)); mPositionTimeRealUs = ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp index fd00576..23f4897 100644 --- a/media/libstagefright/CachingDataSource.cpp +++ b/media/libstagefright/CachingDataSource.cpp @@ -61,11 +61,11 @@ CachingDataSource::~CachingDataSource() { mData = NULL; } -status_t CachingDataSource::InitCheck() const { - return OK; +status_t CachingDataSource::initCheck() const { + return mSource->initCheck(); } -ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) { +ssize_t CachingDataSource::readAt(off_t offset, void *data, size_t size) { Mutex::Autolock autoLock(mLock); size_t total = 0; @@ -82,7 +82,7 @@ ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) { if (page == NULL) { page = allocate_page(); page->mOffset = offset - offset % mPageSize; - ssize_t n = mSource->read_at(page->mOffset, page->mData, mPageSize); + ssize_t n = mSource->readAt(page->mOffset, page->mData, mPageSize); if (n < 0) { page->mLength = 0; } else { diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 596ab67..e9d8557 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -21,120 +21,142 @@ #include <binder/IServiceManager.h> #include <media/stagefright/CameraSource.h> #include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> -#include <ui/ICameraClient.h> -#include <ui/ICameraService.h> +#include <ui/Camera.h> +#include <ui/CameraParameters.h> +#include <ui/GraphicBuffer.h> +#include <ui/ISurface.h> #include <ui/Overlay.h> -#include <utils/String16.h> +#include <utils/String8.h> namespace android { -class CameraBuffer : public MediaBuffer { -public: - CameraBuffer(const sp<IMemory> &frame) - : MediaBuffer(frame->pointer(), frame->size()), - mFrame(frame) { - } +static int64_t getNowUs() { + struct timeval tv; + gettimeofday(&tv, NULL); - sp<IMemory> releaseFrame() { - sp<IMemory> frame = mFrame; - mFrame.clear(); - return frame; - } + return (int64_t)tv.tv_usec + tv.tv_sec * 1000000; +} -private: - sp<IMemory> mFrame; -}; +struct DummySurface : public BnSurface { + DummySurface() {} -class CameraSourceClient : public BnCameraClient { -public: - CameraSourceClient() - : mSource(NULL) { + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) { + return NULL; } - virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { - CHECK(mSource != NULL); - mSource->notifyCallback(msgType, ext1, ext2); + virtual status_t registerBuffers(const BufferHeap &buffers) { + return OK; } - virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) { - CHECK(mSource != NULL); - mSource->dataCallback(msgType, data); - } + virtual void postBuffer(ssize_t offset) {} + virtual void unregisterBuffers() {} - void setCameraSource(CameraSource *source) { - mSource = source; + virtual sp<OverlayRef> createOverlay( + uint32_t w, uint32_t h, int32_t format) { + return NULL; } +protected: + virtual ~DummySurface() {} + + DummySurface(const DummySurface &); + DummySurface &operator=(const DummySurface &); +}; + +struct CameraSourceListener : public CameraListener { + CameraSourceListener(const sp<CameraSource> &source); + + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); + virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr); + + virtual void postDataTimestamp( + nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); + +protected: + virtual ~CameraSourceListener(); + private: - CameraSource *mSource; + wp<CameraSource> mSource; + + CameraSourceListener(const CameraSourceListener &); + CameraSourceListener &operator=(const CameraSourceListener &); }; -class DummySurface : public BnSurface { -public: - DummySurface() {} +CameraSourceListener::CameraSourceListener(const sp<CameraSource> &source) + : mSource(source) { +} - virtual status_t registerBuffers(const BufferHeap &buffers) { - return OK; - } +CameraSourceListener::~CameraSourceListener() { +} - virtual void postBuffer(ssize_t offset) { - } +void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { + LOGV("notify(%d, %d, %d)", msgType, ext1, ext2); +} - virtual void unregisterBuffers() { - } - - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format) { - return NULL; +void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr) { + LOGV("postData(%d, ptr:%p, size:%d)", + msgType, dataPtr->pointer(), dataPtr->size()); + + sp<CameraSource> source = mSource.promote(); + if (source.get() != NULL) { + source->dataCallback(msgType, dataPtr); } -}; +} + +void CameraSourceListener::postDataTimestamp( + nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) { + LOGV("postDataTimestamp(%lld, %d, ptr:%p, size:%d)", + timestamp, msgType, dataPtr->pointer(), dataPtr->size()); +} // static CameraSource *CameraSource::Create() { - sp<IServiceManager> sm = defaultServiceManager(); - - sp<ICameraService> service = - interface_cast<ICameraService>( - sm->getService(String16("media.camera"))); + sp<Camera> camera = Camera::connect(); - sp<CameraSourceClient> client = new CameraSourceClient; - sp<ICamera> camera = service->connect(client); - - CameraSource *source = new CameraSource(camera, client); - client->setCameraSource(source); + if (camera.get() == NULL) { + return NULL; + } - return source; + return new CameraSource(camera); } -CameraSource::CameraSource( - const sp<ICamera> &camera, const sp<ICameraClient> &client) +CameraSource::CameraSource(const sp<Camera> &camera) : mCamera(camera), - mCameraClient(client), + mWidth(0), + mHeight(0), + mFirstFrameTimeUs(0), mNumFrames(0), mStarted(false) { - printf("params: \"%s\"\n", mCamera->getParameters().string()); + String8 s = mCamera->getParameters(); + printf("params: \"%s\"\n", s.string()); + + CameraParameters params(s); + params.getPreviewSize(&mWidth, &mHeight); } CameraSource::~CameraSource() { if (mStarted) { stop(); } - - mCamera->disconnect(); } status_t CameraSource::start(MetaData *) { CHECK(!mStarted); - status_t err = mCamera->lock(); - CHECK_EQ(err, OK); + mCamera->setListener(new CameraSourceListener(this)); - err = mCamera->setPreviewDisplay(new DummySurface); + sp<ISurface> dummy = new DummySurface; + status_t err = mCamera->setPreviewDisplay(dummy); CHECK_EQ(err, OK); - mCamera->setPreviewCallbackFlag(1); - mCamera->startPreview(); + + mCamera->setPreviewCallbackFlags( + FRAME_CALLBACK_FLAG_ENABLE_MASK + | FRAME_CALLBACK_FLAG_COPY_OUT_MASK); + + err = mCamera->startPreview(); CHECK_EQ(err, OK); mStarted = true; @@ -146,7 +168,6 @@ status_t CameraSource::stop() { CHECK(mStarted); mCamera->stopPreview(); - mCamera->unlock(); mStarted = false; @@ -157,8 +178,8 @@ sp<MetaData> CameraSource::getFormat() { sp<MetaData> meta = new MetaData; meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar); - meta->setInt32(kKeyWidth, 480); - meta->setInt32(kKeyHeight, 320); + meta->setInt32(kKeyWidth, mWidth); + meta->setInt32(kKeyHeight, mHeight); return meta; } @@ -175,6 +196,7 @@ status_t CameraSource::read( } sp<IMemory> frame; + int64_t frameTime; { Mutex::Autolock autoLock(mLock); @@ -184,41 +206,33 @@ status_t CameraSource::read( frame = *mFrames.begin(); mFrames.erase(mFrames.begin()); - } - int count = mNumFrames++; + frameTime = *mFrameTimes.begin(); + mFrameTimes.erase(mFrameTimes.begin()); + } - *buffer = new CameraBuffer(frame); + *buffer = new MediaBuffer(frame->size()); + memcpy((*buffer)->data(), frame->pointer(), frame->size()); + (*buffer)->set_range(0, frame->size()); (*buffer)->meta_data()->clear(); - (*buffer)->meta_data()->setInt32(kKeyTimeScale, 15); - (*buffer)->meta_data()->setInt32(kKeyTimeUnits, count); - - (*buffer)->add_ref(); - (*buffer)->setObserver(this); + (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); return OK; } -void CameraSource::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { - printf("notifyCallback %d, %d, %d\n", msgType, ext1, ext2); -} - void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) { Mutex::Autolock autoLock(mLock); + int64_t nowUs = getNowUs(); + if (mNumFrames == 0) { + mFirstFrameTimeUs = nowUs; + } + ++mNumFrames; + mFrames.push_back(data); + mFrameTimes.push_back(nowUs - mFirstFrameTimeUs); mFrameAvailableCondition.signal(); } -void CameraSource::signalBufferReturned(MediaBuffer *_buffer) { - CameraBuffer *buffer = static_cast<CameraBuffer *>(_buffer); - - mCamera->releaseRecordingFrame(buffer->releaseFrame()); - - buffer->setObserver(NULL); - buffer->release(); - buffer = NULL; -} - } // namespace android diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index daac539..2a6dbc4 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -14,11 +14,13 @@ * limitations under the License. */ -#include <media/stagefright/AMRExtractor.h> +#include "include/AMRExtractor.h" +#include "include/MP3Extractor.h" +#include "include/MPEG4Extractor.h" +#include "include/WAVExtractor.h" + #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MP3Extractor.h> -#include <media/stagefright/MPEG4Extractor.h> #include <utils/String8.h> namespace android { @@ -27,7 +29,7 @@ bool DataSource::getUInt16(off_t offset, uint16_t *x) { *x = 0; uint8_t byte[2]; - if (read_at(offset, byte, 2) != 2) { + if (readAt(offset, byte, 2) != 2) { return false; } @@ -86,6 +88,7 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffMP3); RegisterSniffer(SniffMPEG4); RegisterSniffer(SniffAMR); + RegisterSniffer(SniffWAV); } } // namespace android diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp index 53b92a0..28d338c 100644 --- a/media/libstagefright/ESDS.cpp +++ b/media/libstagefright/ESDS.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <media/stagefright/ESDS.h> +#include "include/ESDS.h" #include <string.h> diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index f6b90b2..f318ee3 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -30,11 +30,11 @@ FileSource::~FileSource() { } } -status_t FileSource::InitCheck() const { +status_t FileSource::initCheck() const { return mFile != NULL ? OK : NO_INIT; } -ssize_t FileSource::read_at(off_t offset, void *data, size_t size) { +ssize_t FileSource::readAt(off_t offset, void *data, size_t size) { Mutex::Autolock autoLock(mLock); int err = fseeko(mFile, offset, SEEK_SET); diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp index 4dedebd..5536801 100644 --- a/media/libstagefright/HTTPDataSource.cpp +++ b/media/libstagefright/HTTPDataSource.cpp @@ -14,17 +14,19 @@ * limitations under the License. */ +#include "include/stagefright_string.h" +#include "include/HTTPStream.h" + #include <stdlib.h> #include <media/stagefright/HTTPDataSource.h> -#include <media/stagefright/HTTPStream.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/stagefright_string.h> namespace android { HTTPDataSource::HTTPDataSource(const char *uri) - : mHost(NULL), + : mHttp(new HTTPStream), + mHost(NULL), mPort(0), mPath(NULL), mBuffer(malloc(kBufferSize)), @@ -65,33 +67,40 @@ HTTPDataSource::HTTPDataSource(const char *uri) mPort = port; mPath = strdup(path.c_str()); - status_t err = mHttp.connect(mHost, mPort); - CHECK_EQ(err, OK); + mInitCheck = mHttp->connect(mHost, mPort); } HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path) - : mHost(strdup(host)), + : mHttp(new HTTPStream), + mHost(strdup(host)), mPort(port), mPath(strdup(path)), mBuffer(malloc(kBufferSize)), mBufferLength(0), mBufferOffset(0) { - status_t err = mHttp.connect(mHost, mPort); - CHECK_EQ(err, OK); + mInitCheck = mHttp->connect(mHost, mPort); +} + +status_t HTTPDataSource::initCheck() const { + return mInitCheck; } HTTPDataSource::~HTTPDataSource() { - mHttp.disconnect(); + mHttp->disconnect(); free(mBuffer); mBuffer = NULL; free(mPath); mPath = NULL; + + delete mHttp; + mHttp = NULL; } -ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) { - if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) { +ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) { + if (offset >= mBufferOffset + && offset < (off_t)(mBufferOffset + mBufferLength)) { size_t num_bytes_available = mBufferLength - (offset - mBufferOffset); size_t copy = num_bytes_available; @@ -119,19 +128,19 @@ ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) { status_t err; int attempt = 1; for (;;) { - if ((err = mHttp.send("GET ")) != OK - || (err = mHttp.send(mPath)) != OK - || (err = mHttp.send(" HTTP/1.1\r\n")) != OK - || (err = mHttp.send(host)) != OK - || (err = mHttp.send(range)) != OK - || (err = mHttp.send("\r\n")) != OK - || (err = mHttp.receive_header(&http_status)) != OK) { + if ((err = mHttp->send("GET ")) != OK + || (err = mHttp->send(mPath)) != OK + || (err = mHttp->send(" HTTP/1.1\r\n")) != OK + || (err = mHttp->send(host)) != OK + || (err = mHttp->send(range)) != OK + || (err = mHttp->send("\r\n")) != OK + || (err = mHttp->receive_header(&http_status)) != OK) { if (attempt == 3) { return err; } - mHttp.connect(mHost, mPort); + mHttp->connect(mHost, mPort); ++attempt; } else { break; @@ -143,14 +152,14 @@ ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) { } string value; - if (!mHttp.find_header_value("Content-Length", &value)) { + if (!mHttp->find_header_value("Content-Length", &value)) { return UNKNOWN_ERROR; } char *end; unsigned long contentLength = strtoul(value.c_str(), &end, 10); - ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength); + ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength); if (num_bytes_received <= 0) { return num_bytes_received; diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index 6af7df9..02f9439 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "include/HTTPStream.h" + #include <sys/socket.h> #include <arpa/inet.h> @@ -25,7 +27,6 @@ #include <string.h> #include <unistd.h> -#include <media/stagefright/HTTPStream.h> #include <media/stagefright/MediaDebug.h> namespace android { diff --git a/media/libstagefright/JPEGSource.cpp b/media/libstagefright/JPEGSource.cpp index d1dfd83..a4be2dd 100644 --- a/media/libstagefright/JPEGSource.cpp +++ b/media/libstagefright/JPEGSource.cpp @@ -119,7 +119,7 @@ status_t JPEGSource::read( MediaBuffer *buffer; mGroup->acquire_buffer(&buffer); - ssize_t n = mSource->read_at(mOffset, buffer->data(), mSize - mOffset); + ssize_t n = mSource->readAt(mOffset, buffer->data(), mSize - mOffset); if (n <= 0) { buffer->release(); @@ -156,13 +156,13 @@ status_t JPEGSource::parseJPEG() { for (;;) { uint8_t marker; - if (mSource->read_at(i++, &marker, 1) != 1) { + if (mSource->readAt(i++, &marker, 1) != 1) { return ERROR_IO; } CHECK_EQ(marker, 0xff); - if (mSource->read_at(i++, &marker, 1) != 1) { + if (mSource->readAt(i++, &marker, 1) != 1) { return ERROR_IO; } diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 7fd699f..8dd8ea9 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -18,8 +18,9 @@ #define LOG_TAG "MP3Extractor" #include <utils/Log.h> +#include "include/MP3Extractor.h" + #include <media/stagefright/DataSource.h> -#include <media/stagefright/MP3Extractor.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDebug.h> @@ -147,7 +148,12 @@ static bool get_mp3_frame_size( *out_bitrate = bitrate; } - *frame_size = 144000 * bitrate / sampling_rate + padding; + if (version == 3 /* V1 */) { + *frame_size = 144000 * bitrate / sampling_rate + padding; + } else { + // V2 or V2.5 + *frame_size = 72000 * bitrate / sampling_rate + padding; + } } if (out_sampling_rate) { @@ -166,6 +172,33 @@ static bool get_mp3_frame_size( static bool Resync( const sp<DataSource> &source, uint32_t match_header, off_t *inout_pos, uint32_t *out_header) { + if (*inout_pos == 0) { + // Skip an optional ID3 header if syncing at the very beginning + // of the datasource. + + uint8_t id3header[10]; + if (source->readAt(0, id3header, sizeof(id3header)) + < (ssize_t)sizeof(id3header)) { + // If we can't even read these 10 bytes, we might as well bail out, + // even if there _were_ 10 bytes of valid mp3 audio data... + return false; + } + + if (id3header[0] == 'I' && id3header[1] == 'D' && id3header[2] == '3') { + // Skip the ID3v2 header. + + size_t len = + ((id3header[6] & 0x7f) << 21) + | ((id3header[7] & 0x7f) << 14) + | ((id3header[8] & 0x7f) << 7) + | (id3header[9] & 0x7f); + + len += 10; + + *inout_pos += len; + } + } + // Everything must match except for // protection, bitrate, padding, private bits and mode extension. const uint32_t kMask = 0xfffe0ccf; @@ -195,7 +228,7 @@ static bool Resync( buffer_length = buffer_length - buffer_offset; buffer_offset = 0; - ssize_t n = source->read_at( + ssize_t n = source->readAt( pos, &buffer[buffer_length], kMaxFrameSize - buffer_length); if (n <= 0) { @@ -232,7 +265,7 @@ static bool Resync( valid = true; for (int j = 0; j < 3; ++j) { uint8_t tmp[4]; - if (source->read_at(test_pos, tmp, 4) < 4) { + if (source->readAt(test_pos, tmp, 4) < 4) { valid = false; break; } @@ -338,10 +371,9 @@ MP3Extractor::MP3Extractor(const sp<DataSource> &source) off_t fileSize; if (mDataSource->getSize(&fileSize) == OK) { - mMeta->setInt32( + mMeta->setInt64( kKeyDuration, - 8 * (fileSize - mFirstFramePos) / bitrate); - mMeta->setInt32(kKeyTimeScale, 1000); + 8000LL * (fileSize - mFirstFramePos) / bitrate); } } } @@ -362,7 +394,7 @@ sp<MediaSource> MP3Extractor::getTrack(size_t index) { mMeta, mDataSource, mFirstFramePos, mFixedHeader); } -sp<MetaData> MP3Extractor::getTrackMetaData(size_t index) { +sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) { if (mFirstFramePos < 0 || index != 0) { return NULL; } @@ -448,7 +480,7 @@ status_t MP3Source::read( size_t frame_size; for (;;) { - ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), 4); + ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), 4); if (n < 4) { buffer->release(); buffer = NULL; @@ -482,7 +514,7 @@ status_t MP3Source::read( CHECK(frame_size <= buffer->size()); - ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), frame_size); + ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), frame_size); if (n < (ssize_t)frame_size) { buffer->release(); buffer = NULL; @@ -492,8 +524,7 @@ status_t MP3Source::read( buffer->set_range(0, frame_size); - buffer->meta_data()->setInt32(kKeyTimeUnits, mCurrentTimeUs / 1000); - buffer->meta_data()->setInt32(kKeyTimeScale, 1000); + buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs); mCurrentPos += frame_size; mCurrentTimeUs += 1152 * 1000000 / 44100; diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 9174d19..9d17064 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -17,6 +17,9 @@ #define LOG_TAG "MPEG4Extractor" #include <utils/Log.h> +#include "include/MPEG4Extractor.h" +#include "include/SampleTable.h" + #include <arpa/inet.h> #include <ctype.h> @@ -25,14 +28,12 @@ #include <string.h> #include <media/stagefright/DataSource.h> -#include <media/stagefright/MPEG4Extractor.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> -#include <media/stagefright/SampleTable.h> #include <media/stagefright/Utils.h> #include <utils/String8.h> @@ -43,6 +44,7 @@ public: // Caller retains ownership of both "dataSource" and "sampleTable". MPEG4Source(const sp<MetaData> &format, const sp<DataSource> &dataSource, + int32_t timeScale, const sp<SampleTable> &sampleTable); virtual status_t start(MetaData *params = NULL); @@ -177,7 +179,8 @@ size_t MPEG4Extractor::countTracks() { return n; } -sp<MetaData> MPEG4Extractor::getTrackMetaData(size_t index) { +sp<MetaData> MPEG4Extractor::getTrackMetaData( + size_t index, uint32_t flags) { status_t err; if ((err = readMetaData()) != OK) { return NULL; @@ -197,6 +200,25 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData(size_t index) { return NULL; } + if ((flags & kIncludeExtensiveMetaData) + && !track->includes_expensive_metadata) { + track->includes_expensive_metadata = true; + + const char *mime; + CHECK(track->meta->findCString(kKeyMIMEType, &mime)); + if (!strncasecmp("video/", mime, 6)) { + uint32_t sampleIndex; + uint32_t sampleTime; + if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK + && track->sampleTable->getDecodingTime( + sampleIndex, &sampleTime) == OK) { + track->meta->setInt64( + kKeyThumbnailTime, + ((int64_t)sampleTime * 1000000) / track->timescale); + } + } + } + return track->meta; } @@ -227,7 +249,7 @@ static void MakeFourCCString(uint32_t x, char *s) { status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { uint32_t hdr[2]; - if (mDataSource->read_at(*offset, hdr, 8) < 8) { + if (mDataSource->readAt(*offset, hdr, 8) < 8) { return ERROR_IO; } uint64_t chunk_size = ntohl(hdr[0]); @@ -235,7 +257,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { off_t data_offset = *offset + 8; if (chunk_size == 1) { - if (mDataSource->read_at(*offset + 8, &chunk_size, 8) < 8) { + if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) { return ERROR_IO; } chunk_size = ntoh64(chunk_size); @@ -252,7 +274,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { char buffer[256]; if (chunk_size <= sizeof(buffer)) { - if (mDataSource->read_at(*offset, buffer, chunk_size) < chunk_size) { + if (mDataSource->readAt(*offset, buffer, chunk_size) < chunk_size) { return ERROR_IO; } @@ -298,7 +320,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { CHECK(chunk_data_size >= 4); uint8_t version; - if (mDataSource->read_at(data_offset, &version, 1) < 1) { + if (mDataSource->readAt(data_offset, &version, 1) < 1) { return ERROR_IO; } @@ -312,7 +334,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } uint8_t buffer[36 + 60]; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { return ERROR_IO; } @@ -329,7 +351,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } uint8_t buffer[24 + 60]; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { return ERROR_IO; } @@ -351,6 +373,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { mLastTrack = track; track->meta = new MetaData; + track->includes_expensive_metadata = false; track->timescale = 0; track->sampleTable = new SampleTable(mDataSource); track->meta->setCString(kKeyMIMEType, "application/octet-stream"); @@ -366,7 +389,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } uint8_t version; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, &version, sizeof(version)) < (ssize_t)sizeof(version)) { return ERROR_IO; @@ -383,18 +406,17 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } uint32_t timescale; - if (mDataSource->read_at( + if (mDataSource->readAt( timescale_offset, ×cale, sizeof(timescale)) < (ssize_t)sizeof(timescale)) { return ERROR_IO; } mLastTrack->timescale = ntohl(timescale); - mLastTrack->meta->setInt32(kKeyTimeScale, mLastTrack->timescale); int64_t duration; if (version == 1) { - if (mDataSource->read_at( + if (mDataSource->readAt( timescale_offset + 4, &duration, sizeof(duration)) < (ssize_t)sizeof(duration)) { return ERROR_IO; @@ -402,14 +424,15 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { duration = ntoh64(duration); } else { int32_t duration32; - if (mDataSource->read_at( + if (mDataSource->readAt( timescale_offset + 4, &duration32, sizeof(duration32)) < (ssize_t)sizeof(duration32)) { return ERROR_IO; } duration = ntohl(duration32); } - mLastTrack->meta->setInt32(kKeyDuration, duration); + mLastTrack->meta->setInt64( + kKeyDuration, (duration * 1000000) / mLastTrack->timescale); *offset += chunk_size; break; @@ -422,7 +445,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } uint8_t buffer[24]; - if (mDataSource->read_at(data_offset, buffer, 24) < 24) { + if (mDataSource->readAt(data_offset, buffer, 24) < 24) { return ERROR_IO; } @@ -449,7 +472,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { uint8_t buffer[8]; CHECK(chunk_data_size >= (off_t)sizeof(buffer)); - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, buffer, 8) < 8) { return ERROR_IO; } @@ -492,7 +515,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return ERROR_MALFORMED; } - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { return ERROR_IO; } @@ -544,7 +567,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return ERROR_MALFORMED; } - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { return ERROR_IO; } @@ -655,7 +678,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return ERROR_BUFFER_TOO_SMALL; } - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, buffer, chunk_data_size) < chunk_data_size) { return ERROR_IO; } @@ -679,7 +702,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return ERROR_BUFFER_TOO_SMALL; } - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, buffer, chunk_data_size) < chunk_data_size) { return ERROR_IO; } @@ -722,7 +745,7 @@ sp<MediaSource> MPEG4Extractor::getTrack(size_t index) { } return new MPEG4Source( - track->meta, mDataSource, track->sampleTable); + track->meta, mDataSource, track->timescale, track->sampleTable); } //////////////////////////////////////////////////////////////////////////////// @@ -730,10 +753,11 @@ sp<MediaSource> MPEG4Extractor::getTrack(size_t index) { MPEG4Source::MPEG4Source( const sp<MetaData> &format, const sp<DataSource> &dataSource, + int32_t timeScale, const sp<SampleTable> &sampleTable) : mFormat(format), mDataSource(dataSource), - mTimescale(0), + mTimescale(timeScale), mSampleTable(sampleTable), mCurrentSampleIndex(0), mIsAVC(false), @@ -746,9 +770,6 @@ MPEG4Source::MPEG4Source( bool success = mFormat->findCString(kKeyMIMEType, &mime); CHECK(success); - success = mFormat->findInt32(kKeyTimeScale, &mTimescale); - CHECK(success); - mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); } @@ -868,7 +889,7 @@ status_t MPEG4Source::read( if (!mIsAVC || mWantsNALFragments) { if (newBuffer) { ssize_t num_bytes_read = - mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size); + mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); if (num_bytes_read < (ssize_t)size) { mBuffer->release(); @@ -879,8 +900,8 @@ status_t MPEG4Source::read( mBuffer->set_range(0, size); mBuffer->meta_data()->clear(); - mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts); - mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale); + mBuffer->meta_data()->setInt64( + kKeyTime, ((int64_t)dts * 1000000) / mTimescale); ++mCurrentSampleIndex; } @@ -923,7 +944,7 @@ status_t MPEG4Source::read( // the start code (0x00 00 00 01). ssize_t num_bytes_read = - mDataSource->read_at(offset, mSrcBuffer, size); + mDataSource->readAt(offset, mSrcBuffer, size); if (num_bytes_read < (ssize_t)size) { mBuffer->release(); @@ -959,8 +980,8 @@ status_t MPEG4Source::read( mBuffer->set_range(0, dstOffset); mBuffer->meta_data()->clear(); - mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts); - mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale); + mBuffer->meta_data()->setInt64( + kKeyTime, ((int64_t)dts * 1000000) / mTimescale); ++mCurrentSampleIndex; *out = mBuffer; @@ -974,13 +995,14 @@ bool SniffMPEG4( const sp<DataSource> &source, String8 *mimeType, float *confidence) { uint8_t header[8]; - ssize_t n = source->read_at(4, header, sizeof(header)); + ssize_t n = source->readAt(4, header, sizeof(header)); if (n < (ssize_t)sizeof(header)) { return false; } if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8) - || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)) { + || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8) + || !memcmp(header, "ftypM4A ", 8)) { *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; *confidence = 0.1; diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index fa35768..9a7a873 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -399,15 +399,11 @@ void MPEG4Writer::Track::threadEntry() { info.size = buffer->range_length(); info.offset = offset; - int32_t units, scale; - bool success = - buffer->meta_data()->findInt32(kKeyTimeUnits, &units); - CHECK(success); - success = - buffer->meta_data()->findInt32(kKeyTimeScale, &scale); - CHECK(success); - - info.timestamp = (int64_t)units * 1000 / scale; + int64_t timestampUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); + + // Our timestamp is in ms. + info.timestamp = (timestampUs + 500) / 1000; mSampleInfos.push_back(info); diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp index f3c0e73..b973745 100644 --- a/media/libstagefright/MediaBuffer.cpp +++ b/media/libstagefright/MediaBuffer.cpp @@ -108,10 +108,10 @@ size_t MediaBuffer::range_length() const { } void MediaBuffer::set_range(size_t offset, size_t length) { - if (offset < 0 || offset + length > mSize) { + if (offset + length > mSize) { LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize); } - CHECK(offset >= 0 && offset + length <= mSize); + CHECK(offset + length <= mSize); mRangeOffset = offset; mRangeLength = length; diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 87b5b24..04b1454 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -32,5 +32,6 @@ const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4"; +const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav"; } // namespace android diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 8535f52..19a1f85 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -18,12 +18,17 @@ #define LOG_TAG "MediaExtractor" #include <utils/Log.h> -#include <media/stagefright/AMRExtractor.h> +#include "include/AMRExtractor.h" +#include "include/MP3Extractor.h" +#include "include/MPEG4Extractor.h" +#include "include/WAVExtractor.h" + +#include <media/stagefright/CachingDataSource.h> #include <media/stagefright/DataSource.h> +#include <media/stagefright/HTTPDataSource.h> #include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MP3Extractor.h> -#include <media/stagefright/MPEG4Extractor.h> #include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MmapSource.h> #include <utils/String8.h> namespace android { @@ -41,7 +46,7 @@ sp<MediaExtractor> MediaExtractor::Create( } mime = tmp.string(); - LOGI("Autodetected media content as '%s' with confidence %.2f", + LOGV("Autodetected media content as '%s' with confidence %.2f", mime, confidence); } @@ -53,9 +58,32 @@ sp<MediaExtractor> MediaExtractor::Create( } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { return new AMRExtractor(source); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) { + return new WAVExtractor(source); } return NULL; } +// static +sp<MediaExtractor> MediaExtractor::CreateFromURI( + const char *uri, const char *mime) { + sp<DataSource> source; + if (!strncasecmp("file://", uri, 7)) { + source = new MmapSource(uri + 7); + } else if (!strncasecmp("http://", uri, 7)) { + source = new HTTPDataSource(uri); + source = new CachingDataSource(source, 64 * 1024, 10); + } else { + // Assume it's a filename. + source = new MmapSource(uri); + } + + if (source == NULL || source->initCheck() != OK) { + return NULL; + } + + return Create(source, mime); +} + } // namespace android diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp index 622ea7e..c1044a3 100644 --- a/media/libstagefright/MediaPlayerImpl.cpp +++ b/media/libstagefright/MediaPlayerImpl.cpp @@ -18,16 +18,17 @@ #define LOG_TAG "MediaPlayerImpl" #include "utils/Log.h" +#include "include/stagefright_string.h" +#include "include/HTTPStream.h" + #include <OMX_Component.h> #include <unistd.h> #include <media/stagefright/AudioPlayer.h> -#include <media/stagefright/CachingDataSource.h> // #include <media/stagefright/CameraSource.h> -#include <media/stagefright/HTTPDataSource.h> -#include <media/stagefright/HTTPStream.h> #include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MediaPlayerImpl.h> #include <media/stagefright/MetaData.h> @@ -51,7 +52,7 @@ MediaPlayerImpl::MediaPlayerImpl(const char *uri) mPlaying(false), mPaused(false), mSeeking(false) { - LOGI("MediaPlayerImpl(%s)", uri); + LOGV("MediaPlayerImpl(%s)", uri); DataSource::RegisterDefaultSniffers(); status_t err = mClient.connect(); @@ -69,18 +70,7 @@ MediaPlayerImpl::MediaPlayerImpl(const char *uri) mVideoDecoder = CameraSource::Create(); #endif } else { - sp<DataSource> source; - if (!strncasecmp("file://", uri, 7)) { - source = new MmapSource(uri + 7); - } else if (!strncasecmp("http://", uri, 7)) { - source = new HTTPDataSource(uri); - source = new CachingDataSource(source, 64 * 1024, 10); - } else { - // Assume it's a filename. - source = new MmapSource(uri); - } - - mExtractor = MediaExtractor::Create(source); + mExtractor = MediaExtractor::CreateFromURI(uri); if (mExtractor == NULL) { return; @@ -103,7 +93,7 @@ MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length) mPlaying(false), mPaused(false), mSeeking(false) { - LOGI("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length); + LOGV("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length); DataSource::RegisterDefaultSniffers(); status_t err = mClient.connect(); @@ -140,7 +130,7 @@ MediaPlayerImpl::~MediaPlayerImpl() { } void MediaPlayerImpl::play() { - LOGI("play"); + LOGV("play"); if (mPlaying) { if (mPaused) { @@ -241,7 +231,7 @@ void MediaPlayerImpl::videoEntry() { { Mutex::Autolock autoLock(mLock); if (mSeeking) { - LOGI("seek-options to %lld", mSeekTimeUs); + LOGV("seek-options to %lld", mSeekTimeUs); options.setSeekTo(mSeekTimeUs); mSeeking = false; @@ -258,6 +248,13 @@ void MediaPlayerImpl::videoEntry() { status_t err = mVideoDecoder->read(&buffer, &options); CHECK((err == OK && buffer != NULL) || (err != OK && buffer == NULL)); + if (err == INFO_FORMAT_CHANGED) { + LOGV("format changed."); + depopulateISurface(); + populateISurface(); + continue; + } + if (err == ERROR_END_OF_STREAM || err != OK) { eof = true; continue; @@ -269,15 +266,9 @@ void MediaPlayerImpl::videoEntry() { continue; } - int32_t units, scale; - bool success = - buffer->meta_data()->findInt32(kKeyTimeUnits, &units); - CHECK(success); - success = - buffer->meta_data()->findInt32(kKeyTimeScale, &scale); - CHECK(success); + int64_t pts_us; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &pts_us)); - int64_t pts_us = (int64_t)units * 1000000 / scale; { Mutex::Autolock autoLock(mLock); mVideoPosition = pts_us; @@ -332,14 +323,14 @@ void MediaPlayerImpl::displayOrDiscardFrame( if (delay_us < -15000) { // We're late. - LOGI("we're late by %lld ms, dropping a frame\n", + LOGV("we're late by %lld ms, dropping a frame\n", -delay_us / 1000); buffer->release(); buffer = NULL; return; } else if (delay_us > 100000) { - LOGI("we're much too early (by %lld ms)\n", + LOGV("we're much too early (by %lld ms)\n", delay_us / 1000); usleep(100000); continue; @@ -391,12 +382,10 @@ void MediaPlayerImpl::init() { sp<MediaSource> source = mExtractor->getTrack(i); - int32_t units, scale; - if (meta->findInt32(kKeyDuration, &units) - && meta->findInt32(kKeyTimeScale, &scale)) { - int64_t duration_us = (int64_t)units * 1000000 / scale; - if (duration_us > mDuration) { - mDuration = duration_us; + int64_t durationUs; + if (meta->findInt64(kKeyDuration, &durationUs)) { + if (durationUs > mDuration) { + mDuration = durationUs; } } @@ -410,17 +399,24 @@ void MediaPlayerImpl::init() { } void MediaPlayerImpl::setAudioSource(const sp<MediaSource> &source) { - LOGI("setAudioSource"); + LOGV("setAudioSource"); mAudioSource = source; sp<MetaData> meta = source->getFormat(); - mAudioDecoder = OMXCodec::Create( - mClient.interface(), meta, false /* createEncoder */, source); + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { + mAudioDecoder = source; + } else { + mAudioDecoder = OMXCodec::Create( + mClient.interface(), meta, false /* createEncoder */, source); + } } void MediaPlayerImpl::setVideoSource(const sp<MediaSource> &source) { - LOGI("setVideoSource"); + LOGV("setVideoSource"); mVideoSource = source; sp<MetaData> meta = source->getFormat(); @@ -441,7 +437,7 @@ void MediaPlayerImpl::setVideoSource(const sp<MediaSource> &source) { } void MediaPlayerImpl::setSurface(const sp<Surface> &surface) { - LOGI("setSurface %p", surface.get()); + LOGV("setSurface %p", surface.get()); Mutex::Autolock autoLock(mLock); depopulateISurface(); @@ -455,7 +451,7 @@ void MediaPlayerImpl::setSurface(const sp<Surface> &surface) { } void MediaPlayerImpl::setISurface(const sp<ISurface> &isurface) { - LOGI("setISurface %p", isurface.get()); + LOGV("setISurface %p", isurface.get()); Mutex::Autolock autoLock(mLock); depopulateISurface(); @@ -499,7 +495,7 @@ MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) { host = string(host, 0, colon - host.c_str()); } - LOGI("Connecting to host '%s', port %d, path '%s'", + LOGV("Connecting to host '%s', port %d, path '%s'", host.c_str(), port, path.c_str()); HTTPStream *http = new HTTPStream; @@ -533,7 +529,7 @@ MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) { http->disconnect(); - LOGI("Redirecting to %s\n", location.c_str()); + LOGV("Redirecting to %s\n", location.c_str()); host = string(location, 0, slashPos); @@ -588,7 +584,7 @@ int64_t MediaPlayerImpl::getPosition() { } status_t MediaPlayerImpl::seekTo(int64_t time) { - LOGI("seekTo %lld", time); + LOGV("seekTo %lld", time); if (mPaused) { return UNKNOWN_ERROR; @@ -621,6 +617,9 @@ void MediaPlayerImpl::populateISurface() { success = success && meta->findInt32(kKeyHeight, &decodedHeight); CHECK(success); + LOGV("mVideoWidth=%d, mVideoHeight=%d, decodedWidth=%d, decodedHeight=%d", + mVideoWidth, mVideoHeight, decodedWidth, decodedHeight); + if (mSurface.get() != NULL) { mVideoRenderer = mClient.interface()->createRenderer( @@ -651,7 +650,7 @@ void MediaPlayerImpl::sendFrameToISurface(MediaBuffer *buffer) { void MediaPlayerImpl::setAudioSink( const sp<MediaPlayerBase::AudioSink> &audioSink) { - LOGI("setAudioSink %p", audioSink.get()); + LOGV("setAudioSink %p", audioSink.get()); mAudioSink = audioSink; } diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp index 6b067cb..63b476e 100644 --- a/media/libstagefright/MetaData.cpp +++ b/media/libstagefright/MetaData.cpp @@ -58,6 +58,10 @@ bool MetaData::setInt32(uint32_t key, int32_t value) { return setData(key, TYPE_INT32, &value, sizeof(value)); } +bool MetaData::setInt64(uint32_t key, int64_t value) { + return setData(key, TYPE_INT64, &value, sizeof(value)); +} + bool MetaData::setFloat(uint32_t key, float value) { return setData(key, TYPE_FLOAT, &value, sizeof(value)); } @@ -94,6 +98,21 @@ bool MetaData::findInt32(uint32_t key, int32_t *value) { return true; } +bool MetaData::findInt64(uint32_t key, int64_t *value) { + uint32_t type; + const void *data; + size_t size; + if (!findData(key, &type, &data, &size) || type != TYPE_INT64) { + return false; + } + + CHECK_EQ(size, sizeof(*value)); + + *value = *(int64_t *)data; + + return true; +} + bool MetaData::findFloat(uint32_t key, float *value) { uint32_t type; const void *data; diff --git a/media/libstagefright/MmapSource.cpp b/media/libstagefright/MmapSource.cpp index 47d95f9..42749cf 100644 --- a/media/libstagefright/MmapSource.cpp +++ b/media/libstagefright/MmapSource.cpp @@ -34,7 +34,10 @@ MmapSource::MmapSource(const char *filename) mBase(NULL), mSize(0) { LOGV("MmapSource '%s'", filename); - CHECK(mFd >= 0); + + if (mFd < 0) { + return; + } off_t size = lseek(mFd, 0, SEEK_END); mSize = (size_t)size; @@ -78,12 +81,12 @@ MmapSource::~MmapSource() { } } -status_t MmapSource::InitCheck() const { +status_t MmapSource::initCheck() const { return mFd == -1 ? NO_INIT : OK; } -ssize_t MmapSource::read_at(off_t offset, void *data, size_t size) { - LOGV("read_at offset:%ld data:%p size:%d", offset, data, size); +ssize_t MmapSource::readAt(off_t offset, void *data, size_t size) { + LOGV("readAt offset:%ld data:%p size:%d", offset, data, size); CHECK(offset >= 0); size_t avail = 0; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index ebf1e0c..6c0367a 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -18,11 +18,12 @@ #define LOG_TAG "OMXCodec" #include <utils/Log.h> +#include "include/ESDS.h" + #include <binder/IServiceManager.h> #include <binder/MemoryDealer.h> #include <binder/ProcessState.h> #include <media/IMediaPlayerService.h> -#include <media/stagefright/ESDS.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDebug.h> @@ -39,6 +40,8 @@ namespace android { +static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; + struct CodecInfo { const char *mime; const char *codec; @@ -169,52 +172,37 @@ static void InitOMXParams(T *params) { params->nVersion.s.nStep = 0; } -// static -sp<OMXCodec> OMXCodec::Create( - const sp<IOMX> &omx, - const sp<MetaData> &meta, bool createEncoder, - const sp<MediaSource> &source, - const char *matchComponentName) { - const char *mime; - bool success = meta->findCString(kKeyMIMEType, &mime); - CHECK(success); - - const char *componentName = NULL; - sp<OMXCodecObserver> observer = new OMXCodecObserver; - 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); - } +static bool IsSoftwareCodec(const char *componentName) { + if (!strncmp("OMX.PV.", componentName, 7)) { + return true; + } - if (!componentName) { - return NULL; - } + return false; +} - // If a specific codec is requested, skip the non-matching ones. - if (matchComponentName && strcmp(componentName, matchComponentName)) { - continue; - } +static int CompareSoftwareCodecsFirst( + const String8 *elem1, const String8 *elem2) { + bool isSoftwareCodec1 = IsSoftwareCodec(elem1->string()); + bool isSoftwareCodec2 = IsSoftwareCodec(elem2->string()); - LOGV("Attempting to allocate OMX node '%s'", componentName); + if (isSoftwareCodec1) { + if (isSoftwareCodec2) { return 0; } + return -1; + } - status_t err = omx->allocateNode(componentName, observer, &node); - if (err == OK) { - LOGI("Successfully allocated OMX node '%s'", componentName); - break; - } + if (isSoftwareCodec2) { + return 1; } + return 0; +} + +// static +uint32_t OMXCodec::getComponentQuirks(const char *componentName) { uint32_t quirks = 0; + if (!strcmp(componentName, "OMX.PV.avcdec")) { quirks |= kWantsNALFragments; - quirks |= kOutputDimensionsAre16Aligned; } if (!strcmp(componentName, "OMX.TI.MP3.decode")) { quirks |= kNeedsFlushBeforeDisable; @@ -222,20 +210,15 @@ sp<OMXCodec> OMXCodec::Create( if (!strcmp(componentName, "OMX.TI.AAC.decode")) { quirks |= kNeedsFlushBeforeDisable; quirks |= kRequiresFlushCompleteEmulation; - - // The following is currently necessary for proper shutdown - // behaviour, but NOT enabled by default in order to make the - // bug reproducible... - // quirks |= kRequiresFlushBeforeShutdown; } if (!strncmp(componentName, "OMX.qcom.video.encoder.", 23)) { quirks |= kRequiresLoadedToIdleAfterAllocation; quirks |= kRequiresAllocateBufferOnInputPorts; + quirks |= kRequiresAllocateBufferOnOutputPorts; } if (!strncmp(componentName, "OMX.qcom.video.decoder.", 23)) { // XXX Required on P....on only. quirks |= kRequiresAllocateBufferOnOutputPorts; - quirks |= kOutputDimensionsAre16Aligned; } if (!strncmp(componentName, "OMX.TI.", 7)) { @@ -248,8 +231,94 @@ sp<OMXCodec> OMXCodec::Create( quirks |= kRequiresAllocateBufferOnOutputPorts; } + return quirks; +} + +// static +void OMXCodec::findMatchingCodecs( + const char *mime, + bool createEncoder, const char *matchComponentName, + uint32_t flags, + Vector<String8> *matchingCodecs) { + matchingCodecs->clear(); + + for (int index = 0;; ++index) { + const char *componentName; + + 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) { + break; + } + + // If a specific codec is requested, skip the non-matching ones. + if (matchComponentName && strcmp(componentName, matchComponentName)) { + continue; + } + + matchingCodecs->push(String8(componentName)); + } + + if (flags & kPreferSoftwareCodecs) { + matchingCodecs->sort(CompareSoftwareCodecsFirst); + } +} + +// static +sp<OMXCodec> OMXCodec::Create( + const sp<IOMX> &omx, + const sp<MetaData> &meta, bool createEncoder, + const sp<MediaSource> &source, + const char *matchComponentName, + uint32_t flags) { + const char *mime; + bool success = meta->findCString(kKeyMIMEType, &mime); + CHECK(success); + + Vector<String8> matchingCodecs; + findMatchingCodecs( + mime, createEncoder, matchComponentName, flags, &matchingCodecs); + + if (matchingCodecs.isEmpty()) { + return NULL; + } + + sp<OMXCodecObserver> observer = new OMXCodecObserver; + IOMX::node_id node = 0; + success = false; + + const char *componentName; + for (size_t i = 0; i < matchingCodecs.size(); ++i) { + componentName = matchingCodecs[i].string(); + + LOGV("Attempting to allocate OMX node '%s'", componentName); + + status_t err = omx->allocateNode(componentName, observer, &node); + if (err == OK) { + LOGV("Successfully allocated OMX node '%s'", componentName); + + success = true; + break; + } + } + + if (!success) { + return NULL; + } + sp<OMXCodec> codec = new OMXCodec( - omx, node, quirks, createEncoder, mime, componentName, + omx, node, getComponentQuirks(componentName), + createEncoder, mime, componentName, source); observer->setCodec(codec); @@ -331,7 +400,7 @@ sp<OMXCodec> OMXCodec::Create( size -= length; } - LOGI("AVC profile = %d (%s), level = %d", + LOGV("AVC profile = %d (%s), level = %d", (int)profile, AVCProfileToString(profile), (int)level / 10); if (!strcmp(componentName, "OMX.TI.Video.Decoder") @@ -452,7 +521,7 @@ status_t OMXCodec::setVideoPortFormatType( // CHECK_EQ(format.nIndex, index); #if 1 - CODEC_LOGI("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d", + CODEC_LOGV("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d", portIndex, index, format.eCompressionFormat, format.eColorFormat); #endif @@ -493,9 +562,25 @@ status_t OMXCodec::setVideoPortFormatType( return err; } +static size_t getFrameSize( + OMX_COLOR_FORMATTYPE colorFormat, int32_t width, int32_t height) { + switch (colorFormat) { + case OMX_COLOR_FormatYCbYCr: + case OMX_COLOR_FormatCbYCrY: + return width * height * 2; + + case OMX_COLOR_FormatYUV420SemiPlanar: + return (width * height * 3) / 2; + + default: + CHECK(!"Should not be here. Unsupported color format."); + break; + } +} + void OMXCodec::setVideoInputFormat( const char *mime, OMX_U32 width, OMX_U32 height) { - CODEC_LOGI("setVideoInputFormat width=%ld, height=%ld", width, height); + CODEC_LOGV("setVideoInputFormat width=%ld, height=%ld", width, height); OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { @@ -516,12 +601,13 @@ void OMXCodec::setVideoInputFormat( colorFormat = OMX_COLOR_FormatYUV420SemiPlanar; } - setVideoPortFormatType( + CHECK_EQ(setVideoPortFormatType( kPortIndexInput, OMX_VIDEO_CodingUnused, - colorFormat); + colorFormat), OK); - setVideoPortFormatType( - kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused); + CHECK_EQ(setVideoPortFormatType( + kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused), + OK); OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); @@ -554,8 +640,8 @@ void OMXCodec::setVideoInputFormat( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); - def.nBufferSize = (width * height * 2); // (width * height * 3) / 2; - CODEC_LOGI("Setting nBufferSize = %ld", def.nBufferSize); + def.nBufferSize = getFrameSize(colorFormat, width, height); + CODEC_LOGV("Setting nBufferSize = %ld", def.nBufferSize); CHECK_EQ(def.eDomain, OMX_PortDomainVideo); @@ -564,14 +650,108 @@ void OMXCodec::setVideoInputFormat( video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; video_def->eColorFormat = colorFormat; + video_def->xFramerate = 24 << 16; // XXX crucial! + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); + + switch (compressionFormat) { + case OMX_VIDEO_CodingMPEG4: + { + CHECK_EQ(setupMPEG4EncoderParameters(), OK); + break; + } + + case OMX_VIDEO_CodingH263: + break; + + default: + CHECK(!"Support for this compressionFormat to be implemented."); + break; + } +} + +status_t OMXCodec::setupMPEG4EncoderParameters() { + OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type; + InitOMXParams(&mpeg4type); + mpeg4type.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); + CHECK_EQ(err, OK); + + mpeg4type.nSliceHeaderSpacing = 0; + mpeg4type.bSVH = OMX_FALSE; + mpeg4type.bGov = OMX_FALSE; + + mpeg4type.nAllowedPictureTypes = + OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + + mpeg4type.nPFrames = 23; + mpeg4type.nBFrames = 0; + + mpeg4type.nIDCVLCThreshold = 0; + mpeg4type.bACPred = OMX_TRUE; + mpeg4type.nMaxPacketSize = 256; + mpeg4type.nTimeIncRes = 1000; + mpeg4type.nHeaderExtension = 0; + mpeg4type.bReversibleVLC = OMX_FALSE; + + mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileCore; + mpeg4type.eLevel = OMX_VIDEO_MPEG4Level2; + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); + CHECK_EQ(err, OK); + + // ---------------- + + OMX_VIDEO_PARAM_BITRATETYPE bitrateType; + InitOMXParams(&bitrateType); + bitrateType.nPortIndex = kPortIndexOutput; + + err = mOMX->getParameter( + mNode, OMX_IndexParamVideoBitrate, + &bitrateType, sizeof(bitrateType)); + CHECK_EQ(err, OK); + + bitrateType.eControlRate = OMX_Video_ControlRateVariable; + bitrateType.nTargetBitrate = 1000000; + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoBitrate, + &bitrateType, sizeof(bitrateType)); + CHECK_EQ(err, OK); + + // ---------------- + + OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType; + InitOMXParams(&errorCorrectionType); + errorCorrectionType.nPortIndex = kPortIndexOutput; + + err = mOMX->getParameter( + mNode, OMX_IndexParamVideoErrorCorrection, + &errorCorrectionType, sizeof(errorCorrectionType)); + CHECK_EQ(err, OK); + + errorCorrectionType.bEnableHEC = OMX_FALSE; + errorCorrectionType.bEnableResync = OMX_TRUE; + errorCorrectionType.nResynchMarkerSpacing = 256; + errorCorrectionType.bEnableDataPartitioning = OMX_FALSE; + errorCorrectionType.bEnableRVLC = OMX_FALSE; + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoErrorCorrection, + &errorCorrectionType, sizeof(errorCorrectionType)); + CHECK_EQ(err, OK); + + return OK; } void OMXCodec::setVideoOutputFormat( const char *mime, OMX_U32 width, OMX_U32 height) { - CODEC_LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height); + CODEC_LOGV("setVideoOutputFormat width=%ld, height=%ld", width, height); OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { @@ -639,6 +819,7 @@ void OMXCodec::setVideoOutputFormat( video_def->nFrameWidth = width; video_def->nFrameHeight = height; + video_def->eCompressionFormat = compressionFormat; video_def->eColorFormat = OMX_COLOR_FormatUnused; err = mOMX->setParameter( @@ -687,6 +868,7 @@ OMXCodec::OMXCodec( mInitialBufferSubmit(true), mSignalledEOS(false), mNoMoreOutputData(false), + mOutputPortSettingsHaveChanged(false), mSeekTimeUs(-1) { mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexOutput] = ENABLED; @@ -990,12 +1172,8 @@ void OMXCodec::on_message(const omx_message &msg) { buffer->meta_data()->clear(); - buffer->meta_data()->setInt32( - kKeyTimeUnits, - (msg.u.extended_buffer_data.timestamp + 500) / 1000); - - buffer->meta_data()->setInt32( - kKeyTimeScale, 1000); + buffer->meta_data()->setInt64( + kKeyTime, msg.u.extended_buffer_data.timestamp); if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) { buffer->meta_data()->setInt32(kKeyIsSyncFrame, true); @@ -1064,6 +1242,71 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { } } +// Has the format changed in any way that the client would have to be aware of? +static bool formatHasNotablyChanged( + const sp<MetaData> &from, const sp<MetaData> &to) { + if (from.get() == NULL && to.get() == NULL) { + return false; + } + + if ((from.get() == NULL && to.get() != NULL) + || (from.get() != NULL && to.get() == NULL)) { + return true; + } + + const char *mime_from, *mime_to; + CHECK(from->findCString(kKeyMIMEType, &mime_from)); + CHECK(to->findCString(kKeyMIMEType, &mime_to)); + + if (strcasecmp(mime_from, mime_to)) { + return true; + } + + if (!strcasecmp(mime_from, MEDIA_MIMETYPE_VIDEO_RAW)) { + int32_t colorFormat_from, colorFormat_to; + CHECK(from->findInt32(kKeyColorFormat, &colorFormat_from)); + CHECK(to->findInt32(kKeyColorFormat, &colorFormat_to)); + + if (colorFormat_from != colorFormat_to) { + return true; + } + + int32_t width_from, width_to; + CHECK(from->findInt32(kKeyWidth, &width_from)); + CHECK(to->findInt32(kKeyWidth, &width_to)); + + if (width_from != width_to) { + return true; + } + + int32_t height_from, height_to; + CHECK(from->findInt32(kKeyHeight, &height_from)); + CHECK(to->findInt32(kKeyHeight, &height_to)); + + if (height_from != height_to) { + return true; + } + } else if (!strcasecmp(mime_from, MEDIA_MIMETYPE_AUDIO_RAW)) { + int32_t numChannels_from, numChannels_to; + CHECK(from->findInt32(kKeyChannelCount, &numChannels_from)); + CHECK(to->findInt32(kKeyChannelCount, &numChannels_to)); + + if (numChannels_from != numChannels_to) { + return true; + } + + int32_t sampleRate_from, sampleRate_to; + CHECK(from->findInt32(kKeySampleRate, &sampleRate_from)); + CHECK(to->findInt32(kKeySampleRate, &sampleRate_to)); + + if (sampleRate_from != sampleRate_to) { + return true; + } + } + + return false; +} + void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { switch (cmd) { case OMX_CommandStateSet: @@ -1086,6 +1329,15 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { if (mState == RECONFIGURING) { CHECK_EQ(portIndex, kPortIndexOutput); + sp<MetaData> oldOutputFormat = mOutputFormat; + initOutputFormat(mSource->getFormat()); + + // Don't notify clients if the output port settings change + // wasn't of importance to them, i.e. it may be that just the + // number of buffers has changed and nothing else. + mOutputPortSettingsHaveChanged = + formatHasNotablyChanged(oldOutputFormat, mOutputFormat); + enablePortAsync(portIndex); status_t err = allocateBuffersOnPort(portIndex); @@ -1443,7 +1695,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) { } OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME; - OMX_TICKS timestamp = 0; + OMX_TICKS timestampUs = 0; size_t srcLength = 0; if (err != OK) { @@ -1463,15 +1715,11 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) { (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; - - CODEC_LOGV("Calling empty_buffer on buffer %p (length %d)", + if (srcBuffer->meta_data()->findInt64(kKeyTime, ×tampUs)) { + CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d)", info->mBuffer, srcLength); - CODEC_LOGV("Calling empty_buffer with timestamp %lld us (%.2f secs)", - timestamp, timestamp / 1E6); + CODEC_LOGV("Calling emptyBuffer with timestamp %lld us (%.2f secs)", + timestampUs, timestampUs / 1E6); } } @@ -1482,7 +1730,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) { err = mOMX->emptyBuffer( mNode, info->mBuffer, 0, srcLength, - flags, timestamp); + flags, timestampUs); if (err != OK) { setState(ERROR); @@ -1794,6 +2042,7 @@ status_t OMXCodec::start(MetaData *) { mInitialBufferSubmit = true; mSignalledEOS = false; mNoMoreOutputData = false; + mOutputPortSettingsHaveChanged = false; mSeekTimeUs = -1; mFilledBuffers.clear(); @@ -1864,6 +2113,8 @@ status_t OMXCodec::stop() { } sp<MetaData> OMXCodec::getFormat() { + Mutex::Autolock autoLock(mLock); + return mOutputFormat; } @@ -1941,6 +2192,12 @@ status_t OMXCodec::read( return ERROR_END_OF_STREAM; } + if (mOutputPortSettingsHaveChanged) { + mOutputPortSettingsHaveChanged = false; + + return INFO_FORMAT_CHANGED; + } + size_t index = *mFilledBuffers.begin(); mFilledBuffers.erase(mFilledBuffers.begin()); @@ -2041,8 +2298,6 @@ static const char *colorFormatString(OMX_COLOR_FORMATTYPE type) { 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) { @@ -2410,7 +2665,7 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { CHECK(!"Unknown compression format."); } - if (mQuirks & kOutputDimensionsAre16Aligned) { + if (!strcmp(mComponentName, "OMX.PV.avcdec")) { // This component appears to be lying to me. mOutputFormat->setInt32( kKeyWidth, (video_def->nFrameWidth + 15) & -16); diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index 8efa7c7..4aec0e9 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -17,11 +17,12 @@ #define LOG_TAG "SampleTable" #include <utils/Log.h> +#include "include/SampleTable.h" + #include <arpa/inet.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/SampleTable.h> #include <media/stagefright/Utils.h> namespace android { @@ -54,7 +55,7 @@ SampleTable::~SampleTable() { } status_t SampleTable::setChunkOffsetParams( - uint32_t type, off_t data_offset, off_t data_size) { + uint32_t type, off_t data_offset, size_t data_size) { if (mChunkOffsetOffset >= 0) { return ERROR_MALFORMED; } @@ -69,7 +70,7 @@ status_t SampleTable::setChunkOffsetParams( } uint8_t header[8]; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } @@ -95,7 +96,7 @@ status_t SampleTable::setChunkOffsetParams( } status_t SampleTable::setSampleToChunkParams( - off_t data_offset, off_t data_size) { + off_t data_offset, size_t data_size) { if (mSampleToChunkOffset >= 0) { return ERROR_MALFORMED; } @@ -107,7 +108,7 @@ status_t SampleTable::setSampleToChunkParams( } uint8_t header[8]; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } @@ -127,7 +128,7 @@ status_t SampleTable::setSampleToChunkParams( } status_t SampleTable::setSampleSizeParams( - uint32_t type, off_t data_offset, off_t data_size) { + uint32_t type, off_t data_offset, size_t data_size) { if (mSampleSizeOffset >= 0) { return ERROR_MALFORMED; } @@ -141,7 +142,7 @@ status_t SampleTable::setSampleSizeParams( } uint8_t header[12]; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } @@ -187,13 +188,13 @@ status_t SampleTable::setSampleSizeParams( } status_t SampleTable::setTimeToSampleParams( - off_t data_offset, off_t data_size) { + off_t data_offset, size_t data_size) { if (mTimeToSample != NULL || data_size < 8) { return ERROR_MALFORMED; } uint8_t header[8]; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } @@ -207,7 +208,7 @@ status_t SampleTable::setTimeToSampleParams( mTimeToSample = new uint32_t[mTimeToSampleCount * 2]; size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset + 8, mTimeToSample, size) < (ssize_t)size) { return ERROR_IO; } @@ -219,7 +220,7 @@ status_t SampleTable::setTimeToSampleParams( return OK; } -status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) { +status_t SampleTable::setSyncSampleParams(off_t data_offset, size_t data_size) { if (mSyncSampleOffset >= 0 || data_size < 8) { return ERROR_MALFORMED; } @@ -227,7 +228,7 @@ status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) { mSyncSampleOffset = data_offset; uint8_t header[8]; - if (mDataSource->read_at( + if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } @@ -263,7 +264,7 @@ status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) { if (mChunkOffsetType == kChunkOffsetType32) { uint32_t offset32; - if (mDataSource->read_at( + if (mDataSource->readAt( mChunkOffsetOffset + 8 + 4 * chunk_index, &offset32, sizeof(offset32)) < (ssize_t)sizeof(offset32)) { @@ -275,7 +276,7 @@ status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) { CHECK_EQ(mChunkOffsetType, kChunkOffsetType64); uint64_t offset64; - if (mDataSource->read_at( + if (mDataSource->readAt( mChunkOffsetOffset + 8 + 8 * chunk_index, &offset64, sizeof(offset64)) < (ssize_t)sizeof(offset64)) { @@ -312,7 +313,7 @@ status_t SampleTable::getChunkForSample( uint32_t index = 0; while (index < mNumSampleToChunkOffsets) { uint8_t buffer[12]; - if (mDataSource->read_at(mSampleToChunkOffset + 8 + index * 12, + if (mDataSource->readAt(mSampleToChunkOffset + 8 + index * 12, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { return ERROR_IO; } @@ -361,7 +362,7 @@ status_t SampleTable::getSampleSize( switch (mSampleSizeFieldSize) { case 32: { - if (mDataSource->read_at( + if (mDataSource->readAt( mSampleSizeOffset + 12 + 4 * sample_index, sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) { return ERROR_IO; @@ -374,7 +375,7 @@ status_t SampleTable::getSampleSize( case 16: { uint16_t x; - if (mDataSource->read_at( + if (mDataSource->readAt( mSampleSizeOffset + 12 + 2 * sample_index, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; @@ -387,7 +388,7 @@ status_t SampleTable::getSampleSize( case 8: { uint8_t x; - if (mDataSource->read_at( + if (mDataSource->readAt( mSampleSizeOffset + 12 + sample_index, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; @@ -402,7 +403,7 @@ status_t SampleTable::getSampleSize( CHECK_EQ(mSampleSizeFieldSize, 4); uint8_t x; - if (mDataSource->read_at( + if (mDataSource->readAt( mSampleSizeOffset + 12 + sample_index / 2, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; @@ -553,7 +554,7 @@ status_t SampleTable::findClosestSyncSample( uint32_t right = mNumSyncSamples; while (left < right) { uint32_t mid = (left + right) / 2; - if (mDataSource->read_at( + if (mDataSource->readAt( mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) { return ERROR_IO; } @@ -574,5 +575,52 @@ status_t SampleTable::findClosestSyncSample( return OK; } +status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { + if (mSyncSampleOffset < 0) { + // All samples are sync-samples. + *sample_index = 0; + return OK; + } + + uint32_t bestSampleIndex = 0; + size_t maxSampleSize = 0; + + static const size_t kMaxNumSyncSamplesToScan = 20; + + // Consider the first kMaxNumSyncSamplesToScan sync samples and + // pick the one with the largest (compressed) size as the thumbnail. + + size_t numSamplesToScan = mNumSyncSamples; + if (numSamplesToScan > kMaxNumSyncSamplesToScan) { + numSamplesToScan = kMaxNumSyncSamplesToScan; + } + + for (size_t i = 0; i < numSamplesToScan; ++i) { + uint32_t x; + if (mDataSource->readAt( + mSyncSampleOffset + 8 + i * 4, &x, 4) != 4) { + return ERROR_IO; + } + x = ntohl(x); + --x; + + // Now x is a sample index. + size_t sampleSize; + status_t err = getSampleSize(x, &sampleSize); + if (err != OK) { + return err; + } + + if (i == 0 || sampleSize > maxSampleSize) { + bestSampleIndex = x; + maxSampleSize = sampleSize; + } + } + + *sample_index = bestSampleIndex; + + return OK; +} + } // namespace android diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp index 346b5aa..ec25430 100644 --- a/media/libstagefright/ShoutcastSource.cpp +++ b/media/libstagefright/ShoutcastSource.cpp @@ -14,16 +14,17 @@ * limitations under the License. */ +#include "include/stagefright_string.h" +#include "include/HTTPStream.h" + #include <stdlib.h> -#include <media/stagefright/HTTPStream.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/ShoutcastSource.h> -#include <media/stagefright/stagefright_string.h> namespace android { diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 3d85f75..dd8005c 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -22,10 +22,11 @@ #define LOG_TAG "TimedEventQueue" #include <utils/Log.h> +#include "include/TimedEventQueue.h" + #include <sys/time.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/TimedEventQueue.h> namespace android { diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp new file mode 100644 index 0000000..542c764 --- /dev/null +++ b/media/libstagefright/WAVExtractor.cpp @@ -0,0 +1,317 @@ +/* + * 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 "WAVExtractor" +#include <utils/Log.h> + +#include "include/WAVExtractor.h" + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <utils/String8.h> + +namespace android { + +static uint16_t WAVE_FORMAT_PCM = 1; + +static uint32_t U32_LE_AT(const uint8_t *ptr) { + return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0]; +} + +static uint16_t U16_LE_AT(const uint8_t *ptr) { + return ptr[1] << 8 | ptr[0]; +} + +struct WAVSource : public MediaSource { + WAVSource( + const sp<DataSource> &dataSource, + int32_t sampleRate, int32_t numChannels, + off_t offset, size_t size); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~WAVSource(); + +private: + static const size_t kMaxFrameSize; + + sp<DataSource> mDataSource; + int32_t mSampleRate; + int32_t mNumChannels; + off_t mOffset; + size_t mSize; + bool mStarted; + MediaBufferGroup *mGroup; + off_t mCurrentPos; + + WAVSource(const WAVSource &); + WAVSource &operator=(const WAVSource &); +}; + +WAVExtractor::WAVExtractor(const sp<DataSource> &source) + : mDataSource(source), + mValidFormat(false) { + mInitCheck = init(); +} + +WAVExtractor::~WAVExtractor() { +} + +size_t WAVExtractor::countTracks() { + return mInitCheck == OK ? 1 : 0; +} + +sp<MediaSource> WAVExtractor::getTrack(size_t index) { + if (mInitCheck != OK || index > 0) { + return NULL; + } + + return new WAVSource( + mDataSource, mSampleRate, mNumChannels, mDataOffset, mDataSize); +} + +sp<MetaData> WAVExtractor::getTrackMetaData( + size_t index, uint32_t flags) { + if (mInitCheck != OK || index > 0) { + return NULL; + } + + sp<MetaData> meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + meta->setInt32(kKeyChannelCount, mNumChannels); + meta->setInt32(kKeySampleRate, mSampleRate); + + int64_t durationUs = + 1000000LL * (mDataSize / (mNumChannels * 2)) / mSampleRate; + + meta->setInt64(kKeyDuration, durationUs); + + return meta; +} + +status_t WAVExtractor::init() { + uint8_t header[12]; + if (mDataSource->readAt( + 0, header, sizeof(header)) < (ssize_t)sizeof(header)) { + return NO_INIT; + } + + if (memcmp(header, "RIFF", 4) || memcmp(&header[8], "WAVE", 4)) { + return NO_INIT; + } + + size_t totalSize = U32_LE_AT(&header[4]); + + off_t offset = 12; + size_t remainingSize = totalSize; + while (remainingSize >= 8) { + uint8_t chunkHeader[8]; + if (mDataSource->readAt(offset, chunkHeader, 8) < 8) { + return NO_INIT; + } + + remainingSize -= 8; + offset += 8; + + uint32_t chunkSize = U32_LE_AT(&chunkHeader[4]); + + if (chunkSize > remainingSize) { + return NO_INIT; + } + + if (!memcmp(chunkHeader, "fmt ", 4)) { + if (chunkSize < 16) { + return NO_INIT; + } + + uint8_t formatSpec[16]; + if (mDataSource->readAt(offset, formatSpec, 16) < 16) { + return NO_INIT; + } + + uint16_t format = U16_LE_AT(formatSpec); + if (format != WAVE_FORMAT_PCM) { + return ERROR_UNSUPPORTED; + } + + mNumChannels = U16_LE_AT(&formatSpec[2]); + if (mNumChannels != 1 && mNumChannels != 2) { + return ERROR_UNSUPPORTED; + } + + mSampleRate = U32_LE_AT(&formatSpec[4]); + + if (U16_LE_AT(&formatSpec[14]) != 16) { + return ERROR_UNSUPPORTED; + } + + mValidFormat = true; + } else if (!memcmp(chunkHeader, "data", 4)) { + if (mValidFormat) { + mDataOffset = offset; + mDataSize = chunkSize; + + return OK; + } + } + + offset += chunkSize; + } + + return NO_INIT; +} + +const size_t WAVSource::kMaxFrameSize = 32768; + +WAVSource::WAVSource( + const sp<DataSource> &dataSource, + int32_t sampleRate, int32_t numChannels, + off_t offset, size_t size) + : mDataSource(dataSource), + mSampleRate(sampleRate), + mNumChannels(numChannels), + mOffset(offset), + mSize(size), + mStarted(false), + mGroup(NULL) { +} + +WAVSource::~WAVSource() { + if (mStarted) { + stop(); + } +} + +status_t WAVSource::start(MetaData *params) { + LOGV("WAVSource::start"); + + CHECK(!mStarted); + + mGroup = new MediaBufferGroup; + mGroup->add_buffer(new MediaBuffer(kMaxFrameSize)); + + mCurrentPos = mOffset; + + mStarted = true; + + return OK; +} + +status_t WAVSource::stop() { + LOGV("WAVSource::stop"); + + CHECK(mStarted); + + delete mGroup; + mGroup = NULL; + + mStarted = false; + + return OK; +} + +sp<MetaData> WAVSource::getFormat() { + LOGV("WAVSource::getFormat"); + + sp<MetaData> meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + meta->setInt32(kKeyChannelCount, mNumChannels); + meta->setInt32(kKeySampleRate, mSampleRate); + + int64_t durationUs = + 1000000LL * (mSize / (mNumChannels * 2)) / mSampleRate; + + meta->setInt64(kKeyDuration, durationUs); + + return meta; +} + +status_t WAVSource::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + int64_t seekTimeUs; + if (options != NULL && options->getSeekTo(&seekTimeUs)) { + int64_t pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * 2; + if (pos > mSize) { + pos = mSize; + } + mCurrentPos = pos + mOffset; + } + + MediaBuffer *buffer; + status_t err = mGroup->acquire_buffer(&buffer); + if (err != OK) { + return err; + } + + ssize_t n = mDataSource->readAt( + mCurrentPos, buffer->data(), kMaxFrameSize); + + if (n <= 0) { + buffer->release(); + buffer = NULL; + + return ERROR_END_OF_STREAM; + } + + mCurrentPos += n; + + buffer->set_range(0, n); + buffer->meta_data()->setInt64( + kKeyTime, + 1000000LL * (mCurrentPos - mOffset) + / (mNumChannels * 2) / mSampleRate); + + + *out = buffer; + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +bool SniffWAV( + const sp<DataSource> &source, String8 *mimeType, float *confidence) { + char header[12]; + if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) { + return false; + } + + if (memcmp(header, "RIFF", 4) || memcmp(&header[8], "WAVE", 4)) { + return false; + } + + *mimeType = MEDIA_MIMETYPE_CONTAINER_WAV; + *confidence = 0.3f; + + return true; +} + +} // namespace android + diff --git a/include/media/stagefright/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h index c8710d3..debf006 100644 --- a/include/media/stagefright/AMRExtractor.h +++ b/media/libstagefright/include/AMRExtractor.h @@ -30,7 +30,7 @@ public: virtual size_t countTracks(); virtual sp<MediaSource> getTrack(size_t index); - virtual sp<MetaData> getTrackMetaData(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); static sp<MetaData> makeAMRFormat(bool isWide); diff --git a/include/media/stagefright/ESDS.h b/media/libstagefright/include/ESDS.h index 01bcd18..01bcd18 100644 --- a/include/media/stagefright/ESDS.h +++ b/media/libstagefright/include/ESDS.h diff --git a/include/media/stagefright/HTTPStream.h b/media/libstagefright/include/HTTPStream.h index 72e796c..43ef614 100644 --- a/include/media/stagefright/HTTPStream.h +++ b/media/libstagefright/include/HTTPStream.h @@ -18,10 +18,11 @@ #define HTTP_STREAM_H_ +#include "stagefright_string.h" + #include <sys/types.h> #include <media/stagefright/MediaErrors.h> -#include <media/stagefright/stagefright_string.h> #include <utils/KeyedVector.h> namespace android { diff --git a/include/media/stagefright/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h index 11ba01d..074973b 100644 --- a/include/media/stagefright/MP3Extractor.h +++ b/media/libstagefright/include/MP3Extractor.h @@ -32,7 +32,7 @@ public: virtual size_t countTracks(); virtual sp<MediaSource> getTrack(size_t index); - virtual sp<MetaData> getTrackMetaData(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); protected: virtual ~MP3Extractor(); diff --git a/include/media/stagefright/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 932e30f..ce4736d 100644 --- a/include/media/stagefright/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -33,7 +33,7 @@ public: size_t countTracks(); sp<MediaSource> getTrack(size_t index); - sp<MetaData> getTrackMetaData(size_t index); + sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); protected: virtual ~MPEG4Extractor(); @@ -44,6 +44,7 @@ private: sp<MetaData> meta; uint32_t timescale; sp<SampleTable> sampleTable; + bool includes_expensive_metadata; }; sp<DataSource> mDataSource; diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index d0bd61e..a4b62b2 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -99,7 +99,7 @@ public: OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, OMX_IN OMX_PTR pEventData); - + OMX_ERRORTYPE OnEmptyBufferDone( node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); diff --git a/include/media/stagefright/SampleTable.h b/media/libstagefright/include/SampleTable.h index 808d142..ead3431 100644 --- a/include/media/stagefright/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -35,17 +35,17 @@ public: // type can be 'stco' or 'co64'. status_t setChunkOffsetParams( - uint32_t type, off_t data_offset, off_t data_size); + uint32_t type, off_t data_offset, size_t data_size); - status_t setSampleToChunkParams(off_t data_offset, off_t data_size); + status_t setSampleToChunkParams(off_t data_offset, size_t data_size); // type can be 'stsz' or 'stz2'. status_t setSampleSizeParams( - uint32_t type, off_t data_offset, off_t data_size); + uint32_t type, off_t data_offset, size_t data_size); - status_t setTimeToSampleParams(off_t data_offset, off_t data_size); + status_t setTimeToSampleParams(off_t data_offset, size_t data_size); - status_t setSyncSampleParams(off_t data_offset, off_t data_size); + status_t setSyncSampleParams(off_t data_offset, size_t data_size); //////////////////////////////////////////////////////////////////////////// @@ -75,6 +75,8 @@ public: status_t findClosestSyncSample( uint32_t start_sample_index, uint32_t *sample_index); + status_t findThumbnailSample(uint32_t *sample_index); + protected: ~SampleTable(); diff --git a/include/media/stagefright/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h index 1545493..9eed089 100644 --- a/include/media/stagefright/SoftwareRenderer.h +++ b/media/libstagefright/include/SoftwareRenderer.h @@ -18,7 +18,7 @@ #define SOFTWARE_RENDERER_H_ -#include <OMX_Video.h> +#include <media/stagefright/ColorConverter.h> #include <media/stagefright/VideoRenderer.h> #include <utils/RefBase.h> @@ -41,13 +41,8 @@ public: const void *data, size_t size, void *platformPrivate); private: - uint8_t *initClip(); - - void renderCbYCrY(const void *data, size_t size); - void renderYUV420Planar(const void *data, size_t size); - void renderQCOMYUV420SemiPlanar(const void *data, size_t size); - OMX_COLOR_FORMATTYPE mColorFormat; + ColorConverter mConverter; sp<ISurface> mISurface; size_t mDisplayWidth, mDisplayHeight; size_t mDecodedWidth, mDecodedHeight; @@ -55,8 +50,6 @@ private: sp<MemoryHeapBase> mMemoryHeap; int mIndex; - uint8_t *mClip; - SoftwareRenderer(const SoftwareRenderer &); SoftwareRenderer &operator=(const SoftwareRenderer &); }; diff --git a/include/media/stagefright/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h index a264421..a264421 100644 --- a/include/media/stagefright/TimedEventQueue.h +++ b/media/libstagefright/include/TimedEventQueue.h diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h new file mode 100644 index 0000000..10b9700 --- /dev/null +++ b/media/libstagefright/include/WAVExtractor.h @@ -0,0 +1,61 @@ +/* + * 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 WAV_EXTRACTOR_H_ + +#define WAV_EXTRACTOR_H_ + +#include <media/stagefright/MediaExtractor.h> + +namespace android { + +class DataSource; +class String8; + +class WAVExtractor : public MediaExtractor { +public: + // Extractor assumes ownership of "source". + WAVExtractor(const sp<DataSource> &source); + + virtual size_t countTracks(); + virtual sp<MediaSource> getTrack(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); + +protected: + virtual ~WAVExtractor(); + +private: + sp<DataSource> mDataSource; + status_t mInitCheck; + bool mValidFormat; + uint16_t mNumChannels; + uint32_t mSampleRate; + off_t mDataOffset; + size_t mDataSize; + + status_t init(); + + WAVExtractor(const WAVExtractor &); + WAVExtractor &operator=(const WAVExtractor &); +}; + +bool SniffWAV( + const sp<DataSource> &source, String8 *mimeType, float *confidence); + +} // namespace android + +#endif // WAV_EXTRACTOR_H_ + diff --git a/include/media/stagefright/stagefright_string.h b/media/libstagefright/include/stagefright_string.h index 1ed4c86..5dc7116 100644 --- a/include/media/stagefright/stagefright_string.h +++ b/media/libstagefright/include/stagefright_string.h @@ -14,9 +14,9 @@ * limitations under the License. */ -#ifndef STAGEFRIGHT_STRING_H_ +#ifndef STRING_H_ -#define STAGEFRIGHT_STRING_H_ +#define STRING_H_ #include <utils/String8.h> @@ -51,4 +51,4 @@ private: } // namespace android -#endif // STAGEFRIGHT_STRING_H_ +#endif // STRING_H_ diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 25da813..389c2c9 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -9,6 +9,7 @@ LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY) LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) LOCAL_SRC_FILES:= \ + ColorConverter.cpp \ OMX.cpp \ OMXNodeInstance.cpp \ SoftwareRenderer.cpp diff --git a/media/libstagefright/omx/ColorConverter.cpp b/media/libstagefright/omx/ColorConverter.cpp new file mode 100644 index 0000000..e74782f --- /dev/null +++ b/media/libstagefright/omx/ColorConverter.cpp @@ -0,0 +1,297 @@ +/* + * 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. + */ + +#include <media/stagefright/ColorConverter.h> +#include <media/stagefright/MediaDebug.h> + +namespace android { + +static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; + +ColorConverter::ColorConverter( + OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to) + : mSrcFormat(from), + mDstFormat(to), + mClip(NULL) { +} + +ColorConverter::~ColorConverter() { + delete[] mClip; + mClip = NULL; +} + +bool ColorConverter::isValid() const { + if (mDstFormat != OMX_COLOR_Format16bitRGB565) { + return false; + } + + switch (mSrcFormat) { + case OMX_COLOR_FormatYUV420Planar: + case OMX_COLOR_FormatCbYCrY: + case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: + return true; + + default: + return false; + } +} + +void ColorConverter::convert( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip) { + CHECK_EQ(mDstFormat, OMX_COLOR_Format16bitRGB565); + + switch (mSrcFormat) { + case OMX_COLOR_FormatYUV420Planar: + convertYUV420Planar( + width, height, srcBits, srcSkip, dstBits, dstSkip); + break; + + case OMX_COLOR_FormatCbYCrY: + convertCbYCrY( + width, height, srcBits, srcSkip, dstBits, dstSkip); + break; + + case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: + convertQCOMYUV420SemiPlanar( + width, height, srcBits, srcSkip, dstBits, dstSkip); + break; + + default: + { + CHECK(!"Should not be here. Unknown color conversion."); + break; + } + } +} + +void ColorConverter::convertCbYCrY( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip) { + CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats. + CHECK(dstSkip >= width * 2); + CHECK((dstSkip & 3) == 0); + + uint8_t *kAdjustedClip = initClip(); + + uint32_t *dst_ptr = (uint32_t *)dstBits; + + const uint8_t *src = (const uint8_t *)srcBits; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; x += 2) { + signed y1 = (signed)src[2 * x + 1] - 16; + signed y2 = (signed)src[2 * x + 3] - 16; + signed u = (signed)src[2 * x] - 128; + signed v = (signed)src[2 * x + 2] - 128; + + signed u_b = u * 517; + signed u_g = -u * 100; + signed v_g = -v * 208; + signed v_r = v * 409; + + signed tmp1 = y1 * 298; + signed b1 = (tmp1 + u_b) / 256; + signed g1 = (tmp1 + v_g + u_g) / 256; + signed r1 = (tmp1 + v_r) / 256; + + signed tmp2 = y2 * 298; + signed b2 = (tmp2 + u_b) / 256; + signed g2 = (tmp2 + v_g + u_g) / 256; + signed r2 = (tmp2 + v_r) / 256; + + uint32_t rgb1 = + ((kAdjustedClip[r1] >> 3) << 11) + | ((kAdjustedClip[g1] >> 2) << 5) + | (kAdjustedClip[b1] >> 3); + + uint32_t rgb2 = + ((kAdjustedClip[r2] >> 3) << 11) + | ((kAdjustedClip[g2] >> 2) << 5) + | (kAdjustedClip[b2] >> 3); + + dst_ptr[x / 2] = (rgb2 << 16) | rgb1; + } + + src += width * 2; + dst_ptr += dstSkip / 4; + } +} + +void ColorConverter::convertYUV420Planar( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip) { + CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats. + CHECK(dstSkip >= width * 2); + CHECK((dstSkip & 3) == 0); + + uint8_t *kAdjustedClip = initClip(); + + uint32_t *dst_ptr = (uint32_t *)dstBits; + const uint8_t *src_y = (const uint8_t *)srcBits; + + const uint8_t *src_u = + (const uint8_t *)src_y + width * height; + + const uint8_t *src_v = + (const uint8_t *)src_u + (width / 2) * (height / 2); + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; x += 2) { + // B = 1.164 * (Y - 16) + 2.018 * (U - 128) + // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128) + // R = 1.164 * (Y - 16) + 1.596 * (V - 128) + + // B = 298/256 * (Y - 16) + 517/256 * (U - 128) + // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128) + // R = .................. + 409/256 * (V - 128) + + // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277 + // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172 + // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223 + + // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534 + // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432 + // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481 + + // clip range -278 .. 535 + + signed y1 = (signed)src_y[x] - 16; + signed y2 = (signed)src_y[x + 1] - 16; + + signed u = (signed)src_u[x / 2] - 128; + signed v = (signed)src_v[x / 2] - 128; + + signed u_b = u * 517; + signed u_g = -u * 100; + signed v_g = -v * 208; + signed v_r = v * 409; + + signed tmp1 = y1 * 298; + signed b1 = (tmp1 + u_b) / 256; + signed g1 = (tmp1 + v_g + u_g) / 256; + signed r1 = (tmp1 + v_r) / 256; + + signed tmp2 = y2 * 298; + signed b2 = (tmp2 + u_b) / 256; + signed g2 = (tmp2 + v_g + u_g) / 256; + signed r2 = (tmp2 + v_r) / 256; + + uint32_t rgb1 = + ((kAdjustedClip[r1] >> 3) << 11) + | ((kAdjustedClip[g1] >> 2) << 5) + | (kAdjustedClip[b1] >> 3); + + uint32_t rgb2 = + ((kAdjustedClip[r2] >> 3) << 11) + | ((kAdjustedClip[g2] >> 2) << 5) + | (kAdjustedClip[b2] >> 3); + + dst_ptr[x / 2] = (rgb2 << 16) | rgb1; + } + + src_y += width; + + if (y & 1) { + src_u += width / 2; + src_v += width / 2; + } + + dst_ptr += dstSkip / 4; + } +} + +void ColorConverter::convertQCOMYUV420SemiPlanar( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip) { + CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats. + CHECK(dstSkip >= width * 2); + CHECK((dstSkip & 3) == 0); + + uint8_t *kAdjustedClip = initClip(); + + uint32_t *dst_ptr = (uint32_t *)dstBits; + const uint8_t *src_y = (const uint8_t *)srcBits; + + const uint8_t *src_u = + (const uint8_t *)src_y + width * height; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; x += 2) { + signed y1 = (signed)src_y[x] - 16; + signed y2 = (signed)src_y[x + 1] - 16; + + signed u = (signed)src_u[x & ~1] - 128; + signed v = (signed)src_u[(x & ~1) + 1] - 128; + + signed u_b = u * 517; + signed u_g = -u * 100; + signed v_g = -v * 208; + signed v_r = v * 409; + + signed tmp1 = y1 * 298; + signed b1 = (tmp1 + u_b) / 256; + signed g1 = (tmp1 + v_g + u_g) / 256; + signed r1 = (tmp1 + v_r) / 256; + + signed tmp2 = y2 * 298; + signed b2 = (tmp2 + u_b) / 256; + signed g2 = (tmp2 + v_g + u_g) / 256; + signed r2 = (tmp2 + v_r) / 256; + + uint32_t rgb1 = + ((kAdjustedClip[b1] >> 3) << 11) + | ((kAdjustedClip[g1] >> 2) << 5) + | (kAdjustedClip[r1] >> 3); + + uint32_t rgb2 = + ((kAdjustedClip[b2] >> 3) << 11) + | ((kAdjustedClip[g2] >> 2) << 5) + | (kAdjustedClip[r2] >> 3); + + dst_ptr[x / 2] = (rgb2 << 16) | rgb1; + } + + src_y += width; + + if (y & 1) { + src_u += width; + } + + dst_ptr += dstSkip / 4; + } +} + +uint8_t *ColorConverter::initClip() { + static const signed kClipMin = -278; + static const signed kClipMax = 535; + + if (mClip == NULL) { + mClip = new uint8_t[kClipMax - kClipMin + 1]; + + for (signed i = kClipMin; i <= kClipMax; ++i) { + mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i; + } + } + + return &mClip[-kClipMin]; +} + +} // namespace android diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 4ccd4bd..5b3cc1b 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -24,10 +24,10 @@ #include "pv_omxcore.h" #include "../include/OMXNodeInstance.h" +#include "../include/SoftwareRenderer.h" #include <binder/IMemory.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/SoftwareRenderer.h> #include <media/stagefright/VideoRenderer.h> #include <OMX_Component.h> @@ -233,7 +233,7 @@ status_t OMX::allocateNode( &OMXNodeInstance::kCallbacks); if (err != OMX_ErrorNone) { - LOGE("FAILED to allocate omx component '%s'", name); + LOGV("FAILED to allocate omx component '%s'", name); instance->onGetHandleFailed(); diff --git a/media/libstagefright/omx/SoftwareRenderer.cpp b/media/libstagefright/omx/SoftwareRenderer.cpp index 39de504..ef6ede0 100644 --- a/media/libstagefright/omx/SoftwareRenderer.cpp +++ b/media/libstagefright/omx/SoftwareRenderer.cpp @@ -17,21 +17,21 @@ #define LOG_TAG "SoftwareRenderer" #include <utils/Log.h> +#include "../include/SoftwareRenderer.h" + #include <binder/MemoryHeapBase.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/SoftwareRenderer.h> #include <ui/ISurface.h> namespace android { -#define QCOM_YUV 0 - SoftwareRenderer::SoftwareRenderer( OMX_COLOR_FORMATTYPE colorFormat, const sp<ISurface> &surface, size_t displayWidth, size_t displayHeight, size_t decodedWidth, size_t decodedHeight) : mColorFormat(colorFormat), + mConverter(colorFormat, OMX_COLOR_Format16bitRGB565), mISurface(surface), mDisplayWidth(displayWidth), mDisplayHeight(displayHeight), @@ -39,12 +39,12 @@ SoftwareRenderer::SoftwareRenderer( mDecodedHeight(decodedHeight), mFrameSize(mDecodedWidth * mDecodedHeight * 2), // RGB565 mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)), - mIndex(0), - mClip(NULL) { + mIndex(0) { CHECK(mISurface.get() != NULL); CHECK(mDecodedWidth > 0); CHECK(mDecodedHeight > 0); CHECK(mMemoryHeap->heapID() >= 0); + CHECK(mConverter.isValid()); ISurface::BufferHeap bufferHeap( mDisplayWidth, mDisplayHeight, @@ -58,278 +58,19 @@ SoftwareRenderer::SoftwareRenderer( SoftwareRenderer::~SoftwareRenderer() { mISurface->unregisterBuffers(); - - delete[] mClip; - mClip = NULL; } void SoftwareRenderer::render( const void *data, size_t size, void *platformPrivate) { - static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; - - switch (mColorFormat) { - case OMX_COLOR_FormatYUV420Planar: - return renderYUV420Planar(data, size); - - case OMX_COLOR_FormatCbYCrY: - return renderCbYCrY(data, size); - - case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: - return renderQCOMYUV420SemiPlanar(data, size); - - default: - { - LOGW("Cannot render color format %d", mColorFormat); - break; - } - } -} - -void SoftwareRenderer::renderYUV420Planar( - const void *data, size_t size) { - if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) { - LOGE("size is %d, expected %d", - size, (mDecodedHeight * mDecodedWidth * 3) / 2); - } - CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2); - - uint8_t *kAdjustedClip = initClip(); - size_t offset = mIndex * mFrameSize; - void *dst = (uint8_t *)mMemoryHeap->getBase() + offset; - uint32_t *dst_ptr = (uint32_t *)dst; - - const uint8_t *src_y = (const uint8_t *)data; - - const uint8_t *src_u = - (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight; - -#if !QCOM_YUV - const uint8_t *src_v = - (const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2); -#endif - - for (size_t y = 0; y < mDecodedHeight; ++y) { - for (size_t x = 0; x < mDecodedWidth; x += 2) { - // B = 1.164 * (Y - 16) + 2.018 * (U - 128) - // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128) - // R = 1.164 * (Y - 16) + 1.596 * (V - 128) - - // B = 298/256 * (Y - 16) + 517/256 * (U - 128) - // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128) - // R = .................. + 409/256 * (V - 128) - - // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277 - // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172 - // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223 - - // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534 - // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432 - // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481 - - // clip range -278 .. 535 - - signed y1 = (signed)src_y[x] - 16; - signed y2 = (signed)src_y[x + 1] - 16; - -#if QCOM_YUV - signed u = (signed)src_u[x & ~1] - 128; - signed v = (signed)src_u[(x & ~1) + 1] - 128; -#else - signed u = (signed)src_u[x / 2] - 128; - signed v = (signed)src_v[x / 2] - 128; -#endif - - signed u_b = u * 517; - signed u_g = -u * 100; - signed v_g = -v * 208; - signed v_r = v * 409; - - signed tmp1 = y1 * 298; - signed b1 = (tmp1 + u_b) / 256; - signed g1 = (tmp1 + v_g + u_g) / 256; - signed r1 = (tmp1 + v_r) / 256; - - signed tmp2 = y2 * 298; - signed b2 = (tmp2 + u_b) / 256; - signed g2 = (tmp2 + v_g + u_g) / 256; - signed r2 = (tmp2 + v_r) / 256; - - uint32_t rgb1 = - ((kAdjustedClip[r1] >> 3) << 11) - | ((kAdjustedClip[g1] >> 2) << 5) - | (kAdjustedClip[b1] >> 3); - - uint32_t rgb2 = - ((kAdjustedClip[r2] >> 3) << 11) - | ((kAdjustedClip[g2] >> 2) << 5) - | (kAdjustedClip[b2] >> 3); - - dst_ptr[x / 2] = (rgb2 << 16) | rgb1; - } - - src_y += mDecodedWidth; - - if (y & 1) { -#if QCOM_YUV - src_u += mDecodedWidth; -#else - src_u += mDecodedWidth / 2; - src_v += mDecodedWidth / 2; -#endif - } - - dst_ptr += mDecodedWidth / 2; - } - - mISurface->postBuffer(offset); - mIndex = 1 - mIndex; -} - -void SoftwareRenderer::renderCbYCrY( - const void *data, size_t size) { - if (size != (mDecodedHeight * mDecodedWidth * 2)) { - LOGE("size is %d, expected %d", - size, (mDecodedHeight * mDecodedWidth * 2)); - } - CHECK(size >= (mDecodedWidth * mDecodedHeight * 2)); - - uint8_t *kAdjustedClip = initClip(); - - size_t offset = mIndex * mFrameSize; - void *dst = (uint8_t *)mMemoryHeap->getBase() + offset; - uint32_t *dst_ptr = (uint32_t *)dst; - - const uint8_t *src = (const uint8_t *)data; - - for (size_t y = 0; y < mDecodedHeight; ++y) { - for (size_t x = 0; x < mDecodedWidth; x += 2) { - signed y1 = (signed)src[2 * x + 1] - 16; - signed y2 = (signed)src[2 * x + 3] - 16; - signed u = (signed)src[2 * x] - 128; - signed v = (signed)src[2 * x + 2] - 128; - - signed u_b = u * 517; - signed u_g = -u * 100; - signed v_g = -v * 208; - signed v_r = v * 409; - - signed tmp1 = y1 * 298; - signed b1 = (tmp1 + u_b) / 256; - signed g1 = (tmp1 + v_g + u_g) / 256; - signed r1 = (tmp1 + v_r) / 256; - - signed tmp2 = y2 * 298; - signed b2 = (tmp2 + u_b) / 256; - signed g2 = (tmp2 + v_g + u_g) / 256; - signed r2 = (tmp2 + v_r) / 256; - - uint32_t rgb1 = - ((kAdjustedClip[r1] >> 3) << 11) - | ((kAdjustedClip[g1] >> 2) << 5) - | (kAdjustedClip[b1] >> 3); - - uint32_t rgb2 = - ((kAdjustedClip[r2] >> 3) << 11) - | ((kAdjustedClip[g2] >> 2) << 5) - | (kAdjustedClip[b2] >> 3); - - dst_ptr[x / 2] = (rgb2 << 16) | rgb1; - } - - src += mDecodedWidth * 2; - dst_ptr += mDecodedWidth / 2; - } - - mISurface->postBuffer(offset); - mIndex = 1 - mIndex; -} - -void SoftwareRenderer::renderQCOMYUV420SemiPlanar( - const void *data, size_t size) { - if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) { - LOGE("size is %d, expected %d", - size, (mDecodedHeight * mDecodedWidth * 3) / 2); - } - CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2); - - uint8_t *kAdjustedClip = initClip(); - - size_t offset = mIndex * mFrameSize; - - void *dst = (uint8_t *)mMemoryHeap->getBase() + offset; - - uint32_t *dst_ptr = (uint32_t *)dst; - - const uint8_t *src_y = (const uint8_t *)data; - - const uint8_t *src_u = - (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight; - - for (size_t y = 0; y < mDecodedHeight; ++y) { - for (size_t x = 0; x < mDecodedWidth; x += 2) { - signed y1 = (signed)src_y[x] - 16; - signed y2 = (signed)src_y[x + 1] - 16; - - signed u = (signed)src_u[x & ~1] - 128; - signed v = (signed)src_u[(x & ~1) + 1] - 128; - - signed u_b = u * 517; - signed u_g = -u * 100; - signed v_g = -v * 208; - signed v_r = v * 409; - - signed tmp1 = y1 * 298; - signed b1 = (tmp1 + u_b) / 256; - signed g1 = (tmp1 + v_g + u_g) / 256; - signed r1 = (tmp1 + v_r) / 256; - - signed tmp2 = y2 * 298; - signed b2 = (tmp2 + u_b) / 256; - signed g2 = (tmp2 + v_g + u_g) / 256; - signed r2 = (tmp2 + v_r) / 256; - - uint32_t rgb1 = - ((kAdjustedClip[b1] >> 3) << 11) - | ((kAdjustedClip[g1] >> 2) << 5) - | (kAdjustedClip[r1] >> 3); - - uint32_t rgb2 = - ((kAdjustedClip[b2] >> 3) << 11) - | ((kAdjustedClip[g2] >> 2) << 5) - | (kAdjustedClip[r2] >> 3); - - dst_ptr[x / 2] = (rgb2 << 16) | rgb1; - } - - src_y += mDecodedWidth; - - if (y & 1) { - src_u += mDecodedWidth; - } - - dst_ptr += mDecodedWidth / 2; - } + mConverter.convert( + mDecodedWidth, mDecodedHeight, + data, 0, dst, 2 * mDecodedWidth); mISurface->postBuffer(offset); mIndex = 1 - mIndex; } -uint8_t *SoftwareRenderer::initClip() { - static const signed kClipMin = -278; - static const signed kClipMax = 535; - - if (mClip == NULL) { - mClip = new uint8_t[kClipMax - kClipMin + 1]; - - for (signed i = kClipMin; i <= kClipMax; ++i) { - mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i; - } - } - - return &mClip[-kClipMin]; -} - } // namespace android diff --git a/media/libstagefright/stagefright_string.cpp b/media/libstagefright/string.cpp index 2aedb80..bd6204b 100644 --- a/media/libstagefright/stagefright_string.cpp +++ b/media/libstagefright/string.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <media/stagefright/stagefright_string.h> +#include "include/stagefright_string.h" namespace android { |