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