diff options
28 files changed, 470 insertions, 62 deletions
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp index ffe974b..d2dc200 100644 --- a/camera/camera2/ICameraDeviceUser.cpp +++ b/camera/camera2/ICameraDeviceUser.cpp @@ -48,7 +48,8 @@ enum { GET_CAMERA_INFO, WAIT_UNTIL_IDLE, FLUSH, - PREPARE + PREPARE, + TEAR_DOWN }; namespace { @@ -365,6 +366,20 @@ public: return reply.readInt32(); } + virtual status_t tearDown(int streamId) + { + ALOGV("tearDown"); + Parcel data, reply; + + data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor()); + data.writeInt32(streamId); + + remote()->transact(TEAR_DOWN, data, &reply); + + reply.readExceptionCode(); + return reply.readInt32(); + } + private: @@ -570,6 +585,13 @@ status_t BnCameraDeviceUser::onTransact( reply->writeInt32(prepare(streamId)); return NO_ERROR; } break; + case TEAR_DOWN: { + CHECK_INTERFACE(ICameraDeviceUser, data, reply); + int streamId = data.readInt32(); + reply->writeNoException(); + reply->writeInt32(tearDown(streamId)); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h index b3dd140..a7bf8ab 100644 --- a/include/camera/camera2/ICameraDeviceUser.h +++ b/include/camera/camera2/ICameraDeviceUser.h @@ -138,6 +138,12 @@ public: * Preallocate buffers for a given output stream asynchronously. */ virtual status_t prepare(int streamId) = 0; + + /** + * Free all unused buffers for a given output stream. + */ + virtual status_t tearDown(int streamId) = 0; + }; // ---------------------------------------------------------------------------- diff --git a/include/media/stagefright/MediaSync.h b/include/media/stagefright/MediaSync.h index 1eef211..4b5cd05 100644 --- a/include/media/stagefright/MediaSync.h +++ b/include/media/stagefright/MediaSync.h @@ -37,6 +37,7 @@ class GraphicBuffer; class IGraphicBufferConsumer; class IGraphicBufferProducer; struct MediaClock; +struct VideoFrameScheduler; // MediaSync manages media playback and its synchronization to a media clock // source. It can be also used for video-only playback. @@ -103,6 +104,9 @@ public: // MediaClock::getMediaTime() and MediaClock::getRealTimeFor(). sp<const MediaClock> getMediaClock(); + // Flush mediasync + void flush(); + // Set the video frame rate hint - this is used by the video FrameScheduler status_t setVideoFrameRateHint(float rate); @@ -131,11 +135,10 @@ protected: private: enum { - kWhatDrainVideo = 'dVid', + kWhatDrainVideo = 'dVid', + kWhatCheckFrameAvailable = 'cFrA', }; - static const int MAX_OUTSTANDING_BUFFERS = 2; - // This is a thin wrapper class that lets us listen to // IConsumerListener::onFrameAvailable from mInput. class InputListener : public BnConsumerListener, @@ -194,6 +197,8 @@ private: sp<IGraphicBufferConsumer> mInput; sp<IGraphicBufferProducer> mOutput; int mUsageFlagsFromOutput; + uint32_t mMaxAcquiredBufferCount; // max acquired buffer count + bool mReturnPendingInputFrame; // set while we are pending before acquiring an input frame sp<AudioTrack> mAudioTrack; uint32_t mNativeSampleRateInHz; @@ -202,6 +207,7 @@ private: int64_t mNextBufferItemMediaUs; List<BufferItem> mBufferItems; + sp<VideoFrameScheduler> mFrameScheduler; // Keep track of buffers received from |mInput|. This is needed because // it's possible the consumer of |mOutput| could return a different @@ -242,8 +248,9 @@ private: // onBufferReleasedByOutput releases a buffer back to the input. void onFrameAvailableFromInput(); - // Send |bufferItem| to the output for rendering. - void renderOneBufferItem_l(const BufferItem &bufferItem); + // Send |bufferItem| to the output for rendering. If this is not the only + // buffer sent for rendering, check for any dropped frames in |checkInUs| us. + void renderOneBufferItem_l(const BufferItem &bufferItem, int64_t checkInUs); // This implements the onBufferReleased callback from IProducerListener. // It gets called from an OutputListener. diff --git a/media/libmediaplayerservice/VideoFrameScheduler.h b/include/media/stagefright/VideoFrameScheduler.h index b1765c9..9d97dfd 100644 --- a/media/libmediaplayerservice/VideoFrameScheduler.h +++ b/include/media/stagefright/VideoFrameScheduler.h @@ -39,6 +39,9 @@ struct VideoFrameScheduler : public RefBase { // returns the vsync period for the main display nsecs_t getVsyncPeriod(); + // returns the current frames-per-second, or 0.f if not primed + float getFrameRate(); + void release(); static const size_t kHistorySize = 8; @@ -54,8 +57,9 @@ private: void reset(float fps = -1); // keep current estimate, but restart phase void restart(); - // returns period + // returns period or 0 if not yet primed nsecs_t addSample(nsecs_t time); + nsecs_t getPeriod() const; private: nsecs_t mPeriod; diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 1d7aed2..6a51a76 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -374,6 +374,9 @@ void AudioTrackClientProxy::flush() size_t increment = mFrameCountP2 << 1; size_t mask = increment - 1; audio_track_cblk_t* cblk = mCblk; + // mFlush is 32 bits concatenated as [ flush_counter ] [ newfront_offset ] + // Should newFlush = cblk->u.mStreaming.mRear? Only problem is + // if you want to flush twice to the same rear location after a 32 bit wrap. int32_t newFlush = (cblk->u.mStreaming.mRear & mask) | ((cblk->u.mStreaming.mFlush & ~mask) + increment); android_atomic_release_store(newFlush, &cblk->u.mStreaming.mFlush); @@ -613,9 +616,18 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush) front = cblk->u.mStreaming.mFront; if (flush != mFlush) { // effectively obtain then release whatever is in the buffer - size_t mask = (mFrameCountP2 << 1) - 1; + const size_t overflowBit = mFrameCountP2 << 1; + const size_t mask = overflowBit - 1; int32_t newFront = (front & ~mask) | (flush & mask); ssize_t filled = rear - newFront; + if (filled >= (ssize_t)overflowBit) { + // front and rear offsets span the overflow bit of the p2 mask + // so rebasing newFront on the front offset is off by the overflow bit. + // adjust newFront to match rear offset. + ALOGV("flush wrap: filled %zx >= overflowBit %zx", filled, overflowBit); + newFront += overflowBit; + filled -= overflowBit; + } // Rather than shutting down on a corrupt flush, just treat it as a full flush if (!(0 <= filled && (size_t) filled <= mFrameCount)) { ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, " diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 7f0cca2..4d1b587 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -21,7 +21,6 @@ LOCAL_SRC_FILES:= \ StagefrightPlayer.cpp \ StagefrightRecorder.cpp \ TestPlayerStub.cpp \ - VideoFrameScheduler.cpp \ LOCAL_SHARED_LIBRARIES := \ libbinder \ diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 13a7d94..767417b 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -29,8 +29,7 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/Utils.h> - -#include <VideoFrameScheduler.h> +#include <media/stagefright/VideoFrameScheduler.h> #include <inttypes.h> diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index 5210fc8..58ff113 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -138,7 +138,9 @@ void NuPlayer::RTSPSource::pause() { } void NuPlayer::RTSPSource::resume() { - mHandler->resume(); + if (mHandler != NULL) { + mHandler->resume(); + } } status_t NuPlayer::RTSPSource::feedMoreTSData() { @@ -295,13 +297,19 @@ status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { sp<AMessage> msg = new AMessage(kWhatPerformSeek, this); msg->setInt32("generation", ++mSeekGeneration); msg->setInt64("timeUs", seekTimeUs); - msg->post(200000ll); - return OK; + sp<AMessage> response; + status_t err = msg->postAndAwaitResponse(&response); + if (err == OK && response != NULL) { + CHECK(response->findInt32("err", &err)); + } + + return err; } void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { if (mState != CONNECTED) { + finishSeek(INVALID_OPERATION); return; } @@ -320,9 +328,11 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { } else if (msg->what() == kWhatPerformSeek) { int32_t generation; CHECK(msg->findInt32("generation", &generation)); + CHECK(msg->senderAwaitsResponse(&mSeekReplyID)); if (generation != mSeekGeneration) { // obsolete. + finishSeek(OK); return; } @@ -368,6 +378,37 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { case MyHandler::kWhatSeekDone: { mState = CONNECTED; + if (mSeekReplyID != NULL) { + // Unblock seekTo here in case we attempted to seek in a live stream + finishSeek(OK); + } + break; + } + + case MyHandler::kWhatSeekPaused: + { + sp<AnotherPacketSource> source = getSource(true /* audio */); + if (source != NULL) { + source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE, + /* extra */ NULL, + /* discard */ true); + } + source = getSource(false /* video */); + if (source != NULL) { + source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE, + /* extra */ NULL, + /* discard */ true); + }; + + status_t err = OK; + msg->findInt32("err", &err); + finishSeek(err); + + if (err == OK) { + int64_t timeUs; + CHECK(msg->findInt64("time", &timeUs)); + mHandler->continueSeekAfterPause(timeUs); + } break; } @@ -700,5 +741,12 @@ bool NuPlayer::RTSPSource::stopBufferingIfNecessary() { return true; } +void NuPlayer::RTSPSource::finishSeek(status_t err) { + CHECK(mSeekReplyID != NULL); + sp<AMessage> seekReply = new AMessage; + seekReply->setInt32("err", err); + seekReply->postReply(mSeekReplyID); + mSeekReplyID = NULL; +} } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index 5f2cf33..6438a1e 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -116,6 +116,8 @@ private: int64_t mEOSTimeoutAudio; int64_t mEOSTimeoutVideo; + sp<AReplyToken> mSeekReplyID; + sp<AnotherPacketSource> getSource(bool audio); void onConnected(); @@ -131,6 +133,7 @@ private: void setError(status_t err); void startBufferingIfNecessary(); bool stopBufferingIfNecessary(); + void finishSeek(status_t err); DISALLOW_EVIL_CONSTRUCTORS(RTSPSource); }; diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 69128bd..b86c749 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -64,6 +64,7 @@ LOCAL_SRC_FILES:= \ TimedEventQueue.cpp \ Utils.cpp \ VBRISeeker.cpp \ + VideoFrameScheduler.cpp \ WAVExtractor.cpp \ WVMExtractor.cpp \ XINGSeeker.cpp \ diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp index 52077a7..0df3ec9 100644 --- a/media/libstagefright/MediaSync.cpp +++ b/media/libstagefright/MediaSync.cpp @@ -25,6 +25,7 @@ #include <media/AudioTrack.h> #include <media/stagefright/MediaClock.h> #include <media/stagefright/MediaSync.h> +#include <media/stagefright/VideoFrameScheduler.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> @@ -50,6 +51,8 @@ MediaSync::MediaSync() mReleaseCondition(), mNumOutstandingBuffers(0), mUsageFlagsFromOutput(0), + mMaxAcquiredBufferCount(1), + mReturnPendingInputFrame(false), mNativeSampleRateInHz(0), mNumFramesWritten(0), mHasAudio(false), @@ -121,6 +124,11 @@ status_t MediaSync::setSurface(const sp<IGraphicBufferProducer> &output) { ALOGE("setSurface: failed to connect (%d)", status); return status; } + + if (mFrameScheduler == NULL) { + mFrameScheduler = new VideoFrameScheduler(); + mFrameScheduler->init(); + } } if (mOutput != NULL) { @@ -209,6 +217,12 @@ status_t MediaSync::createInputSurface( bufferConsumer->setConsumerUsageBits(mUsageFlagsFromOutput); *outBufferProducer = bufferProducer; mInput = bufferConsumer; + + // set undequeued buffer count + int minUndequeuedBuffers; + mOutput->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers); + mMaxAcquiredBufferCount = minUndequeuedBuffers; + bufferConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBufferCount); } return status; } @@ -232,6 +246,7 @@ void MediaSync::updatePlaybackRate_l(float rate) { mNextBufferItemMediaUs = -1; } mPlaybackRate = rate; + // TODO: update frame scheduler with this info mMediaClock->setPlaybackRate(rate); onDrainVideo_l(); } @@ -325,13 +340,44 @@ void MediaSync::setName(const AString &name) { mInput->setConsumerName(String8(name.c_str())); } +void MediaSync::flush() { + Mutex::Autolock lock(mMutex); + if (mFrameScheduler != NULL) { + mFrameScheduler->restart(); + } + while (!mBufferItems.empty()) { + BufferItem *bufferItem = &*mBufferItems.begin(); + returnBufferToInput_l(bufferItem->mGraphicBuffer, bufferItem->mFence); + mBufferItems.erase(mBufferItems.begin()); + } + mNextBufferItemMediaUs = -1; + mNumFramesWritten = 0; + mReturnPendingInputFrame = true; + mReleaseCondition.signal(); + mMediaClock->clearAnchor(); +} + status_t MediaSync::setVideoFrameRateHint(float rate) { - // ignored until we add the FrameScheduler - return rate >= 0.f ? OK : BAD_VALUE; + Mutex::Autolock lock(mMutex); + if (rate < 0.f) { + return BAD_VALUE; + } + if (mFrameScheduler != NULL) { + mFrameScheduler->init(rate); + } + return OK; } float MediaSync::getVideoFrameRate() { - // we don't know the frame rate + Mutex::Autolock lock(mMutex); + if (mFrameScheduler != NULL) { + float fps = mFrameScheduler->getFrameRate(); + if (fps > 0.f) { + return fps; + } + } + + // we don't have or know the frame rate return -1.f; } @@ -470,7 +516,7 @@ int64_t MediaSync::getPlayedOutAudioDurationMedia_l(int64_t nowUs) { CHECK_EQ(res, (status_t)OK); numFramesPlayedAt = nowUs; numFramesPlayedAt += 1000LL * mAudioTrack->latency() / 2; /* XXX */ - //ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt); + //ALOGD("getPosition: %d %lld", numFramesPlayed, (long long)numFramesPlayedAt); } //can't be negative until 12.4 hrs, test. @@ -510,18 +556,30 @@ void MediaSync::onDrainVideo_l() { int64_t itemMediaUs = bufferItem->mTimestamp / 1000; int64_t itemRealUs = getRealTime(itemMediaUs, nowUs); - if (itemRealUs <= nowUs) { + // adjust video frame PTS based on vsync + itemRealUs = mFrameScheduler->schedule(itemRealUs * 1000) / 1000; + int64_t oneVsyncUs = (mFrameScheduler->getVsyncPeriod() / 1000); + int64_t twoVsyncsUs = oneVsyncUs * 2; + + // post 2 display refreshes before rendering is due + if (itemRealUs <= nowUs + twoVsyncsUs) { + ALOGV("adjusting PTS from %lld to %lld", + (long long)bufferItem->mTimestamp / 1000, (long long)itemRealUs); + bufferItem->mTimestamp = itemRealUs * 1000; + bufferItem->mIsAutoTimestamp = false; + if (mHasAudio) { if (nowUs - itemRealUs <= kMaxAllowedVideoLateTimeUs) { - renderOneBufferItem_l(*bufferItem); + renderOneBufferItem_l(*bufferItem, nowUs + oneVsyncUs - itemRealUs); } else { // too late. returnBufferToInput_l( bufferItem->mGraphicBuffer, bufferItem->mFence); + mFrameScheduler->restart(); } } else { // always render video buffer in video-only mode. - renderOneBufferItem_l(*bufferItem); + renderOneBufferItem_l(*bufferItem, nowUs + oneVsyncUs - itemRealUs); // smooth out videos >= 10fps mMediaClock->updateAnchor( @@ -534,7 +592,7 @@ void MediaSync::onDrainVideo_l() { if (mNextBufferItemMediaUs == -1 || mNextBufferItemMediaUs > itemMediaUs) { sp<AMessage> msg = new AMessage(kWhatDrainVideo, this); - msg->post(itemRealUs - nowUs); + msg->post(itemRealUs - nowUs - twoVsyncsUs); mNextBufferItemMediaUs = itemMediaUs; } break; @@ -545,10 +603,18 @@ void MediaSync::onDrainVideo_l() { void MediaSync::onFrameAvailableFromInput() { Mutex::Autolock lock(mMutex); + const static nsecs_t kAcquireWaitTimeout = 2000000000; // 2 seconds + + mReturnPendingInputFrame = false; + // If there are too many outstanding buffers, wait until a buffer is // released back to the input in onBufferReleased. - while (mNumOutstandingBuffers >= MAX_OUTSTANDING_BUFFERS) { - mReleaseCondition.wait(mMutex); + // NOTE: BufferQueue allows dequeuing maxAcquiredBufferCount + 1 buffers + while (mNumOutstandingBuffers > mMaxAcquiredBufferCount + && !mIsAbandoned && !mReturnPendingInputFrame) { + if (mReleaseCondition.waitRelative(mMutex, kAcquireWaitTimeout) != OK) { + ALOGI("still waiting to release a buffer before acquire"); + } // If the sync is abandoned while we are waiting, the release // condition variable will be broadcast, and we should just return @@ -582,12 +648,21 @@ void MediaSync::onFrameAvailableFromInput() { if (mBuffersFromInput.indexOfKey(bufferItem.mGraphicBuffer->getId()) >= 0) { // Something is wrong since this buffer should be at our hands, bail. + ALOGE("received buffer multiple times from input"); mInput->consumerDisconnect(); onAbandoned_l(true /* isInput */); return; } mBuffersFromInput.add(bufferItem.mGraphicBuffer->getId(), bufferItem.mGraphicBuffer); + // If flush happened while waiting for a buffer to be released, simply return it + // TRICKY: do it here after it is detached so that we don't have to cache mGraphicBuffer. + if (mReturnPendingInputFrame) { + mReturnPendingInputFrame = false; + returnBufferToInput_l(bufferItem.mGraphicBuffer, bufferItem.mFence); + return; + } + mBufferItems.push_back(bufferItem); if (mBufferItems.size() == 1) { @@ -595,7 +670,7 @@ void MediaSync::onFrameAvailableFromInput() { } } -void MediaSync::renderOneBufferItem_l( const BufferItem &bufferItem) { +void MediaSync::renderOneBufferItem_l(const BufferItem &bufferItem, int64_t checkInUs) { IGraphicBufferProducer::QueueBufferInput queueInput( bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp, @@ -635,6 +710,12 @@ void MediaSync::renderOneBufferItem_l( const BufferItem &bufferItem) { mBuffersSentToOutput.add(bufferItem.mGraphicBuffer->getId(), bufferItem.mGraphicBuffer); ALOGV("queued buffer %#llx to output", (long long)bufferItem.mGraphicBuffer->getId()); + + // If we have already queued more than one buffer, check for any free buffers in case + // one of them were dropped - as BQ does not signal onBufferReleased in that case. + if (mBuffersSentToOutput.size() > 1) { + (new AMessage(kWhatCheckFrameAvailable, this))->post(checkInUs); + } } void MediaSync::onBufferReleasedByOutput(sp<IGraphicBufferProducer> &output) { @@ -646,32 +727,38 @@ void MediaSync::onBufferReleasedByOutput(sp<IGraphicBufferProducer> &output) { sp<GraphicBuffer> buffer; sp<Fence> fence; - status_t status = mOutput->detachNextBuffer(&buffer, &fence); - ALOGE_IF(status != NO_ERROR, "detaching buffer from output failed (%d)", status); + status_t status; + // NOTE: This is a workaround for a BufferQueue bug where onBufferReleased is + // called only for released buffers, but not for buffers that were dropped during + // acquire. Dropped buffers can still be detached as they are on the free list. + // TODO: remove if released callback happens also for dropped buffers + while ((status = mOutput->detachNextBuffer(&buffer, &fence)) != NO_MEMORY) { + ALOGE_IF(status != NO_ERROR, "detaching buffer from output failed (%d)", status); - if (status == NO_INIT) { - // If the output has been abandoned, we can't do anything else, - // since buffer is invalid. - onAbandoned_l(false /* isInput */); - return; - } + if (status == NO_INIT) { + // If the output has been abandoned, we can't do anything else, + // since buffer is invalid. + onAbandoned_l(false /* isInput */); + return; + } - ALOGV("detached buffer %#llx from output", (long long)buffer->getId()); + ALOGV("detached buffer %#llx from output", (long long)buffer->getId()); - // If we've been abandoned, we can't return the buffer to the input, so just - // move on. - if (mIsAbandoned) { - return; - } + // If we've been abandoned, we can't return the buffer to the input, so just + // move on. + if (mIsAbandoned) { + return; + } - ssize_t ix = mBuffersSentToOutput.indexOfKey(buffer->getId()); - if (ix < 0) { - // The buffer is unknown, maybe leftover, ignore. - return; - } - mBuffersSentToOutput.removeItemsAt(ix); + ssize_t ix = mBuffersSentToOutput.indexOfKey(buffer->getId()); + if (ix < 0) { + // The buffer is unknown, maybe leftover, ignore. + return; + } + mBuffersSentToOutput.removeItemsAt(ix); - returnBufferToInput_l(buffer, fence); + returnBufferToInput_l(buffer, fence); + } } void MediaSync::returnBufferToInput_l( @@ -679,6 +766,7 @@ void MediaSync::returnBufferToInput_l( ssize_t ix = mBuffersFromInput.indexOfKey(buffer->getId()); if (ix < 0) { // The buffer is unknown, something is wrong, bail. + ALOGE("output returned unknown buffer"); mOutput->disconnect(NATIVE_WINDOW_API_MEDIA); onAbandoned_l(false /* isInput */); return; @@ -741,6 +829,12 @@ void MediaSync::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatCheckFrameAvailable: + { + onBufferReleasedByOutput(mOutput); + break; + } + default: TRESPASS(); break; diff --git a/media/libmediaplayerservice/VideoFrameScheduler.cpp b/media/libstagefright/VideoFrameScheduler.cpp index ce5f5fe..5fe9bf9 100644 --- a/media/libmediaplayerservice/VideoFrameScheduler.cpp +++ b/media/libstagefright/VideoFrameScheduler.cpp @@ -28,8 +28,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AUtils.h> - -#include "VideoFrameScheduler.h" +#include <media/stagefright/VideoFrameScheduler.h> namespace android { @@ -56,7 +55,7 @@ static const size_t kMinSamplesToEstimatePeriod = 3; static const size_t kMaxSamplesToEstimatePeriod = VideoFrameScheduler::kHistorySize; static const size_t kPrecision = 12; -static const size_t kErrorThreshold = (1 << (kPrecision * 2)) / 10; +static const int64_t kErrorThreshold = (1 << (kPrecision * 2)) / 10; static const int64_t kMultiplesThresholdDiv = 4; // 25% static const int64_t kReFitThresholdDiv = 100; // 1% static const nsecs_t kMaxAllowedFrameSkip = kNanosIn1s; // 1 sec @@ -258,7 +257,8 @@ void VideoFrameScheduler::PLL::prime(size_t numSamplesToUse) { mPhase = firstTime; } } - ALOGV("priming[%zu] phase:%lld period:%lld", numSamplesToUse, mPhase, mPeriod); + ALOGV("priming[%zu] phase:%lld period:%lld", + numSamplesToUse, (long long)mPhase, (long long)mPeriod); } nsecs_t VideoFrameScheduler::PLL::addSample(nsecs_t time) { @@ -316,6 +316,10 @@ nsecs_t VideoFrameScheduler::PLL::addSample(nsecs_t time) { return mPeriod; } +nsecs_t VideoFrameScheduler::PLL::getPeriod() const { + return mPrimed ? mPeriod : 0; +} + /* ======================================================================= */ /* Frame Scheduler */ /* ======================================================================= */ @@ -382,6 +386,14 @@ nsecs_t VideoFrameScheduler::getVsyncPeriod() { return kDefaultVsyncPeriod; } +float VideoFrameScheduler::getFrameRate() { + nsecs_t videoPeriod = mPll.getPeriod(); + if (videoPeriod > 0) { + return 1e9 / videoPeriod; + } + return 0.f; +} + nsecs_t VideoFrameScheduler::schedule(nsecs_t renderTime) { nsecs_t origRenderTime = renderTime; diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp index 5c05a0e..1db350f 100644 --- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp @@ -82,7 +82,10 @@ SoftHEVC::SoftHEVC( initPorts( kNumBuffers, max(kMaxOutputBufferSize / kMinCompressionRatio, (size_t)INPUT_BUF_SIZE), kNumBuffers, CODEC_MIME_TYPE, kMinCompressionRatio); - CHECK_EQ(initDecoder(), (status_t)OK); +} + +status_t SoftHEVC::init() { + return initDecoder(); } SoftHEVC::~SoftHEVC() { @@ -766,5 +769,10 @@ void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { android::SoftOMXComponent *createSoftOMXComponent(const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { - return new android::SoftHEVC(name, callbacks, appData, component); + android::SoftHEVC *codec = new android::SoftHEVC(name, callbacks, appData, component); + if (codec->init() != android::OK) { + android::sp<android::SoftOMXComponent> release = codec; + return NULL; + } + return codec; } diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.h b/media/libstagefright/codecs/hevcdec/SoftHEVC.h index a91f528..c6344cf 100644 --- a/media/libstagefright/codecs/hevcdec/SoftHEVC.h +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.h @@ -56,6 +56,8 @@ struct SoftHEVC: public SoftVideoDecoderOMXComponent { SoftHEVC(const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component); + status_t init(); + protected: virtual ~SoftHEVC(); diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index e64a7a1..0d0baf3 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -98,6 +98,7 @@ struct MyHandler : public AHandler { enum { kWhatConnected = 'conn', kWhatDisconnected = 'disc', + kWhatSeekPaused = 'spau', kWhatSeekDone = 'sdon', kWhatAccessUnit = 'accU', @@ -220,6 +221,12 @@ struct MyHandler : public AHandler { msg->post(); } + void continueSeekAfterPause(int64_t timeUs) { + sp<AMessage> msg = new AMessage('see1', this); + msg->setInt64("time", timeUs); + msg->post(); + } + bool isSeekable() const { return mSeekable; } @@ -1180,7 +1187,7 @@ struct MyHandler : public AHandler { mCheckPending = true; ++mCheckGeneration; - sp<AMessage> reply = new AMessage('see1', this); + sp<AMessage> reply = new AMessage('see0', this); reply->setInt64("time", timeUs); if (mPausing) { @@ -1203,9 +1210,26 @@ struct MyHandler : public AHandler { break; } - case 'see1': + case 'see0': { // Session is paused now. + status_t err = OK; + msg->findInt32("result", &err); + + int64_t timeUs; + CHECK(msg->findInt64("time", &timeUs)); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatSeekPaused); + notify->setInt32("err", err); + notify->setInt64("time", timeUs); + notify->post(); + break; + + } + + case 'see1': + { for (size_t i = 0; i < mTracks.size(); ++i) { TrackInfo *info = &mTracks.editItemAt(i); diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk index 46b2725..a523656 100755 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk @@ -26,13 +26,8 @@ LOCAL_C_INCLUDES := \ LOCAL_SHARED_LIBRARIES := \ libaudiopolicyengineconfigurable \ libparameter \ - libicuuc \ - liblog \ - -LOCAL_STATIC_LIBRARIES := \ libxmlserializer \ - libpfw_utility \ - libxml2 \ + liblog \ LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libpolicy-subsystem diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index 8e88ca7..36e99dd 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -1973,6 +1973,14 @@ status_t Camera2Client::createJpegStreamL(Parameters ¶ms) { return res; } + // Ideally we don't need this, but current camera device + // status tracking mechanism demands it. + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Waiting device drain failed: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + res = updateProcessorStream(mJpegProcessor, params); return res; } diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp index 699de2f..442eb75 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.cpp +++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp @@ -2865,7 +2865,8 @@ int64_t Parameters::getJpegStreamMinFrameDurationNs(Parameters::Size size) { } } } - ALOGE("%s: cannot find jpeg size %dx%d", size.width, size.height); + ALOGE("%s: cannot find min frame duration for jpeg size %dx%d", + __FUNCTION__, size.width, size.height); return -1; } diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 3b83f63..c717a56 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -719,6 +719,38 @@ status_t CameraDeviceClient::prepare(int streamId) { return res; } +status_t CameraDeviceClient::tearDown(int streamId) { + ATRACE_CALL(); + ALOGV("%s", __FUNCTION__); + + status_t res = OK; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + Mutex::Autolock icl(mBinderSerializationLock); + + // Guard against trying to prepare non-created streams + ssize_t index = NAME_NOT_FOUND; + for (size_t i = 0; i < mStreamMap.size(); ++i) { + if (streamId == mStreamMap.valueAt(i)) { + index = i; + break; + } + } + + if (index == NAME_NOT_FOUND) { + ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream " + "created yet", __FUNCTION__, mCameraId, streamId); + return BAD_VALUE; + } + + // Also returns BAD_VALUE if stream ID was not valid or if the stream is in + // use + res = mDevice->tearDown(streamId); + + return res; +} + + status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) { String8 result; result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n", diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index 0f485ca..1f8b39d 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -111,6 +111,9 @@ public: // Prepare stream by preallocating its buffers virtual status_t prepare(int streamId); + // Tear down stream resources by freeing its unused buffers + virtual status_t tearDown(int streamId); + /** * Interface used by CameraService */ diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index 06177e3..cd25949 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -289,6 +289,11 @@ class CameraDeviceBase : public virtual RefBase { virtual status_t prepare(int streamId) = 0; /** + * Free stream resources by dumping its unused gralloc buffers. + */ + virtual status_t tearDown(int streamId) = 0; + + /** * Get the HAL device version. */ virtual uint32_t getDeviceVersion() = 0; diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp index dfe5565..c9c990c 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.cpp +++ b/services/camera/libcameraservice/device2/Camera2Device.cpp @@ -626,6 +626,12 @@ status_t Camera2Device::prepare(int streamId) { return NO_INIT; } +status_t Camera2Device::tearDown(int streamId) { + ATRACE_CALL(); + ALOGE("%s: Camera %d: unimplemented", __FUNCTION__, mId); + return NO_INIT; +} + uint32_t Camera2Device::getDeviceVersion() { ATRACE_CALL(); return mDeviceVersion; diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h index c9f3a2c..34c1ded 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.h +++ b/services/camera/libcameraservice/device2/Camera2Device.h @@ -85,8 +85,9 @@ class Camera2Device: public CameraDeviceBase { buffer_handle_t *buffer, wp<BufferReleasedListener> listener); // Flush implemented as just a wait virtual status_t flush(int64_t *lastFrameNumber = NULL); - // Prepare is a no-op + // Prepare and tearDown are no-ops virtual status_t prepare(int streamId); + virtual status_t tearDown(int streamId); virtual uint32_t getDeviceVersion(); virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 9e73b5c..3afbd89 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -1384,6 +1384,37 @@ status_t Camera3Device::prepare(int streamId) { return mPreparerThread->prepare(stream); } +status_t Camera3Device::tearDown(int streamId) { + ATRACE_CALL(); + ALOGV("%s: Camera %d: Tearing down stream %d", __FUNCTION__, mId, streamId); + Mutex::Autolock il(mInterfaceLock); + Mutex::Autolock l(mLock); + + // Teardown can only be accomplished on devices that don't require register_stream_buffers, + // since we cannot call register_stream_buffers except right after configure_streams. + if (mHal3Device->common.version < CAMERA_DEVICE_API_VERSION_3_2) { + ALOGE("%s: Unable to tear down streams on device HAL v%x", + __FUNCTION__, mHal3Device->common.version); + return NO_INIT; + } + + sp<Camera3StreamInterface> stream; + ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId); + if (outputStreamIdx == NAME_NOT_FOUND) { + CLOGE("Stream %d does not exist", streamId); + return BAD_VALUE; + } + + stream = mOutputStreams.editValueAt(outputStreamIdx); + + if (stream->hasOutstandingBuffers() || mRequestThread->isStreamPending(stream)) { + CLOGE("Stream %d is a target of a in-progress request", streamId); + return BAD_VALUE; + } + + return stream->tearDown(); +} + uint32_t Camera3Device::getDeviceVersion() { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index 31b6132..140da98 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -141,6 +141,8 @@ class Camera3Device : virtual status_t prepare(int streamId); + virtual status_t tearDown(int streamId); + virtual uint32_t getDeviceVersion(); virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 4c40bb6..2527fd6 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -364,6 +364,61 @@ status_t Camera3Stream::cancelPrepareLocked() { return res; } +status_t Camera3Stream::tearDown() { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + status_t res = OK; + + // This function should be only called when the stream is configured. + if (mState != STATE_CONFIGURED) { + ALOGE("%s: Stream %d: Can't tear down stream if stream is not in " + "CONFIGURED state %d", __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // If any buffers have been handed to the HAL, the stream cannot be torn down. + if (getHandoutOutputBufferCountLocked() > 0) { + ALOGE("%s: Stream %d: Can't tear down a stream that has outstanding buffers", + __FUNCTION__, mId); + return INVALID_OPERATION; + } + + // Free buffers by disconnecting and then reconnecting to the buffer queue + // Only unused buffers will be dropped immediately; buffers that have been filled + // and are waiting to be acquired by the consumer and buffers that are currently + // acquired will be freed once they are released by the consumer. + + res = disconnectLocked(); + if (res != OK) { + if (res == -ENOTCONN) { + // queue has been disconnected, nothing left to do, so exit with success + return OK; + } + ALOGE("%s: Stream %d: Unable to disconnect to tear down buffers: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + mState = STATE_IN_CONFIG; + + res = configureQueueLocked(); + if (res != OK) { + ALOGE("%s: Unable to configure stream %d queue: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + mState = STATE_ERROR; + return res; + } + + // Reset prepared state, since we've reconnected to the queue and can prepare again. + mPrepared = false; + mStreamUnpreparable = false; + + mState = STATE_CONFIGURED; + + return OK; +} + status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) { ATRACE_CALL(); Mutex::Autolock l(mLock); diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index 0543c66..bab2177 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -247,6 +247,20 @@ class Camera3Stream : status_t cancelPrepare(); /** + * Tear down memory for this stream. This frees all unused gralloc buffers + * allocated for this stream, but leaves it ready for operation afterward. + * + * May only be called in the CONFIGURED state, and keeps the stream in + * the CONFIGURED state. + * + * Returns: + * OK if teardown succeeded. + * INVALID_OPERATION if not in the CONFIGURED state + * NO_INIT in case of a serious error from the HAL device + */ + status_t tearDown(); + + /** * Fill in the camera3_stream_buffer with the next valid buffer for this * stream, to hand over to the HAL. * diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index 6c87a45..c086eaf 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -152,6 +152,20 @@ class Camera3StreamInterface : public virtual RefBase { virtual status_t cancelPrepare() = 0; /** + * Tear down memory for this stream. This frees all unused gralloc buffers + * allocated for this stream, but leaves it ready for operation afterward. + * + * May only be called in the CONFIGURED state, and keeps the stream in + * the CONFIGURED state. + * + * Returns: + * OK if teardown succeeded. + * INVALID_OPERATION if not in the CONFIGURED state + * NO_INIT in case of a serious error from the HAL device + */ + virtual status_t tearDown() = 0; + + /** * Fill in the camera3_stream_buffer with the next valid buffer for this * stream, to hand over to the HAL. * |