From 54ff19ac69ace7c05ea90d225e26dab3b133f487 Mon Sep 17 00:00:00 2001 From: James Dong Date: Fri, 8 Oct 2010 11:59:32 -0700 Subject: Move Camera specific logic out from StagefrightRecorder to CameraSource o updated comments and streamlined the logic in checkVideoSize() and checkFrameRate() as suggested Change-Id: I49d04ac7998d4a215997aa63555dfb6e814e38d3 --- media/libstagefright/CameraSource.cpp | 382 ++++++++++++++++++++++--- media/libstagefright/CameraSourceTimeLapse.cpp | 68 +++-- 2 files changed, 378 insertions(+), 72 deletions(-) (limited to 'media/libstagefright') diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 0c9eef4..f7b76f8 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -108,28 +109,45 @@ static int32_t getColorFormat(const char* colorFormat) { CHECK_EQ(0, "Unknown color format"); } -// static CameraSource *CameraSource::Create() { - sp camera = Camera::connect(0); - - if (camera.get() == NULL) { - return NULL; - } + Size size; + size.width = -1; + size.height = -1; - return new CameraSource(camera); + sp camera; + return new CameraSource(camera, 0, size, -1, NULL); } // static -CameraSource *CameraSource::CreateFromCamera(const sp &camera) { - if (camera.get() == NULL) { - return NULL; +CameraSource *CameraSource::CreateFromCamera( + const sp& camera, + int32_t cameraId, + Size videoSize, + int32_t frameRate, + const sp& surface) { + + CameraSource *source = new CameraSource(camera, cameraId, + videoSize, frameRate, surface); + + if (source != NULL) { + if (source->initCheck() != OK) { + delete source; + return NULL; + } } - - return new CameraSource(camera); + return source; } -CameraSource::CameraSource(const sp &camera) - : mCamera(camera), +CameraSource::CameraSource( + const sp& camera, + int32_t cameraId, + Size videoSize, + int32_t frameRate, + const sp& surface) + : mCameraFlags(0), + mVideoFrameRate(-1), + mCamera(0), + mSurface(surface), mNumFramesReceived(0), mLastFrameTimestampUs(0), mStarted(false), @@ -140,39 +158,316 @@ CameraSource::CameraSource(const sp &camera) mGlitchDurationThresholdUs(200000), mCollectStats(false) { + mVideoSize.width = -1; + mVideoSize.height = -1; + + mInitCheck = init(camera, cameraId, videoSize, frameRate); +} + +status_t CameraSource::initCheck() const { + return mInitCheck; +} + +status_t CameraSource::isCameraAvailable( + const sp& camera, int32_t cameraId) { + + if (camera == 0) { + mCamera = Camera::connect(cameraId); + mCameraFlags &= ~FLAGS_HOT_CAMERA; + } else { + mCamera = Camera::create(camera); + mCameraFlags |= FLAGS_HOT_CAMERA; + } + + // Is camera available? + if (mCamera == 0) { + LOGE("Camera connection could not be established."); + return -EBUSY; + } + if (!(mCameraFlags & FLAGS_HOT_CAMERA)) { + mCamera->lock(); + } + return OK; +} + + +/* + * Check to see whether the requested video width and height is one + * of the supported sizes. + * @param width the video frame width in pixels + * @param height the video frame height in pixels + * @param suppportedSizes the vector of sizes that we check against + * @return true if the dimension (width and height) is supported. + */ +static bool isVideoSizeSupported( + int32_t width, int32_t height, + const Vector& supportedSizes) { + + LOGV("isVideoSizeSupported"); + for (size_t i = 0; i < supportedSizes.size(); ++i) { + if (width == supportedSizes[i].width && + height == supportedSizes[i].height) { + return true; + } + } + return false; +} + +/* + * If the preview and video output is separate, we only set the + * the video size, and applications should set the preview size + * to some proper value, and the recording framework will not + * change the preview size; otherwise, if the video and preview + * output is the same, we need to set the preview to be the same + * as the requested video size. + * + */ +/* + * Query the camera to retrieve the supported video frame sizes + * and also to see whether CameraParameters::setVideoSize() + * is supported or not. + * @param params CameraParameters to retrieve the information + * @@param isSetVideoSizeSupported retunrs whether method + * CameraParameters::setVideoSize() is supported or not. + * @param sizes returns the vector of Size objects for the + * supported video frame sizes advertised by the camera. + */ +static void getSupportedVideoSizes( + const CameraParameters& params, + bool *isSetVideoSizeSupported, + Vector& sizes) { + + *isSetVideoSizeSupported = true; + params.getSupportedVideoSizes(sizes); + if (sizes.size() == 0) { + LOGD("Camera does not support setVideoSize()"); + params.getSupportedPreviewSizes(sizes); + *isSetVideoSizeSupported = false; + } +} + +/* + * Check whether the camera has the supported color format + * @param params CameraParameters to retrieve the information + * @return OK if no error. + */ +status_t CameraSource::isCameraColorFormatSupported( + const CameraParameters& params) { + mColorFormat = getColorFormat(params.get( + CameraParameters::KEY_VIDEO_FRAME_FORMAT)); + if (mColorFormat == -1) { + return BAD_VALUE; + } + return OK; +} + +/* + * Configure the camera to use the requested video size + * (width and height) and/or frame rate. If both width and + * height are -1, configuration on the video size is skipped. + * if frameRate is -1, configuration on the frame rate + * is skipped. Skipping the configuration allows one to + * use the current camera setting without the need to + * actually know the specific values (see Create() method). + * + * @param params the CameraParameters to be configured + * @param width the target video frame width in pixels + * @param height the target video frame height in pixels + * @param frameRate the target frame rate in frames per second. + * @return OK if no error. + */ +status_t CameraSource::configureCamera( + CameraParameters* params, + int32_t width, int32_t height, + int32_t frameRate) { + + Vector sizes; + bool isSetVideoSizeSupportedByCamera = true; + getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes); + bool isCameraParamChanged = false; + if (width != -1 && height != -1) { + if (!isVideoSizeSupported(width, height, sizes)) { + LOGE("Video dimension (%dx%d) is unsupported", width, height); + return BAD_VALUE; + } + if (isSetVideoSizeSupportedByCamera) { + params->setVideoSize(width, height); + } else { + params->setPreviewSize(width, height); + } + isCameraParamChanged = true; + } else if ((width == -1 && height != -1) || + (width != -1 && height == -1)) { + // If one and only one of the width and height is -1 + // we reject such a request. + LOGE("Requested video size (%dx%d) is not supported", width, height); + return BAD_VALUE; + } else { // width == -1 && height == -1 + // Do not configure the camera. + // Use the current width and height value setting from the camera. + } + + if (frameRate != -1) { + params->setPreviewFrameRate(frameRate); + isCameraParamChanged = true; + } else { // frameRate == -1 + // Do not configure the camera. + // Use the current frame rate value setting from the camera + } + + if (isCameraParamChanged) { + // Either frame rate or frame size needs to be changed. + String8 s = params->flatten(); + if (OK != mCamera->setParameters(s)) { + LOGE("Could not change settings." + " Someone else is using camera %p?", mCamera.get()); + return -EBUSY; + } + } + return OK; +} + +/* + * Check whether the requested video frame size + * has been successfully configured or not. If both width and height + * are -1, check on the current width and height value setting + * is performed. + * + * @param params CameraParameters to retrieve the information + * @param the target video frame width in pixels to check against + * @param the target video frame height in pixels to check against + * @return OK if no error + */ +status_t CameraSource::checkVideoSize( + const CameraParameters& params, + int32_t width, int32_t height) { + + int32_t frameWidthActual = -1; + int32_t frameHeightActual = -1; + params.getPreviewSize(&frameWidthActual, &frameHeightActual); + if (frameWidthActual < 0 || frameHeightActual < 0) { + LOGE("Failed to retrieve video frame size (%dx%d)", + frameWidthActual, frameHeightActual); + return UNKNOWN_ERROR; + } + + // Check the actual video frame size against the target/requested + // video frame size. + if (width != -1 && height != -1) { + if (frameWidthActual != width || frameHeightActual != height) { + LOGE("Failed to set video frame size to %dx%d. " + "The actual video size is %dx%d ", width, height, + frameWidthActual, frameHeightActual); + return UNKNOWN_ERROR; + } + } + + // Good now. + mVideoSize.width = frameWidthActual; + mVideoSize.height = frameHeightActual; + return OK; +} + +/* + * Check the requested frame rate has been successfully configured or not. + * If the target frameRate is -1, check on the current frame rate value + * setting is performed. + * + * @param params CameraParameters to retrieve the information + * @param the target video frame rate to check against + * @return OK if no error. + */ +status_t CameraSource::checkFrameRate( + const CameraParameters& params, + int32_t frameRate) { + + int32_t frameRateActual = params.getPreviewFrameRate(); + if (frameRateActual < 0) { + LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual); + return UNKNOWN_ERROR; + } + + // Check the actual video frame rate against the target/requested + // video frame rate. + if (frameRate != -1 && (frameRateActual - frameRate) != 0) { + LOGE("Failed to set preview frame rate to %d fps. The actual " + "frame rate is %d", frameRate, frameRateActual); + return UNKNOWN_ERROR; + } + + // Good now. + mVideoFrameRate = frameRateActual; + return OK; +} + +/* + * Initialize the CameraSource to so that it becomes + * ready for providing the video input streams as requested. + * @param camera the camera object used for the video source + * @param cameraId if camera == 0, use camera with this id + * as the video source + * @param videoSize the target video frame size. If both + * width and height in videoSize is -1, use the current + * width and heigth settings by the camera + * @param frameRate the target frame rate in frames per second. + * if it is -1, use the current camera frame rate setting. + * @return OK if no error. + */ +status_t CameraSource::init( + const sp& camera, + int32_t cameraId, + Size videoSize, + int32_t frameRate) { + + status_t err = OK; int64_t token = IPCThreadState::self()->clearCallingIdentity(); - String8 s = mCamera->getParameters(); - IPCThreadState::self()->restoreCallingIdentity(token); - printf("params: \"%s\"\n", s.string()); + if ((err = isCameraAvailable(camera, cameraId)) != OK) { + return err; + } + CameraParameters params(mCamera->getParameters()); + if ((err = isCameraColorFormatSupported(params)) != OK) { + return err; + } - int32_t width, height, stride, sliceHeight; - CameraParameters params(s); - params.getPreviewSize(&width, &height); + // Set the camera to use the requested video frame size + // and/or frame rate. + if ((err = configureCamera(¶ms, + videoSize.width, videoSize.height, + frameRate))) { + return err; + } - // Calculate glitch duraton threshold based on frame rate - int32_t frameRate = params.getPreviewFrameRate(); - int64_t glitchDurationUs = (1000000LL / frameRate); + // Check on video frame size and frame rate. + CameraParameters newCameraParams(mCamera->getParameters()); + if ((err = checkVideoSize(newCameraParams, + videoSize.width, videoSize.height)) != OK) { + return err; + } + if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) { + return err; + } + + // This CHECK is good, since we just passed the lock/unlock + // check earlier by calling mCamera->setParameters(). + CHECK_EQ(OK, mCamera->setPreviewDisplay(mSurface)); + IPCThreadState::self()->restoreCallingIdentity(token); + + int64_t glitchDurationUs = (1000000LL / mVideoFrameRate); if (glitchDurationUs > mGlitchDurationThresholdUs) { mGlitchDurationThresholdUs = glitchDurationUs; } - const char *colorFormatStr = params.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT); - CHECK(colorFormatStr != NULL); - int32_t colorFormat = getColorFormat(colorFormatStr); - // XXX: query camera for the stride and slice height // when the capability becomes available. - stride = width; - sliceHeight = height; - mMeta = new MetaData; - mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); - mMeta->setInt32(kKeyColorFormat, colorFormat); - mMeta->setInt32(kKeyWidth, width); - mMeta->setInt32(kKeyHeight, height); - mMeta->setInt32(kKeyStride, stride); - mMeta->setInt32(kKeySliceHeight, sliceHeight); + mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); + mMeta->setInt32(kKeyColorFormat, mColorFormat); + mMeta->setInt32(kKeyWidth, mVideoSize.width); + mMeta->setInt32(kKeyHeight, mVideoSize.height); + mMeta->setInt32(kKeyStride, mVideoSize.width); + mMeta->setInt32(kKeySliceHeight, mVideoSize.height); + return OK; } CameraSource::~CameraSource() { @@ -187,6 +482,10 @@ void CameraSource::startCameraRecording() { status_t CameraSource::start(MetaData *meta) { CHECK(!mStarted); + if (mInitCheck != OK) { + LOGE("CameraSource is not initialized yet"); + return mInitCheck; + } char value[PROPERTY_VALUE_MAX]; if (property_get("media.stagefright.record-stats", value, NULL) @@ -228,7 +527,16 @@ status_t CameraSource::stop() { mFramesBeingEncoded.size()); mFrameCompleteCondition.wait(mLock); } - mCamera = NULL; + + LOGV("Disconnect camera"); + if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { + LOGV("Camera was cold when we started, stopping preview"); + mCamera->stopPreview(); + } + mCamera->unlock(); + mCamera.clear(); + mCamera = 0; + mCameraFlags = 0; IPCThreadState::self()->restoreCallingIdentity(token); if (mCollectStats) { diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index c1bc433..2f3f20b 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -37,57 +37,55 @@ namespace android { // static -CameraSourceTimeLapse *CameraSourceTimeLapse::Create( - int64_t timeBetweenTimeLapseFrameCaptureUs, - int32_t width, int32_t height, - int32_t videoFrameRate) { - sp camera = Camera::connect(0); - - if (camera.get() == NULL) { - return NULL; - } - - return new CameraSourceTimeLapse(camera, timeBetweenTimeLapseFrameCaptureUs, - width, height, videoFrameRate); -} - -// static -CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera(const sp &camera, - int64_t timeBetweenTimeLapseFrameCaptureUs, - int32_t width, int32_t height, - int32_t videoFrameRate) { - if (camera.get() == NULL) { - return NULL; +CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera( + const sp &camera, + int32_t cameraId, + Size videoSize, + int32_t videoFrameRate, + const sp& surface, + int64_t timeBetweenTimeLapseFrameCaptureUs) { + + CameraSourceTimeLapse *source = new + CameraSourceTimeLapse(camera, cameraId, + videoSize, videoFrameRate, surface, + timeBetweenTimeLapseFrameCaptureUs); + + if (source != NULL) { + if (source->initCheck() != OK) { + delete source; + return NULL; + } } - - return new CameraSourceTimeLapse(camera, timeBetweenTimeLapseFrameCaptureUs, - width, height, videoFrameRate); + return source; } -CameraSourceTimeLapse::CameraSourceTimeLapse(const sp &camera, - int64_t timeBetweenTimeLapseFrameCaptureUs, - int32_t width, int32_t height, - int32_t videoFrameRate) - : CameraSource(camera), +CameraSourceTimeLapse::CameraSourceTimeLapse( + const sp& camera, + int32_t cameraId, + Size videoSize, + int32_t videoFrameRate, + const sp& surface, + int64_t timeBetweenTimeLapseFrameCaptureUs) + : CameraSource(camera, cameraId, videoSize, videoFrameRate, surface), mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs), mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate), mLastTimeLapseFrameRealTimestampUs(0), mSkipCurrentFrame(false) { LOGV("starting time lapse mode"); - mVideoWidth = width; - mVideoHeight = height; + mVideoWidth = videoSize.width; + mVideoHeight = videoSize.height; - if (trySettingPreviewSize(width, height)) { + if (trySettingPreviewSize(videoSize.width, videoSize.height)) { mUseStillCameraForTimeLapse = false; } else { // TODO: Add a check to see that mTimeBetweenTimeLapseFrameCaptureUs is greater // than the fastest rate at which the still camera can take pictures. mUseStillCameraForTimeLapse = true; - CHECK(setPictureSizeToClosestSupported(width, height)); + CHECK(setPictureSizeToClosestSupported(videoSize.width, videoSize.height)); mNeedCropping = computeCropRectangleOffset(); - mMeta->setInt32(kKeyWidth, width); - mMeta->setInt32(kKeyHeight, height); + mMeta->setInt32(kKeyWidth, videoSize.width); + mMeta->setInt32(kKeyHeight, videoSize.height); } // Initialize quick stop variables. -- cgit v1.1