diff options
-rw-r--r-- | include/media/stagefright/CameraSourceTimeLapse.h | 61 | ||||
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.cpp | 17 | ||||
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.h | 2 | ||||
-rw-r--r-- | media/libstagefright/CameraSource.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/CameraSourceTimeLapse.cpp | 152 |
5 files changed, 217 insertions, 17 deletions
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h index e1cd0c8..3b303f8 100644 --- a/include/media/stagefright/CameraSourceTimeLapse.h +++ b/include/media/stagefright/CameraSourceTimeLapse.h @@ -43,6 +43,16 @@ public: virtual ~CameraSourceTimeLapse(); + // If the frame capture interval is large, read will block for a long time. + // Due to the way the mediaRecorder framework works, a stop() call from + // mediaRecorder waits until the read returns, causing a long wait for + // stop() to return. To avoid this, we can make read() return a copy of the + // last read frame with the same time stamp frequently. This keeps the + // read() call from blocking too long. Calling this function quickly + // captures another frame, keeps its copy, and enables this mode of read() + // returning quickly. + void startQuickReadReturns(); + private: // If true, will use still camera takePicture() for time lapse frames // If false, will use the videocamera frames instead. @@ -82,14 +92,59 @@ private: // to know if current frame needs to be skipped. bool mSkipCurrentFrame; + // Lock for accessing mCameraIdle + Mutex mCameraIdleLock; + + // Condition variable to wait on if camera is is not yet idle. Once the + // camera gets idle, this variable will be signalled. + Condition mCameraIdleCondition; + // True if camera is in preview mode and ready for takePicture(). - bool mCameraIdle; + // False after a call to takePicture() but before the final compressed + // data callback has been called and preview has been restarted. + volatile bool mCameraIdle; + + // True if stop() is waiting for camera to get idle, i.e. for the last + // takePicture() to complete. This is needed so that dataCallbackTimestamp() + // can return immediately. + volatile bool mStopWaitingForIdleCamera; + + // Lock for accessing quick stop variables. + Mutex mQuickStopLock; + + // Condition variable to wake up still picture thread. + Condition mTakePictureCondition; + + // mQuickStop is set to true if we use quick read() returns, otherwise it is set + // to false. Once in this mode read() return a copy of the last read frame + // with the same time stamp. See startQuickReadReturns(). + volatile bool mQuickStop; + + // Forces the next frame passed to dataCallbackTimestamp() to be read + // as a time lapse frame. Used by startQuickReadReturns() so that the next + // frame wakes up any blocking read. + volatile bool mForceRead; + + // Stores a copy of the MediaBuffer read in the last read() call after + // mQuickStop was true. + MediaBuffer* mLastReadBufferCopy; + + // Status code for last read. + status_t mLastReadStatus; CameraSourceTimeLapse(const sp<Camera> &camera, int64_t timeBetweenTimeLapseFrameCaptureUs, int32_t width, int32_t height, int32_t videoFrameRate); + // Wrapper over CameraSource::signalBufferReturned() to implement quick stop. + // It only handles the case when mLastReadBufferCopy is signalled. Otherwise + // it calls the base class' function. + virtual void signalBufferReturned(MediaBuffer* buffer); + + // Wrapper over CameraSource::read() to implement quick stop. + virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL); + // For still camera case starts a thread which calls camera's takePicture() // in a loop. For video camera case, just starts the camera's video recording. virtual void startCameraRecording(); @@ -122,6 +177,10 @@ private: virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType, const sp<IMemory> &data); + // Convenience function to fill mLastReadBufferCopy from the just read + // buffer. + void fillLastReadBufferCopy(MediaBuffer& sourceBuffer); + // If the passed in size (width x height) is a supported preview size, // the function sets the camera's preview size to it and returns true. // Otherwise returns false. diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index fe5074b..bf0c6e1 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1053,10 +1053,13 @@ status_t StagefrightRecorder::setupCameraSource(sp<CameraSource> *cameraSource) status_t err = setupCamera(); if (err != OK) return err; - *cameraSource = (mCaptureTimeLapse) ? - CameraSourceTimeLapse::CreateFromCamera(mCamera, - mTimeBetweenTimeLapseFrameCaptureUs, mVideoWidth, mVideoHeight, mFrameRate): - CameraSource::CreateFromCamera(mCamera); + if (mCaptureTimeLapse) { + mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(mCamera, + mTimeBetweenTimeLapseFrameCaptureUs, mVideoWidth, mVideoHeight, mFrameRate); + *cameraSource = mCameraSourceTimeLapse; + } else { + *cameraSource = CameraSource::CreateFromCamera(mCamera); + } CHECK(*cameraSource != NULL); return OK; @@ -1325,6 +1328,11 @@ status_t StagefrightRecorder::stop() { LOGV("stop"); status_t err = OK; + if (mCaptureTimeLapse && mCameraSourceTimeLapse != NULL) { + mCameraSourceTimeLapse->startQuickReadReturns(); + mCameraSourceTimeLapse = NULL; + } + if (mCaptureAuxVideo) { if (mWriterAux != NULL) { mWriterAux->stop(); @@ -1411,6 +1419,7 @@ status_t StagefrightRecorder::reset() { mTimeBetweenTimeLapseFrameCaptureUs = -1; mCaptureAuxVideo = false; mCameraSourceSplitter = NULL; + mCameraSourceTimeLapse = NULL; mEncoderProfiles = MediaProfiles::getInstance(); mOutputFd = -1; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index f1dc9e6..02d9a01 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -25,6 +25,7 @@ namespace android { class Camera; class CameraSource; +class CameraSourceTimeLapse; class MediaSourceSplitter; struct MediaSource; struct MediaWriter; @@ -101,6 +102,7 @@ private: int64_t mTimeBetweenTimeLapseFrameCaptureUs; bool mCaptureAuxVideo; sp<MediaSourceSplitter> mCameraSourceSplitter; + sp<CameraSourceTimeLapse> mCameraSourceTimeLapse; String8 mParams; int mOutputFd, mOutputFdAux; diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 12ac168..0c9eef4 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -210,6 +210,7 @@ status_t CameraSource::start(MetaData *meta) { } void CameraSource::stopCameraRecording() { + mCamera->setListener(NULL); mCamera->stopRecording(); } @@ -220,7 +221,6 @@ status_t CameraSource::stop() { mFrameAvailableCondition.signal(); int64_t token = IPCThreadState::self()->clearCallingIdentity(); - mCamera->setListener(NULL); stopCameraRecording(); releaseQueuedFrames(); while (!mFramesBeingEncoded.empty()) { diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 227e090..c1bc433 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -89,11 +89,34 @@ CameraSourceTimeLapse::CameraSourceTimeLapse(const sp<Camera> &camera, mMeta->setInt32(kKeyWidth, width); mMeta->setInt32(kKeyHeight, height); } + + // Initialize quick stop variables. + mQuickStop = false; + mForceRead = false; + mLastReadBufferCopy = NULL; + mStopWaitingForIdleCamera = false; } CameraSourceTimeLapse::~CameraSourceTimeLapse() { } +void CameraSourceTimeLapse::startQuickReadReturns() { + Mutex::Autolock autoLock(mQuickStopLock); + LOGV("Enabling quick read returns"); + + // Enable quick stop mode. + mQuickStop = true; + + if (mUseStillCameraForTimeLapse) { + // wake up the thread right away. + mTakePictureCondition.signal(); + } else { + // Force dataCallbackTimestamp() coming from the video camera to not skip the + // next frame as we want read() to get a get a frame right away. + mForceRead = true; + } +} + bool CameraSourceTimeLapse::trySettingPreviewSize(int32_t width, int32_t height) { int64_t token = IPCThreadState::self()->clearCallingIdentity(); String8 s = mCamera->getParameters(); @@ -168,6 +191,53 @@ bool CameraSourceTimeLapse::computeCropRectangleOffset() { return true; } +void CameraSourceTimeLapse::signalBufferReturned(MediaBuffer* buffer) { + Mutex::Autolock autoLock(mQuickStopLock); + if (mQuickStop && (buffer == mLastReadBufferCopy)) { + buffer->setObserver(NULL); + buffer->release(); + } else { + return CameraSource::signalBufferReturned(buffer); + } +} + +void createMediaBufferCopy(const MediaBuffer& sourceBuffer, int64_t frameTime, MediaBuffer **newBuffer) { + size_t sourceSize = sourceBuffer.size(); + void* sourcePointer = sourceBuffer.data(); + + (*newBuffer) = new MediaBuffer(sourceSize); + memcpy((*newBuffer)->data(), sourcePointer, sourceSize); + + (*newBuffer)->meta_data()->setInt64(kKeyTime, frameTime); +} + +void CameraSourceTimeLapse::fillLastReadBufferCopy(MediaBuffer& sourceBuffer) { + int64_t frameTime; + CHECK(sourceBuffer.meta_data()->findInt64(kKeyTime, &frameTime)); + createMediaBufferCopy(sourceBuffer, frameTime, &mLastReadBufferCopy); + mLastReadBufferCopy->add_ref(); + mLastReadBufferCopy->setObserver(this); +} + +status_t CameraSourceTimeLapse::read( + MediaBuffer **buffer, const ReadOptions *options) { + if (mLastReadBufferCopy == NULL) { + mLastReadStatus = CameraSource::read(buffer, options); + + // mQuickStop may have turned to true while read was blocked. Make a copy of + // the buffer in that case. + Mutex::Autolock autoLock(mQuickStopLock); + if (mQuickStop && *buffer) { + fillLastReadBufferCopy(**buffer); + } + return mLastReadStatus; + } else { + (*buffer) = mLastReadBufferCopy; + (*buffer)->add_ref(); + return mLastReadStatus; + } +} + // static void *CameraSourceTimeLapse::ThreadTimeLapseWrapper(void *me) { CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me); @@ -176,17 +246,31 @@ void *CameraSourceTimeLapse::ThreadTimeLapseWrapper(void *me) { } void CameraSourceTimeLapse::threadTimeLapseEntry() { - while(mStarted) { - if (mCameraIdle) { - LOGV("threadTimeLapseEntry: taking picture"); - CHECK_EQ(OK, mCamera->takePicture()); + while (mStarted) { + { + Mutex::Autolock autoLock(mCameraIdleLock); + if (!mCameraIdle) { + mCameraIdleCondition.wait(mCameraIdleLock); + } + CHECK(mCameraIdle); mCameraIdle = false; - usleep(mTimeBetweenTimeLapseFrameCaptureUs); - } else { - LOGV("threadTimeLapseEntry: camera busy with old takePicture. Sleeping a little."); - usleep(1E4); } + + // Even if mQuickStop == true we need to take one more picture + // as a read() may be blocked, waiting for a frame to get available. + // After this takePicture, if mQuickStop == true, we can safely exit + // this thread as read() will make a copy of this last frame and keep + // returning it in the quick stop mode. + Mutex::Autolock autoLock(mQuickStopLock); + CHECK_EQ(OK, mCamera->takePicture()); + if (mQuickStop) { + LOGV("threadTimeLapseEntry: Exiting due to mQuickStop = true"); + return; + } + mTakePictureCondition.waitRelative(mQuickStopLock, + mTimeBetweenTimeLapseFrameCaptureUs * 1000); } + LOGV("threadTimeLapseEntry: Exiting due to mStarted = false"); } void CameraSourceTimeLapse::startCameraRecording() { @@ -201,6 +285,7 @@ void CameraSourceTimeLapse::startCameraRecording() { params.setPictureSize(mPictureWidth, mPictureHeight); mCamera->setParameters(params.flatten()); mCameraIdle = true; + mStopWaitingForIdleCamera = false; // disable shutter sound and play the recording sound. mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0); @@ -224,12 +309,26 @@ void CameraSourceTimeLapse::stopCameraRecording() { void *dummy; pthread_join(mThreadTimeLapse, &dummy); - // play the recording sound and restart preview. + // Last takePicture may still be underway. Wait for the camera to get + // idle. + Mutex::Autolock autoLock(mCameraIdleLock); + mStopWaitingForIdleCamera = true; + if (!mCameraIdle) { + mCameraIdleCondition.wait(mCameraIdleLock); + } + CHECK(mCameraIdle); + mCamera->setListener(NULL); + + // play the recording sound. mCamera->sendCommand(CAMERA_CMD_PLAY_RECORDING_SOUND, 0, 0); - CHECK_EQ(OK, mCamera->startPreview()); } else { + mCamera->setListener(NULL); mCamera->stopRecording(); } + if (mLastReadBufferCopy) { + mLastReadBufferCopy->release(); + mLastReadBufferCopy = NULL; + } } void CameraSourceTimeLapse::releaseRecordingFrame(const sp<IMemory>& frame) { @@ -264,7 +363,9 @@ void *CameraSourceTimeLapse::ThreadStartPreviewWrapper(void *me) { void CameraSourceTimeLapse::threadStartPreview() { CHECK_EQ(OK, mCamera->startPreview()); + Mutex::Autolock autoLock(mCameraIdleLock); mCameraIdle = true; + mCameraIdleCondition.signal(); } void CameraSourceTimeLapse::restartPreview() { @@ -358,7 +459,23 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { LOGV("dataCallbackTimestamp timelapse: initial frame"); mLastTimeLapseFrameRealTimestampUs = *timestampUs; - } else if (*timestampUs < + return false; + } + + { + Mutex::Autolock autoLock(mQuickStopLock); + + // mForceRead may be set to true by startQuickReadReturns(). In that + // case don't skip this frame. + if (mForceRead) { + LOGV("dataCallbackTimestamp timelapse: forced read"); + mForceRead = false; + *timestampUs = mLastFrameTimestampUs; + return false; + } + } + + if (*timestampUs < (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) { // Skip all frames from last encoded frame until // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed. @@ -374,6 +491,7 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { mLastTimeLapseFrameRealTimestampUs = *timestampUs; *timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs; + return false; } } return false; @@ -383,6 +501,18 @@ void CameraSourceTimeLapse::dataCallbackTimestamp(int64_t timestampUs, int32_t m const sp<IMemory> &data) { if (!mUseStillCameraForTimeLapse) { mSkipCurrentFrame = skipFrameAndModifyTimeStamp(×tampUs); + } else { + Mutex::Autolock autoLock(mCameraIdleLock); + // If we are using the still camera and stop() has been called, it may + // be waiting for the camera to get idle. In that case return + // immediately. Calling CameraSource::dataCallbackTimestamp() will lead + // to a deadlock since it tries to access CameraSource::mLock which in + // this case is held by CameraSource::stop() currently waiting for the + // camera to get idle. And camera will not get idle until this call + // returns. + if (mStopWaitingForIdleCamera) { + return; + } } CameraSource::dataCallbackTimestamp(timestampUs, msgType, data); } |