diff options
25 files changed, 742 insertions, 145 deletions
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h index 0488292..a006f7f 100644 --- a/include/media/stagefright/MediaBufferGroup.h +++ b/include/media/stagefright/MediaBufferGroup.h @@ -34,9 +34,12 @@ public: void add_buffer(MediaBuffer *buffer); - // Blocks until a buffer is available and returns it to the caller, - // the returned buffer will have a reference count of 1. - status_t acquire_buffer(MediaBuffer **buffer); + // If nonBlocking is false, it blocks until a buffer is available and + // passes it to the caller in *buffer, while returning OK. + // The returned buffer will have a reference count of 1. + // If nonBlocking is true and a buffer is not immediately available, + // buffer is set to NULL and it returns WOULD_BLOCK. + status_t acquire_buffer(MediaBuffer **buffer, bool nonBlocking = false); protected: virtual void signalBufferReturned(MediaBuffer *buffer); diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h index 01a5daf..c11fcc9 100644 --- a/include/media/stagefright/MediaCodecList.h +++ b/include/media/stagefright/MediaCodecList.h @@ -25,9 +25,12 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Vector.h> +#include <utils/StrongPointer.h> namespace android { +struct AMessage; + struct MediaCodecList { static const MediaCodecList *getInstance(); @@ -51,15 +54,19 @@ struct MediaCodecList { size_t index, const char *type, Vector<ProfileLevel> *profileLevels, Vector<uint32_t> *colorFormats, - uint32_t *flags) const; + uint32_t *flags, + // TODO default argument is only for compatibility with existing JNI + sp<AMessage> *capabilities = NULL) const; private: enum Section { SECTION_TOPLEVEL, SECTION_DECODERS, SECTION_DECODER, + SECTION_DECODER_TYPE, SECTION_ENCODERS, SECTION_ENCODER, + SECTION_ENCODER_TYPE, SECTION_INCLUDE, }; @@ -67,7 +74,10 @@ private: AString mName; bool mIsEncoder; uint32_t mTypes; + uint32_t mSoleType; uint32_t mQuirks; + KeyedVector<uint32_t, sp<AMessage> > mCaps; + sp<AMessage> mCurrentCaps; }; static MediaCodecList *sCodecList; @@ -103,6 +113,8 @@ private: status_t addQuirk(const char **attrs); status_t addTypeFromAttributes(const char **attrs); + status_t addLimit(const char **attrs); + status_t addFeature(const char **attrs); void addType(const char *name); DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList); diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h index 204d1c6..a653db9 100644 --- a/include/media/stagefright/MediaSource.h +++ b/include/media/stagefright/MediaSource.h @@ -82,6 +82,10 @@ struct MediaSource : public virtual RefBase { void setLateBy(int64_t lateness_us); int64_t getLateBy() const; + void setNonBlocking(); + void clearNonBlocking(); + bool getNonBlocking() const; + private: enum Options { kSeekTo_Option = 1, @@ -91,6 +95,7 @@ struct MediaSource : public virtual RefBase { int64_t mSeekTimeUs; SeekMode mSeekMode; int64_t mLatenessUs; + bool mNonBlocking; }; // Causes this source to suspend pulling data from its upstream source diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h index 7e823eb..5846d6b 100644 --- a/include/media/stagefright/foundation/AMessage.h +++ b/include/media/stagefright/foundation/AMessage.h @@ -50,6 +50,7 @@ struct AMessage : public RefBase { void setDouble(const char *name, double value); void setPointer(const char *name, void *value); void setString(const char *name, const char *s, ssize_t len = -1); + void setString(const char *name, const AString &s); void setObject(const char *name, const sp<RefBase> &obj); void setBuffer(const char *name, const sp<ABuffer> &buffer); void setMessage(const char *name, const sp<AMessage> &obj); @@ -58,6 +59,8 @@ struct AMessage : public RefBase { const char *name, int32_t left, int32_t top, int32_t right, int32_t bottom); + bool contains(const char *name) const; + bool findInt32(const char *name, int32_t *value) const; bool findInt64(const char *name, int64_t *value) const; bool findSize(const char *name, size_t *value) const; diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h index 0edaa1c..4be3c6d 100644 --- a/include/media/stagefright/foundation/AString.h +++ b/include/media/stagefright/foundation/AString.h @@ -70,6 +70,9 @@ struct AString { size_t hash() const; bool operator==(const AString &other) const; + bool operator!=(const AString &other) const { + return !operator==(other); + } bool operator<(const AString &other) const; bool operator>(const AString &other) const; diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index cc0cb01..d75408d 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -28,6 +28,7 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include "../../libstagefright/include/WVMExtractor.h" namespace android { @@ -35,10 +36,16 @@ NuPlayer::GenericSource::GenericSource( const sp<AMessage> ¬ify, const sp<IMediaHTTPService> &httpService, const char *url, - const KeyedVector<String8, String8> *headers) + const KeyedVector<String8, String8> *headers, + bool isWidevine, + bool uidValid, + uid_t uid) : Source(notify), mDurationUs(0ll), - mAudioIsVorbis(false) { + mAudioIsVorbis(false), + mIsWidevine(isWidevine), + mUIDValid(uidValid), + mUID(uid) { DataSource::RegisterDefaultSniffers(); sp<DataSource> dataSource = @@ -63,7 +70,31 @@ NuPlayer::GenericSource::GenericSource( void NuPlayer::GenericSource::initFromDataSource( const sp<DataSource> &dataSource) { - sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + sp<MediaExtractor> extractor; + + if (mIsWidevine) { + String8 mimeType; + float confidence; + sp<AMessage> dummy; + bool success; + + success = SniffWVM(dataSource, &mimeType, &confidence, &dummy); + if (!success + || strcasecmp( + mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { + ALOGE("unsupported widevine mime: %s", mimeType.string()); + return; + } + + sp<WVMExtractor> wvmExtractor = new WVMExtractor(dataSource); + wvmExtractor->setAdaptiveStreamingMode(true); + if (mUIDValid) { + wvmExtractor->setUID(mUID); + } + extractor = wvmExtractor; + } else { + extractor = MediaExtractor::Create(dataSource); + } CHECK(extractor != NULL); @@ -113,6 +144,13 @@ void NuPlayer::GenericSource::initFromDataSource( } } +status_t NuPlayer::GenericSource::setBuffers(bool audio, Vector<MediaBuffer *> &buffers) { + if (mIsWidevine && !audio) { + return mVideoTrack.mSource->setBuffers(buffers); + } + return INVALID_OPERATION; +} + NuPlayer::GenericSource::~GenericSource() { } @@ -128,7 +166,8 @@ void NuPlayer::GenericSource::prepareAsync() { } notifyFlagsChanged( - FLAG_CAN_PAUSE + (mIsWidevine ? FLAG_SECURE : 0) + | FLAG_CAN_PAUSE | FLAG_CAN_SEEK_BACKWARD | FLAG_CAN_SEEK_FORWARD | FLAG_CAN_SEEK); @@ -180,9 +219,14 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit( return -EWOULDBLOCK; } + if (mIsWidevine && !audio) { + // try to read a buffer as we may not have been able to the last time + readBuffer(audio, -1ll); + } + status_t finalResult; if (!track->mPackets->hasBufferAvailable(&finalResult)) { - return finalResult == OK ? -EWOULDBLOCK : finalResult; + return (finalResult == OK ? -EWOULDBLOCK : finalResult); } status_t result = track->mPackets->dequeueAccessUnit(accessUnit); @@ -280,6 +324,10 @@ void NuPlayer::GenericSource::readBuffer( seeking = true; } + if (mIsWidevine && !audio) { + options.setNonBlocking(); + } + for (;;) { MediaBuffer *mbuf; status_t err = track->mSource->read(&mbuf, &options); @@ -293,11 +341,18 @@ void NuPlayer::GenericSource::readBuffer( outLength += sizeof(int32_t); } - sp<ABuffer> buffer = new ABuffer(outLength); - - memcpy(buffer->data(), - (const uint8_t *)mbuf->data() + mbuf->range_offset(), - mbuf->range_length()); + sp<ABuffer> buffer; + if (mIsWidevine && !audio) { + // data is already provided in the buffer + buffer = new ABuffer(NULL, mbuf->range_length()); + buffer->meta()->setPointer("mediaBuffer", mbuf); + mbuf->add_ref(); + } else { + buffer = new ABuffer(outLength); + memcpy(buffer->data(), + (const uint8_t *)mbuf->data() + mbuf->range_offset(), + mbuf->range_length()); + } if (audio && mAudioIsVorbis) { int32_t numPageSamples; @@ -332,6 +387,8 @@ void NuPlayer::GenericSource::readBuffer( track->mPackets->queueAccessUnit(buffer); break; + } else if (err == WOULD_BLOCK) { + break; } else if (err == INFO_FORMAT_CHANGED) { #if 0 track->mPackets->queueDiscontinuity( diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index e0cd20f..8e0209d 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -35,7 +35,10 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { const sp<AMessage> ¬ify, const sp<IMediaHTTPService> &httpService, const char *url, - const KeyedVector<String8, String8> *headers); + const KeyedVector<String8, String8> *headers, + bool isWidevine = false, + bool uidValid = false, + uid_t uid = 0); GenericSource( const sp<AMessage> ¬ify, @@ -54,6 +57,8 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { virtual sp<AMessage> getTrackInfo(size_t trackIndex) const; virtual status_t seekTo(int64_t seekTimeUs); + virtual status_t setBuffers(bool audio, Vector<MediaBuffer *> &buffers); + protected: virtual ~GenericSource(); @@ -73,6 +78,9 @@ private: int64_t mDurationUs; bool mAudioIsVorbis; + bool mIsWidevine; + bool mUIDValid; + uid_t mUID; void initFromDataSource(const sp<DataSource> &dataSource); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 88c59bf..6ccd27a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -36,6 +36,7 @@ #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> @@ -221,6 +222,10 @@ void NuPlayer::setDataSourceAsync( || strstr(url, ".sdp?"))) { source = new RTSPSource( notify, httpService, url, headers, mUIDValid, mUID, true); + } else if ((!strncasecmp(url, "widevine://", 11))) { + source = new GenericSource(notify, httpService, url, headers, + true /* isWidevine */, mUIDValid, mUID); + mSourceFlags |= Source::FLAG_SECURE; } else { source = new GenericSource(notify, httpService, url, headers); } @@ -512,6 +517,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { mNumFramesDropped = 0; mStarted = true; + /* instantiate decoders now for secure playback */ + if (mSourceFlags & Source::FLAG_SECURE) { + if (mNativeWindow != NULL) { + instantiateDecoder(false, &mVideoDecoder); + } + + if (mAudioSink != NULL) { + instantiateDecoder(true, &mAudioDecoder); + } + } + mSource->start(); uint32_t flags = 0; @@ -540,7 +556,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { new AMessage(kWhatRendererNotify, id()), flags); - looper()->registerHandler(mRenderer); + mRendererLooper = new ALooper; + mRendererLooper->setName("NuPlayerRenderer"); + mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO); + mRendererLooper->registerHandler(mRenderer); postScanSources(); break; @@ -1055,6 +1074,10 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id()); mCCDecoder = new CCDecoder(ccNotify); + + if (mSourceFlags & Source::FLAG_SECURE) { + format->setInt32("secure", true); + } } sp<AMessage> notify = @@ -1073,6 +1096,28 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { (*decoder)->init(); (*decoder)->configure(format); + // allocate buffers to decrypt widevine source buffers + if (!audio && (mSourceFlags & Source::FLAG_SECURE)) { + Vector<sp<ABuffer> > inputBufs; + CHECK_EQ((*decoder)->getInputBuffers(&inputBufs), (status_t)OK); + + Vector<MediaBuffer *> mediaBufs; + for (size_t i = 0; i < inputBufs.size(); i++) { + const sp<ABuffer> &buffer = inputBufs[i]; + MediaBuffer *mbuf = new MediaBuffer(buffer->data(), buffer->size()); + mediaBufs.push(mbuf); + } + + status_t err = mSource->setBuffers(audio, mediaBufs); + if (err != OK) { + for (size_t i = 0; i < mediaBufs.size(); ++i) { + mediaBufs[i]->release(); + } + mediaBufs.clear(); + ALOGE("Secure source didn't support secure mediaBufs."); + return err; + } + } return OK; } @@ -1184,6 +1229,7 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { dropAccessUnit = false; if (!audio + && !(mSourceFlags & Source::FLAG_SECURE) && mVideoLateByUs > 100000ll && mVideoIsAVC && !IsAVCReferenceFrame(accessUnit)) { @@ -1497,6 +1543,13 @@ void NuPlayer::performReset() { ++mScanSourcesGeneration; mScanSourcesPending = false; + if (mRendererLooper != NULL) { + if (mRenderer != NULL) { + mRendererLooper->unregisterHandler(mRenderer->id()); + } + mRendererLooper->stop(); + mRendererLooper.clear(); + } mRenderer.clear(); if (mSource != NULL) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index d7c00aa..c04e277 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -125,6 +125,7 @@ private: sp<Decoder> mAudioDecoder; sp<CCDecoder> mCCDecoder; sp<Renderer> mRenderer; + sp<ALooper> mRendererLooper; List<sp<Action> > mDeferredActions; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index dd73cc4..1b9bafb 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -26,6 +26,7 @@ #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaCodec.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> @@ -54,6 +55,22 @@ NuPlayer::Decoder::Decoder( NuPlayer::Decoder::~Decoder() { } +static +status_t PostAndAwaitResponse( + const sp<AMessage> &msg, sp<AMessage> *response) { + status_t err = msg->postAndAwaitResponse(response); + + if (err != OK) { + return err; + } + + if (!(*response)->findInt32("err", &err)) { + err = OK; + } + + return err; +} + void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK(mCodec == NULL); @@ -72,8 +89,20 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get()); mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */); + int32_t secure = 0; + if (format->findInt32("secure", &secure) && secure != 0) { + if (mCodec != NULL) { + mCodec->getName(&mComponentName); + mComponentName.append(".secure"); + mCodec->release(); + ALOGI("[%s] creating", mComponentName.c_str()); + mCodec = MediaCodec::CreateByComponentName( + mCodecLooper, mComponentName.c_str()); + } + } if (mCodec == NULL) { - ALOGE("Failed to create %s decoder", mime.c_str()); + ALOGE("Failed to create %s%s decoder", + (secure ? "secure " : ""), mime.c_str()); handleError(UNKNOWN_ERROR); return; } @@ -107,6 +136,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { // the following should work after start CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers)); + releaseAndResetMediaBuffers(); CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers)); ALOGV("[%s] got %zu input and %zu output buffers", mComponentName.c_str(), @@ -117,6 +147,18 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { mPaused = false; } +void NuPlayer::Decoder::releaseAndResetMediaBuffers() { + for (size_t i = 0; i < mMediaBuffers.size(); i++) { + if (mMediaBuffers[i] != NULL) { + mMediaBuffers[i]->release(); + mMediaBuffers.editItemAt(i) = NULL; + } + } + mMediaBuffers.resize(mInputBuffers.size()); + mInputBufferIsDequeued.clear(); + mInputBufferIsDequeued.resize(mInputBuffers.size()); +} + void NuPlayer::Decoder::requestCodecNotification() { if (mCodec != NULL) { sp<AMessage> reply = new AMessage(kWhatCodecNotify, id()); @@ -141,6 +183,14 @@ void NuPlayer::Decoder::configure(const sp<AMessage> &format) { msg->post(); } +status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { + sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id()); + msg->setPointer("buffers", buffers); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + void NuPlayer::Decoder::handleError(int32_t err) { sp<AMessage> notify = mNotify->dup(); @@ -163,6 +213,12 @@ bool NuPlayer::Decoder::handleAnInputBuffer() { CHECK_LT(bufferIx, mInputBuffers.size()); + if (mMediaBuffers[bufferIx] != NULL) { + mMediaBuffers[bufferIx]->release(); + mMediaBuffers.editItemAt(bufferIx) = NULL; + } + mInputBufferIsDequeued.editItemAt(bufferIx) = true; + sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); reply->setSize("buffer-ix", bufferIx); reply->setInt32("generation", mBufferGeneration); @@ -183,6 +239,44 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { sp<ABuffer> buffer; bool hasBuffer = msg->findBuffer("buffer", &buffer); + + // handle widevine classic source - that fills an arbitrary input buffer + MediaBuffer *mediaBuffer = NULL; + if (hasBuffer && buffer->meta()->findPointer( + "mediaBuffer", (void **)&mediaBuffer)) { + if (mediaBuffer == NULL) { + // received no actual buffer + ALOGW("[%s] received null MediaBuffer %s", + mComponentName.c_str(), msg->debugString().c_str()); + buffer = NULL; + } else { + // likely filled another buffer than we requested: adjust buffer index + size_t ix; + for (ix = 0; ix < mInputBuffers.size(); ix++) { + const sp<ABuffer> &buf = mInputBuffers[ix]; + if (buf->data() == mediaBuffer->data()) { + // all input buffers are dequeued on start, hence the check + CHECK(mInputBufferIsDequeued[ix]); + ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu", + mComponentName.c_str(), ix, bufferIx); + + // TRICKY: need buffer for the metadata, so instead, set + // codecBuffer to the same (though incorrect) buffer to + // avoid a memcpy into the codecBuffer + codecBuffer = buffer; + codecBuffer->setRange( + mediaBuffer->range_offset(), + mediaBuffer->range_length()); + bufferIx = ix; + break; + } + } + CHECK(ix < mInputBuffers.size()); + } + } + + mInputBufferIsDequeued.editItemAt(bufferIx) = false; + if (buffer == NULL /* includes !hasBuffer */) { int32_t streamErr = ERROR_END_OF_STREAM; CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); @@ -236,6 +330,11 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { mComponentName.c_str(), err); handleError(err); } + + if (mediaBuffer != NULL) { + CHECK(mMediaBuffers[bufferIx] == NULL); + mMediaBuffers.editItemAt(bufferIx) = mediaBuffer; + } } } @@ -352,6 +451,8 @@ void NuPlayer::Decoder::onFlush() { return; } + releaseAndResetMediaBuffers(); + sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); @@ -379,6 +480,8 @@ void NuPlayer::Decoder::onShutdown() { mComponentName = "decoder"; } + releaseAndResetMediaBuffers(); + if (err != OK) { ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err); handleError(err); @@ -403,6 +506,23 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatGetInputBuffers: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + Vector<sp<ABuffer> > *dstBuffers; + CHECK(msg->findPointer("buffers", (void **)&dstBuffers)); + + dstBuffers->clear(); + for (size_t i = 0; i < mInputBuffers.size(); i++) { + dstBuffers->push(mInputBuffers[i]); + } + + (new AMessage)->postReply(replyID); + break; + } + case kWhatCodecNotify: { if (!isStaleReply(msg)) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 4fa0dbd..c6fc237 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -26,6 +26,7 @@ namespace android { struct ABuffer; struct MediaCodec; +struct MediaBuffer; struct NuPlayer::Decoder : public AHandler { Decoder(const sp<AMessage> ¬ify, @@ -34,6 +35,7 @@ struct NuPlayer::Decoder : public AHandler { virtual void configure(const sp<AMessage> &format); virtual void init(); + status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const; virtual void signalFlush(); virtual void signalResume(); virtual void initiateShutdown(); @@ -60,6 +62,7 @@ private: enum { kWhatCodecNotify = 'cdcN', kWhatConfigure = 'conf', + kWhatGetInputBuffers = 'gInB', kWhatInputBufferFilled = 'inpF', kWhatRenderBuffer = 'rndr', kWhatFlush = 'flus', @@ -77,11 +80,14 @@ private: Vector<sp<ABuffer> > mInputBuffers; Vector<sp<ABuffer> > mOutputBuffers; + Vector<bool> mInputBufferIsDequeued; + Vector<MediaBuffer *> mMediaBuffers; void handleError(int32_t err); bool handleAnInputBuffer(); bool handleAnOutputBuffer(); + void releaseAndResetMediaBuffers(); void requestCodecNotification(); bool isStaleReply(const sp<AMessage> &msg); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index f520ff7..8592ec2 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -26,6 +26,8 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> +#include <inttypes.h> + namespace android { // static @@ -502,6 +504,7 @@ void NuPlayer::Renderer::postDrainVideoQueue() { } } + ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs); msg->post(delayUs); mDrainVideoQueuePending = true; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 632c4a6..259925f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -21,11 +21,14 @@ #include "NuPlayer.h" #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <utils/Vector.h> namespace android { struct ABuffer; struct MetaData; +struct MediaBuffer; struct NuPlayer::Source : public AHandler { enum Flags { @@ -34,6 +37,7 @@ struct NuPlayer::Source : public AHandler { FLAG_CAN_SEEK_FORWARD = 4, // the "10 sec forward button" FLAG_CAN_SEEK = 8, // the "seek bar" FLAG_DYNAMIC_DURATION = 16, + FLAG_SECURE = 32, }; enum { @@ -89,6 +93,10 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } + virtual status_t setBuffers(bool /* audio */, Vector<MediaBuffer *> &/* buffers */) { + return INVALID_OPERATION; + } + virtual bool isRealTime() const { return false; } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 9c64d72..6cb1c64 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3989,6 +3989,8 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { if (err == OK) { break; + } else { + ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str()); } node = NULL; @@ -4504,11 +4506,14 @@ void ACodec::ExecutingState::resume() { submitOutputBuffers(); - // Post the first input buffer. + // Post all available input buffers CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u); - BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0); - - postFillThisBuffer(info); + for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); i++) { + BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i); + if (info->mStatus == BufferInfo::OWNED_BY_US) { + postFillThisBuffer(info); + } + } mActive = true; } diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 207acc8..19da6ee 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -3665,7 +3665,7 @@ status_t MPEG4Source::read( uint32_t sampleIndex; status_t err = mSampleTable->findSampleAtTime( - seekTimeUs * mTimescale / 1000000, + seekTimeUs, 1000000, mTimescale, &sampleIndex, findFlags); if (mode == ReadOptions::SEEK_CLOSEST) { diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp index 80aae51..6ac6d4a 100644 --- a/media/libstagefright/MediaBufferGroup.cpp +++ b/media/libstagefright/MediaBufferGroup.cpp @@ -55,7 +55,8 @@ void MediaBufferGroup::add_buffer(MediaBuffer *buffer) { mLastBuffer = buffer; } -status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) { +status_t MediaBufferGroup::acquire_buffer( + MediaBuffer **out, bool nonBlocking) { Mutex::Autolock autoLock(mLock); for (;;) { @@ -70,6 +71,11 @@ status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) { } } + if (nonBlocking) { + *out = NULL; + return WOULD_BLOCK; + } + // All buffers are in use. Block until one of them is returned to us. mCondition.wait(mLock); } diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index cd51582..8f54343 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -21,6 +21,7 @@ #include <media/stagefright/MediaCodecList.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> @@ -79,6 +80,19 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) { info->mName.c_str()); mCodecInfos.removeAt(i); +#if LOG_NDEBUG == 0 + } else { + for (size_t type_ix = 0; type_ix < mTypes.size(); ++type_ix) { + uint32_t typeMask = 1ul << mTypes.valueAt(type_ix); + if (info->mTypes & typeMask) { + AString mime = mTypes.keyAt(type_ix); + uint32_t bit = mTypes.valueAt(type_ix); + + ALOGV("%s codec info for %s: %s", info->mName.c_str(), mime.c_str(), + info->mCaps.editValueFor(bit)->debugString().c_str()); + } + } +#endif } } @@ -217,6 +231,8 @@ void MediaCodecList::startElementHandler( return; } + bool inType = true; + if (!strcmp(name, "Include")) { mInitCheck = includeXMLFile(attrs); if (mInitCheck == OK) { @@ -267,6 +283,26 @@ void MediaCodecList::startElementHandler( mInitCheck = addQuirk(attrs); } else if (!strcmp(name, "Type")) { mInitCheck = addTypeFromAttributes(attrs); + mCurrentSection = + (mCurrentSection == SECTION_DECODER + ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE); + } + } + inType = false; + // fall through + + case SECTION_DECODER_TYPE: + case SECTION_ENCODER_TYPE: + { + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + // ignore limits and features specified outside of type + bool outside = !inType && info->mSoleType == 0; + if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) { + ALOGW("ignoring %s specified outside of a Type", name); + } else if (!strcmp(name, "Limit")) { + mInitCheck = addLimit(attrs); + } else if (!strcmp(name, "Feature")) { + mInitCheck = addFeature(attrs); } break; } @@ -300,10 +336,27 @@ void MediaCodecList::endElementHandler(const char *name) { break; } + case SECTION_DECODER_TYPE: + case SECTION_ENCODER_TYPE: + { + if (!strcmp(name, "Type")) { + mCurrentSection = + (mCurrentSection == SECTION_DECODER_TYPE + ? SECTION_DECODER : SECTION_ENCODER); + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + info->mCurrentCaps = NULL; + } + break; + } + case SECTION_DECODER: { if (!strcmp(name, "MediaCodec")) { mCurrentSection = SECTION_DECODERS; + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + info->mCurrentCaps = NULL; } break; } @@ -312,6 +365,9 @@ void MediaCodecList::endElementHandler(const char *name) { { if (!strcmp(name, "MediaCodec")) { mCurrentSection = SECTION_ENCODERS; + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + info->mCurrentCaps = NULL; } break; } @@ -373,11 +429,16 @@ void MediaCodecList::addMediaCodec( CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); info->mName = name; info->mIsEncoder = encoder; + info->mSoleType = 0; info->mTypes = 0; info->mQuirks = 0; + info->mCurrentCaps = NULL; if (type != NULL) { addType(type); + // if type was specified in attributes, we do not allow + // subsequent types + info->mSoleType = info->mTypes; } } @@ -427,6 +488,12 @@ status_t MediaCodecList::addQuirk(const char **attrs) { status_t MediaCodecList::addTypeFromAttributes(const char **attrs) { const char *name = NULL; + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + if (info->mSoleType != 0) { + ALOGE("Codec '%s' already had its type specified", info->mName.c_str()); + return -EINVAL; + } + size_t i = 0; while (attrs[i] != NULL) { if (!strcmp(attrs[i], "name")) { @@ -469,6 +536,11 @@ void MediaCodecList::addType(const char *name) { CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); info->mTypes |= 1ul << bit; + if (info->mCaps.indexOfKey(bit) < 0) { + AMessage *msg = new AMessage(); + info->mCaps.add(bit, msg); + } + info->mCurrentCaps = info->mCaps.editValueFor(bit); } ssize_t MediaCodecList::findCodecByType( @@ -494,6 +566,216 @@ ssize_t MediaCodecList::findCodecByType( return -ENOENT; } +static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) { + ALOGE("limit '%s' with %s'%s' attribute", name.c_str(), + (found ? "" : "no "), attr); + return -EINVAL; +} + +static status_t limitError(AString name, const char *msg) { + ALOGE("limit '%s' %s", name.c_str(), msg); + return -EINVAL; +} + +static status_t limitInvalidAttr(AString name, const char *attr, AString value) { + ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(), + attr, value.c_str()); + return -EINVAL; +} + +status_t MediaCodecList::addLimit(const char **attrs) { + sp<AMessage> msg = new AMessage(); + + size_t i = 0; + while (attrs[i] != NULL) { + if (attrs[i + 1] == NULL) { + return -EINVAL; + } + + // attributes with values + if (!strcmp(attrs[i], "name") + || !strcmp(attrs[i], "default") + || !strcmp(attrs[i], "in") + || !strcmp(attrs[i], "max") + || !strcmp(attrs[i], "min") + || !strcmp(attrs[i], "range") + || !strcmp(attrs[i], "ranges") + || !strcmp(attrs[i], "scale") + || !strcmp(attrs[i], "value")) { + msg->setString(attrs[i], attrs[i + 1]); + ++i; + } else { + return -EINVAL; + } + ++i; + } + + AString name; + if (!msg->findString("name", &name)) { + ALOGE("limit with no 'name' attribute"); + return -EINVAL; + } + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + + // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range + // quality: range + default + [scale] + // complexity: range + default + bool found; + if (name == "aspect-ratio" || name == "bitrate" || name == "block-count" + || name == "blocks-per-second" || name == "complexity" + || name == "frame-rate" || name == "quality" || name == "size") { + AString min, max; + if (msg->findString("min", &min) && msg->findString("max", &max)) { + min.append("-"); + min.append(max); + if (msg->contains("range") || msg->contains("value")) { + return limitError(name, "has 'min' and 'max' as well as 'range' or " + "'value' attributes"); + } + msg->setString("range", min); + } else if (msg->contains("min") || msg->contains("max")) { + return limitError(name, "has only 'min' or 'max' attribute"); + } else if (msg->findString("value", &max)) { + min = max; + min.append("-"); + min.append(max); + if (msg->contains("range")) { + return limitError(name, "has both 'range' and 'value' attributes"); + } + msg->setString("range", min); + } + + AString range, scale = "linear", def, in_; + if (!msg->findString("range", &range)) { + return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes"); + } + + if ((name == "quality" || name == "complexity") ^ + (found = msg->findString("default", &def))) { + return limitFoundMissingAttr(name, "default", found); + } + if (name != "quality" && msg->findString("scale", &scale)) { + return limitFoundMissingAttr(name, "scale"); + } + if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) { + return limitFoundMissingAttr(name, "in", found); + } + + if (name == "aspect-ratio") { + if (!(in_ == "pixels") && !(in_ == "blocks")) { + return limitInvalidAttr(name, "in", in_); + } + in_.erase(5, 1); // (pixel|block)-aspect-ratio + in_.append("-"); + in_.append(name); + name = in_; + } + if (name == "quality") { + info->mCurrentCaps->setString("quality-scale", scale); + } + if (name == "quality" || name == "complexity") { + AString tag = name; + tag.append("-default"); + info->mCurrentCaps->setString(tag.c_str(), def); + } + AString tag = name; + tag.append("-range"); + info->mCurrentCaps->setString(tag.c_str(), range); + } else { + AString max, value, ranges; + if (msg->contains("default")) { + return limitFoundMissingAttr(name, "default"); + } else if (msg->contains("in")) { + return limitFoundMissingAttr(name, "in"); + } else if ((name == "channel-count") ^ + (found = msg->findString("max", &max))) { + return limitFoundMissingAttr(name, "max", found); + } else if (msg->contains("min")) { + return limitFoundMissingAttr(name, "min"); + } else if (msg->contains("range")) { + return limitFoundMissingAttr(name, "range"); + } else if ((name == "sample-rate") ^ + (found = msg->findString("ranges", &ranges))) { + return limitFoundMissingAttr(name, "ranges", found); + } else if (msg->contains("scale")) { + return limitFoundMissingAttr(name, "scale"); + } else if ((name == "alignment" || name == "block-size") ^ + (found = msg->findString("value", &value))) { + return limitFoundMissingAttr(name, "value", found); + } + + if (max.size()) { + AString tag = "max-"; + tag.append(name); + info->mCurrentCaps->setString(tag.c_str(), max); + } else if (value.size()) { + info->mCurrentCaps->setString(name.c_str(), value); + } else if (ranges.size()) { + AString tag = name; + tag.append("-ranges"); + info->mCurrentCaps->setString(tag.c_str(), ranges); + } else { + ALOGW("Ignoring unrecognized limit '%s'", name.c_str()); + } + } + return OK; +} + +static bool parseBoolean(const char *s) { + if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) { + return true; + } + char *end; + unsigned long res = strtoul(s, &end, 10); + return *s != '\0' && *end == '\0' && res > 0; +} + +status_t MediaCodecList::addFeature(const char **attrs) { + size_t i = 0; + const char *name = NULL; + int32_t optional = -1; + int32_t required = -1; + + while (attrs[i] != NULL) { + if (attrs[i + 1] == NULL) { + return -EINVAL; + } + + // attributes with values + if (!strcmp(attrs[i], "name")) { + name = attrs[i + 1]; + ++i; + } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) { + int value = (int)parseBoolean(attrs[i + 1]); + if (!strcmp(attrs[i], "optional")) { + optional = value; + } else { + required = value; + } + ++i; + } else { + return -EINVAL; + } + ++i; + } + if (name == NULL) { + ALOGE("feature with no 'name' attribute"); + return -EINVAL; + } + + if (optional == required && optional != -1) { + ALOGE("feature '%s' is both/neither optional and required", name); + return -EINVAL; + } + + CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1); + AString tag = "feature-"; + tag.append(name); + info->mCurrentCaps->setInt32(tag.c_str(), (required == 1) || (optional == 0)); + return OK; +} + ssize_t MediaCodecList::findCodecByName(const char *name) const { for (size_t i = 0; i < mCodecInfos.size(); ++i) { const CodecInfo &info = mCodecInfos.itemAt(i); @@ -571,7 +853,8 @@ status_t MediaCodecList::getCodecCapabilities( size_t index, const char *type, Vector<ProfileLevel> *profileLevels, Vector<uint32_t> *colorFormats, - uint32_t *flags) const { + uint32_t *flags, + sp<AMessage> *capabilities) const { profileLevels->clear(); colorFormats->clear(); @@ -581,6 +864,13 @@ status_t MediaCodecList::getCodecCapabilities( const CodecInfo &info = mCodecInfos.itemAt(index); + ssize_t typeIndex = mTypes.indexOfKey(type); + if (typeIndex < 0) { + return -EINVAL; + } + // essentially doing valueFor without the CHECK abort + typeIndex = mTypes.valueAt(typeIndex); + OMXClient client; status_t err = client.connect(); if (err != OK) { @@ -611,6 +901,11 @@ status_t MediaCodecList::getCodecCapabilities( *flags = caps.mFlags; + // TODO this check will be removed once JNI side is merged + if (capabilities != NULL) { + *capabilities = info.mCaps.valueFor(typeIndex); + } + return OK; } diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp index fd0e79c..576471a 100644 --- a/media/libstagefright/MediaSource.cpp +++ b/media/libstagefright/MediaSource.cpp @@ -32,6 +32,19 @@ void MediaSource::ReadOptions::reset() { mOptions = 0; mSeekTimeUs = 0; mLatenessUs = 0; + mNonBlocking = false; +} + +void MediaSource::ReadOptions::setNonBlocking() { + mNonBlocking = true; +} + +void MediaSource::ReadOptions::clearNonBlocking() { + mNonBlocking = false; +} + +bool MediaSource::ReadOptions::getNonBlocking() const { + return mNonBlocking; } void MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) { diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 8c15929..0838004 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -320,14 +320,14 @@ status_t MyVorbisExtractor::seekToTime(int64_t timeUs) { } size_t left = 0; - size_t right = mTableOfContents.size(); - while (left < right) { - size_t center = left / 2 + right / 2 + (left & right & 1); + size_t right = mTableOfContents.size() - 1; + while (left <= right) { + size_t center = left + (right - left) / 2; const TOCEntry &entry = mTableOfContents.itemAt(center); if (timeUs < entry.mTimeUs) { - right = center; + right = center - 1; } else if (timeUs > entry.mTimeUs) { left = center + 1; } else { diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index 9a92805..c7e24fc 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -520,83 +520,72 @@ void SampleTable::buildSampleEntriesTable() { } status_t SampleTable::findSampleAtTime( - uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + uint64_t req_time, uint64_t scale_num, uint64_t scale_den, + uint32_t *sample_index, uint32_t flags) { buildSampleEntriesTable(); uint32_t left = 0; - uint32_t right = mNumSampleSizes; - while (left < right) { - uint32_t center = (left + right) / 2; - uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime; + uint32_t right = mNumSampleSizes - 1; + while (left <= right) { + uint32_t center = left + (right - left) / 2; + uint64_t centerTime = + getSampleTime(center, scale_num, scale_den); if (req_time < centerTime) { - right = center; + right = center - 1; } else if (req_time > centerTime) { left = center + 1; } else { - left = center; - break; + *sample_index = mSampleTimeEntries[center].mSampleIndex; + return OK; } } - if (left == mNumSampleSizes) { + uint32_t closestIndex = left; + + if (closestIndex == mNumSampleSizes) { if (flags == kFlagAfter) { return ERROR_OUT_OF_RANGE; } - - --left; + flags = kFlagBefore; + } else if (closestIndex == 0) { + if (flags == kFlagBefore) { + // normally we should return out of range, but that is + // treated as end-of-stream. instead return first sample + // + // return ERROR_OUT_OF_RANGE; + } + flags = kFlagAfter; } - uint32_t closestIndex = left; - switch (flags) { case kFlagBefore: { - while (closestIndex > 0 - && mSampleTimeEntries[closestIndex].mCompositionTime - > req_time) { - --closestIndex; - } + --closestIndex; break; } case kFlagAfter: { - while (closestIndex + 1 < mNumSampleSizes - && mSampleTimeEntries[closestIndex].mCompositionTime - < req_time) { - ++closestIndex; - } + // nothing to do break; } default: { CHECK(flags == kFlagClosest); - - if (closestIndex > 0) { - // Check left neighbour and pick closest. - uint32_t absdiff1 = - abs_difference( - mSampleTimeEntries[closestIndex].mCompositionTime, - req_time); - - uint32_t absdiff2 = - abs_difference( - mSampleTimeEntries[closestIndex - 1].mCompositionTime, - req_time); - - if (absdiff1 > absdiff2) { - closestIndex = closestIndex - 1; - } + // pick closest based on timestamp. use abs_difference for safety + if (abs_difference( + getSampleTime(closestIndex, scale_num, scale_den), req_time) > + abs_difference( + req_time, getSampleTime(closestIndex - 1, scale_num, scale_den))) { + --closestIndex; } - break; } } *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex; - return OK; } @@ -618,109 +607,85 @@ status_t SampleTable::findSyncSampleNear( } uint32_t left = 0; - uint32_t right = mNumSyncSamples; - while (left < right) { + uint32_t right = mNumSyncSamples - 1; + while (left <= right) { uint32_t center = left + (right - left) / 2; uint32_t x = mSyncSamples[center]; if (start_sample_index < x) { - right = center; + right = center - 1; } else if (start_sample_index > x) { left = center + 1; } else { - left = center; - break; + *sample_index = x; + return OK; } } + if (left == mNumSyncSamples) { if (flags == kFlagAfter) { ALOGE("tried to find a sync frame after the last one: %d", left); return ERROR_OUT_OF_RANGE; } - left = left - 1; + flags = kFlagBefore; } + else if (left == 0) { + if (flags == kFlagBefore) { + ALOGE("tried to find a sync frame before the first one: %d", left); - // Now ssi[left] is the sync sample index just before (or at) - // start_sample_index. - // Also start_sample_index < ssi[left + 1], if left + 1 < mNumSyncSamples. - - uint32_t x = mSyncSamples[left]; - - if (left + 1 < mNumSyncSamples) { - uint32_t y = mSyncSamples[left + 1]; - - // our sample lies between sync samples x and y. - - status_t err = mSampleIterator->seekTo(start_sample_index); - if (err != OK) { - return err; - } - - uint32_t sample_time = mSampleIterator->getSampleTime(); - - err = mSampleIterator->seekTo(x); - if (err != OK) { - return err; - } - uint32_t x_time = mSampleIterator->getSampleTime(); - - err = mSampleIterator->seekTo(y); - if (err != OK) { - return err; - } - - uint32_t y_time = mSampleIterator->getSampleTime(); - - if (abs_difference(x_time, sample_time) - > abs_difference(y_time, sample_time)) { - // Pick the sync sample closest (timewise) to the start-sample. - x = y; - ++left; + // normally we should return out of range, but that is + // treated as end-of-stream. instead seek to first sync + // + // return ERROR_OUT_OF_RANGE; } + flags = kFlagAfter; } + // Now ssi[left - 1] <(=) start_sample_index <= ssi[left] switch (flags) { case kFlagBefore: { - if (x > start_sample_index) { - CHECK(left > 0); - - x = mSyncSamples[left - 1]; - - if (x > start_sample_index) { - // The table of sync sample indices was not sorted - // properly. - return ERROR_MALFORMED; - } - } + --left; break; } - case kFlagAfter: { - if (x < start_sample_index) { - if (left + 1 >= mNumSyncSamples) { - return ERROR_OUT_OF_RANGE; - } - - x = mSyncSamples[left + 1]; - - if (x < start_sample_index) { - // The table of sync sample indices was not sorted - // properly. - return ERROR_MALFORMED; - } - } - + // nothing to do break; } - default: + { + // this route is not used, but implement it nonetheless + CHECK(flags == kFlagClosest); + + status_t err = mSampleIterator->seekTo(start_sample_index); + if (err != OK) { + return err; + } + uint32_t sample_time = mSampleIterator->getSampleTime(); + + err = mSampleIterator->seekTo(mSyncSamples[left]); + if (err != OK) { + return err; + } + uint32_t upper_time = mSampleIterator->getSampleTime(); + + err = mSampleIterator->seekTo(mSyncSamples[left - 1]); + if (err != OK) { + return err; + } + uint32_t lower_time = mSampleIterator->getSampleTime(); + + // use abs_difference for safety + if (abs_difference(upper_time, sample_time) > + abs_difference(sample_time, lower_time)) { + --left; + } break; + } } - *sample_index = x; - + *sample_index = mSyncSamples[left]; return OK; } diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp index dc42f91..d268aa4 100644 --- a/media/libstagefright/foundation/AMessage.cpp +++ b/media/libstagefright/foundation/AMessage.cpp @@ -127,6 +127,20 @@ const AMessage::Item *AMessage::findItem( return NULL; } +bool AMessage::contains(const char *name) const { + name = AAtomizer::Atomize(name); + + for (size_t i = 0; i < mNumItems; ++i) { + const Item *item = &mItems[i]; + + if (item->mName == name) { + return true; + } + } + + return false; +} + #define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \ void AMessage::set##NAME(const char *name, TYPENAME value) { \ Item *item = allocateItem(name); \ @@ -160,6 +174,11 @@ void AMessage::setString( item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len); } +void AMessage::setString( + const char *name, const AString &s) { + setString(name, s.c_str(), s.size()); +} + void AMessage::setObjectInternal( const char *name, const sp<RefBase> &obj, Type type) { Item *item = allocateItem(name); diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h index fe146f2..d06df7b 100644 --- a/media/libstagefright/include/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -75,7 +75,8 @@ public: kFlagClosest }; status_t findSampleAtTime( - uint32_t req_time, uint32_t *sample_index, uint32_t flags); + uint64_t req_time, uint64_t scale_num, uint64_t scale_den, + uint32_t *sample_index, uint32_t flags); status_t findSyncSampleNear( uint32_t start_sample_index, uint32_t *sample_index, @@ -138,6 +139,13 @@ private: friend struct SampleIterator; + // normally we don't round + inline uint64_t getSampleTime( + size_t sample_index, uint64_t scale_num, uint64_t scale_den) const { + return (mSampleTimeEntries[sample_index].mCompositionTime + * scale_num) / scale_den; + } + status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); uint32_t getCompositionTimeOffset(uint32_t sampleIndex); diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 871824a..a0319ab 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -230,6 +230,11 @@ void AnotherPacketSource::queueDiscontinuity( int32_t oldDiscontinuityType; if (!oldBuffer->meta()->findInt32( "discontinuity", &oldDiscontinuityType)) { + MediaBuffer *mbuf = NULL; + oldBuffer->meta()->findPointer("mediaBuffer", (void**)&mbuf); + if (mbuf != NULL) { + mbuf->release(); + } it = mBuffers.erase(it); continue; } diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 22b12d9..cc4770a 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -233,7 +233,7 @@ status_t OMX::allocateNode( instance, &handle); if (err != OMX_ErrorNone) { - ALOGV("FAILED to allocate omx component '%s'", name); + ALOGE("FAILED to allocate omx component '%s'", name); instance->onGetHandleFailed(); diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index eb3e6b4..af761e4 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1630,12 +1630,11 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( frameCount, mChannelMask); // since client and server are in the same process, // the buffer has the same virtual address on both sides - mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize); + mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize, + true /*clientInServer*/); mClientProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY); mClientProxy->setSendLevel(0.0); mClientProxy->setSampleRate(sampleRate); - mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize, - true /*clientInServer*/); } else { ALOGW("Error creating output track on thread %p", playbackThread); } |