michael@0: /* michael@0: * Copyright (C) 2009 The Android Open Source Project michael@0: * Copyright (C) 2013 Mozilla Foundation michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #include michael@0: #include "nsDebug.h" michael@0: #define DOM_CAMERA_LOG_LEVEL 3 michael@0: #include "CameraCommon.h" michael@0: /* michael@0: #define CS_LOGD(...) DOM_CAMERA_LOGA(__VA_ARGS__) michael@0: #define CS_LOGV(...) DOM_CAMERA_LOGI(__VA_ARGS__) michael@0: #define CS_LOGI(...) DOM_CAMERA_LOGI(__VA_ARGS__) michael@0: #define CS_LOGW(...) DOM_CAMERA_LOGW(__VA_ARGS__) michael@0: #define CS_LOGE(...) DOM_CAMERA_LOGE(__VA_ARGS__) michael@0: */ michael@0: michael@0: #define CS_LOGD(fmt, ...) DOM_CAMERA_LOGA("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) michael@0: #define CS_LOGV(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) michael@0: #define CS_LOGI(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) michael@0: #define CS_LOGW(fmt, ...) DOM_CAMERA_LOGW("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) michael@0: #define CS_LOGE(fmt, ...) DOM_CAMERA_LOGE("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "GonkCameraSource.h" michael@0: #include "GonkCameraListener.h" michael@0: #include "GonkCameraHwMgr.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: namespace android { michael@0: michael@0: static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL; michael@0: michael@0: struct GonkCameraSourceListener : public GonkCameraListener { michael@0: GonkCameraSourceListener(const sp &source); michael@0: michael@0: virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); michael@0: virtual void postData(int32_t msgType, const sp &dataPtr, michael@0: camera_frame_metadata_t *metadata); michael@0: michael@0: virtual void postDataTimestamp( michael@0: nsecs_t timestamp, int32_t msgType, const sp& dataPtr); michael@0: michael@0: protected: michael@0: virtual ~GonkCameraSourceListener(); michael@0: michael@0: private: michael@0: wp mSource; michael@0: michael@0: GonkCameraSourceListener(const GonkCameraSourceListener &); michael@0: GonkCameraSourceListener &operator=(const GonkCameraSourceListener &); michael@0: }; michael@0: michael@0: GonkCameraSourceListener::GonkCameraSourceListener(const sp &source) michael@0: : mSource(source) { michael@0: } michael@0: michael@0: GonkCameraSourceListener::~GonkCameraSourceListener() { michael@0: } michael@0: michael@0: void GonkCameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { michael@0: CS_LOGV("notify(%d, %d, %d)", msgType, ext1, ext2); michael@0: } michael@0: michael@0: void GonkCameraSourceListener::postData(int32_t msgType, const sp &dataPtr, michael@0: camera_frame_metadata_t *metadata) { michael@0: CS_LOGV("postData(%d, ptr:%p, size:%d)", michael@0: msgType, dataPtr->pointer(), dataPtr->size()); michael@0: michael@0: sp source = mSource.promote(); michael@0: if (source.get() != NULL) { michael@0: source->dataCallback(msgType, dataPtr); michael@0: } michael@0: } michael@0: michael@0: void GonkCameraSourceListener::postDataTimestamp( michael@0: nsecs_t timestamp, int32_t msgType, const sp& dataPtr) { michael@0: michael@0: sp source = mSource.promote(); michael@0: if (source.get() != NULL) { michael@0: source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr); michael@0: } michael@0: } michael@0: michael@0: static int32_t getColorFormat(const char* colorFormat) { michael@0: return OMX_COLOR_FormatYUV420SemiPlanar; //XXX nsGonkCameraControl uses only YUV420SemiPlanar michael@0: michael@0: if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) { michael@0: return OMX_COLOR_FormatYUV420Planar; michael@0: } michael@0: michael@0: if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) { michael@0: return OMX_COLOR_FormatYUV422SemiPlanar; michael@0: } michael@0: michael@0: if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP)) { michael@0: return OMX_COLOR_FormatYUV420SemiPlanar; michael@0: } michael@0: michael@0: if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I)) { michael@0: return OMX_COLOR_FormatYCbYCr; michael@0: } michael@0: michael@0: if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) { michael@0: return OMX_COLOR_Format16bitRGB565; michael@0: } michael@0: michael@0: if (!strcmp(colorFormat, "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar")) { michael@0: return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar; michael@0: } michael@0: #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 michael@0: if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE)) { michael@0: return OMX_COLOR_FormatAndroidOpaque; michael@0: } michael@0: #endif michael@0: CS_LOGE("Uknown color format (%s), please add it to " michael@0: "GonkCameraSource::getColorFormat", colorFormat); michael@0: michael@0: CHECK(!"Unknown color format"); michael@0: } michael@0: michael@0: GonkCameraSource *GonkCameraSource::Create( michael@0: const sp& aCameraHw, michael@0: Size videoSize, michael@0: int32_t frameRate, michael@0: bool storeMetaDataInVideoBuffers) { michael@0: michael@0: GonkCameraSource *source = new GonkCameraSource(aCameraHw, michael@0: videoSize, frameRate, michael@0: storeMetaDataInVideoBuffers); michael@0: return source; michael@0: } michael@0: michael@0: GonkCameraSource::GonkCameraSource( michael@0: const sp& aCameraHw, michael@0: Size videoSize, michael@0: int32_t frameRate, michael@0: bool storeMetaDataInVideoBuffers) michael@0: : mCameraFlags(0), michael@0: mNumInputBuffers(0), michael@0: mVideoFrameRate(-1), michael@0: mNumFramesReceived(0), michael@0: mLastFrameTimestampUs(0), michael@0: mStarted(false), michael@0: mNumFramesEncoded(0), michael@0: mTimeBetweenFrameCaptureUs(0), michael@0: mFirstFrameTimeUs(0), michael@0: mNumFramesDropped(0), michael@0: mNumGlitches(0), michael@0: mGlitchDurationThresholdUs(200000), michael@0: mCollectStats(false), michael@0: mCameraHw(aCameraHw) { michael@0: mVideoSize.width = -1; michael@0: mVideoSize.height = -1; michael@0: michael@0: mInitCheck = init( michael@0: videoSize, frameRate, michael@0: storeMetaDataInVideoBuffers); michael@0: if (mInitCheck != OK) releaseCamera(); michael@0: } michael@0: michael@0: status_t GonkCameraSource::initCheck() const { michael@0: return mInitCheck; michael@0: } michael@0: michael@0: //TODO: Do we need to reimplement isCameraAvailable? michael@0: michael@0: /* michael@0: * Check to see whether the requested video width and height is one michael@0: * of the supported sizes. michael@0: * @param width the video frame width in pixels michael@0: * @param height the video frame height in pixels michael@0: * @param suppportedSizes the vector of sizes that we check against michael@0: * @return true if the dimension (width and height) is supported. michael@0: */ michael@0: static bool isVideoSizeSupported( michael@0: int32_t width, int32_t height, michael@0: const Vector& supportedSizes) { michael@0: michael@0: CS_LOGV("isVideoSizeSupported"); michael@0: for (size_t i = 0; i < supportedSizes.size(); ++i) { michael@0: if (width == supportedSizes[i].width && michael@0: height == supportedSizes[i].height) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * If the preview and video output is separate, we only set the michael@0: * the video size, and applications should set the preview size michael@0: * to some proper value, and the recording framework will not michael@0: * change the preview size; otherwise, if the video and preview michael@0: * output is the same, we need to set the preview to be the same michael@0: * as the requested video size. michael@0: * michael@0: */ michael@0: /* michael@0: * Query the camera to retrieve the supported video frame sizes michael@0: * and also to see whether CameraParameters::setVideoSize() michael@0: * is supported or not. michael@0: * @param params CameraParameters to retrieve the information michael@0: * @@param isSetVideoSizeSupported retunrs whether method michael@0: * CameraParameters::setVideoSize() is supported or not. michael@0: * @param sizes returns the vector of Size objects for the michael@0: * supported video frame sizes advertised by the camera. michael@0: */ michael@0: static void getSupportedVideoSizes( michael@0: const CameraParameters& params, michael@0: bool *isSetVideoSizeSupported, michael@0: Vector& sizes) { michael@0: michael@0: *isSetVideoSizeSupported = true; michael@0: params.getSupportedVideoSizes(sizes); michael@0: if (sizes.size() == 0) { michael@0: CS_LOGD("Camera does not support setVideoSize()"); michael@0: params.getSupportedPreviewSizes(sizes); michael@0: *isSetVideoSizeSupported = false; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Check whether the camera has the supported color format michael@0: * @param params CameraParameters to retrieve the information michael@0: * @return OK if no error. michael@0: */ michael@0: status_t GonkCameraSource::isCameraColorFormatSupported( michael@0: const CameraParameters& params) { michael@0: mColorFormat = getColorFormat(params.get( michael@0: CameraParameters::KEY_VIDEO_FRAME_FORMAT)); michael@0: if (mColorFormat == -1) { michael@0: return BAD_VALUE; michael@0: } michael@0: return OK; michael@0: } michael@0: michael@0: /* michael@0: * Configure the camera to use the requested video size michael@0: * (width and height) and/or frame rate. If both width and michael@0: * height are -1, configuration on the video size is skipped. michael@0: * if frameRate is -1, configuration on the frame rate michael@0: * is skipped. Skipping the configuration allows one to michael@0: * use the current camera setting without the need to michael@0: * actually know the specific values (see Create() method). michael@0: * michael@0: * @param params the CameraParameters to be configured michael@0: * @param width the target video frame width in pixels michael@0: * @param height the target video frame height in pixels michael@0: * @param frameRate the target frame rate in frames per second. michael@0: * @return OK if no error. michael@0: */ michael@0: status_t GonkCameraSource::configureCamera( michael@0: CameraParameters* params, michael@0: int32_t width, int32_t height, michael@0: int32_t frameRate) { michael@0: CS_LOGV("configureCamera"); michael@0: Vector sizes; michael@0: bool isSetVideoSizeSupportedByCamera = true; michael@0: getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes); michael@0: bool isCameraParamChanged = false; michael@0: if (width != -1 && height != -1) { michael@0: if (!isVideoSizeSupported(width, height, sizes)) { michael@0: CS_LOGE("Video dimension (%dx%d) is unsupported", width, height); michael@0: return BAD_VALUE; michael@0: } michael@0: if (isSetVideoSizeSupportedByCamera) { michael@0: params->setVideoSize(width, height); michael@0: } else { michael@0: params->setPreviewSize(width, height); michael@0: } michael@0: isCameraParamChanged = true; michael@0: } else if ((width == -1 && height != -1) || michael@0: (width != -1 && height == -1)) { michael@0: // If one and only one of the width and height is -1 michael@0: // we reject such a request. michael@0: CS_LOGE("Requested video size (%dx%d) is not supported", width, height); michael@0: return BAD_VALUE; michael@0: } else { // width == -1 && height == -1 michael@0: // Do not configure the camera. michael@0: // Use the current width and height value setting from the camera. michael@0: } michael@0: michael@0: if (frameRate != -1) { michael@0: CHECK(frameRate > 0 && frameRate <= 120); michael@0: const char* supportedFrameRates = michael@0: params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES); michael@0: CHECK(supportedFrameRates != NULL); michael@0: CS_LOGV("Supported frame rates: %s", supportedFrameRates); michael@0: char buf[4]; michael@0: snprintf(buf, 4, "%d", frameRate); michael@0: if (strstr(supportedFrameRates, buf) == NULL) { michael@0: CS_LOGE("Requested frame rate (%d) is not supported: %s", michael@0: frameRate, supportedFrameRates); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: // The frame rate is supported, set the camera to the requested value. michael@0: params->setPreviewFrameRate(frameRate); michael@0: isCameraParamChanged = true; michael@0: } else { // frameRate == -1 michael@0: // Do not configure the camera. michael@0: // Use the current frame rate value setting from the camera michael@0: } michael@0: michael@0: if (isCameraParamChanged) { michael@0: // Either frame rate or frame size needs to be changed. michael@0: if (OK != mCameraHw->PushParameters(*params)) { michael@0: CS_LOGE("Could not change settings." michael@0: " Someone else is using camera?"); michael@0: return -EBUSY; michael@0: } michael@0: } michael@0: return OK; michael@0: } michael@0: michael@0: /* michael@0: * Check whether the requested video frame size michael@0: * has been successfully configured or not. If both width and height michael@0: * are -1, check on the current width and height value setting michael@0: * is performed. michael@0: * michael@0: * @param params CameraParameters to retrieve the information michael@0: * @param the target video frame width in pixels to check against michael@0: * @param the target video frame height in pixels to check against michael@0: * @return OK if no error michael@0: */ michael@0: status_t GonkCameraSource::checkVideoSize( michael@0: const CameraParameters& params, michael@0: int32_t width, int32_t height) { michael@0: michael@0: CS_LOGV("checkVideoSize"); michael@0: // The actual video size is the same as the preview size michael@0: // if the camera hal does not support separate video and michael@0: // preview output. In this case, we retrieve the video michael@0: // size from preview. michael@0: int32_t frameWidthActual = -1; michael@0: int32_t frameHeightActual = -1; michael@0: Vector sizes; michael@0: params.getSupportedVideoSizes(sizes); michael@0: if (sizes.size() == 0) { michael@0: // video size is the same as preview size michael@0: params.getPreviewSize(&frameWidthActual, &frameHeightActual); michael@0: } else { michael@0: // video size may not be the same as preview michael@0: params.getVideoSize(&frameWidthActual, &frameHeightActual); michael@0: } michael@0: if (frameWidthActual < 0 || frameHeightActual < 0) { michael@0: CS_LOGE("Failed to retrieve video frame size (%dx%d)", michael@0: frameWidthActual, frameHeightActual); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: michael@0: // Check the actual video frame size against the target/requested michael@0: // video frame size. michael@0: if (width != -1 && height != -1) { michael@0: if (frameWidthActual != width || frameHeightActual != height) { michael@0: CS_LOGE("Failed to set video frame size to %dx%d. " michael@0: "The actual video size is %dx%d ", width, height, michael@0: frameWidthActual, frameHeightActual); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: } michael@0: michael@0: // Good now. michael@0: mVideoSize.width = frameWidthActual; michael@0: mVideoSize.height = frameHeightActual; michael@0: return OK; michael@0: } michael@0: michael@0: /* michael@0: * Check the requested frame rate has been successfully configured or not. michael@0: * If the target frameRate is -1, check on the current frame rate value michael@0: * setting is performed. michael@0: * michael@0: * @param params CameraParameters to retrieve the information michael@0: * @param the target video frame rate to check against michael@0: * @return OK if no error. michael@0: */ michael@0: status_t GonkCameraSource::checkFrameRate( michael@0: const CameraParameters& params, michael@0: int32_t frameRate) { michael@0: michael@0: CS_LOGV("checkFrameRate"); michael@0: int32_t frameRateActual = params.getPreviewFrameRate(); michael@0: if (frameRateActual < 0) { michael@0: CS_LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: michael@0: // Check the actual video frame rate against the target/requested michael@0: // video frame rate. michael@0: if (frameRate != -1 && (frameRateActual - frameRate) != 0) { michael@0: CS_LOGE("Failed to set preview frame rate to %d fps. The actual " michael@0: "frame rate is %d", frameRate, frameRateActual); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: michael@0: // Good now. michael@0: mVideoFrameRate = frameRateActual; michael@0: return OK; michael@0: } michael@0: michael@0: /* michael@0: * Initialize the GonkCameraSource so that it becomes michael@0: * ready for providing the video input streams as requested. michael@0: * @param camera the camera object used for the video source michael@0: * @param cameraId if camera == 0, use camera with this id michael@0: * as the video source michael@0: * @param videoSize the target video frame size. If both michael@0: * width and height in videoSize is -1, use the current michael@0: * width and heigth settings by the camera michael@0: * @param frameRate the target frame rate in frames per second. michael@0: * if it is -1, use the current camera frame rate setting. michael@0: * @param storeMetaDataInVideoBuffers request to store meta michael@0: * data or real YUV data in video buffers. Request to michael@0: * store meta data in video buffers may not be honored michael@0: * if the source does not support this feature. michael@0: * michael@0: * @return OK if no error. michael@0: */ michael@0: status_t GonkCameraSource::init( michael@0: Size videoSize, michael@0: int32_t frameRate, michael@0: bool storeMetaDataInVideoBuffers) { michael@0: michael@0: CS_LOGV("init"); michael@0: status_t err = OK; michael@0: //TODO: need to do something here to check the sanity of camera michael@0: michael@0: CameraParameters params; michael@0: mCameraHw->PullParameters(params); michael@0: if ((err = isCameraColorFormatSupported(params)) != OK) { michael@0: return err; michael@0: } michael@0: michael@0: // Set the camera to use the requested video frame size michael@0: // and/or frame rate. michael@0: if ((err = configureCamera(¶ms, michael@0: videoSize.width, videoSize.height, michael@0: frameRate))) { michael@0: return err; michael@0: } michael@0: michael@0: // Check on video frame size and frame rate. michael@0: CameraParameters newCameraParams; michael@0: mCameraHw->PullParameters(newCameraParams); michael@0: if ((err = checkVideoSize(newCameraParams, michael@0: videoSize.width, videoSize.height)) != OK) { michael@0: return err; michael@0: } michael@0: if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) { michael@0: return err; michael@0: } michael@0: michael@0: // By default, do not store metadata in video buffers michael@0: mIsMetaDataStoredInVideoBuffers = false; michael@0: mCameraHw->StoreMetaDataInBuffers(false); michael@0: if (storeMetaDataInVideoBuffers) { michael@0: if (OK == mCameraHw->StoreMetaDataInBuffers(true)) { michael@0: mIsMetaDataStoredInVideoBuffers = true; michael@0: } michael@0: } michael@0: michael@0: int64_t glitchDurationUs = (1000000LL / mVideoFrameRate); michael@0: if (glitchDurationUs > mGlitchDurationThresholdUs) { michael@0: mGlitchDurationThresholdUs = glitchDurationUs; michael@0: } michael@0: michael@0: // XXX: query camera for the stride and slice height michael@0: // when the capability becomes available. michael@0: mMeta = new MetaData; michael@0: mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); michael@0: mMeta->setInt32(kKeyColorFormat, mColorFormat); michael@0: mMeta->setInt32(kKeyWidth, mVideoSize.width); michael@0: mMeta->setInt32(kKeyHeight, mVideoSize.height); michael@0: mMeta->setInt32(kKeyStride, mVideoSize.width); michael@0: mMeta->setInt32(kKeySliceHeight, mVideoSize.height); michael@0: mMeta->setInt32(kKeyFrameRate, mVideoFrameRate); michael@0: return OK; michael@0: } michael@0: michael@0: GonkCameraSource::~GonkCameraSource() { michael@0: if (mStarted) { michael@0: reset(); michael@0: } else if (mInitCheck == OK) { michael@0: // Camera is initialized but because start() is never called, michael@0: // the lock on Camera is never released(). This makes sure michael@0: // Camera's lock is released in this case. michael@0: // TODO: Don't think I need to do this michael@0: releaseCamera(); michael@0: } michael@0: } michael@0: michael@0: int GonkCameraSource::startCameraRecording() { michael@0: CS_LOGV("startCameraRecording"); michael@0: return mCameraHw->StartRecording(); michael@0: } michael@0: michael@0: status_t GonkCameraSource::start(MetaData *meta) { michael@0: int rv; michael@0: michael@0: CS_LOGV("start"); michael@0: CHECK(!mStarted); michael@0: if (mInitCheck != OK) { michael@0: CS_LOGE("GonkCameraSource is not initialized yet"); michael@0: return mInitCheck; michael@0: } michael@0: michael@0: char value[PROPERTY_VALUE_MAX]; michael@0: if (property_get("media.stagefright.record-stats", value, NULL) michael@0: && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { michael@0: mCollectStats = true; michael@0: } michael@0: michael@0: mStartTimeUs = 0; michael@0: mNumInputBuffers = 0; michael@0: if (meta) { michael@0: int64_t startTimeUs; michael@0: if (meta->findInt64(kKeyTime, &startTimeUs)) { michael@0: mStartTimeUs = startTimeUs; michael@0: } michael@0: #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 michael@0: int32_t nBuffers; michael@0: if (meta->findInt32(kKeyNumBuffers, &nBuffers)) { michael@0: CHECK_GT(nBuffers, 0); michael@0: mNumInputBuffers = nBuffers; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: // Register a listener with GonkCameraHardware so that we can get callbacks michael@0: mCameraHw->SetListener(new GonkCameraSourceListener(this)); michael@0: michael@0: rv = startCameraRecording(); michael@0: michael@0: mStarted = (rv == OK); michael@0: return rv; michael@0: } michael@0: michael@0: void GonkCameraSource::stopCameraRecording() { michael@0: CS_LOGV("stopCameraRecording"); michael@0: mCameraHw->StopRecording(); michael@0: } michael@0: michael@0: void GonkCameraSource::releaseCamera() { michael@0: CS_LOGV("releaseCamera"); michael@0: } michael@0: michael@0: status_t GonkCameraSource::reset() { michael@0: CS_LOGD("reset: E"); michael@0: Mutex::Autolock autoLock(mLock); michael@0: mStarted = false; michael@0: mFrameAvailableCondition.signal(); michael@0: michael@0: releaseQueuedFrames(); michael@0: while (!mFramesBeingEncoded.empty()) { michael@0: if (NO_ERROR != michael@0: mFrameCompleteCondition.waitRelative(mLock, michael@0: mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { michael@0: CS_LOGW("Timed out waiting for outstanding frames being encoded: %d", michael@0: mFramesBeingEncoded.size()); michael@0: } michael@0: } michael@0: stopCameraRecording(); michael@0: releaseCamera(); michael@0: michael@0: if (mCollectStats) { michael@0: CS_LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us", michael@0: mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, michael@0: mLastFrameTimestampUs - mFirstFrameTimeUs); michael@0: } michael@0: michael@0: if (mNumGlitches > 0) { michael@0: CS_LOGW("%d long delays between neighboring video frames", mNumGlitches); michael@0: } michael@0: michael@0: CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); michael@0: CS_LOGD("reset: X"); michael@0: return OK; michael@0: } michael@0: michael@0: void GonkCameraSource::releaseRecordingFrame(const sp& frame) { michael@0: CS_LOGV("releaseRecordingFrame"); michael@0: mCameraHw->ReleaseRecordingFrame(frame); michael@0: } michael@0: michael@0: void GonkCameraSource::releaseQueuedFrames() { michael@0: List >::iterator it; michael@0: while (!mFramesReceived.empty()) { michael@0: it = mFramesReceived.begin(); michael@0: releaseRecordingFrame(*it); michael@0: mFramesReceived.erase(it); michael@0: ++mNumFramesDropped; michael@0: } michael@0: } michael@0: michael@0: sp GonkCameraSource::getFormat() { michael@0: return mMeta; michael@0: } michael@0: michael@0: void GonkCameraSource::releaseOneRecordingFrame(const sp& frame) { michael@0: releaseRecordingFrame(frame); michael@0: } michael@0: michael@0: void GonkCameraSource::signalBufferReturned(MediaBuffer *buffer) { michael@0: CS_LOGV("signalBufferReturned: %p", buffer->data()); michael@0: Mutex::Autolock autoLock(mLock); michael@0: for (List >::iterator it = mFramesBeingEncoded.begin(); michael@0: it != mFramesBeingEncoded.end(); ++it) { michael@0: if ((*it)->pointer() == buffer->data()) { michael@0: releaseOneRecordingFrame((*it)); michael@0: mFramesBeingEncoded.erase(it); michael@0: ++mNumFramesEncoded; michael@0: buffer->setObserver(0); michael@0: buffer->release(); michael@0: mFrameCompleteCondition.signal(); michael@0: return; michael@0: } michael@0: } michael@0: CHECK(!"signalBufferReturned: bogus buffer"); michael@0: } michael@0: michael@0: status_t GonkCameraSource::read( michael@0: MediaBuffer **buffer, const ReadOptions *options) { michael@0: CS_LOGV("read"); michael@0: michael@0: *buffer = NULL; michael@0: michael@0: int64_t seekTimeUs; michael@0: ReadOptions::SeekMode mode; michael@0: if (options && options->getSeekTo(&seekTimeUs, &mode)) { michael@0: return ERROR_UNSUPPORTED; michael@0: } michael@0: michael@0: sp frame; michael@0: int64_t frameTime; michael@0: michael@0: { michael@0: Mutex::Autolock autoLock(mLock); michael@0: while (mStarted && mFramesReceived.empty()) { michael@0: if (NO_ERROR != michael@0: mFrameAvailableCondition.waitRelative(mLock, michael@0: mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { michael@0: //TODO: check sanity of camera? michael@0: CS_LOGW("Timed out waiting for incoming camera video frames: %lld us", michael@0: mLastFrameTimestampUs); michael@0: } michael@0: } michael@0: if (!mStarted) { michael@0: return OK; michael@0: } michael@0: frame = *mFramesReceived.begin(); michael@0: mFramesReceived.erase(mFramesReceived.begin()); michael@0: michael@0: frameTime = *mFrameTimes.begin(); michael@0: mFrameTimes.erase(mFrameTimes.begin()); michael@0: mFramesBeingEncoded.push_back(frame); michael@0: *buffer = new MediaBuffer(frame->pointer(), frame->size()); michael@0: (*buffer)->setObserver(this); michael@0: (*buffer)->add_ref(); michael@0: (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); michael@0: } michael@0: return OK; michael@0: } michael@0: michael@0: void GonkCameraSource::dataCallbackTimestamp(int64_t timestampUs, michael@0: int32_t msgType, const sp &data) { michael@0: CS_LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs); michael@0: Mutex::Autolock autoLock(mLock); michael@0: if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) { michael@0: CS_LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs); michael@0: releaseOneRecordingFrame(data); michael@0: return; michael@0: } michael@0: michael@0: if (mNumFramesReceived > 0) { michael@0: CHECK(timestampUs > mLastFrameTimestampUs); michael@0: if (timestampUs - mLastFrameTimestampUs > mGlitchDurationThresholdUs) { michael@0: ++mNumGlitches; michael@0: } michael@0: } michael@0: michael@0: // May need to skip frame or modify timestamp. Currently implemented michael@0: // by the subclass CameraSourceTimeLapse. michael@0: if (skipCurrentFrame(timestampUs)) { michael@0: releaseOneRecordingFrame(data); michael@0: return; michael@0: } michael@0: michael@0: mLastFrameTimestampUs = timestampUs; michael@0: if (mNumFramesReceived == 0) { michael@0: mFirstFrameTimeUs = timestampUs; michael@0: // Initial delay michael@0: if (mStartTimeUs > 0) { michael@0: if (timestampUs < mStartTimeUs) { michael@0: // Frame was captured before recording was started michael@0: // Drop it without updating the statistical data. michael@0: releaseOneRecordingFrame(data); michael@0: return; michael@0: } michael@0: mStartTimeUs = timestampUs - mStartTimeUs; michael@0: } michael@0: } michael@0: ++mNumFramesReceived; michael@0: michael@0: CHECK(data != NULL && data->size() > 0); michael@0: mFramesReceived.push_back(data); michael@0: int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs); michael@0: mFrameTimes.push_back(timeUs); michael@0: CS_LOGV("initial delay: %lld, current time stamp: %lld", michael@0: mStartTimeUs, timeUs); michael@0: mFrameAvailableCondition.signal(); michael@0: } michael@0: michael@0: bool GonkCameraSource::isMetaDataStoredInVideoBuffers() const { michael@0: CS_LOGV("isMetaDataStoredInVideoBuffers"); michael@0: return mIsMetaDataStoredInVideoBuffers; michael@0: } michael@0: michael@0: } // namespace android