summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorNipun Kwatra <nkwatra@google.com>2010-09-15 15:08:49 -0700
committerNipun Kwatra <nkwatra@google.com>2010-09-17 09:50:27 -0700
commit78eff720c86eb6d4e3d45a144df60b2ca464d2d4 (patch)
tree74d93ba7c4ae2d3904d40ef69f614d257c596547 /media
parent302a965f4bf5d179ccc68b7d853e2806941de020 (diff)
downloadframeworks_av-78eff720c86eb6d4e3d45a144df60b2ca464d2d4.zip
frameworks_av-78eff720c86eb6d4e3d45a144df60b2ca464d2d4.tar.gz
frameworks_av-78eff720c86eb6d4e3d45a144df60b2ca464d2d4.tar.bz2
Implemented frequent read returns for quick stop in time lapse.
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 return a copy of the last read frame with the same time stamp if a frame is not available quickly. This keeps the read() call from blocking too long. This method is triggered when startQuickReadReturns() is called on CameraSourceTimeLapse. In the still camera case, also using waitRelative on Condition instaed of sleeping, so that we can wake it up. Also for the idle check instead of sleeping, we now wait on a condition variable, which is woken up when the last takePicture callback gets called. Change-Id: Ia74386e175536aee0f44ae2f8b114c353d3d72f5
Diffstat (limited to 'media')
-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
4 files changed, 157 insertions, 16 deletions
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);
}