diff options
Diffstat (limited to 'media')
55 files changed, 2925 insertions, 1075 deletions
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index e80d8aa..1713324 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -137,11 +137,17 @@ public class Ringtone { cursor = res.query(uri, MEDIA_COLUMNS, null, null, null); } - if (cursor != null && cursor.getCount() == 1) { - cursor.moveToFirst(); - return cursor.getString(2); - } else { - title = uri.getLastPathSegment(); + try { + if (cursor != null && cursor.getCount() == 1) { + cursor.moveToFirst(); + return cursor.getString(2); + } else { + title = uri.getLastPathSegment(); + } + } finally { + if (cursor != null) { + cursor.close(); + } } } } diff --git a/media/java/android/media/ThumbnailUtil.java b/media/java/android/media/ThumbnailUtil.java index f9d69fb..7c6bca3 100644 --- a/media/java/android/media/ThumbnailUtil.java +++ b/media/java/android/media/ThumbnailUtil.java @@ -33,6 +33,7 @@ import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; import android.media.MediaMetadataRetriever; +import android.media.MediaFile.MediaFileType; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; @@ -305,8 +306,12 @@ public class ThumbnailUtil { ThumbnailUtil.THUMBNAIL_TARGET_SIZE : ThumbnailUtil.MINI_THUMB_TARGET_SIZE; int maxPixels = wantMini ? ThumbnailUtil.THUMBNAIL_MAX_NUM_PIXELS : ThumbnailUtil.MINI_THUMB_MAX_NUM_PIXELS; - byte[] thumbData = createThumbnailFromEXIF(filePath, targetSize); + byte[] thumbData = null; Bitmap bitmap = null; + MediaFileType fileType = MediaFile.getFileType(filePath); + if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) { + thumbData = createThumbnailFromEXIF(filePath, targetSize); + } if (thumbData != null) { BitmapFactory.Options options = new BitmapFactory.Options(); @@ -454,6 +459,7 @@ public class ThumbnailUtil { Cursor c = cr.query(thumbUri, THUMB_PROJECTION, Thumbnails.IMAGE_ID + "=?", new String[]{String.valueOf(origId)}, null); + if (c == null) return null; try { if (c.moveToNext()) { return ContentUris.withAppendedId(thumbUri, c.getLong(0)); @@ -482,6 +488,7 @@ public class ThumbnailUtil { if (thumb == null) return false; try { Uri uri = getImageThumbnailUri(cr, origId, thumb.getWidth(), thumb.getHeight()); + if (uri == null) return false; OutputStream thumbOut = cr.openOutputStream(uri); thumb.compress(Bitmap.CompressFormat.JPEG, 85, thumbOut); thumbOut.close(); diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 98f7ef1..cca3e9b 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -35,7 +35,7 @@ enum { DECODE_FD, CREATE_MEDIA_RECORDER, CREATE_METADATA_RETRIEVER, - CREATE_OMX, + GET_OMX, SNOOP }; @@ -123,10 +123,10 @@ public: return interface_cast<IMemory>(reply.readStrongBinder()); } - virtual sp<IOMX> createOMX() { + virtual sp<IOMX> getOMX() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - remote()->transact(CREATE_OMX, data, &reply); + remote()->transact(GET_OMX, data, &reply); return interface_cast<IOMX>(reply.readStrongBinder()); } }; @@ -207,9 +207,9 @@ status_t BnMediaPlayerService::onTransact( reply->writeStrongBinder(retriever->asBinder()); return NO_ERROR; } break; - case CREATE_OMX: { + case GET_OMX: { CHECK_INTERFACE(IMediaPlayerService, data, reply); - sp<IOMX> omx = createOMX(); + sp<IOMX> omx = getOMX(); reply->writeStrongBinder(omx->asBinder()); return NO_ERROR; } break; diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 0cec7bb..76a9e7d 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -24,7 +24,6 @@ enum { ALLOC_BUFFER, ALLOC_BUFFER_WITH_BACKUP, FREE_BUFFER, - OBSERVE_NODE, FILL_BUFFER, EMPTY_BUFFER, GET_EXTENSION_INDEX, @@ -76,7 +75,7 @@ public: : BpInterface<IOMX>(impl) { } - virtual status_t list_nodes(List<String8> *list) { + virtual status_t listNodes(List<String8> *list) { list->clear(); Parcel data, reply; @@ -93,10 +92,12 @@ public: return OK; } - virtual status_t allocate_node(const char *name, node_id *node) { + virtual status_t allocateNode( + const char *name, const sp<IOMXObserver> &observer, node_id *node) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeCString(name); + data.writeStrongBinder(observer->asBinder()); remote()->transact(ALLOCATE_NODE, data, &reply); status_t err = reply.readInt32(); @@ -109,7 +110,7 @@ public: return err; } - virtual status_t free_node(node_id node) { + virtual status_t freeNode(node_id node) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); @@ -118,7 +119,7 @@ public: return reply.readInt32(); } - virtual status_t send_command( + virtual status_t sendCommand( node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); @@ -130,7 +131,7 @@ public: return reply.readInt32(); } - virtual status_t get_parameter( + virtual status_t getParameter( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { Parcel data, reply; @@ -151,7 +152,7 @@ public: return OK; } - virtual status_t set_parameter( + virtual status_t setParameter( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { Parcel data, reply; @@ -165,7 +166,7 @@ public: return reply.readInt32(); } - virtual status_t get_config( + virtual status_t getConfig( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { Parcel data, reply; @@ -186,7 +187,7 @@ public: return OK; } - virtual status_t set_config( + virtual status_t setConfig( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { Parcel data, reply; @@ -200,7 +201,7 @@ public: return reply.readInt32(); } - virtual status_t use_buffer( + virtual status_t useBuffer( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, buffer_id *buffer) { Parcel data, reply; @@ -222,7 +223,7 @@ public: return err; } - virtual status_t allocate_buffer( + virtual status_t allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer) { Parcel data, reply; @@ -244,7 +245,7 @@ public: return err; } - virtual status_t allocate_buffer_with_backup( + virtual status_t allocateBufferWithBackup( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, buffer_id *buffer) { Parcel data, reply; @@ -266,7 +267,7 @@ public: return err; } - virtual status_t free_buffer( + virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); @@ -278,26 +279,17 @@ public: return reply.readInt32(); } - virtual status_t observe_node( - node_id node, const sp<IOMXObserver> &observer) { + virtual status_t fillBuffer(node_id node, buffer_id buffer) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); - data.writeStrongBinder(observer->asBinder()); - remote()->transact(OBSERVE_NODE, data, &reply); + data.writeIntPtr((intptr_t)buffer); + remote()->transact(FILL_BUFFER, data, &reply); return reply.readInt32(); } - virtual void fill_buffer(node_id node, buffer_id buffer) { - Parcel data, reply; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - data.writeIntPtr((intptr_t)node); - data.writeIntPtr((intptr_t)buffer); - remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); - } - - virtual void empty_buffer( + virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, @@ -310,10 +302,12 @@ 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(); } - virtual status_t get_extension_index( + virtual status_t getExtensionIndex( node_id node, const char *parameter_name, OMX_INDEXTYPE *index) { @@ -375,7 +369,7 @@ status_t BnOMX::onTransact( CHECK_INTERFACE(IOMX, data, reply); List<String8> list; - list_nodes(&list); + listNodes(&list); reply->writeInt32(list.size()); for (List<String8>::iterator it = list.begin(); @@ -390,8 +384,14 @@ status_t BnOMX::onTransact( { CHECK_INTERFACE(IOMX, data, reply); + const char *name = data.readCString(); + + sp<IOMXObserver> observer = + interface_cast<IOMXObserver>(data.readStrongBinder()); + node_id node; - status_t err = allocate_node(data.readCString(), &node); + + status_t err = allocateNode(name, observer, &node); reply->writeInt32(err); if (err == OK) { reply->writeIntPtr((intptr_t)node); @@ -406,7 +406,7 @@ status_t BnOMX::onTransact( node_id node = (void*)data.readIntPtr(); - reply->writeInt32(free_node(node)); + reply->writeInt32(freeNode(node)); return NO_ERROR; } @@ -421,7 +421,7 @@ status_t BnOMX::onTransact( static_cast<OMX_COMMANDTYPE>(data.readInt32()); OMX_S32 param = data.readInt32(); - reply->writeInt32(send_command(node, cmd, param)); + reply->writeInt32(sendCommand(node, cmd, param)); return NO_ERROR; } @@ -439,7 +439,7 @@ status_t BnOMX::onTransact( void *params = malloc(size); data.read(params, size); - status_t err = get_parameter(node, index, params, size); + status_t err = getParameter(node, index, params, size); reply->writeInt32(err); @@ -463,7 +463,7 @@ status_t BnOMX::onTransact( size_t size = data.readInt32(); void *params = const_cast<void *>(data.readInplace(size)); - reply->writeInt32(set_parameter(node, index, params, size)); + reply->writeInt32(setParameter(node, index, params, size)); return NO_ERROR; } @@ -481,7 +481,7 @@ status_t BnOMX::onTransact( void *params = malloc(size); data.read(params, size); - status_t err = get_config(node, index, params, size); + status_t err = getConfig(node, index, params, size); reply->writeInt32(err); @@ -505,7 +505,7 @@ status_t BnOMX::onTransact( size_t size = data.readInt32(); void *params = const_cast<void *>(data.readInplace(size)); - reply->writeInt32(set_config(node, index, params, size)); + reply->writeInt32(setConfig(node, index, params, size)); return NO_ERROR; } @@ -520,7 +520,7 @@ status_t BnOMX::onTransact( interface_cast<IMemory>(data.readStrongBinder()); buffer_id buffer; - status_t err = use_buffer(node, port_index, params, &buffer); + status_t err = useBuffer(node, port_index, params, &buffer); reply->writeInt32(err); if (err == OK) { @@ -539,7 +539,7 @@ status_t BnOMX::onTransact( size_t size = data.readInt32(); buffer_id buffer; - status_t err = allocate_buffer(node, port_index, size, &buffer); + status_t err = allocateBuffer(node, port_index, size, &buffer); reply->writeInt32(err); if (err == OK) { @@ -559,7 +559,7 @@ status_t BnOMX::onTransact( interface_cast<IMemory>(data.readStrongBinder()); buffer_id buffer; - status_t err = allocate_buffer_with_backup( + status_t err = allocateBufferWithBackup( node, port_index, params, &buffer); reply->writeInt32(err); @@ -578,19 +578,7 @@ status_t BnOMX::onTransact( node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); buffer_id buffer = (void*)data.readIntPtr(); - reply->writeInt32(free_buffer(node, port_index, buffer)); - - return NO_ERROR; - } - - case OBSERVE_NODE: - { - CHECK_INTERFACE(IOMX, data, reply); - - node_id node = (void*)data.readIntPtr(); - sp<IOMXObserver> observer = - interface_cast<IOMXObserver>(data.readStrongBinder()); - reply->writeInt32(observe_node(node, observer)); + reply->writeInt32(freeBuffer(node, port_index, buffer)); return NO_ERROR; } @@ -601,7 +589,7 @@ status_t BnOMX::onTransact( node_id node = (void*)data.readIntPtr(); buffer_id buffer = (void*)data.readIntPtr(); - fill_buffer(node, buffer); + reply->writeInt32(fillBuffer(node, buffer)); return NO_ERROR; } @@ -617,9 +605,10 @@ status_t BnOMX::onTransact( OMX_U32 flags = data.readInt32(); OMX_TICKS timestamp = data.readInt64(); - empty_buffer( - node, buffer, range_offset, range_length, - flags, timestamp); + reply->writeInt32( + emptyBuffer( + node, buffer, range_offset, range_length, + flags, timestamp)); return NO_ERROR; } @@ -632,7 +621,7 @@ status_t BnOMX::onTransact( const char *parameter_name = data.readCString(); OMX_INDEXTYPE index; - status_t err = get_extension_index(node, parameter_name, &index); + status_t err = getExtensionIndex(node, parameter_name, &index); reply->writeInt32(err); @@ -683,7 +672,7 @@ public: : BpInterface<IOMXObserver>(impl) { } - virtual void on_message(const omx_message &msg) { + virtual void onMessage(const omx_message &msg) { Parcel data, reply; data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor()); data.write(&msg, sizeof(msg)); @@ -705,7 +694,7 @@ status_t BnOMXObserver::onTransact( data.read(&msg, sizeof(msg)); // XXX Could use readInplace maybe? - on_message(msg); + onMessage(msg); return NO_ERROR; } diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index f21eb73..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 @@ -50,7 +51,7 @@ LOCAL_C_INCLUDES := external/tremor/Tremor \ $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ - $(TOP)/frameworks/base/media/libstagefright/omx + $(TOP)/frameworks/base/media/libstagefright/include LOCAL_MODULE:= libmediaplayerservice diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 84be874..64267de 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -274,8 +274,14 @@ sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClie return c; } -sp<IOMX> MediaPlayerService::createOMX() { - return new OMX; +sp<IOMX> MediaPlayerService::getOMX() { + Mutex::Autolock autoLock(mLock); + + if (mOMX.get() == NULL) { + mOMX = new OMX; + } + + return mOMX; } status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const @@ -350,11 +356,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/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 7d2e611..931667e 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -183,7 +183,7 @@ public: virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual sp<IMemory> snoop(); - virtual sp<IOMX> createOMX(); + virtual sp<IOMX> getOMX(); virtual status_t dump(int fd, const Vector<String16>& args); @@ -281,6 +281,7 @@ private: mutable Mutex mLock; SortedVector< wp<Client> > mClients; int32_t mNextConnId; + sp<IOMX> mOMX; }; // ---------------------------------------------------------------------------- diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 8eabe5d..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; } 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/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 3c343a3..b774609 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -17,6 +17,7 @@ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true) LOCAL_SRC_FILES += \ AMRExtractor.cpp \ CachingDataSource.cpp \ + CameraSource.cpp \ DataSource.cpp \ FileSource.cpp \ HTTPDataSource.cpp \ diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 319488e..7b4d178 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -209,15 +209,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..210b2f6 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ -#include <media/stagefright/AMRExtractor.h> +#include "include/AMRExtractor.h" +#include "include/MP3Extractor.h" +#include "include/MPEG4Extractor.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 +28,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; } 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 698223b..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/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..736f1a9d6 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); + 8000 * (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..5c3720e 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,7 +995,7 @@ 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; } 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/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 8535f52..d51802c 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -18,12 +18,16 @@ #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 <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 +45,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); } @@ -58,4 +62,25 @@ sp<MediaExtractor> MediaExtractor::Create( 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 2e609e3..76f4182 100644 --- a/media/libstagefright/MediaPlayerImpl.cpp +++ b/media/libstagefright/MediaPlayerImpl.cpp @@ -18,15 +18,15 @@ #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/MediaExtractor.h> #include <media/stagefright/MediaPlayerImpl.h> @@ -69,18 +69,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; @@ -250,6 +239,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) { + LOGI("format changed."); + depopulateISurface(); + populateISurface(); + continue; + } + if (err == ERROR_END_OF_STREAM || err != OK) { eof = true; continue; @@ -261,15 +257,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; @@ -379,12 +369,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; } } @@ -609,6 +597,9 @@ void MediaPlayerImpl::populateISurface() { success = success && meta->findInt32(kKeyHeight, &decodedHeight); CHECK(success); + LOGI("mVideoWidth=%d, mVideoHeight=%d, decodedWidth=%d, decodedHeight=%d", + mVideoWidth, mVideoHeight, decodedWidth, decodedHeight); + if (mSurface.get() != NULL) { mVideoRenderer = mClient.interface()->createRenderer( 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/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index dba7a2a..9de873e 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -35,7 +35,7 @@ status_t OMXClient::connect() { CHECK(service.get() != NULL); - mOMX = service->createOMX(); + mOMX = service->getOMX(); CHECK(mOMX.get() != NULL); return OK; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 1a23fb2..01ec973 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; @@ -85,12 +88,15 @@ static const CodecInfo kEncoderInfo[] = { #define CODEC_LOGV(x, ...) LOGV("[%s] "x, mComponentName, ##__VA_ARGS__) struct OMXCodecObserver : public BnOMXObserver { - OMXCodecObserver(const wp<OMXCodec> &target) - : mTarget(target) { + OMXCodecObserver() { + } + + void setCodec(const sp<OMXCodec> &target) { + mTarget = target; } // from IOMXObserver - virtual void on_message(const omx_message &msg) { + virtual void onMessage(const omx_message &msg) { sp<OMXCodec> codec = mTarget.promote(); if (codec.get() != NULL) { @@ -166,51 +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; - 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->allocate_node(componentName, &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; @@ -218,11 +210,6 @@ 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; @@ -232,7 +219,6 @@ sp<OMXCodec> OMXCodec::Create( // XXX Required on P....on only. quirks |= kRequiresAllocateBufferOnInputPorts; quirks |= kRequiresAllocateBufferOnOutputPorts; - quirks |= kOutputDimensionsAre16Aligned; } if (!strncmp(componentName, "OMX.TI.", 7)) { @@ -245,10 +231,98 @@ 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); + uint32_t type; const void *data; size_t size; @@ -326,12 +400,14 @@ 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") && (profile != kAVCProfileBaseline || level > 39)) { - // This stream exceeds the decoder's capabilities. + // This stream exceeds the decoder's capabilities. The decoder + // does not handle this gracefully and would clobber the heap + // and wreak havoc instead... LOGE("Profile and/or level exceed the decoder's capabilities."); return NULL; @@ -406,7 +482,7 @@ void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) { InitOMXParams(&def); def.nPortIndex = portIndex; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -415,7 +491,7 @@ void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) { } - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); } @@ -433,7 +509,7 @@ status_t OMXCodec::setVideoPortFormatType( OMX_U32 index = 0; for (;;) { format.nIndex = index; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); @@ -445,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 @@ -478,8 +554,8 @@ status_t OMXCodec::setVideoPortFormatType( return UNKNOWN_ERROR; } - CODEC_LOGI("found a match."); - status_t err = mOMX->set_parameter( + CODEC_LOGV("found a match."); + status_t err = mOMX->setParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); @@ -488,7 +564,7 @@ status_t OMXCodec::setVideoPortFormatType( 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)) { @@ -522,7 +598,7 @@ void OMXCodec::setVideoInputFormat( OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -534,7 +610,7 @@ void OMXCodec::setVideoInputFormat( video_def->eCompressionFormat = compressionFormat; video_def->eColorFormat = OMX_COLOR_FormatUnused; - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -543,12 +619,12 @@ void OMXCodec::setVideoInputFormat( InitOMXParams(&def); def.nPortIndex = kPortIndexInput; - err = mOMX->get_parameter( + err = mOMX->getParameter( 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); + CODEC_LOGV("Setting nBufferSize = %ld", def.nBufferSize); CHECK_EQ(def.eDomain, OMX_PortDomainVideo); @@ -557,14 +633,14 @@ void OMXCodec::setVideoInputFormat( video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; video_def->eColorFormat = colorFormat; - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, 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)) { @@ -588,7 +664,7 @@ void OMXCodec::setVideoOutputFormat( format.nPortIndex = kPortIndexOutput; format.nIndex = 0; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); CHECK_EQ(err, OK); @@ -601,7 +677,7 @@ void OMXCodec::setVideoOutputFormat( || format.eColorFormat == OMX_COLOR_FormatCbYCrY || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar); - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); CHECK_EQ(err, OK); @@ -614,7 +690,7 @@ void OMXCodec::setVideoOutputFormat( OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -634,7 +710,7 @@ void OMXCodec::setVideoOutputFormat( video_def->eColorFormat = OMX_COLOR_FormatUnused; - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -643,7 +719,7 @@ void OMXCodec::setVideoOutputFormat( InitOMXParams(&def); def.nPortIndex = kPortIndexOutput; - err = mOMX->get_parameter( + err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); CHECK_EQ(def.eDomain, OMX_PortDomainVideo); @@ -656,7 +732,7 @@ void OMXCodec::setVideoOutputFormat( video_def->nFrameWidth = width; video_def->nFrameHeight = height; - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); } @@ -680,13 +756,11 @@ OMXCodec::OMXCodec( mInitialBufferSubmit(true), mSignalledEOS(false), mNoMoreOutputData(false), + mOutputPortSettingsHaveChanged(false), mSeekTimeUs(-1) { mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexOutput] = ENABLED; - mObserver = new OMXCodecObserver(this); - mOMX->observe_node(mNode, mObserver); - setComponentRole(); } @@ -744,7 +818,7 @@ void OMXCodec::setComponentRole( roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; - status_t err = omx->set_parameter( + status_t err = omx->setParameter( node, OMX_IndexParamStandardComponentRole, &roleParams, sizeof(roleParams)); @@ -761,10 +835,7 @@ void OMXCodec::setComponentRole() { OMXCodec::~OMXCodec() { CHECK(mState == LOADED || mState == ERROR); - status_t err = mOMX->observe_node(mNode, NULL); - CHECK_EQ(err, OK); - - err = mOMX->free_node(mNode); + status_t err = mOMX->freeNode(mNode); CHECK_EQ(err, OK); mNode = NULL; @@ -786,7 +857,7 @@ status_t OMXCodec::init() { status_t err; if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) { - err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle); + err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); CHECK_EQ(err, OK); setState(LOADED_TO_IDLE); } @@ -795,7 +866,7 @@ status_t OMXCodec::init() { CHECK_EQ(err, OK); if (mQuirks & kRequiresLoadedToIdleAfterAllocation) { - err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle); + err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); CHECK_EQ(err, OK); setState(LOADED_TO_IDLE); @@ -832,7 +903,7 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { InitOMXParams(&def); def.nPortIndex = portIndex; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); if (err != OK) { @@ -849,14 +920,14 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { IOMX::buffer_id buffer; if (portIndex == kPortIndexInput && (mQuirks & kRequiresAllocateBufferOnInputPorts)) { - err = mOMX->allocate_buffer_with_backup( + err = mOMX->allocateBufferWithBackup( mNode, portIndex, mem, &buffer); } else if (portIndex == kPortIndexOutput && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) { - err = mOMX->allocate_buffer_with_backup( + err = mOMX->allocateBufferWithBackup( mNode, portIndex, mem, &buffer); } else { - err = mOMX->use_buffer(mNode, portIndex, mem, &buffer); + err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); } if (err != OK) { @@ -923,7 +994,7 @@ void OMXCodec::on_message(const omx_message &msg) { CODEC_LOGV("Port is disabled, freeing buffer %p", buffer); status_t err = - mOMX->free_buffer(mNode, kPortIndexInput, buffer); + mOMX->freeBuffer(mNode, kPortIndexInput, buffer); CHECK_EQ(err, OK); buffers->removeAt(i); @@ -969,7 +1040,7 @@ void OMXCodec::on_message(const omx_message &msg) { CODEC_LOGV("Port is disabled, freeing buffer %p", buffer); status_t err = - mOMX->free_buffer(mNode, kPortIndexOutput, buffer); + mOMX->freeBuffer(mNode, kPortIndexOutput, buffer); CHECK_EQ(err, OK); buffers->removeAt(i); @@ -989,12 +1060,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); @@ -1063,6 +1130,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: @@ -1085,6 +1217,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); @@ -1139,7 +1280,7 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { mPortStatus[kPortIndexOutput] = SHUTTING_DOWN; status_t err = - mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle); + mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); CHECK_EQ(err, OK); } } else { @@ -1179,7 +1320,7 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) { { CODEC_LOGV("Now Idle."); if (mState == LOADED_TO_IDLE) { - status_t err = mOMX->send_command( + status_t err = mOMX->sendCommand( mNode, OMX_CommandStateSet, OMX_StateExecuting); CHECK_EQ(err, OK); @@ -1196,7 +1337,7 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) { countBuffersWeOwn(mPortBuffers[kPortIndexOutput]), mPortBuffers[kPortIndexOutput].size()); - status_t err = mOMX->send_command( + status_t err = mOMX->sendCommand( mNode, OMX_CommandStateSet, OMX_StateLoaded); CHECK_EQ(err, OK); @@ -1279,7 +1420,7 @@ status_t OMXCodec::freeBuffersOnPort( CODEC_LOGV("freeing buffer %p on port %ld", info->mBuffer, portIndex); status_t err = - mOMX->free_buffer(mNode, portIndex, info->mBuffer); + mOMX->freeBuffer(mNode, portIndex, info->mBuffer); if (err != OK) { stickyErr = err; @@ -1339,7 +1480,7 @@ bool OMXCodec::flushPortAsync(OMX_U32 portIndex) { } status_t err = - mOMX->send_command(mNode, OMX_CommandFlush, portIndex); + mOMX->sendCommand(mNode, OMX_CommandFlush, portIndex); CHECK_EQ(err, OK); return true; @@ -1352,7 +1493,7 @@ void OMXCodec::disablePortAsync(OMX_U32 portIndex) { mPortStatus[portIndex] = DISABLING; status_t err = - mOMX->send_command(mNode, OMX_CommandPortDisable, portIndex); + mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex); CHECK_EQ(err, OK); freeBuffersOnPort(portIndex, true); @@ -1365,7 +1506,7 @@ void OMXCodec::enablePortAsync(OMX_U32 portIndex) { mPortStatus[portIndex] = ENABLING; status_t err = - mOMX->send_command(mNode, OMX_CommandPortEnable, portIndex); + mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex); CHECK_EQ(err, OK); } @@ -1417,10 +1558,11 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) { memcpy(info->mMem->pointer(), specific->mData, specific->mSize); } - mOMX->empty_buffer( + status_t err = mOMX->emptyBuffer( mNode, info->mBuffer, 0, size, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG, 0); + CHECK_EQ(err, OK); info->mOwnedByComponent = true; @@ -1441,7 +1583,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) { @@ -1461,28 +1603,29 @@ 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); } } - mOMX->empty_buffer( - mNode, info->mBuffer, 0, srcLength, - flags, timestamp); - - info->mOwnedByComponent = true; - if (srcBuffer != NULL) { srcBuffer->release(); srcBuffer = NULL; } + + err = mOMX->emptyBuffer( + mNode, info->mBuffer, 0, srcLength, + flags, timestampUs); + + if (err != OK) { + setState(ERROR); + return; + } + + info->mOwnedByComponent = true; } void OMXCodec::fillOutputBuffer(BufferInfo *info) { @@ -1495,7 +1638,8 @@ void OMXCodec::fillOutputBuffer(BufferInfo *info) { } CODEC_LOGV("Calling fill_buffer on buffer %p", info->mBuffer); - mOMX->fill_buffer(mNode, info->mBuffer); + status_t err = mOMX->fillBuffer(mNode, info->mBuffer); + CHECK_EQ(err, OK); info->mOwnedByComponent = true; } @@ -1539,7 +1683,7 @@ void OMXCodec::setRawAudioFormat( InitOMXParams(&pcmParams); pcmParams.nPortIndex = portIndex; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); CHECK_EQ(err, OK); @@ -1560,7 +1704,7 @@ void OMXCodec::setRawAudioFormat( pcmParams.eChannelMapping[1] = OMX_AUDIO_ChannelRF; } - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); CHECK_EQ(err, OK); @@ -1573,14 +1717,14 @@ void OMXCodec::setAMRFormat() { def.nPortIndex = kPortIndexInput; status_t err = - mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); + mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); CHECK_EQ(err, OK); def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; def.eAMRBandMode = OMX_AUDIO_AMRBandModeNB0; - err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); + err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); CHECK_EQ(err, OK); } @@ -1604,14 +1748,14 @@ void OMXCodec::setAMRWBFormat() { def.nPortIndex = kPortIndexInput; status_t err = - mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); + mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); CHECK_EQ(err, OK); def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; def.eAMRBandMode = OMX_AUDIO_AMRBandModeWB0; - err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); + err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); CHECK_EQ(err, OK); } @@ -1636,7 +1780,7 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate) { InitOMXParams(&profile); profile.nPortIndex = kPortIndexInput; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); CHECK_EQ(err, OK); @@ -1644,7 +1788,7 @@ void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate) { profile.nSampleRate = sampleRate; profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); CHECK_EQ(err, OK); } @@ -1668,7 +1812,7 @@ void OMXCodec::setImageOutputFormat( InitOMXParams(&def); def.nPortIndex = kPortIndexOutput; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -1717,7 +1861,7 @@ void OMXCodec::setImageOutputFormat( def.nBufferCountActual = def.nBufferCountMin; - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); } @@ -1728,7 +1872,7 @@ void OMXCodec::setJPEGInputFormat( InitOMXParams(&def); def.nPortIndex = kPortIndexInput; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -1742,7 +1886,7 @@ void OMXCodec::setJPEGInputFormat( def.nBufferSize = compressedSize; def.nBufferCountActual = def.nBufferCountMin; - err = mOMX->set_parameter( + err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); } @@ -1786,6 +1930,7 @@ status_t OMXCodec::start(MetaData *) { mInitialBufferSubmit = true; mSignalledEOS = false; mNoMoreOutputData = false; + mOutputPortSettingsHaveChanged = false; mSeekTimeUs = -1; mFilledBuffers.clear(); @@ -1832,7 +1977,7 @@ status_t OMXCodec::stop() { mPortStatus[kPortIndexOutput] = SHUTTING_DOWN; status_t err = - mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle); + mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); CHECK_EQ(err, OK); } @@ -1856,6 +2001,8 @@ status_t OMXCodec::stop() { } sp<MetaData> OMXCodec::getFormat() { + Mutex::Autolock autoLock(mLock); + return mOutputFormat; } @@ -1869,9 +2016,24 @@ status_t OMXCodec::read( return UNKNOWN_ERROR; } + bool seeking = false; + int64_t seekTimeUs; + if (options && options->getSeekTo(&seekTimeUs)) { + seeking = true; + } + if (mInitialBufferSubmit) { mInitialBufferSubmit = false; + if (seeking) { + CHECK(seekTimeUs >= 0); + mSeekTimeUs = seekTimeUs; + + // There's no reason to trigger the code below, there's + // nothing to flush yet. + seeking = false; + } + drainInputBuffers(); if (mState == EXECUTING) { @@ -1881,8 +2043,7 @@ status_t OMXCodec::read( } } - int64_t seekTimeUs; - if (options && options->getSeekTo(&seekTimeUs)) { + if (seeking) { CODEC_LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6); mSignalledEOS = false; @@ -1919,6 +2080,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()); @@ -2019,8 +2186,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) { @@ -2164,7 +2329,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { InitOMXParams(&def); def.nPortIndex = portIndex; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -2230,7 +2395,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { InitOMXParams(¶ms); params.nPortIndex = portIndex; - err = mOMX->get_parameter( + err = mOMX->getParameter( mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params)); CHECK_EQ(err, OK); @@ -2249,7 +2414,7 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { InitOMXParams(&amr); amr.nPortIndex = portIndex; - err = mOMX->get_parameter( + err = mOMX->getParameter( mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr)); CHECK_EQ(err, OK); @@ -2281,7 +2446,7 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { InitOMXParams(&def); def.nPortIndex = kPortIndexOutput; - status_t err = mOMX->get_parameter( + status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); @@ -2307,7 +2472,7 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { InitOMXParams(¶ms); params.nPortIndex = kPortIndexOutput; - err = mOMX->get_parameter( + err = mOMX->getParameter( mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params)); CHECK_EQ(err, OK); @@ -2339,7 +2504,7 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { InitOMXParams(&amr); amr.nPortIndex = kPortIndexOutput; - err = mOMX->get_parameter( + err = mOMX->getParameter( mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr)); CHECK_EQ(err, OK); @@ -2388,7 +2553,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); @@ -2436,8 +2601,9 @@ status_t QueryCodecs( return OK; } + sp<OMXCodecObserver> observer = new OMXCodecObserver; IOMX::node_id node; - status_t err = omx->allocate_node(componentName, &node); + status_t err = omx->allocateNode(componentName, observer, &node); if (err != OK) { continue; @@ -2455,7 +2621,7 @@ status_t QueryCodecs( param.nPortIndex = queryDecoders ? 0 : 1; for (param.nProfileIndex = 0;; ++param.nProfileIndex) { - err = omx->get_parameter( + err = omx->getParameter( node, OMX_IndexParamVideoProfileLevelQuerySupported, ¶m, sizeof(param)); @@ -2470,7 +2636,7 @@ status_t QueryCodecs( caps->mProfileLevels.push(profileLevel); } - CHECK_EQ(omx->free_node(node), OK); + CHECK_EQ(omx->freeNode(node), OK); } } 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 8e8f4fa..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/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/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h new file mode 100644 index 0000000..debf006 --- /dev/null +++ b/media/libstagefright/include/AMRExtractor.h @@ -0,0 +1,54 @@ +/* + * 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 AMR_EXTRACTOR_H_ + +#define AMR_EXTRACTOR_H_ + +#include <media/stagefright/MediaExtractor.h> + +namespace android { + +class String8; + +class AMRExtractor : public MediaExtractor { +public: + AMRExtractor(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); + + static sp<MetaData> makeAMRFormat(bool isWide); + +protected: + virtual ~AMRExtractor(); + +private: + sp<DataSource> mDataSource; + status_t mInitCheck; + bool mIsWide; + + AMRExtractor(const AMRExtractor &); + AMRExtractor &operator=(const AMRExtractor &); +}; + +bool SniffAMR( + const sp<DataSource> &source, String8 *mimeType, float *confidence); + +} // namespace android + +#endif // AMR_EXTRACTOR_H_ diff --git a/media/libstagefright/include/ESDS.h b/media/libstagefright/include/ESDS.h new file mode 100644 index 0000000..01bcd18 --- /dev/null +++ b/media/libstagefright/include/ESDS.h @@ -0,0 +1,64 @@ +/* + * 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 ESDS_H_ + +#define ESDS_H_ + +#include <stdint.h> + +#include <media/stagefright/MediaErrors.h> + +namespace android { + +class ESDS { +public: + ESDS(const void *data, size_t size); + ~ESDS(); + + status_t InitCheck() const; + + status_t getCodecSpecificInfo(const void **data, size_t *size) const; + +private: + enum { + kTag_ESDescriptor = 0x03, + kTag_DecoderConfigDescriptor = 0x04, + kTag_DecoderSpecificInfo = 0x05 + }; + + uint8_t *mData; + size_t mSize; + + status_t mInitCheck; + + size_t mDecoderSpecificOffset; + size_t mDecoderSpecificLength; + + status_t skipDescriptorHeader( + size_t offset, size_t size, + uint8_t *tag, size_t *data_offset, size_t *data_size) const; + + status_t parse(); + status_t parseESDescriptor(size_t offset, size_t size); + status_t parseDecoderConfigDescriptor(size_t offset, size_t size); + + ESDS(const ESDS &); + ESDS &operator=(const ESDS &); +}; + +} // namespace android +#endif // ESDS_H_ diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h new file mode 100644 index 0000000..43ef614 --- /dev/null +++ b/media/libstagefright/include/HTTPStream.h @@ -0,0 +1,75 @@ +/* + * 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 HTTP_STREAM_H_ + +#define HTTP_STREAM_H_ + +#include "stagefright_string.h" + +#include <sys/types.h> + +#include <media/stagefright/MediaErrors.h> +#include <utils/KeyedVector.h> + +namespace android { + +class HTTPStream { +public: + HTTPStream(); + ~HTTPStream(); + + status_t connect(const char *server, int port = 80); + status_t disconnect(); + + status_t send(const char *data, size_t size); + + // Assumes data is a '\0' terminated string. + status_t send(const char *data); + + // Receive up to "size" bytes of data. + ssize_t receive(void *data, size_t size); + + status_t receive_header(int *http_status); + + // The header key used to retrieve the status line. + static const char *kStatusKey; + + bool find_header_value( + const string &key, string *value) const; + +private: + enum State { + READY, + CONNECTED + }; + + State mState; + int mSocket; + + KeyedVector<string, string> mHeaders; + + // Receive a line of data terminated by CRLF, line will be '\0' terminated + // _excluding_ the termianting CRLF. + status_t receive_line(char *line, size_t size); + + HTTPStream(const HTTPStream &); + HTTPStream &operator=(const HTTPStream &); +}; + +} // namespace android + +#endif // HTTP_STREAM_H_ diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h new file mode 100644 index 0000000..074973b --- /dev/null +++ b/media/libstagefright/include/MP3Extractor.h @@ -0,0 +1,55 @@ +/* + * 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 MP3_EXTRACTOR_H_ + +#define MP3_EXTRACTOR_H_ + +#include <media/stagefright/MediaExtractor.h> + +namespace android { + +class DataSource; +class String8; + +class MP3Extractor : public MediaExtractor { +public: + // Extractor assumes ownership of "source". + MP3Extractor(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 ~MP3Extractor(); + +private: + sp<DataSource> mDataSource; + off_t mFirstFramePos; + sp<MetaData> mMeta; + uint32_t mFixedHeader; + + MP3Extractor(const MP3Extractor &); + MP3Extractor &operator=(const MP3Extractor &); +}; + +bool SniffMP3( + const sp<DataSource> &source, String8 *mimeType, float *confidence); + +} // namespace android + +#endif // MP3_EXTRACTOR_H_ diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h new file mode 100644 index 0000000..ce4736d --- /dev/null +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -0,0 +1,69 @@ +/* + * 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 MPEG4_EXTRACTOR_H_ + +#define MPEG4_EXTRACTOR_H_ + +#include <media/stagefright/MediaExtractor.h> + +namespace android { + +class DataSource; +class SampleTable; +class String8; + +class MPEG4Extractor : public MediaExtractor { +public: + // Extractor assumes ownership of "source". + MPEG4Extractor(const sp<DataSource> &source); + + size_t countTracks(); + sp<MediaSource> getTrack(size_t index); + sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); + +protected: + virtual ~MPEG4Extractor(); + +private: + struct Track { + Track *next; + sp<MetaData> meta; + uint32_t timescale; + sp<SampleTable> sampleTable; + bool includes_expensive_metadata; + }; + + sp<DataSource> mDataSource; + bool mHaveMetadata; + + Track *mFirstTrack, *mLastTrack; + + uint32_t mHandlerType; + + status_t readMetaData(); + status_t parseChunk(off_t *offset, int depth); + + MPEG4Extractor(const MPEG4Extractor &); + MPEG4Extractor &operator=(const MPEG4Extractor &); +}; + +bool SniffMPEG4( + const sp<DataSource> &source, String8 *mimeType, float *confidence); + +} // namespace android + +#endif // MPEG4_EXTRACTOR_H_ diff --git a/media/libstagefright/omx/OMX.h b/media/libstagefright/include/OMX.h index 6325f79..a4b62b2 100644 --- a/media/libstagefright/omx/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -19,66 +19,67 @@ #include <media/IOMX.h> #include <utils/threads.h> +#include <utils/KeyedVector.h> namespace android { -class NodeMeta; +class OMXNodeInstance; -class OMX : public BnOMX { +class OMX : public BnOMX, + public IBinder::DeathRecipient { public: OMX(); - virtual status_t list_nodes(List<String8> *list); + virtual status_t listNodes(List<String8> *list); - virtual status_t allocate_node(const char *name, node_id *node); - virtual status_t free_node(node_id node); + virtual status_t allocateNode( + const char *name, const sp<IOMXObserver> &observer, node_id *node); - virtual status_t send_command( + virtual status_t freeNode(node_id node); + + virtual status_t sendCommand( node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param); - virtual status_t get_parameter( + virtual status_t getParameter( node_id node, OMX_INDEXTYPE index, void *params, size_t size); - virtual status_t set_parameter( + virtual status_t setParameter( node_id node, OMX_INDEXTYPE index, const void *params, size_t size); - virtual status_t get_config( + virtual status_t getConfig( node_id node, OMX_INDEXTYPE index, void *params, size_t size); - virtual status_t set_config( + virtual status_t setConfig( node_id node, OMX_INDEXTYPE index, const void *params, size_t size); - virtual status_t use_buffer( + virtual status_t useBuffer( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, buffer_id *buffer); - virtual status_t allocate_buffer( + virtual status_t allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer); - virtual status_t allocate_buffer_with_backup( + virtual status_t allocateBufferWithBackup( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, buffer_id *buffer); - virtual status_t free_buffer( + virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer); - virtual status_t observe_node( - node_id node, const sp<IOMXObserver> &observer); - - virtual void fill_buffer(node_id node, buffer_id buffer); + virtual status_t fillBuffer(node_id node, buffer_id buffer); - virtual void empty_buffer( + virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, OMX_U32 flags, OMX_TICKS timestamp); - virtual status_t get_extension_index( + virtual status_t getExtensionIndex( node_id node, const char *parameter_name, OMX_INDEXTYPE *index); @@ -90,44 +91,38 @@ public: size_t encodedWidth, size_t encodedHeight, size_t displayWidth, size_t displayHeight); -private: - static OMX_CALLBACKTYPE kCallbacks; - - Mutex mLock; - - struct CallbackDispatcher; - sp<CallbackDispatcher> mDispatcher; - - static OMX_ERRORTYPE OnEvent( - OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, - OMX_IN OMX_EVENTTYPE eEvent, - OMX_IN OMX_U32 nData1, - OMX_IN OMX_U32 nData2, - OMX_IN OMX_PTR pEventData); - - static OMX_ERRORTYPE OnEmptyBufferDone( - OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); - - static OMX_ERRORTYPE OnFillBufferDone( - OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + virtual void binderDied(const wp<IBinder> &the_late_who); OMX_ERRORTYPE OnEvent( - NodeMeta *meta, + node_id node, OMX_IN OMX_EVENTTYPE eEvent, OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, OMX_IN OMX_PTR pEventData); OMX_ERRORTYPE OnEmptyBufferDone( - NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); OMX_ERRORTYPE OnFillBufferDone( - NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + + void invalidateNodeID(node_id node); + +private: + Mutex mLock; + + struct CallbackDispatcher; + sp<CallbackDispatcher> mDispatcher; + + int32_t mNodeCounter; + + KeyedVector<wp<IBinder>, OMXNodeInstance *> mLiveNodes; + KeyedVector<node_id, OMXNodeInstance *> mNodeIDToInstance; + + node_id makeNodeID(OMXNodeInstance *instance); + OMXNodeInstance *findInstance(node_id node); + + void invalidateNodeID_l(node_id node); OMX(const OMX &); OMX &operator=(const OMX &); diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h new file mode 100644 index 0000000..09a8816 --- /dev/null +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OMX_NODE_INSTANCE_H_ + +#define OMX_NODE_INSTANCE_H_ + +#include "OMX.h" + +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace android { + +class IOMXObserver; + +struct OMXNodeInstance { + OMXNodeInstance( + OMX *owner, const sp<IOMXObserver> &observer); + + void setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle); + + OMX *owner(); + sp<IOMXObserver> observer(); + OMX::node_id nodeID(); + + status_t freeNode(); + + status_t sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param); + status_t getParameter(OMX_INDEXTYPE index, void *params, size_t size); + + status_t setParameter( + OMX_INDEXTYPE index, const void *params, size_t size); + + status_t getConfig(OMX_INDEXTYPE index, void *params, size_t size); + status_t setConfig(OMX_INDEXTYPE index, const void *params, size_t size); + + status_t useBuffer( + OMX_U32 portIndex, const sp<IMemory> ¶ms, + OMX::buffer_id *buffer); + + status_t allocateBuffer( + OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer); + + status_t allocateBufferWithBackup( + OMX_U32 portIndex, const sp<IMemory> ¶ms, + OMX::buffer_id *buffer); + + status_t freeBuffer(OMX_U32 portIndex, OMX::buffer_id buffer); + + status_t fillBuffer(OMX::buffer_id buffer); + + status_t emptyBuffer( + OMX::buffer_id buffer, + OMX_U32 rangeOffset, OMX_U32 rangeLength, + OMX_U32 flags, OMX_TICKS timestamp); + + status_t getExtensionIndex( + const char *parameterName, OMX_INDEXTYPE *index); + + void onMessage(const omx_message &msg); + void onObserverDied(); + void onGetHandleFailed(); + + static OMX_CALLBACKTYPE kCallbacks; + +private: + Mutex mLock; + + OMX *mOwner; + OMX::node_id mNodeID; + OMX_HANDLETYPE mHandle; + sp<IOMXObserver> mObserver; + + struct ActiveBuffer { + OMX_U32 mPortIndex; + OMX::buffer_id mID; + }; + Vector<ActiveBuffer> mActiveBuffers; + + ~OMXNodeInstance(); + + void addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id); + void removeActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id); + void freeActiveBuffers(); + + static OMX_ERRORTYPE OnEvent( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); + + static OMX_ERRORTYPE OnEmptyBufferDone( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + + static OMX_ERRORTYPE OnFillBufferDone( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + + OMXNodeInstance(const OMXNodeInstance &); + OMXNodeInstance &operator=(const OMXNodeInstance &); +}; + +} // namespace android + +#endif // OMX_NODE_INSTANCE_H_ + diff --git a/media/libstagefright/include/QComHardwareRenderer.h b/media/libstagefright/include/QComHardwareRenderer.h new file mode 100644 index 0000000..8292dd5 --- /dev/null +++ b/media/libstagefright/include/QComHardwareRenderer.h @@ -0,0 +1,57 @@ +/* + * 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 QCOM_HARDWARE_RENDERER_H_ + +#define QCOM_HARDWARE_RENDERER_H_ + +#include <media/stagefright/VideoRenderer.h> +#include <utils/RefBase.h> + +namespace android { + +class ISurface; +class MemoryHeapPmem; + +class QComHardwareRenderer : public VideoRenderer { +public: + QComHardwareRenderer( + const sp<ISurface> &surface, + size_t displayWidth, size_t displayHeight, + size_t decodedWidth, size_t decodedHeight); + + virtual ~QComHardwareRenderer(); + + virtual void render( + const void *data, size_t size, void *platformPrivate); + +private: + sp<ISurface> mISurface; + size_t mDisplayWidth, mDisplayHeight; + size_t mDecodedWidth, mDecodedHeight; + size_t mFrameSize; + sp<MemoryHeapPmem> mMemoryHeap; + + bool getOffset(void *platformPrivate, size_t *offset); + void publishBuffers(uint32_t pmem_fd); + + QComHardwareRenderer(const QComHardwareRenderer &); + QComHardwareRenderer &operator=(const QComHardwareRenderer &); +}; + +} // namespace android + +#endif // QCOM_HARDWARE_RENDERER_H_ diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h new file mode 100644 index 0000000..ead3431 --- /dev/null +++ b/media/libstagefright/include/SampleTable.h @@ -0,0 +1,111 @@ +/* + * 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 SAMPLE_TABLE_H_ + +#define SAMPLE_TABLE_H_ + +#include <sys/types.h> +#include <stdint.h> + +#include <media/stagefright/MediaErrors.h> +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace android { + +class DataSource; + +class SampleTable : public RefBase { +public: + SampleTable(const sp<DataSource> &source); + + // type can be 'stco' or 'co64'. + status_t setChunkOffsetParams( + uint32_t type, off_t data_offset, size_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, size_t data_size); + + status_t setTimeToSampleParams(off_t data_offset, size_t data_size); + + status_t setSyncSampleParams(off_t data_offset, size_t data_size); + + //////////////////////////////////////////////////////////////////////////// + + uint32_t countChunkOffsets() const; + status_t getChunkOffset(uint32_t chunk_index, off_t *offset); + + status_t getChunkForSample( + uint32_t sample_index, uint32_t *chunk_index, + uint32_t *chunk_relative_sample_index, uint32_t *desc_index); + + uint32_t countSamples() const; + status_t getSampleSize(uint32_t sample_index, size_t *sample_size); + + status_t getSampleOffsetAndSize( + uint32_t sample_index, off_t *offset, size_t *size); + + status_t getMaxSampleSize(size_t *size); + + status_t getDecodingTime(uint32_t sample_index, uint32_t *time); + + enum { + kSyncSample_Flag = 1 + }; + status_t findClosestSample( + uint32_t req_time, uint32_t *sample_index, uint32_t flags); + + status_t findClosestSyncSample( + uint32_t start_sample_index, uint32_t *sample_index); + + status_t findThumbnailSample(uint32_t *sample_index); + +protected: + ~SampleTable(); + +private: + sp<DataSource> mDataSource; + Mutex mLock; + + off_t mChunkOffsetOffset; + uint32_t mChunkOffsetType; + uint32_t mNumChunkOffsets; + + off_t mSampleToChunkOffset; + uint32_t mNumSampleToChunkOffsets; + + off_t mSampleSizeOffset; + uint32_t mSampleSizeFieldSize; + uint32_t mDefaultSampleSize; + uint32_t mNumSampleSizes; + + uint32_t mTimeToSampleCount; + uint32_t *mTimeToSample; + + off_t mSyncSampleOffset; + uint32_t mNumSyncSamples; + + SampleTable(const SampleTable &); + SampleTable &operator=(const SampleTable &); +}; + +} // namespace android + +#endif // SAMPLE_TABLE_H_ diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h new file mode 100644 index 0000000..9eed089 --- /dev/null +++ b/media/libstagefright/include/SoftwareRenderer.h @@ -0,0 +1,59 @@ +/* + * 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 SOFTWARE_RENDERER_H_ + +#define SOFTWARE_RENDERER_H_ + +#include <media/stagefright/ColorConverter.h> +#include <media/stagefright/VideoRenderer.h> +#include <utils/RefBase.h> + +namespace android { + +class ISurface; +class MemoryHeapBase; + +class SoftwareRenderer : public VideoRenderer { +public: + SoftwareRenderer( + OMX_COLOR_FORMATTYPE colorFormat, + const sp<ISurface> &surface, + size_t displayWidth, size_t displayHeight, + size_t decodedWidth, size_t decodedHeight); + + virtual ~SoftwareRenderer(); + + virtual void render( + const void *data, size_t size, void *platformPrivate); + +private: + OMX_COLOR_FORMATTYPE mColorFormat; + ColorConverter mConverter; + sp<ISurface> mISurface; + size_t mDisplayWidth, mDisplayHeight; + size_t mDecodedWidth, mDecodedHeight; + size_t mFrameSize; + sp<MemoryHeapBase> mMemoryHeap; + int mIndex; + + SoftwareRenderer(const SoftwareRenderer &); + SoftwareRenderer &operator=(const SoftwareRenderer &); +}; + +} // namespace android + +#endif // SOFTWARE_RENDERER_H_ diff --git a/media/libstagefright/include/TIHardwareRenderer.h b/media/libstagefright/include/TIHardwareRenderer.h new file mode 100644 index 0000000..ef42648 --- /dev/null +++ b/media/libstagefright/include/TIHardwareRenderer.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TI_HARDWARE_RENDERER_H_ + +#define TI_HARDWARE_RENDERER_H_ + +#include <media/stagefright/VideoRenderer.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> + +namespace android { + +class ISurface; +class Overlay; + +class TIHardwareRenderer : public VideoRenderer { +public: + TIHardwareRenderer( + const sp<ISurface> &surface, + size_t displayWidth, size_t displayHeight, + size_t decodedWidth, size_t decodedHeight); + + virtual ~TIHardwareRenderer(); + + virtual void render( + const void *data, size_t size, void *platformPrivate); + +private: + sp<ISurface> mISurface; + size_t mDisplayWidth, mDisplayHeight; + size_t mDecodedWidth, mDecodedHeight; + size_t mFrameSize; + sp<Overlay> mOverlay; + Vector<void *> mOverlayAddresses; + bool mIsFirstFrame; + size_t mIndex; + + TIHardwareRenderer(const TIHardwareRenderer &); + TIHardwareRenderer &operator=(const TIHardwareRenderer &); +}; + +} // namespace android + +#endif // TI_HARDWARE_RENDERER_H_ + diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h new file mode 100644 index 0000000..a264421 --- /dev/null +++ b/media/libstagefright/include/TimedEventQueue.h @@ -0,0 +1,106 @@ +/* + * 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 TIMED_EVENT_QUEUE_H_ + +#define TIMED_EVENT_QUEUE_H_ + +#include <pthread.h> + +#include <utils/List.h> +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace android { + +struct TimedEventQueue { + + struct Event : public RefBase { + Event() {} + virtual ~Event() {} + + protected: + virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0; + + private: + friend class TimedEventQueue; + + Event(const Event &); + Event &operator=(const Event &); + }; + + TimedEventQueue(); + ~TimedEventQueue(); + + // Start executing the event loop. + void start(); + + // Stop executing the event loop, if flush is false, any pending + // events are discarded, otherwise the queue will stop (and this call + // return) once all pending events have been handled. + void stop(bool flush = false); + + // Posts an event to the front of the queue (after all events that + // have previously been posted to the front but before timed events). + void postEvent(const sp<Event> &event); + + void postEventToBack(const sp<Event> &event); + + // It is an error to post an event with a negative delay. + void postEventWithDelay(const sp<Event> &event, int64_t delay_us); + + // If the event is to be posted at a time that has already passed, + // it will fire as soon as possible. + void postTimedEvent(const sp<Event> &event, int64_t realtime_us); + + // Returns true iff event is currently in the queue and has been + // successfully cancelled. In this case the event will have been + // removed from the queue and won't fire. + bool cancelEvent(const sp<Event> &event); + + static int64_t getRealTimeUs(); + +private: + struct QueueItem { + sp<Event> event; + int64_t realtime_us; + }; + + struct StopEvent : public TimedEventQueue::Event { + virtual void fire(TimedEventQueue *queue, int64_t now_us) { + queue->mStopped = true; + } + }; + + pthread_t mThread; + List<QueueItem> mQueue; + Mutex mLock; + Condition mQueueNotEmptyCondition; + Condition mQueueHeadChangedCondition; + + bool mRunning; + bool mStopped; + + static void *ThreadWrapper(void *me); + void threadEntry(); + + TimedEventQueue(const TimedEventQueue &); + TimedEventQueue &operator=(const TimedEventQueue &); +}; + +} // namespace android + +#endif // TIMED_EVENT_QUEUE_H_ diff --git a/media/libstagefright/include/stagefright_string.h b/media/libstagefright/include/stagefright_string.h new file mode 100644 index 0000000..5dc7116 --- /dev/null +++ b/media/libstagefright/include/stagefright_string.h @@ -0,0 +1,54 @@ +/* + * 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 STRING_H_ + +#define STRING_H_ + +#include <utils/String8.h> + +namespace android { + +class string { +public: + typedef size_t size_type; + static size_type npos; + + string(); + string(const char *s); + string(const char *s, size_t length); + string(const string &from, size_type start, size_type length = npos); + + const char *c_str() const; + size_type size() const; + + void clear(); + void erase(size_type from, size_type length); + + size_type find(char c) const; + + bool operator<(const string &other) const; + bool operator==(const string &other) const; + + string &operator+=(char c); + +private: + String8 mString; +}; + +} // namespace android + +#endif // STRING_H_ diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 4cadccd..edbc04e 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -10,7 +10,9 @@ LOCAL_C_INCLUDES += $(TOP)/hardware/ti/omap3/liboverlay LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) LOCAL_SRC_FILES:= \ + ColorConverter.cpp \ OMX.cpp \ + OMXNodeInstance.cpp \ QComHardwareRenderer.cpp \ SoftwareRenderer.cpp \ TIHardwareRenderer.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 8b83dd6..e361018 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -18,65 +18,28 @@ #define LOG_TAG "OMX" #include <utils/Log.h> -#include <sys/socket.h> - -#include "OMX.h" +#include "../include/OMX.h" #include "OMXRenderer.h" #include "pv_omxcore.h" +#include "../include/OMXNodeInstance.h" +#include "../include/QComHardwareRenderer.h" +#include "../include/SoftwareRenderer.h" +#include "../include/TIHardwareRenderer.h" + #include <binder/IMemory.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/QComHardwareRenderer.h> -#include <media/stagefright/SoftwareRenderer.h> -#include <media/stagefright/TIHardwareRenderer.h> #include <media/stagefright/VideoRenderer.h> #include <OMX_Component.h> namespace android { -class NodeMeta { -public: - NodeMeta(OMX *owner) - : mOwner(owner), - mHandle(NULL) { - } - - OMX *owner() const { - return mOwner; - } - - void setHandle(OMX_HANDLETYPE handle) { - CHECK_EQ(mHandle, NULL); - mHandle = handle; - } - - OMX_HANDLETYPE handle() const { - return mHandle; - } - - void setObserver(const sp<IOMXObserver> &observer) { - mObserver = observer; - } - - sp<IOMXObserver> observer() { - return mObserver; - } - -private: - OMX *mOwner; - OMX_HANDLETYPE mHandle; - sp<IOMXObserver> mObserver; - - NodeMeta(const NodeMeta &); - NodeMeta &operator=(const NodeMeta &); -}; - //////////////////////////////////////////////////////////////////////////////// struct OMX::CallbackDispatcher : public RefBase { - CallbackDispatcher(); + CallbackDispatcher(OMX *owner); void post(const omx_message &msg); @@ -85,6 +48,8 @@ protected: private: Mutex mLock; + + OMX *mOwner; bool mDone; Condition mQueueChanged; List<omx_message> mQueue; @@ -100,8 +65,9 @@ private: CallbackDispatcher &operator=(const CallbackDispatcher &); }; -OMX::CallbackDispatcher::CallbackDispatcher() - : mDone(false) { +OMX::CallbackDispatcher::CallbackDispatcher(OMX *owner) + : mOwner(owner), + mDone(false) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); @@ -130,12 +96,12 @@ void OMX::CallbackDispatcher::post(const omx_message &msg) { } void OMX::CallbackDispatcher::dispatch(const omx_message &msg) { - NodeMeta *meta = static_cast<NodeMeta *>(msg.node); - - sp<IOMXObserver> observer = meta->observer(); - if (observer.get() != NULL) { - observer->on_message(msg); + OMXNodeInstance *instance = mOwner->findInstance(msg.node); + if (instance == NULL) { + LOGV("Would have dispatched a message to a node that's already gone."); + return; } + instance->onMessage(msg); } // static @@ -213,46 +179,30 @@ private: BufferMeta &operator=(const BufferMeta &); }; -// static -OMX_CALLBACKTYPE OMX::kCallbacks = { - &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone -}; - -// static -OMX_ERRORTYPE OMX::OnEvent( - OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, - OMX_IN OMX_EVENTTYPE eEvent, - OMX_IN OMX_U32 nData1, - OMX_IN OMX_U32 nData2, - OMX_IN OMX_PTR pEventData) { - NodeMeta *meta = static_cast<NodeMeta *>(pAppData); - return meta->owner()->OnEvent(meta, eEvent, nData1, nData2, pEventData); +OMX::OMX() + : mDispatcher(new CallbackDispatcher(this)), + mNodeCounter(0) { } -// static -OMX_ERRORTYPE OMX::OnEmptyBufferDone( - OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) { - NodeMeta *meta = static_cast<NodeMeta *>(pAppData); - return meta->owner()->OnEmptyBufferDone(meta, pBuffer); -} +void OMX::binderDied(const wp<IBinder> &the_late_who) { + OMXNodeInstance *instance; -// static -OMX_ERRORTYPE OMX::OnFillBufferDone( - OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) { - NodeMeta *meta = static_cast<NodeMeta *>(pAppData); - return meta->owner()->OnFillBufferDone(meta, pBuffer); -} + { + Mutex::Autolock autoLock(mLock); -OMX::OMX() - : mDispatcher(new CallbackDispatcher) { + ssize_t index = mLiveNodes.indexOfKey(the_late_who); + CHECK(index >= 0); + + instance = mLiveNodes.editValueAt(index); + mLiveNodes.removeItemsAt(index); + + invalidateNodeID_l(instance->nodeID()); + } + + instance->onObserverDied(); } -status_t OMX::list_nodes(List<String8> *list) { +status_t OMX::listNodes(List<String8> *list) { OMX_MasterInit(); // XXX Put this somewhere else. list->clear(); @@ -269,204 +219,132 @@ status_t OMX::list_nodes(List<String8> *list) { return OK; } -status_t OMX::allocate_node(const char *name, node_id *node) { +status_t OMX::allocateNode( + const char *name, const sp<IOMXObserver> &observer, node_id *node) { Mutex::Autolock autoLock(mLock); *node = 0; OMX_MasterInit(); // XXX Put this somewhere else. - NodeMeta *meta = new NodeMeta(this); + OMXNodeInstance *instance = new OMXNodeInstance(this, observer); OMX_HANDLETYPE handle; OMX_ERRORTYPE err = OMX_MasterGetHandle( - &handle, const_cast<char *>(name), meta, &kCallbacks); + &handle, const_cast<char *>(name), instance, + &OMXNodeInstance::kCallbacks); if (err != OMX_ErrorNone) { - LOGE("FAILED to allocate omx component '%s'", name); + LOGV("FAILED to allocate omx component '%s'", name); - delete meta; - meta = NULL; + instance->onGetHandleFailed(); return UNKNOWN_ERROR; } - meta->setHandle(handle); + *node = makeNodeID(instance); - *node = meta; + instance->setHandle(*node, handle); + + mLiveNodes.add(observer->asBinder(), instance); + observer->asBinder()->linkToDeath(this); return OK; } -status_t OMX::free_node(node_id node) { - Mutex::Autolock autoLock(mLock); - - NodeMeta *meta = static_cast<NodeMeta *>(node); - - OMX_ERRORTYPE err = OMX_MasterFreeHandle(meta->handle()); +status_t OMX::freeNode(node_id node) { + OMXNodeInstance *instance = findInstance(node); - delete meta; - meta = NULL; + ssize_t index = mLiveNodes.indexOfKey(instance->observer()->asBinder()); + CHECK(index >= 0); + mLiveNodes.removeItemsAt(index); + instance->observer()->asBinder()->unlinkToDeath(this); - return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; + return instance->freeNode(); } -status_t OMX::send_command( +status_t OMX::sendCommand( node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) { - Mutex::Autolock autoLock(mLock); - - NodeMeta *meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = OMX_SendCommand(meta->handle(), cmd, param, NULL); - - return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; + return findInstance(node)->sendCommand(cmd, param); } -status_t OMX::get_parameter( +status_t OMX::getParameter( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { - Mutex::Autolock autoLock(mLock); - - NodeMeta *meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = OMX_GetParameter(meta->handle(), index, params); - - return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; + return findInstance(node)->getParameter( + index, params, size); } -status_t OMX::set_parameter( +status_t OMX::setParameter( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { - Mutex::Autolock autoLock(mLock); - - NodeMeta *meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = - OMX_SetParameter(meta->handle(), index, const_cast<void *>(params)); - - return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; + return findInstance(node)->setParameter( + index, params, size); } -status_t OMX::get_config( +status_t OMX::getConfig( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { - Mutex::Autolock autoLock(mLock); - - NodeMeta *meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = OMX_GetConfig(meta->handle(), index, params); - - return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; + return findInstance(node)->getConfig( + index, params, size); } -status_t OMX::set_config( +status_t OMX::setConfig( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { - Mutex::Autolock autoLock(mLock); - - NodeMeta *meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = - OMX_SetConfig(meta->handle(), index, const_cast<void *>(params)); - - return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; + return findInstance(node)->setConfig( + index, params, size); } -status_t OMX::use_buffer( +status_t OMX::useBuffer( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, buffer_id *buffer) { - Mutex::Autolock autoLock(mLock); - - BufferMeta *buffer_meta = new BufferMeta(this, params); - - OMX_BUFFERHEADERTYPE *header; - - NodeMeta *node_meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = - OMX_UseBuffer(node_meta->handle(), &header, port_index, buffer_meta, - params->size(), static_cast<OMX_U8 *>(params->pointer())); - - if (err != OMX_ErrorNone) { - LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err); - - delete buffer_meta; - buffer_meta = NULL; - - *buffer = 0; - return UNKNOWN_ERROR; - } - - *buffer = header; - - return OK; + return findInstance(node)->useBuffer( + port_index, params, buffer); } -status_t OMX::allocate_buffer( +status_t OMX::allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer) { - Mutex::Autolock autoLock(mLock); - - BufferMeta *buffer_meta = new BufferMeta(this, size); - - OMX_BUFFERHEADERTYPE *header; - - NodeMeta *node_meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = - OMX_AllocateBuffer(node_meta->handle(), &header, port_index, - buffer_meta, size); - - if (err != OMX_ErrorNone) { - delete buffer_meta; - buffer_meta = NULL; - - *buffer = 0; - return UNKNOWN_ERROR; - } - - *buffer = header; - - return OK; + return findInstance(node)->allocateBuffer( + port_index, size, buffer); } -status_t OMX::allocate_buffer_with_backup( +status_t OMX::allocateBufferWithBackup( node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, buffer_id *buffer) { - Mutex::Autolock autoLock(mLock); - - BufferMeta *buffer_meta = new BufferMeta(this, params, true); - - OMX_BUFFERHEADERTYPE *header; - - NodeMeta *node_meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = - OMX_AllocateBuffer( - node_meta->handle(), &header, port_index, buffer_meta, - params->size()); - - if (err != OMX_ErrorNone) { - delete buffer_meta; - buffer_meta = NULL; - - *buffer = 0; - return UNKNOWN_ERROR; - } - - *buffer = header; - - return OK; + return findInstance(node)->allocateBufferWithBackup( + port_index, params, buffer); } -status_t OMX::free_buffer(node_id node, OMX_U32 port_index, buffer_id buffer) { - OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; - BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate); +status_t OMX::freeBuffer(node_id node, OMX_U32 port_index, buffer_id buffer) { + return findInstance(node)->freeBuffer( + port_index, buffer); +} - NodeMeta *node_meta = static_cast<NodeMeta *>(node); - OMX_ERRORTYPE err = - OMX_FreeBuffer(node_meta->handle(), port_index, header); +status_t OMX::fillBuffer(node_id node, buffer_id buffer) { + return findInstance(node)->fillBuffer(buffer); +} - delete buffer_meta; - buffer_meta = NULL; +status_t OMX::emptyBuffer( + node_id node, + buffer_id buffer, + OMX_U32 range_offset, OMX_U32 range_length, + OMX_U32 flags, OMX_TICKS timestamp) { + return findInstance(node)->emptyBuffer( + buffer, range_offset, range_length, flags, timestamp); +} - return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK; +status_t OMX::getExtensionIndex( + node_id node, + const char *parameter_name, + OMX_INDEXTYPE *index) { + return findInstance(node)->getExtensionIndex( + parameter_name, index); } OMX_ERRORTYPE OMX::OnEvent( - NodeMeta *meta, + node_id node, OMX_IN OMX_EVENTTYPE eEvent, OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, @@ -475,7 +353,7 @@ OMX_ERRORTYPE OMX::OnEvent( omx_message msg; msg.type = omx_message::EVENT; - msg.node = meta; + msg.node = node; msg.u.event_data.event = eEvent; msg.u.event_data.data1 = nData1; msg.u.event_data.data2 = nData2; @@ -484,14 +362,14 @@ OMX_ERRORTYPE OMX::OnEvent( return OMX_ErrorNone; } - + OMX_ERRORTYPE OMX::OnEmptyBufferDone( - NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { + node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { LOGV("OnEmptyBufferDone buffer=%p", pBuffer); omx_message msg; msg.type = omx_message::EMPTY_BUFFER_DONE; - msg.node = meta; + msg.node = node; msg.u.buffer_data.buffer = pBuffer; mDispatcher->post(msg); @@ -500,14 +378,12 @@ OMX_ERRORTYPE OMX::OnEmptyBufferDone( } OMX_ERRORTYPE OMX::OnFillBufferDone( - NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { + node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { LOGV("OnFillBufferDone buffer=%p", pBuffer); - BufferMeta *buffer_meta = static_cast<BufferMeta *>(pBuffer->pAppPrivate); - buffer_meta->CopyFromOMX(pBuffer); omx_message msg; msg.type = omx_message::FILL_BUFFER_DONE; - msg.node = meta; + msg.node = node; msg.u.extended_buffer_data.buffer = pBuffer; msg.u.extended_buffer_data.range_offset = pBuffer->nOffset; msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen; @@ -520,62 +396,31 @@ OMX_ERRORTYPE OMX::OnFillBufferDone( return OMX_ErrorNone; } -status_t OMX::observe_node( - node_id node, const sp<IOMXObserver> &observer) { - NodeMeta *node_meta = static_cast<NodeMeta *>(node); +OMX::node_id OMX::makeNodeID(OMXNodeInstance *instance) { + // mLock is already held. - node_meta->setObserver(observer); + node_id node = (node_id)++mNodeCounter; + mNodeIDToInstance.add(node, instance); - return OK; + return node; } -void OMX::fill_buffer(node_id node, buffer_id buffer) { - OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; - header->nFilledLen = 0; - header->nOffset = 0; - header->nFlags = 0; +OMXNodeInstance *OMX::findInstance(node_id node) { + Mutex::Autolock autoLock(mLock); - NodeMeta *node_meta = static_cast<NodeMeta *>(node); + ssize_t index = mNodeIDToInstance.indexOfKey(node); - OMX_ERRORTYPE err = - OMX_FillThisBuffer(node_meta->handle(), header); - CHECK_EQ(err, OMX_ErrorNone); + return index < 0 ? NULL : mNodeIDToInstance.valueAt(index); } -void OMX::empty_buffer( - node_id node, - buffer_id buffer, - OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { - OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; - header->nFilledLen = range_length; - header->nOffset = range_offset; - header->nFlags = flags; - header->nTimeStamp = timestamp; - - BufferMeta *buffer_meta = - static_cast<BufferMeta *>(header->pAppPrivate); - buffer_meta->CopyToOMX(header); - - NodeMeta *node_meta = static_cast<NodeMeta *>(node); - - OMX_ERRORTYPE err = - OMX_EmptyThisBuffer(node_meta->handle(), header); - CHECK_EQ(err, OMX_ErrorNone); +void OMX::invalidateNodeID(node_id node) { + Mutex::Autolock autoLock(mLock); + invalidateNodeID_l(node); } -status_t OMX::get_extension_index( - node_id node, - const char *parameter_name, - OMX_INDEXTYPE *index) { - NodeMeta *node_meta = static_cast<NodeMeta *>(node); - - OMX_ERRORTYPE err = - OMX_GetExtensionIndex( - node_meta->handle(), - const_cast<char *>(parameter_name), index); - - return err == OMX_ErrorNone ? OK : UNKNOWN_ERROR; +void OMX::invalidateNodeID_l(node_id node) { + // mLock is held. + mNodeIDToInstance.removeItem(node); } //////////////////////////////////////////////////////////////////////////////// diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp new file mode 100644 index 0000000..ab8be53 --- /dev/null +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -0,0 +1,460 @@ +/* + * 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 "OMXNodeInstance" +#include <utils/Log.h> + +#include "../include/OMXNodeInstance.h" + +#include "pv_omxcore.h" + +#include <binder/IMemory.h> +#include <media/stagefright/MediaDebug.h> + +namespace android { + +struct BufferMeta { + BufferMeta(const sp<IMemory> &mem, bool is_backup = false) + : mMem(mem), + mIsBackup(is_backup) { + } + + BufferMeta(size_t size) + : mSize(size), + mIsBackup(false) { + } + + void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) { + if (!mIsBackup) { + return; + } + + memcpy((OMX_U8 *)mMem->pointer() + header->nOffset, + header->pBuffer + header->nOffset, + header->nFilledLen); + } + + void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) { + if (!mIsBackup) { + return; + } + + memcpy(header->pBuffer + header->nOffset, + (const OMX_U8 *)mMem->pointer() + header->nOffset, + header->nFilledLen); + } + +private: + sp<IMemory> mMem; + size_t mSize; + bool mIsBackup; + + BufferMeta(const BufferMeta &); + BufferMeta &operator=(const BufferMeta &); +}; + +// static +OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = { + &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone +}; + +OMXNodeInstance::OMXNodeInstance( + OMX *owner, const sp<IOMXObserver> &observer) + : mOwner(owner), + mNodeID(NULL), + mHandle(NULL), + mObserver(observer) { +} + +OMXNodeInstance::~OMXNodeInstance() { + CHECK_EQ(mHandle, NULL); +} + +void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) { + CHECK_EQ(mHandle, NULL); + mNodeID = node_id; + mHandle = handle; +} + +OMX *OMXNodeInstance::owner() { + return mOwner; +} + +sp<IOMXObserver> OMXNodeInstance::observer() { + return mObserver; +} + +OMX::node_id OMXNodeInstance::nodeID() { + return mNodeID; +} + +static status_t StatusFromOMXError(OMX_ERRORTYPE err) { + return (err == OMX_ErrorNone) ? OK : UNKNOWN_ERROR; +} + +status_t OMXNodeInstance::freeNode() { + // Transition the node from its current state all the way down + // to "Loaded". + // This ensures that all active buffers are properly freed even + // for components that don't do this themselves on a call to + // "FreeHandle". + + OMX_STATETYPE state; + CHECK_EQ(OMX_GetState(mHandle, &state), OMX_ErrorNone); + switch (state) { + case OMX_StateExecuting: + { + LOGV("forcing Executing->Idle"); + sendCommand(OMX_CommandStateSet, OMX_StateIdle); + OMX_ERRORTYPE err; + while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone + && state != OMX_StateIdle) { + usleep(100000); + } + CHECK_EQ(err, OMX_ErrorNone); + + // fall through + } + + case OMX_StateIdle: + { + LOGV("forcing Idle->Loaded"); + sendCommand(OMX_CommandStateSet, OMX_StateLoaded); + + freeActiveBuffers(); + + OMX_ERRORTYPE err; + while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone + && state != OMX_StateLoaded) { + LOGV("waiting for Loaded state..."); + usleep(100000); + } + CHECK_EQ(err, OMX_ErrorNone); + + // fall through + } + + case OMX_StateLoaded: + break; + + default: + CHECK(!"should not be here, unknown state."); + break; + } + + OMX_ERRORTYPE err = OMX_MasterFreeHandle(mHandle); + mHandle = NULL; + + if (err != OMX_ErrorNone) { + LOGE("FreeHandle FAILED with error 0x%08x.", err); + } + + mOwner->invalidateNodeID(mNodeID); + mNodeID = NULL; + + LOGV("OMXNodeInstance going away."); + delete this; + + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::sendCommand( + OMX_COMMANDTYPE cmd, OMX_S32 param) { + Mutex::Autolock autoLock(mLock); + + OMX_ERRORTYPE err = OMX_SendCommand(mHandle, cmd, param, NULL); + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::getParameter( + OMX_INDEXTYPE index, void *params, size_t size) { + Mutex::Autolock autoLock(mLock); + + OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params); + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::setParameter( + OMX_INDEXTYPE index, const void *params, size_t size) { + Mutex::Autolock autoLock(mLock); + + OMX_ERRORTYPE err = OMX_SetParameter( + mHandle, index, const_cast<void *>(params)); + + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::getConfig( + OMX_INDEXTYPE index, void *params, size_t size) { + Mutex::Autolock autoLock(mLock); + + OMX_ERRORTYPE err = OMX_GetConfig(mHandle, index, params); + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::setConfig( + OMX_INDEXTYPE index, const void *params, size_t size) { + Mutex::Autolock autoLock(mLock); + + OMX_ERRORTYPE err = OMX_SetConfig( + mHandle, index, const_cast<void *>(params)); + + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::useBuffer( + OMX_U32 portIndex, const sp<IMemory> ¶ms, + OMX::buffer_id *buffer) { + Mutex::Autolock autoLock(mLock); + + BufferMeta *buffer_meta = new BufferMeta(params); + + OMX_BUFFERHEADERTYPE *header; + + OMX_ERRORTYPE err = OMX_UseBuffer( + mHandle, &header, portIndex, buffer_meta, + params->size(), static_cast<OMX_U8 *>(params->pointer())); + + if (err != OMX_ErrorNone) { + LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err); + + delete buffer_meta; + buffer_meta = NULL; + + *buffer = 0; + + return UNKNOWN_ERROR; + } + + *buffer = header; + + addActiveBuffer(portIndex, *buffer); + + return OK; +} + +status_t OMXNodeInstance::allocateBuffer( + OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer) { + Mutex::Autolock autoLock(mLock); + + BufferMeta *buffer_meta = new BufferMeta(size); + + OMX_BUFFERHEADERTYPE *header; + + OMX_ERRORTYPE err = OMX_AllocateBuffer( + mHandle, &header, portIndex, buffer_meta, size); + + if (err != OMX_ErrorNone) { + LOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err); + + delete buffer_meta; + buffer_meta = NULL; + + *buffer = 0; + + return UNKNOWN_ERROR; + } + + *buffer = header; + + addActiveBuffer(portIndex, *buffer); + + return OK; +} + +status_t OMXNodeInstance::allocateBufferWithBackup( + OMX_U32 portIndex, const sp<IMemory> ¶ms, + OMX::buffer_id *buffer) { + Mutex::Autolock autoLock(mLock); + + BufferMeta *buffer_meta = new BufferMeta(params, true); + + OMX_BUFFERHEADERTYPE *header; + + OMX_ERRORTYPE err = OMX_AllocateBuffer( + mHandle, &header, portIndex, buffer_meta, params->size()); + + if (err != OMX_ErrorNone) { + LOGE("OMX_AllocateBuffer failed with error %d (0x%08x)", err, err); + + delete buffer_meta; + buffer_meta = NULL; + + *buffer = 0; + + return UNKNOWN_ERROR; + } + + *buffer = header; + + addActiveBuffer(portIndex, *buffer); + + return OK; +} + +status_t OMXNodeInstance::freeBuffer( + OMX_U32 portIndex, OMX::buffer_id buffer) { + Mutex::Autolock autoLock(mLock); + + removeActiveBuffer(portIndex, buffer); + + OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; + BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate); + + OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header); + + delete buffer_meta; + buffer_meta = NULL; + + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { + Mutex::Autolock autoLock(mLock); + + OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; + header->nFilledLen = 0; + header->nOffset = 0; + header->nFlags = 0; + + OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header); + + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::emptyBuffer( + OMX::buffer_id buffer, + OMX_U32 rangeOffset, OMX_U32 rangeLength, + OMX_U32 flags, OMX_TICKS timestamp) { + Mutex::Autolock autoLock(mLock); + + OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; + header->nFilledLen = rangeLength; + header->nOffset = rangeOffset; + header->nFlags = flags; + header->nTimeStamp = timestamp; + + BufferMeta *buffer_meta = + static_cast<BufferMeta *>(header->pAppPrivate); + buffer_meta->CopyToOMX(header); + + OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); + + return StatusFromOMXError(err); +} + +status_t OMXNodeInstance::getExtensionIndex( + const char *parameterName, OMX_INDEXTYPE *index) { + Mutex::Autolock autoLock(mLock); + + OMX_ERRORTYPE err = OMX_GetExtensionIndex( + mHandle, const_cast<char *>(parameterName), index); + + return StatusFromOMXError(err); +} + +void OMXNodeInstance::onMessage(const omx_message &msg) { + if (msg.type == omx_message::FILL_BUFFER_DONE) { + OMX_BUFFERHEADERTYPE *buffer = + static_cast<OMX_BUFFERHEADERTYPE *>( + msg.u.extended_buffer_data.buffer); + + BufferMeta *buffer_meta = + static_cast<BufferMeta *>(buffer->pAppPrivate); + + buffer_meta->CopyFromOMX(buffer); + } + + mObserver->onMessage(msg); +} + +void OMXNodeInstance::onObserverDied() { + LOGE("!!! Observer died. Quickly, do something, ... anything..."); + + // Try to force shutdown of the node and hope for the best. + freeNode(); +} + +void OMXNodeInstance::onGetHandleFailed() { + delete this; +} + +// static +OMX_ERRORTYPE OMXNodeInstance::OnEvent( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) { + OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData); + return instance->owner()->OnEvent( + instance->nodeID(), eEvent, nData1, nData2, pEventData); +} + +// static +OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) { + OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData); + return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer); +} + +// static +OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) { + OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData); + return instance->owner()->OnFillBufferDone(instance->nodeID(), pBuffer); +} + +void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) { + ActiveBuffer active; + active.mPortIndex = portIndex; + active.mID = id; + mActiveBuffers.push(active); +} + +void OMXNodeInstance::removeActiveBuffer( + OMX_U32 portIndex, OMX::buffer_id id) { + bool found = false; + for (size_t i = 0; i < mActiveBuffers.size(); ++i) { + if (mActiveBuffers[i].mPortIndex == portIndex + && mActiveBuffers[i].mID == id) { + found = true; + mActiveBuffers.removeItemsAt(i); + break; + } + } + + if (!found) { + LOGW("Attempt to remove an active buffer we know nothing about..."); + } +} + +void OMXNodeInstance::freeActiveBuffers() { + // Make sure to count down here, as freeBuffer will in turn remove + // the active buffer from the vector... + for (size_t i = mActiveBuffers.size(); i--;) { + freeBuffer(mActiveBuffers[i].mPortIndex, mActiveBuffers[i].mID); + } +} + +} // namespace android + diff --git a/media/libstagefright/omx/QComHardwareRenderer.cpp b/media/libstagefright/omx/QComHardwareRenderer.cpp index 7dc368f..8e78c77 100644 --- a/media/libstagefright/omx/QComHardwareRenderer.cpp +++ b/media/libstagefright/omx/QComHardwareRenderer.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ +#include "../include/QComHardwareRenderer.h" + #include <binder/MemoryHeapBase.h> #include <binder/MemoryHeapPmem.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/QComHardwareRenderer.h> #include <ui/ISurface.h> namespace android { diff --git a/media/libstagefright/omx/SoftwareRenderer.cpp b/media/libstagefright/omx/SoftwareRenderer.cpp index 4ed6869..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 %ld", 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/omx/TIHardwareRenderer.cpp b/media/libstagefright/omx/TIHardwareRenderer.cpp index ebade4a..6dde86a 100644 --- a/media/libstagefright/omx/TIHardwareRenderer.cpp +++ b/media/libstagefright/omx/TIHardwareRenderer.cpp @@ -17,7 +17,8 @@ #define LOG_TAG "TIHardwareRenderer" #include <utils/Log.h> -#include <media/stagefright/TIHardwareRenderer.h> +#include "../include/TIHardwareRenderer.h" + #include <media/stagefright/MediaDebug.h> #include <ui/ISurface.h> #include <ui/Overlay.h> diff --git a/media/libstagefright/string.cpp b/media/libstagefright/string.cpp index 5b16784..bd6204b 100644 --- a/media/libstagefright/string.cpp +++ b/media/libstagefright/string.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <media/stagefright/string.h> +#include "include/stagefright_string.h" namespace android { |
