summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/stagefright/CameraSourceTimeLapse.h61
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp17
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.h2
-rw-r--r--media/libstagefright/CameraSource.cpp2
-rw-r--r--media/libstagefright/CameraSourceTimeLapse.cpp152
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(&timestampUs);
+ } 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);
}