diff options
-rw-r--r-- | camera/Camera.cpp | 3 | ||||
-rw-r--r-- | camera/ICameraService.cpp | 21 | ||||
-rw-r--r-- | include/media/IOMX.h | 5 | ||||
-rw-r--r-- | include/media/stagefright/ACodec.h | 1 | ||||
-rw-r--r-- | media/libmedia/IOMX.cpp | 58 | ||||
-rw-r--r-- | media/libstagefright/ACodec.cpp | 149 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Writer.cpp | 7 | ||||
-rw-r--r-- | media/libstagefright/OMXCodec.cpp | 7 | ||||
-rw-r--r-- | media/libstagefright/OggExtractor.cpp | 441 | ||||
-rw-r--r-- | media/libstagefright/include/OMXNodeInstance.h | 6 | ||||
-rw-r--r-- | media/libstagefright/include/OggExtractor.h | 4 | ||||
-rw-r--r-- | media/libstagefright/omx/OMX.cpp | 27 | ||||
-rw-r--r-- | media/libstagefright/omx/OMXNodeInstance.cpp | 23 | ||||
-rw-r--r-- | media/libstagefright/omx/tests/OMXHarness.cpp | 6 | ||||
-rw-r--r-- | media/libstagefright/omx/tests/OMXHarness.h | 2 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 3 |
16 files changed, 588 insertions, 175 deletions
diff --git a/camera/Camera.cpp b/camera/Camera.cpp index 3a9fb4c..9bf3134 100644 --- a/camera/Camera.cpp +++ b/camera/Camera.cpp @@ -97,7 +97,8 @@ status_t Camera::connectLegacy(int cameraId, int halVersion, c->mStatus = NO_ERROR; camera = c; } else { - ALOGW("An error occurred while connecting to camera: %d", cameraId); + ALOGW("An error occurred while connecting to camera %d: %d (%s)", + cameraId, status, strerror(-status)); c.clear(); } return status; diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index 0071e70..7bb24ee 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -175,10 +175,13 @@ public: data.writeInt32(cameraId); data.writeString16(clientPackageName); data.writeInt32(clientUid); - remote()->transact(BnCameraService::CONNECT, data, &reply); + + status_t status; + status = remote()->transact(BnCameraService::CONNECT, data, &reply); + if (status != OK) return status; if (readExceptionCode(reply)) return -EPROTO; - status_t status = reply.readInt32(); + status = reply.readInt32(); if (reply.readInt32() != 0) { device = interface_cast<ICamera>(reply.readStrongBinder()); } @@ -198,10 +201,13 @@ public: data.writeInt32(halVersion); data.writeString16(clientPackageName); data.writeInt32(clientUid); - remote()->transact(BnCameraService::CONNECT_LEGACY, data, &reply); + + status_t status; + status = remote()->transact(BnCameraService::CONNECT_LEGACY, data, &reply); + if (status != OK) return status; if (readExceptionCode(reply)) return -EPROTO; - status_t status = reply.readInt32(); + status = reply.readInt32(); if (reply.readInt32() != 0) { device = interface_cast<ICamera>(reply.readStrongBinder()); } @@ -237,10 +243,13 @@ public: data.writeInt32(cameraId); data.writeString16(clientPackageName); data.writeInt32(clientUid); - remote()->transact(BnCameraService::CONNECT_DEVICE, data, &reply); + + status_t status; + status = remote()->transact(BnCameraService::CONNECT_DEVICE, data, &reply); + if (status != OK) return status; if (readExceptionCode(reply)) return -EPROTO; - status_t status = reply.readInt32(); + status = reply.readInt32(); if (reply.readInt32() != 0) { device = interface_cast<ICameraDeviceUser>(reply.readStrongBinder()); } diff --git a/include/media/IOMX.h b/include/media/IOMX.h index 84fdf83..7023453 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -25,6 +25,8 @@ #include <utils/List.h> #include <utils/String8.h> +#include <list> + #include <media/hardware/MetadataBufferType.h> #include <OMX_Core.h> @@ -231,7 +233,8 @@ class IOMXObserver : public IInterface { public: DECLARE_META_INTERFACE(OMXObserver); - virtual void onMessage(const omx_message &msg) = 0; + // Handle (list of) messages. + virtual void onMessages(const std::list<omx_message> &messages) = 0; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index f7a3df7..d4891a1 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -109,6 +109,7 @@ private: enum { kWhatSetup = 'setu', kWhatOMXMessage = 'omx ', + kWhatOMXMessageList = 'omxL', kWhatInputBufferFilled = 'inpF', kWhatOutputBufferDrained = 'outD', kWhatShutdown = 'shut', diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index ca1cdc7..16da65e 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -1077,16 +1077,29 @@ public: : BpInterface<IOMXObserver>(impl) { } - virtual void onMessage(const omx_message &msg) { + virtual void onMessages(const std::list<omx_message> &messages) { Parcel data, reply; - data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor()); - data.write(&msg, sizeof(msg)); - if (msg.fenceFd >= 0) { - data.writeFileDescriptor(msg.fenceFd, true /* takeOwnership */); + std::list<omx_message>::const_iterator it = messages.cbegin(); + bool first = true; + while (it != messages.cend()) { + const omx_message &msg = *it++; + if (first) { + data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor()); + data.writeInt32(msg.node); + first = false; + } + data.writeInt32(msg.fenceFd >= 0); + if (msg.fenceFd >= 0) { + data.writeFileDescriptor(msg.fenceFd, true /* takeOwnership */); + } + data.writeInt32(msg.type); + data.write(&msg.u, sizeof(msg.u)); + ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg)); + } + if (!first) { + data.writeInt32(-1); // mark end + remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY); } - ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg)); - - remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY); } }; @@ -1098,19 +1111,28 @@ status_t BnOMXObserver::onTransact( case OBSERVER_ON_MSG: { CHECK_OMX_INTERFACE(IOMXObserver, data, reply); + IOMX::node_id node = data.readInt32(); + std::list<omx_message> messages; + status_t err = FAILED_TRANSACTION; // must receive at least one message + do { + int haveFence = data.readInt32(); + if (haveFence < 0) { // we use -1 to mark end of messages + break; + } + omx_message msg; + msg.node = node; + msg.fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + msg.type = (typeof(msg.type))data.readInt32(); + err = data.read(&msg.u, sizeof(msg.u)); + ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg)); + messages.push_back(msg); + } while (err == OK); - omx_message msg; - data.read(&msg, sizeof(msg)); - if (msg.fenceFd >= 0) { - msg.fenceFd = ::dup(data.readFileDescriptor()); + if (err == OK) { + onMessages(messages); } - ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg)); - - // XXX Could use readInplace maybe? - onMessage(msg); - - return NO_ERROR; + return err; } default: diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 3a98e8c..172e19c 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -106,6 +106,16 @@ static void InitOMXParams(T *params) { params->nVersion.s.nStep = 0; } +struct MessageList : public RefBase { + MessageList() { + } + std::list<sp<AMessage> > &getList() { return mList; } +private: + std::list<sp<AMessage> > mList; + + DISALLOW_EVIL_CONSTRUCTORS(MessageList); +}; + struct CodecObserver : public BnOMXObserver { CodecObserver() {} @@ -114,55 +124,65 @@ struct CodecObserver : public BnOMXObserver { } // from IOMXObserver - virtual void onMessage(const omx_message &omx_msg) { - sp<AMessage> msg = mNotify->dup(); - - msg->setInt32("type", omx_msg.type); - msg->setInt32("node", omx_msg.node); - - switch (omx_msg.type) { - case omx_message::EVENT: - { - msg->setInt32("event", omx_msg.u.event_data.event); - msg->setInt32("data1", omx_msg.u.event_data.data1); - msg->setInt32("data2", omx_msg.u.event_data.data2); - break; + virtual void onMessages(const std::list<omx_message> &messages) { + sp<AMessage> notify; + bool first = true; + sp<MessageList> msgList = new MessageList(); + for (std::list<omx_message>::const_iterator it = messages.cbegin(); + it != messages.cend(); ++it) { + const omx_message &omx_msg = *it; + if (first) { + notify = mNotify->dup(); + notify->setInt32("node", omx_msg.node); } - case omx_message::EMPTY_BUFFER_DONE: - { - msg->setInt32("buffer", omx_msg.u.buffer_data.buffer); - msg->setInt32("fence_fd", omx_msg.fenceFd); - break; - } + sp<AMessage> msg = new AMessage; + msg->setInt32("type", omx_msg.type); + switch (omx_msg.type) { + case omx_message::EVENT: + { + msg->setInt32("event", omx_msg.u.event_data.event); + msg->setInt32("data1", omx_msg.u.event_data.data1); + msg->setInt32("data2", omx_msg.u.event_data.data2); + break; + } - case omx_message::FILL_BUFFER_DONE: - { - msg->setInt32( - "buffer", omx_msg.u.extended_buffer_data.buffer); - msg->setInt32( - "range_offset", - omx_msg.u.extended_buffer_data.range_offset); - msg->setInt32( - "range_length", - omx_msg.u.extended_buffer_data.range_length); - msg->setInt32( - "flags", - omx_msg.u.extended_buffer_data.flags); - msg->setInt64( - "timestamp", - omx_msg.u.extended_buffer_data.timestamp); - msg->setInt32( - "fence_fd", omx_msg.fenceFd); - break; - } + case omx_message::EMPTY_BUFFER_DONE: + { + msg->setInt32("buffer", omx_msg.u.buffer_data.buffer); + msg->setInt32("fence_fd", omx_msg.fenceFd); + break; + } - default: - ALOGE("Unrecognized message type: %d", omx_msg.type); - break; - } + case omx_message::FILL_BUFFER_DONE: + { + msg->setInt32( + "buffer", omx_msg.u.extended_buffer_data.buffer); + msg->setInt32( + "range_offset", + omx_msg.u.extended_buffer_data.range_offset); + msg->setInt32( + "range_length", + omx_msg.u.extended_buffer_data.range_length); + msg->setInt32( + "flags", + omx_msg.u.extended_buffer_data.flags); + msg->setInt64( + "timestamp", + omx_msg.u.extended_buffer_data.timestamp); + msg->setInt32( + "fence_fd", omx_msg.fenceFd); + break; + } - msg->post(); + default: + ALOGE("Unrecognized message type: %d", omx_msg.type); + break; + } + msgList->getList().push_back(msg); + } + notify->setObject("messages", msgList); + notify->post(); } protected: @@ -200,8 +220,15 @@ protected: void postFillThisBuffer(BufferInfo *info); private: + // Handles an OMX message. Returns true iff message was handled. bool onOMXMessage(const sp<AMessage> &msg); + // Handles a list of messages. Returns true iff messages were handled. + bool onOMXMessageList(const sp<AMessage> &msg); + + // returns true iff this message is for this component and the component is alive + bool checkOMXMessage(const sp<AMessage> &msg); + bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd); bool onOMXFillBufferDone( @@ -4402,9 +4429,14 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { break; } + case ACodec::kWhatOMXMessageList: + { + return checkOMXMessage(msg) ? onOMXMessageList(msg) : true; + } + case ACodec::kWhatOMXMessage: { - return onOMXMessage(msg); + return checkOMXMessage(msg) ? onOMXMessage(msg) : true; } case ACodec::kWhatSetSurface: @@ -4463,16 +4495,13 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { return true; } -bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { - int32_t type; - CHECK(msg->findInt32("type", &type)); - +bool ACodec::BaseState::checkOMXMessage(const sp<AMessage> &msg) { // there is a possibility that this is an outstanding message for a // codec that we have already destroyed if (mCodec->mNode == 0) { ALOGI("ignoring message as already freed component: %s", msg->debugString().c_str()); - return true; + return false; } IOMX::node_id nodeID; @@ -4481,6 +4510,24 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { ALOGE("Unexpected message for nodeID: %u, should have been %u", nodeID, mCodec->mNode); return false; } + return true; +} + +bool ACodec::BaseState::onOMXMessageList(const sp<AMessage> &msg) { + sp<RefBase> obj; + CHECK(msg->findObject("messages", &obj)); + sp<MessageList> msgList = static_cast<MessageList *>(obj.get()); + + for (std::list<sp<AMessage>>::const_iterator it = msgList->getList().cbegin(); + it != msgList->getList().cend(); ++it) { + onOMXMessage(*it); + } + return true; +} + +bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { + int32_t type; + CHECK(msg->findInt32("type", &type)); switch (type) { case omx_message::EVENT: @@ -5316,7 +5363,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { return false; } - notify = new AMessage(kWhatOMXMessage, mCodec); + notify = new AMessage(kWhatOMXMessageList, mCodec); observer->setNotificationMessage(notify); mCodec->mComponentName = componentName; diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 95f361e..867ce0b 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -385,6 +385,13 @@ MPEG4Writer::MPEG4Writer(int fd) mStartTimeOffsetMs(-1), mMetaKeys(new AMessage()) { addDeviceMeta(); + + // Verify mFd is seekable + off64_t off = lseek64(mFd, 0, SEEK_SET); + if (off < 0) { + ALOGE("cannot seek mFd: %s (%d)", strerror(errno), errno); + release(); + } } MPEG4Writer::~MPEG4Writer() { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 927cc6c..96aa808 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -116,12 +116,15 @@ struct OMXCodecObserver : public BnOMXObserver { } // from IOMXObserver - virtual void onMessage(const omx_message &msg) { + virtual void onMessages(const std::list<omx_message> &messages) { sp<OMXCodec> codec = mTarget.promote(); if (codec.get() != NULL) { Mutex::Autolock autoLock(codec->mLock); - codec->on_message(msg); + for (std::list<omx_message>::const_iterator it = messages.cbegin(); + it != messages.cend(); ++it) { + codec->on_message(*it); + } codec.clear(); } } diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 4297549..2451641 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -21,6 +21,7 @@ #include "include/OggExtractor.h" #include <cutils/properties.h> +#include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaBuffer.h> @@ -65,24 +66,28 @@ private: OggSource &operator=(const OggSource &); }; -struct MyVorbisExtractor { - MyVorbisExtractor(const sp<DataSource> &source); - virtual ~MyVorbisExtractor(); +struct MyOggExtractor { + MyOggExtractor( + const sp<DataSource> &source, + const char *mimeType, + size_t numHeaders, + int64_t seekPreRollUs); + virtual ~MyOggExtractor(); sp<MetaData> getFormat() const; // Returns an approximate bitrate in bits per second. - uint64_t approxBitrate(); + virtual uint64_t approxBitrate() const = 0; status_t seekToTime(int64_t timeUs); status_t seekToOffset(off64_t offset); - status_t readNextPacket(MediaBuffer **buffer, bool conf); + virtual status_t readNextPacket(MediaBuffer **buffer) = 0; status_t init(); sp<MetaData> getFileMetaData() { return mFileMeta; } -private: +protected: struct Page { uint64_t mGranulePosition; int32_t mPrevPacketSize; @@ -102,12 +107,17 @@ private: sp<DataSource> mSource; off64_t mOffset; Page mCurrentPage; + uint64_t mCurGranulePosition; uint64_t mPrevGranulePosition; size_t mCurrentPageSize; bool mFirstPacketInPage; uint64_t mCurrentPageSamples; size_t mNextLaceIndex; + const char *mMimeType; + size_t mNumHeaders; + int64_t mSeekPreRollUs; + off64_t mFirstDataOffset; vorbis_info mVi; @@ -121,10 +131,26 @@ private: ssize_t readPage(off64_t offset, Page *page); status_t findNextPage(off64_t startOffset, off64_t *pageOffset); - status_t verifyHeader( - MediaBuffer *buffer, uint8_t type); + virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const = 0; + + // Extract codec format, metadata tags, and various codec specific data; + // the format and CSD's are required to setup the decoders for the enclosed media content. + // + // Valid values for `type` are: + // 1 - bitstream identification header + // 3 - comment header + // 5 - codec setup header (Vorbis only) + virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type) = 0; + + // Read the next ogg packet from the underlying data source; optionally + // calculate the timestamp for the output packet whilst pretending + // that we are parsing an Ogg Vorbis stream. + // + // *buffer is NULL'ed out immediately upon entry, and if successful a new buffer is allocated; + // clients are responsible for releasing the original buffer. + status_t _readNextPacket(MediaBuffer **buffer, bool calcVorbisTimestamp); - int32_t packetBlockSize(MediaBuffer *buffer); + int32_t getPacketBlockSize(MediaBuffer *buffer); void parseFileMetaData(); @@ -132,8 +158,59 @@ private: void buildTableOfContents(); - MyVorbisExtractor(const MyVorbisExtractor &); - MyVorbisExtractor &operator=(const MyVorbisExtractor &); + MyOggExtractor(const MyOggExtractor &); + MyOggExtractor &operator=(const MyOggExtractor &); +}; + +struct MyVorbisExtractor : public MyOggExtractor { + MyVorbisExtractor(const sp<DataSource> &source) + : MyOggExtractor(source, + MEDIA_MIMETYPE_AUDIO_VORBIS, + /* numHeaders */ 3, + /* seekPreRollUs */ 0) { + } + + virtual uint64_t approxBitrate() const; + + virtual status_t readNextPacket(MediaBuffer **buffer) { + return _readNextPacket(buffer, /* calcVorbisTimestamp = */ true); + } + +protected: + virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const { + return granulePos * 1000000ll / mVi.rate; + } + + virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type); +}; + +struct MyOpusExtractor : public MyOggExtractor { + static const int32_t kOpusSampleRate = 48000; + static const int64_t kOpusSeekPreRollUs = 80000; // 80 ms + + MyOpusExtractor(const sp<DataSource> &source) + : MyOggExtractor(source, MEDIA_MIMETYPE_AUDIO_OPUS, /*numHeaders*/ 2, kOpusSeekPreRollUs), + mChannelCount(0), + mCodecDelay(0) { + } + + virtual uint64_t approxBitrate() const { + return 0; + } + + virtual status_t readNextPacket(MediaBuffer **buffer); + +protected: + virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const; + virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type); + +private: + status_t verifyOpusHeader(MediaBuffer *buffer); + status_t verifyOpusComments(MediaBuffer *buffer); + uint32_t getNumSamplesInPacket(MediaBuffer *buffer) const; + + uint8_t mChannelCount; + uint16_t mCodecDelay; }; static void extractAlbumArt( @@ -179,13 +256,14 @@ status_t OggSource::read( int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { - if (mExtractor->mImpl->seekToTime(seekTimeUs) != OK) { - return ERROR_END_OF_STREAM; + status_t err = mExtractor->mImpl->seekToTime(seekTimeUs); + if (err != OK) { + return err; } } MediaBuffer *packet; - status_t err = mExtractor->mImpl->readNextPacket(&packet, /* conf = */ false); + status_t err = mExtractor->mImpl->readNextPacket(&packet); if (err != OK) { return err; @@ -209,14 +287,22 @@ status_t OggSource::read( //////////////////////////////////////////////////////////////////////////////// -MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source) +MyOggExtractor::MyOggExtractor( + const sp<DataSource> &source, + const char *mimeType, + size_t numHeaders, + int64_t seekPreRollUs) : mSource(source), mOffset(0), + mCurGranulePosition(0), mPrevGranulePosition(0), mCurrentPageSize(0), mFirstPacketInPage(true), mCurrentPageSamples(0), mNextLaceIndex(0), + mMimeType(mimeType), + mNumHeaders(numHeaders), + mSeekPreRollUs(seekPreRollUs), mFirstDataOffset(-1) { mCurrentPage.mNumSegments = 0; @@ -224,16 +310,16 @@ MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source) vorbis_comment_init(&mVc); } -MyVorbisExtractor::~MyVorbisExtractor() { +MyOggExtractor::~MyOggExtractor() { vorbis_comment_clear(&mVc); vorbis_info_clear(&mVi); } -sp<MetaData> MyVorbisExtractor::getFormat() const { +sp<MetaData> MyOggExtractor::getFormat() const { return mMeta; } -status_t MyVorbisExtractor::findNextPage( +status_t MyOggExtractor::findNextPage( off64_t startOffset, off64_t *pageOffset) { *pageOffset = startOffset; @@ -264,7 +350,7 @@ status_t MyVorbisExtractor::findNextPage( // it (if any) and return its granule position. // To do this we back up from the "current" page's offset until we find any // page preceding it and then scan forward to just before the current page. -status_t MyVorbisExtractor::findPrevGranulePosition( +status_t MyOggExtractor::findPrevGranulePosition( off64_t pageOffset, uint64_t *granulePos) { *granulePos = 0; @@ -280,7 +366,11 @@ status_t MyVorbisExtractor::findPrevGranulePosition( ALOGV("backing up %lld bytes", (long long)(pageOffset - prevGuess)); status_t err = findNextPage(prevGuess, &prevPageOffset); - if (err != OK) { + if (err == ERROR_END_OF_STREAM) { + // We are at the last page and didn't back off enough; + // back off 5000 bytes more and try again. + continue; + } else if (err != OK) { return err; } @@ -314,11 +404,20 @@ status_t MyVorbisExtractor::findPrevGranulePosition( } } -status_t MyVorbisExtractor::seekToTime(int64_t timeUs) { +status_t MyOggExtractor::seekToTime(int64_t timeUs) { + timeUs -= mSeekPreRollUs; + if (timeUs < 0) { + timeUs = 0; + } + if (mTableOfContents.isEmpty()) { // Perform approximate seeking based on avg. bitrate. + uint64_t bps = approxBitrate(); + if (bps <= 0) { + return INVALID_OPERATION; + } - off64_t pos = timeUs * approxBitrate() / 8000000ll; + off64_t pos = timeUs * bps / 8000000ll; ALOGV("seeking to offset %lld", (long long)pos); return seekToOffset(pos); @@ -353,7 +452,7 @@ status_t MyVorbisExtractor::seekToTime(int64_t timeUs) { return seekToOffset(entry.mPageOffset); } -status_t MyVorbisExtractor::seekToOffset(off64_t offset) { +status_t MyOggExtractor::seekToOffset(off64_t offset) { if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) { // Once we know where the actual audio data starts (past the headers) // don't ever seek to anywhere before that. @@ -386,7 +485,7 @@ status_t MyVorbisExtractor::seekToOffset(off64_t offset) { return OK; } -ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) { +ssize_t MyOggExtractor::readPage(off64_t offset, Page *page) { uint8_t header[27]; ssize_t n; if ((n = mSource->readAt(offset, header, sizeof(header))) @@ -457,7 +556,75 @@ ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) { return sizeof(header) + page->mNumSegments + totalSize; } -status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out, bool conf) { +status_t MyOpusExtractor::readNextPacket(MediaBuffer **out) { + status_t err = _readNextPacket(out, /* calcVorbisTimestamp = */false); + if (err != OK) { + return err; + } + + int32_t currentPageSamples; + // Calculate timestamps by accumulating durations starting from the first sample of a page; + // We assume that we only seek to page boundaries. + if ((*out)->meta_data()->findInt32(kKeyValidSamples, ¤tPageSamples)) { + // first packet in page + mCurGranulePosition = mCurrentPage.mGranulePosition - currentPageSamples; + } + + int64_t timeUs = getTimeUsOfGranule(mCurGranulePosition); + (*out)->meta_data()->setInt64(kKeyTime, timeUs); + + uint32_t frames = getNumSamplesInPacket(*out); + mCurGranulePosition += frames; + return OK; +} + +uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBuffer *buffer) const { + if (buffer == NULL || buffer->range_length() < 1) { + return 0; + } + + uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset(); + uint8_t toc = data[0]; + uint8_t config = (toc >> 3) & 0x1f; + uint32_t frameSizesUs[] = { + 10000, 20000, 40000, 60000, // 0...3 + 10000, 20000, 40000, 60000, // 4...7 + 10000, 20000, 40000, 60000, // 8...11 + 10000, 20000, // 12...13 + 10000, 20000, // 14...15 + 2500, 5000, 10000, 20000, // 16...19 + 2500, 5000, 10000, 20000, // 20...23 + 2500, 5000, 10000, 20000, // 24...27 + 2500, 5000, 10000, 20000 // 28...31 + }; + uint32_t frameSizeUs = frameSizesUs[config]; + + uint32_t numFrames; + uint8_t c = toc & 3; + switch (c) { + case 0: + numFrames = 1; + break; + case 1: + case 2: + numFrames = 2; + break; + case 3: + if (buffer->range_length() < 3) { + numFrames = 0; + } else { + numFrames = data[2] & 0x3f; + } + break; + default: + TRESPASS(); + } + + uint32_t numSamples = frameSizeUs * numFrames * kOpusSampleRate / 1000000; + return numSamples; +} + +status_t MyOggExtractor::_readNextPacket(MediaBuffer **out, bool calcVorbisTimestamp) { *out = NULL; MediaBuffer *buffer = NULL; @@ -523,9 +690,8 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out, bool conf) { mFirstPacketInPage = false; } - // ignore timestamp for configuration packets - if (!conf) { - int32_t curBlockSize = packetBlockSize(buffer); + if (calcVorbisTimestamp) { + int32_t curBlockSize = getPacketBlockSize(buffer); if (mCurrentPage.mPrevPacketSize < 0) { mCurrentPage.mPrevPacketSize = curBlockSize; mCurrentPage.mPrevPacketPos = @@ -597,43 +763,24 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out, bool conf) { } } -status_t MyVorbisExtractor::init() { +status_t MyOggExtractor::init() { mMeta = new MetaData; - mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); + mMeta->setCString(kKeyMIMEType, mMimeType); - MediaBuffer *packet; status_t err; - if ((err = readNextPacket(&packet, /* conf = */ true)) != OK) { - return err; - } - ALOGV("read packet of size %zu\n", packet->range_length()); - err = verifyHeader(packet, 1); - packet->release(); - packet = NULL; - if (err != OK) { - return err; - } - - if ((err = readNextPacket(&packet, /* conf = */ true)) != OK) { - return err; - } - ALOGV("read packet of size %zu\n", packet->range_length()); - err = verifyHeader(packet, 3); - packet->release(); - packet = NULL; - if (err != OK) { - return err; - } - - if ((err = readNextPacket(&packet, /* conf = */ true)) != OK) { - return err; - } - ALOGV("read packet of size %zu\n", packet->range_length()); - err = verifyHeader(packet, 5); - packet->release(); - packet = NULL; - if (err != OK) { - return err; + MediaBuffer *packet; + for (size_t i = 0; i < mNumHeaders; ++i) { + // ignore timestamp for configuration packets + if ((err = _readNextPacket(&packet, /* calcVorbisTimestamp = */ false)) != OK) { + return err; + } + ALOGV("read packet of size %zu\n", packet->range_length()); + err = verifyHeader(packet, /* type = */ i * 2 + 1); + packet->release(); + packet = NULL; + if (err != OK) { + return err; + } } mFirstDataOffset = mOffset + mCurrentPageSize; @@ -649,7 +796,7 @@ status_t MyVorbisExtractor::init() { // we can only approximate using avg. bitrate if seeking to // the end is too expensive or impossible (live streaming). - int64_t durationUs = lastGranulePosition * 1000000ll / mVi.rate; + int64_t durationUs = getTimeUsOfGranule(lastGranulePosition); mMeta->setInt64(kKeyDuration, durationUs); @@ -659,7 +806,7 @@ status_t MyVorbisExtractor::init() { return OK; } -void MyVorbisExtractor::buildTableOfContents() { +void MyOggExtractor::buildTableOfContents() { off64_t offset = mFirstDataOffset; Page page; ssize_t pageSize; @@ -670,7 +817,7 @@ void MyVorbisExtractor::buildTableOfContents() { mTableOfContents.editItemAt(mTableOfContents.size() - 1); entry.mPageOffset = offset; - entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate; + entry.mTimeUs = getTimeUsOfGranule(page.mGranulePosition); offset += (size_t)pageSize; } @@ -698,7 +845,7 @@ void MyVorbisExtractor::buildTableOfContents() { } } -int32_t MyVorbisExtractor::packetBlockSize(MediaBuffer *buffer) { +int32_t MyOggExtractor::getPacketBlockSize(MediaBuffer *buffer) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -727,6 +874,144 @@ int32_t MyVorbisExtractor::packetBlockSize(MediaBuffer *buffer) { return vorbis_packet_blocksize(&mVi, &pack); } +int64_t MyOpusExtractor::getTimeUsOfGranule(uint64_t granulePos) const { + uint64_t pcmSamplePosition = 0; + if (granulePos > mCodecDelay) { + pcmSamplePosition = granulePos - mCodecDelay; + } + return pcmSamplePosition * 1000000ll / kOpusSampleRate; +} + +status_t MyOpusExtractor::verifyHeader(MediaBuffer *buffer, uint8_t type) { + switch (type) { + // there are actually no header types defined in the Opus spec; we choose 1 and 3 to mean + // header and comments such that we can share code with MyVorbisExtractor. + case 1: + return verifyOpusHeader(buffer); + case 3: + return verifyOpusComments(buffer); + default: + return INVALID_OPERATION; + } +} + +status_t MyOpusExtractor::verifyOpusHeader(MediaBuffer *buffer) { + const size_t kOpusHeaderSize = 19; + const uint8_t *data = + (const uint8_t *)buffer->data() + buffer->range_offset(); + + size_t size = buffer->range_length(); + + if (size < kOpusHeaderSize + || memcmp(data, "OpusHead", 8) + || /* version = */ data[8] != 1) { + return ERROR_MALFORMED; + } + + mChannelCount = data[9]; + mCodecDelay = U16LE_AT(&data[10]); + + mMeta->setData(kKeyOpusHeader, 0, data, size); + mMeta->setInt32(kKeySampleRate, kOpusSampleRate); + mMeta->setInt32(kKeyChannelCount, mChannelCount); + mMeta->setInt64(kKeyOpusSeekPreRoll /* ns */, kOpusSeekPreRollUs * 1000 /* = 80 ms*/); + mMeta->setInt64(kKeyOpusCodecDelay /* ns */, + mCodecDelay /* sample/s */ * 1000000000 / kOpusSampleRate); + + return OK; +} + +status_t MyOpusExtractor::verifyOpusComments(MediaBuffer *buffer) { + // add artificial framing bit so we can reuse _vorbis_unpack_comment + int32_t commentSize = buffer->range_length() + 1; + sp<ABuffer> aBuf = new ABuffer(commentSize); + if (aBuf->capacity() <= buffer->range_length()) { + return ERROR_MALFORMED; + } + + uint8_t* commentData = aBuf->data(); + memcpy(commentData, + (uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length()); + + ogg_buffer buf; + buf.data = commentData; + buf.size = commentSize; + buf.refcount = 1; + buf.ptr.owner = NULL; + + ogg_reference ref; + ref.buffer = &buf; + ref.begin = 0; + ref.length = commentSize; + ref.next = NULL; + + oggpack_buffer bits; + oggpack_readinit(&bits, &ref); + + // skip 'OpusTags' + const char *OpusTags = "OpusTags"; + const int32_t headerLen = strlen(OpusTags); + int32_t framingBitOffset = headerLen; + for (int i = 0; i < headerLen; ++i) { + char chr = oggpack_read(&bits, 8); + if (chr != OpusTags[i]) { + return ERROR_MALFORMED; + } + } + + int32_t vendorLen = oggpack_read(&bits, 32); + framingBitOffset += 4; + if (vendorLen < 0 || vendorLen > commentSize - 8) { + return ERROR_MALFORMED; + } + // skip vendor string + framingBitOffset += vendorLen; + for (int i = 0; i < vendorLen; ++i) { + oggpack_read(&bits, 8); + } + + int32_t n = oggpack_read(&bits, 32); + framingBitOffset += 4; + if (n < 0 || n > ((commentSize - oggpack_bytes(&bits)) >> 2)) { + return ERROR_MALFORMED; + } + for (int i = 0; i < n; ++i) { + int32_t len = oggpack_read(&bits, 32); + framingBitOffset += 4; + if (len < 0 || len > (commentSize - oggpack_bytes(&bits))) { + return ERROR_MALFORMED; + } + framingBitOffset += len; + for (int j = 0; j < len; ++j) { + oggpack_read(&bits, 8); + } + } + if (framingBitOffset < 0 || framingBitOffset >= commentSize) { + return ERROR_MALFORMED; + } + commentData[framingBitOffset] = 1; + + buf.data = commentData + headerLen; + buf.size = commentSize - headerLen; + buf.refcount = 1; + buf.ptr.owner = NULL; + + ref.buffer = &buf; + ref.begin = 0; + ref.length = commentSize - headerLen; + ref.next = NULL; + + oggpack_readinit(&bits, &ref); + int err = _vorbis_unpack_comment(&mVc, &bits); + if (0 != err) { + return ERROR_MALFORMED; + } + + parseFileMetaData(); + return OK; +} + status_t MyVorbisExtractor::verifyHeader( MediaBuffer *buffer, uint8_t type) { const uint8_t *data = @@ -814,7 +1099,7 @@ status_t MyVorbisExtractor::verifyHeader( return OK; } -uint64_t MyVorbisExtractor::approxBitrate() { +uint64_t MyVorbisExtractor::approxBitrate() const { if (mVi.bitrate_nominal != 0) { return mVi.bitrate_nominal; } @@ -822,7 +1107,7 @@ uint64_t MyVorbisExtractor::approxBitrate() { return (mVi.bitrate_lower + mVi.bitrate_upper) / 2; } -void MyVorbisExtractor::parseFileMetaData() { +void MyOggExtractor::parseFileMetaData() { mFileMeta = new MetaData; mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG); @@ -1026,11 +1311,23 @@ OggExtractor::OggExtractor(const sp<DataSource> &source) : mDataSource(source), mInitCheck(NO_INIT), mImpl(NULL) { - mImpl = new MyVorbisExtractor(mDataSource); - mInitCheck = mImpl->seekToOffset(0); + for (int i = 0; i < 2; ++i) { + if (mImpl != NULL) { + delete mImpl; + } + if (i == 0) { + mImpl = new MyVorbisExtractor(mDataSource); + } else { + mImpl = new MyOpusExtractor(mDataSource); + } + mInitCheck = mImpl->seekToOffset(0); - if (mInitCheck == OK) { - mInitCheck = mImpl->init(); + if (mInitCheck == OK) { + mInitCheck = mImpl->init(); + if (mInitCheck == OK) { + break; + } + } } } diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index 76df815..f68e0a9 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -125,6 +125,8 @@ struct OMXNodeInstance { const void *data, size_t size); + // handles messages and removes them from the list + void onMessages(std::list<omx_message> &messages); void onMessage(const omx_message &msg); void onObserverDied(OMXMaster *master); void onGetHandleFailed(); @@ -231,6 +233,10 @@ private: sp<GraphicBufferSource> getGraphicBufferSource(); void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource); + // Handles |msg|, and may modify it. Returns true iff completely handled it and + // |msg| does not need to be sent to the event listener. + bool handleMessage(omx_message &msg); + OMXNodeInstance(const OMXNodeInstance &); OMXNodeInstance &operator=(const OMXNodeInstance &); }; diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h index e97c8cd..c647cbb 100644 --- a/media/libstagefright/include/OggExtractor.h +++ b/media/libstagefright/include/OggExtractor.h @@ -27,7 +27,7 @@ struct AMessage; class DataSource; class String8; -struct MyVorbisExtractor; +struct MyOggExtractor; struct OggSource; struct OggExtractor : public MediaExtractor { @@ -48,7 +48,7 @@ private: sp<DataSource> mDataSource; status_t mInitCheck; - MyVorbisExtractor *mImpl; + MyOggExtractor *mImpl; OggExtractor(const OggExtractor &); OggExtractor &operator=(const OggExtractor &); diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 76217ec..e94adbd 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -61,7 +61,11 @@ private: struct OMX::CallbackDispatcher : public RefBase { CallbackDispatcher(OMXNodeInstance *owner); - void post(const omx_message &msg); + // Posts |msg| to the listener's queue. If |realTime| is true, the listener thread is notified + // that a new message is available on the queue. Otherwise, the message stays on the queue, but + // the listener is not notified of it. It will process this message when a subsequent message + // is posted with |realTime| set to true. + void post(const omx_message &msg, bool realTime = true); bool loop(); @@ -74,11 +78,11 @@ private: OMXNodeInstance *mOwner; bool mDone; Condition mQueueChanged; - List<omx_message> mQueue; + std::list<omx_message> mQueue; sp<CallbackDispatcherThread> mThread; - void dispatch(const omx_message &msg); + void dispatch(std::list<omx_message> &messages); CallbackDispatcher(const CallbackDispatcher &); CallbackDispatcher &operator=(const CallbackDispatcher &); @@ -109,24 +113,26 @@ OMX::CallbackDispatcher::~CallbackDispatcher() { } } -void OMX::CallbackDispatcher::post(const omx_message &msg) { +void OMX::CallbackDispatcher::post(const omx_message &msg, bool realTime) { Mutex::Autolock autoLock(mLock); mQueue.push_back(msg); - mQueueChanged.signal(); + if (realTime) { + mQueueChanged.signal(); + } } -void OMX::CallbackDispatcher::dispatch(const omx_message &msg) { +void OMX::CallbackDispatcher::dispatch(std::list<omx_message> &messages) { if (mOwner == NULL) { ALOGV("Would have dispatched a message to a node that's already gone."); return; } - mOwner->onMessage(msg); + mOwner->onMessages(messages); } bool OMX::CallbackDispatcher::loop() { for (;;) { - omx_message msg; + std::list<omx_message> messages; { Mutex::Autolock autoLock(mLock); @@ -138,11 +144,10 @@ bool OMX::CallbackDispatcher::loop() { break; } - msg = *mQueue.begin(); - mQueue.erase(mQueue.begin()); + messages.swap(mQueue); } - dispatch(msg); + dispatch(messages); } return false; diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 9e399f9..7e92da8 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -1357,7 +1357,7 @@ status_t OMXNodeInstance::setInternalOption( } } -void OMXNodeInstance::onMessage(const omx_message &msg) { +bool OMXNodeInstance::handleMessage(omx_message &msg) { const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource()); if (msg.type == omx_message::FILL_BUFFER_DONE) { @@ -1384,10 +1384,7 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { // fix up the buffer info (especially timestamp) if needed bufferSource->codecBufferFilled(buffer); - omx_message newMsg = msg; - newMsg.u.extended_buffer_data.timestamp = buffer->nTimeStamp; - mObserver->onMessage(newMsg); - return; + msg.u.extended_buffer_data.timestamp = buffer->nTimeStamp; } } else if (msg.type == omx_message::EMPTY_BUFFER_DONE) { OMX_BUFFERHEADERTYPE *buffer = @@ -1408,11 +1405,23 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { // know that anyone asked to have the buffer emptied and will // be very confused. bufferSource->codecBufferEmptied(buffer, msg.fenceFd); - return; + return true; + } + } + + return false; +} + +void OMXNodeInstance::onMessages(std::list<omx_message> &messages) { + for (std::list<omx_message>::iterator it = messages.begin(); it != messages.end(); ) { + if (handleMessage(*it)) { + messages.erase(it++); + } else { + ++it; } } - mObserver->onMessage(msg); + mObserver->onMessages(messages); } void OMXNodeInstance::onObserverDied(OMXMaster *master) { diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index 294b2ed..644b6ed 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -64,9 +64,11 @@ status_t Harness::initOMX() { return mOMX != 0 ? OK : NO_INIT; } -void Harness::onMessage(const omx_message &msg) { +void Harness::onMessages(const std::list<omx_message> &messages) { Mutex::Autolock autoLock(mLock); - mMessageQueue.push_back(msg); + for (std::list<omx_message>::const_iterator it = messages.cbegin(); it != messages.cend(); ) { + mMessageQueue.push_back(*it++); + } mMessageAddedCondition.signal(); } diff --git a/media/libstagefright/omx/tests/OMXHarness.h b/media/libstagefright/omx/tests/OMXHarness.h index bb8fd0c..1ebf3aa 100644 --- a/media/libstagefright/omx/tests/OMXHarness.h +++ b/media/libstagefright/omx/tests/OMXHarness.h @@ -74,7 +74,7 @@ struct Harness : public BnOMXObserver { status_t testAll(); - virtual void onMessage(const omx_message &msg); + virtual void onMessages(const std::list<omx_message> &messages); protected: virtual ~Harness(); diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 6f073ed..f42fada 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -1864,7 +1864,8 @@ status_t CameraService::BasicClient::startCameraOps() { if (res == AppOpsManager::MODE_IGNORED) { ALOGI("Camera %d: Access for \"%s\" has been restricted", mCameraId, String8(mClientPackageName).string()); - return INVALID_OPERATION; + // Return the same error as for device policy manager rejection + return -EACCES; } mOpsActive = true; |