diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h index f153f095fd786..fa11b3ed5e57d 100644 --- a/include/media/stagefright/CameraSourceTimeLapse.h +++ b/include/media/stagefright/CameraSourceTimeLapse.h @@ -33,11 +33,13 @@ class CameraSourceTimeLapse : public CameraSource { public: static CameraSourceTimeLapse *Create(bool useStillCameraForTimeLapse, int64_t timeBetweenTimeLapseFrameCaptureUs, + int32_t width, int32_t height, int32_t videoFrameRate); static CameraSourceTimeLapse *CreateFromCamera(const sp &camera, bool useStillCameraForTimeLapse, int64_t timeBetweenTimeLapseFrameCaptureUs, + int32_t width, int32_t height, int32_t videoFrameRate); virtual ~CameraSourceTimeLapse(); @@ -70,6 +72,7 @@ private: CameraSourceTimeLapse(const sp &camera, bool useStillCameraForTimeLapse, int64_t timeBetweenTimeLapseFrameCaptureUs, + int32_t width, int32_t height, int32_t videoFrameRate); // For still camera case starts a thread which calls camera's takePicture() diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 47a8cfc1c07b3..c259b5831b50d 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -278,6 +278,23 @@ public class MediaRecorder setAudioEncoder(profile.audioCodec); } + /** + * Enables/Disables time lapse capture and sets its parameters. This method should + * be called after setProfile(). + * + * @param enableTimeLapse Pass true to enable time lapse capture, false to disable it. + * @param timeBetweenTimeLapseFrameCaptureMs time between two captures of time lapse frames. + * @param encoderLevel the video encoder level. + */ + public void setTimeLapseParameters(boolean enableTimeLapse, + int timeBetweenTimeLapseFrameCaptureMs, int encoderLevel) { + setParameter(String.format("time-lapse-enable=%d", + (enableTimeLapse) ? 1 : 0)); + setParameter(String.format("time-between-time-lapse-frame-capture=%d", + timeBetweenTimeLapseFrameCaptureMs)); + setVideoEncoderLevel(encoderLevel); + } + /** * Sets the format of the output file produced during recording. Call this * after setAudioSource()/setVideoSource() but before prepare(). @@ -444,6 +461,15 @@ public class MediaRecorder setParameter(String.format("video-param-encoding-bitrate=%d", bitRate)); } + /** + * Sets the level of the encoder. Call this before prepare(). + * + * @param encoderLevel the video encoder level. + */ + public void setVideoEncoderLevel(int encoderLevel) { + setParameter(String.format("video-param-encoder-level=%d", encoderLevel)); + } + /** * Pass in the file descriptor of the file to be written. Call this after * setOutputFormat() but before prepare(). diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 3dbcfd5b494ab..f747f903e2928 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -473,6 +473,32 @@ status_t StagefrightRecorder::setParamAudioTimeScale(int32_t timeScale) { return OK; } +status_t StagefrightRecorder::setParamTimeLapseEnable(int32_t timeLapseEnable) { + LOGV("setParamTimeLapseEnable: %d", timeLapseEnable); + + if(timeLapseEnable == 0) { + mCaptureTimeLapse = false; + } else if (timeLapseEnable == 1) { + mCaptureTimeLapse = true; + } else { + return BAD_VALUE; + } + return OK; +} + +status_t StagefrightRecorder::setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs) { + LOGV("setParamTimeBetweenTimeLapseFrameCapture: %lld us", timeUs); + + // Not allowing time more than a day + if (timeUs <= 0 || timeUs > 86400*1E6) { + LOGE("Time between time lapse frame capture (%lld) is out of range [0, 1 Day]", timeUs); + return BAD_VALUE; + } + + mTimeBetweenTimeLapseFrameCaptureUs = timeUs; + return OK; +} + status_t StagefrightRecorder::setParameter( const String8 &key, const String8 &value) { LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); @@ -556,6 +582,17 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &timeScale)) { return setParamVideoTimeScale(timeScale); } + } else if (key == "time-lapse-enable") { + int32_t timeLapseEnable; + if (safe_strtoi32(value.string(), &timeLapseEnable)) { + return setParamTimeLapseEnable(timeLapseEnable); + } + } else if (key == "time-between-time-lapse-frame-capture") { + int64_t timeBetweenTimeLapseFrameCaptureMs; + if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureMs)) { + return setParamTimeBetweenTimeLapseFrameCapture( + 1000LL * timeBetweenTimeLapseFrameCaptureMs); + } } else { LOGE("setParameter: failed to find key %s", key.string()); } @@ -813,10 +850,14 @@ void StagefrightRecorder::clipVideoFrameWidth() { } status_t StagefrightRecorder::setupCameraSource() { - clipVideoBitRate(); - clipVideoFrameRate(); - clipVideoFrameWidth(); - clipVideoFrameHeight(); + if(!mCaptureTimeLapse) { + // Dont clip for time lapse capture as encoder will have enough + // time to encode because of slow capture rate of time lapse. + clipVideoBitRate(); + clipVideoFrameRate(); + clipVideoFrameWidth(); + clipVideoFrameHeight(); + } int64_t token = IPCThreadState::self()->clearCallingIdentity(); if (mCamera == 0) { @@ -831,7 +872,13 @@ status_t StagefrightRecorder::setupCameraSource() { // Set the actual video recording frame size CameraParameters params(mCamera->getParameters()); - params.setPreviewSize(mVideoWidth, mVideoHeight); + + // dont change the preview size for time lapse as mVideoWidth, mVideoHeight + // may correspond to HD resolution not supported by video camera. + if (!mCaptureTimeLapse) { + params.setPreviewSize(mVideoWidth, mVideoHeight); + } + params.setPreviewFrameRate(mFrameRate); String8 s = params.flatten(); CHECK_EQ(OK, mCamera->setParameters(s)); @@ -840,8 +887,9 @@ status_t StagefrightRecorder::setupCameraSource() { // Check on video frame size int frameWidth = 0, frameHeight = 0; newCameraParams.getPreviewSize(&frameWidth, &frameHeight); - if (frameWidth < 0 || frameWidth != mVideoWidth || - frameHeight < 0 || frameHeight != mVideoHeight) { + if (!mCaptureTimeLapse && + (frameWidth < 0 || frameWidth != mVideoWidth || + frameHeight < 0 || frameHeight != mVideoHeight)) { LOGE("Failed to set the video frame size to %dx%d", mVideoWidth, mVideoHeight); IPCThreadState::self()->restoreCallingIdentity(token); @@ -882,7 +930,8 @@ status_t StagefrightRecorder::setupVideoEncoder(const sp& writer) { if (err != OK) return err; sp cameraSource = (mCaptureTimeLapse) ? - CameraSourceTimeLapse::CreateFromCamera(mCamera, true, 3E6, mFrameRate): + CameraSourceTimeLapse::CreateFromCamera(mCamera, true, + mTimeBetweenTimeLapseFrameCaptureUs, mVideoWidth, mVideoHeight, mFrameRate): CameraSource::CreateFromCamera(mCamera); CHECK(cameraSource != NULL); @@ -934,6 +983,7 @@ status_t StagefrightRecorder::setupVideoEncoder(const sp& writer) { OMXClient client; CHECK_EQ(client.connect(), OK); + // Use software codec for time lapse uint32_t encoder_flags = (mCaptureTimeLapse) ? OMXCodec::kPreferSoftwareCodecs : 0; sp encoder = OMXCodec::Create( client.interface(), enc_meta, @@ -1081,6 +1131,7 @@ status_t StagefrightRecorder::reset() { mMaxFileSizeBytes = 0; mTrackEveryTimeDurationUs = 0; mCaptureTimeLapse = false; + mTimeBetweenTimeLapseFrameCaptureUs = -1; mEncoderProfiles = MediaProfiles::getInstance(); mOutputFd = -1; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 232fc0ed3f0a3..fc287a26f8b24 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -93,6 +93,7 @@ private: int64_t mTrackEveryTimeDurationUs; bool mCaptureTimeLapse; + int64_t mTimeBetweenTimeLapseFrameCaptureUs; String8 mParams; int mOutputFd; @@ -114,6 +115,8 @@ private: status_t setParamAudioNumberOfChannels(int32_t channles); status_t setParamAudioSamplingRate(int32_t sampleRate); status_t setParamAudioTimeScale(int32_t timeScale); + status_t setParamTimeLapseEnable(int32_t timeLapseEnable); + status_t setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs); status_t setParamVideoEncodingBitRate(int32_t bitRate); status_t setParamVideoIFramesInterval(int32_t seconds); status_t setParamVideoEncoderProfile(int32_t profile); diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 30ed1436cc44e..c6186f61a1294 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -33,6 +33,7 @@ namespace android { // static CameraSourceTimeLapse *CameraSourceTimeLapse::Create(bool useStillCameraForTimeLapse, int64_t timeBetweenTimeLapseFrameCaptureUs, + int32_t width, int32_t height, int32_t videoFrameRate) { sp camera = Camera::connect(0); @@ -41,25 +42,27 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::Create(bool useStillCameraForTimeL } return new CameraSourceTimeLapse(camera, useStillCameraForTimeLapse, - timeBetweenTimeLapseFrameCaptureUs, videoFrameRate); + timeBetweenTimeLapseFrameCaptureUs, width, height, videoFrameRate); } // static CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera(const sp &camera, bool useStillCameraForTimeLapse, int64_t timeBetweenTimeLapseFrameCaptureUs, + int32_t width, int32_t height, int32_t videoFrameRate) { if (camera.get() == NULL) { return NULL; } return new CameraSourceTimeLapse(camera, useStillCameraForTimeLapse, - timeBetweenTimeLapseFrameCaptureUs, videoFrameRate); + timeBetweenTimeLapseFrameCaptureUs, width, height, videoFrameRate); } CameraSourceTimeLapse::CameraSourceTimeLapse(const sp &camera, bool useStillCameraForTimeLapse, int64_t timeBetweenTimeLapseFrameCaptureUs, + int32_t width, int32_t height, int32_t videoFrameRate) : CameraSource(camera), mUseStillCameraForTimeLapse(useStillCameraForTimeLapse), @@ -70,11 +73,6 @@ CameraSourceTimeLapse::CameraSourceTimeLapse(const sp &camera, LOGV("starting time lapse mode"); if(mUseStillCameraForTimeLapse) { - // Currently hardcoded the picture size. Will need to choose - // automatically or pass in from the app. - int32_t width, height; - width = 1024; - height = 768; mMeta->setInt32(kKeyWidth, width); mMeta->setInt32(kKeyHeight, height); }