Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright (C) 2009 The Android Open Source Project |
michael@0 | 3 | * Copyright (C) 2013 Mozilla Foundation |
michael@0 | 4 | * |
michael@0 | 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
michael@0 | 6 | * you may not use this file except in compliance with the License. |
michael@0 | 7 | * You may obtain a copy of the License at |
michael@0 | 8 | * |
michael@0 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
michael@0 | 10 | * |
michael@0 | 11 | * Unless required by applicable law or agreed to in writing, software |
michael@0 | 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
michael@0 | 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
michael@0 | 14 | * See the License for the specific language governing permissions and |
michael@0 | 15 | * limitations under the License. |
michael@0 | 16 | */ |
michael@0 | 17 | |
michael@0 | 18 | #include <base/basictypes.h> |
michael@0 | 19 | #include "nsDebug.h" |
michael@0 | 20 | #define DOM_CAMERA_LOG_LEVEL 3 |
michael@0 | 21 | #include "CameraCommon.h" |
michael@0 | 22 | /* |
michael@0 | 23 | #define CS_LOGD(...) DOM_CAMERA_LOGA(__VA_ARGS__) |
michael@0 | 24 | #define CS_LOGV(...) DOM_CAMERA_LOGI(__VA_ARGS__) |
michael@0 | 25 | #define CS_LOGI(...) DOM_CAMERA_LOGI(__VA_ARGS__) |
michael@0 | 26 | #define CS_LOGW(...) DOM_CAMERA_LOGW(__VA_ARGS__) |
michael@0 | 27 | #define CS_LOGE(...) DOM_CAMERA_LOGE(__VA_ARGS__) |
michael@0 | 28 | */ |
michael@0 | 29 | |
michael@0 | 30 | #define CS_LOGD(fmt, ...) DOM_CAMERA_LOGA("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) |
michael@0 | 31 | #define CS_LOGV(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) |
michael@0 | 32 | #define CS_LOGI(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) |
michael@0 | 33 | #define CS_LOGW(fmt, ...) DOM_CAMERA_LOGW("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) |
michael@0 | 34 | #define CS_LOGE(fmt, ...) DOM_CAMERA_LOGE("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__) |
michael@0 | 35 | |
michael@0 | 36 | #include <OMX_Component.h> |
michael@0 | 37 | #include <binder/IPCThreadState.h> |
michael@0 | 38 | #include <media/stagefright/foundation/ADebug.h> |
michael@0 | 39 | #include <media/stagefright/MediaDefs.h> |
michael@0 | 40 | #include <media/stagefright/MediaErrors.h> |
michael@0 | 41 | #include <media/stagefright/MetaData.h> |
michael@0 | 42 | #include <camera/CameraParameters.h> |
michael@0 | 43 | #include <utils/String8.h> |
michael@0 | 44 | #include <cutils/properties.h> |
michael@0 | 45 | |
michael@0 | 46 | #include "GonkCameraSource.h" |
michael@0 | 47 | #include "GonkCameraListener.h" |
michael@0 | 48 | #include "GonkCameraHwMgr.h" |
michael@0 | 49 | |
michael@0 | 50 | using namespace mozilla; |
michael@0 | 51 | |
michael@0 | 52 | namespace android { |
michael@0 | 53 | |
michael@0 | 54 | static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL; |
michael@0 | 55 | |
michael@0 | 56 | struct GonkCameraSourceListener : public GonkCameraListener { |
michael@0 | 57 | GonkCameraSourceListener(const sp<GonkCameraSource> &source); |
michael@0 | 58 | |
michael@0 | 59 | virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); |
michael@0 | 60 | virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr, |
michael@0 | 61 | camera_frame_metadata_t *metadata); |
michael@0 | 62 | |
michael@0 | 63 | virtual void postDataTimestamp( |
michael@0 | 64 | nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); |
michael@0 | 65 | |
michael@0 | 66 | protected: |
michael@0 | 67 | virtual ~GonkCameraSourceListener(); |
michael@0 | 68 | |
michael@0 | 69 | private: |
michael@0 | 70 | wp<GonkCameraSource> mSource; |
michael@0 | 71 | |
michael@0 | 72 | GonkCameraSourceListener(const GonkCameraSourceListener &); |
michael@0 | 73 | GonkCameraSourceListener &operator=(const GonkCameraSourceListener &); |
michael@0 | 74 | }; |
michael@0 | 75 | |
michael@0 | 76 | GonkCameraSourceListener::GonkCameraSourceListener(const sp<GonkCameraSource> &source) |
michael@0 | 77 | : mSource(source) { |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | GonkCameraSourceListener::~GonkCameraSourceListener() { |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | void GonkCameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { |
michael@0 | 84 | CS_LOGV("notify(%d, %d, %d)", msgType, ext1, ext2); |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | void GonkCameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr, |
michael@0 | 88 | camera_frame_metadata_t *metadata) { |
michael@0 | 89 | CS_LOGV("postData(%d, ptr:%p, size:%d)", |
michael@0 | 90 | msgType, dataPtr->pointer(), dataPtr->size()); |
michael@0 | 91 | |
michael@0 | 92 | sp<GonkCameraSource> source = mSource.promote(); |
michael@0 | 93 | if (source.get() != NULL) { |
michael@0 | 94 | source->dataCallback(msgType, dataPtr); |
michael@0 | 95 | } |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | void GonkCameraSourceListener::postDataTimestamp( |
michael@0 | 99 | nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) { |
michael@0 | 100 | |
michael@0 | 101 | sp<GonkCameraSource> source = mSource.promote(); |
michael@0 | 102 | if (source.get() != NULL) { |
michael@0 | 103 | source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr); |
michael@0 | 104 | } |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | static int32_t getColorFormat(const char* colorFormat) { |
michael@0 | 108 | return OMX_COLOR_FormatYUV420SemiPlanar; //XXX nsGonkCameraControl uses only YUV420SemiPlanar |
michael@0 | 109 | |
michael@0 | 110 | if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) { |
michael@0 | 111 | return OMX_COLOR_FormatYUV420Planar; |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) { |
michael@0 | 115 | return OMX_COLOR_FormatYUV422SemiPlanar; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP)) { |
michael@0 | 119 | return OMX_COLOR_FormatYUV420SemiPlanar; |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I)) { |
michael@0 | 123 | return OMX_COLOR_FormatYCbYCr; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) { |
michael@0 | 127 | return OMX_COLOR_Format16bitRGB565; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | if (!strcmp(colorFormat, "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar")) { |
michael@0 | 131 | return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar; |
michael@0 | 132 | } |
michael@0 | 133 | #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 |
michael@0 | 134 | if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE)) { |
michael@0 | 135 | return OMX_COLOR_FormatAndroidOpaque; |
michael@0 | 136 | } |
michael@0 | 137 | #endif |
michael@0 | 138 | CS_LOGE("Uknown color format (%s), please add it to " |
michael@0 | 139 | "GonkCameraSource::getColorFormat", colorFormat); |
michael@0 | 140 | |
michael@0 | 141 | CHECK(!"Unknown color format"); |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | GonkCameraSource *GonkCameraSource::Create( |
michael@0 | 145 | const sp<GonkCameraHardware>& aCameraHw, |
michael@0 | 146 | Size videoSize, |
michael@0 | 147 | int32_t frameRate, |
michael@0 | 148 | bool storeMetaDataInVideoBuffers) { |
michael@0 | 149 | |
michael@0 | 150 | GonkCameraSource *source = new GonkCameraSource(aCameraHw, |
michael@0 | 151 | videoSize, frameRate, |
michael@0 | 152 | storeMetaDataInVideoBuffers); |
michael@0 | 153 | return source; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | GonkCameraSource::GonkCameraSource( |
michael@0 | 157 | const sp<GonkCameraHardware>& aCameraHw, |
michael@0 | 158 | Size videoSize, |
michael@0 | 159 | int32_t frameRate, |
michael@0 | 160 | bool storeMetaDataInVideoBuffers) |
michael@0 | 161 | : mCameraFlags(0), |
michael@0 | 162 | mNumInputBuffers(0), |
michael@0 | 163 | mVideoFrameRate(-1), |
michael@0 | 164 | mNumFramesReceived(0), |
michael@0 | 165 | mLastFrameTimestampUs(0), |
michael@0 | 166 | mStarted(false), |
michael@0 | 167 | mNumFramesEncoded(0), |
michael@0 | 168 | mTimeBetweenFrameCaptureUs(0), |
michael@0 | 169 | mFirstFrameTimeUs(0), |
michael@0 | 170 | mNumFramesDropped(0), |
michael@0 | 171 | mNumGlitches(0), |
michael@0 | 172 | mGlitchDurationThresholdUs(200000), |
michael@0 | 173 | mCollectStats(false), |
michael@0 | 174 | mCameraHw(aCameraHw) { |
michael@0 | 175 | mVideoSize.width = -1; |
michael@0 | 176 | mVideoSize.height = -1; |
michael@0 | 177 | |
michael@0 | 178 | mInitCheck = init( |
michael@0 | 179 | videoSize, frameRate, |
michael@0 | 180 | storeMetaDataInVideoBuffers); |
michael@0 | 181 | if (mInitCheck != OK) releaseCamera(); |
michael@0 | 182 | } |
michael@0 | 183 | |
michael@0 | 184 | status_t GonkCameraSource::initCheck() const { |
michael@0 | 185 | return mInitCheck; |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | //TODO: Do we need to reimplement isCameraAvailable? |
michael@0 | 189 | |
michael@0 | 190 | /* |
michael@0 | 191 | * Check to see whether the requested video width and height is one |
michael@0 | 192 | * of the supported sizes. |
michael@0 | 193 | * @param width the video frame width in pixels |
michael@0 | 194 | * @param height the video frame height in pixels |
michael@0 | 195 | * @param suppportedSizes the vector of sizes that we check against |
michael@0 | 196 | * @return true if the dimension (width and height) is supported. |
michael@0 | 197 | */ |
michael@0 | 198 | static bool isVideoSizeSupported( |
michael@0 | 199 | int32_t width, int32_t height, |
michael@0 | 200 | const Vector<Size>& supportedSizes) { |
michael@0 | 201 | |
michael@0 | 202 | CS_LOGV("isVideoSizeSupported"); |
michael@0 | 203 | for (size_t i = 0; i < supportedSizes.size(); ++i) { |
michael@0 | 204 | if (width == supportedSizes[i].width && |
michael@0 | 205 | height == supportedSizes[i].height) { |
michael@0 | 206 | return true; |
michael@0 | 207 | } |
michael@0 | 208 | } |
michael@0 | 209 | return false; |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | /* |
michael@0 | 213 | * If the preview and video output is separate, we only set the |
michael@0 | 214 | * the video size, and applications should set the preview size |
michael@0 | 215 | * to some proper value, and the recording framework will not |
michael@0 | 216 | * change the preview size; otherwise, if the video and preview |
michael@0 | 217 | * output is the same, we need to set the preview to be the same |
michael@0 | 218 | * as the requested video size. |
michael@0 | 219 | * |
michael@0 | 220 | */ |
michael@0 | 221 | /* |
michael@0 | 222 | * Query the camera to retrieve the supported video frame sizes |
michael@0 | 223 | * and also to see whether CameraParameters::setVideoSize() |
michael@0 | 224 | * is supported or not. |
michael@0 | 225 | * @param params CameraParameters to retrieve the information |
michael@0 | 226 | * @@param isSetVideoSizeSupported retunrs whether method |
michael@0 | 227 | * CameraParameters::setVideoSize() is supported or not. |
michael@0 | 228 | * @param sizes returns the vector of Size objects for the |
michael@0 | 229 | * supported video frame sizes advertised by the camera. |
michael@0 | 230 | */ |
michael@0 | 231 | static void getSupportedVideoSizes( |
michael@0 | 232 | const CameraParameters& params, |
michael@0 | 233 | bool *isSetVideoSizeSupported, |
michael@0 | 234 | Vector<Size>& sizes) { |
michael@0 | 235 | |
michael@0 | 236 | *isSetVideoSizeSupported = true; |
michael@0 | 237 | params.getSupportedVideoSizes(sizes); |
michael@0 | 238 | if (sizes.size() == 0) { |
michael@0 | 239 | CS_LOGD("Camera does not support setVideoSize()"); |
michael@0 | 240 | params.getSupportedPreviewSizes(sizes); |
michael@0 | 241 | *isSetVideoSizeSupported = false; |
michael@0 | 242 | } |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | /* |
michael@0 | 246 | * Check whether the camera has the supported color format |
michael@0 | 247 | * @param params CameraParameters to retrieve the information |
michael@0 | 248 | * @return OK if no error. |
michael@0 | 249 | */ |
michael@0 | 250 | status_t GonkCameraSource::isCameraColorFormatSupported( |
michael@0 | 251 | const CameraParameters& params) { |
michael@0 | 252 | mColorFormat = getColorFormat(params.get( |
michael@0 | 253 | CameraParameters::KEY_VIDEO_FRAME_FORMAT)); |
michael@0 | 254 | if (mColorFormat == -1) { |
michael@0 | 255 | return BAD_VALUE; |
michael@0 | 256 | } |
michael@0 | 257 | return OK; |
michael@0 | 258 | } |
michael@0 | 259 | |
michael@0 | 260 | /* |
michael@0 | 261 | * Configure the camera to use the requested video size |
michael@0 | 262 | * (width and height) and/or frame rate. If both width and |
michael@0 | 263 | * height are -1, configuration on the video size is skipped. |
michael@0 | 264 | * if frameRate is -1, configuration on the frame rate |
michael@0 | 265 | * is skipped. Skipping the configuration allows one to |
michael@0 | 266 | * use the current camera setting without the need to |
michael@0 | 267 | * actually know the specific values (see Create() method). |
michael@0 | 268 | * |
michael@0 | 269 | * @param params the CameraParameters to be configured |
michael@0 | 270 | * @param width the target video frame width in pixels |
michael@0 | 271 | * @param height the target video frame height in pixels |
michael@0 | 272 | * @param frameRate the target frame rate in frames per second. |
michael@0 | 273 | * @return OK if no error. |
michael@0 | 274 | */ |
michael@0 | 275 | status_t GonkCameraSource::configureCamera( |
michael@0 | 276 | CameraParameters* params, |
michael@0 | 277 | int32_t width, int32_t height, |
michael@0 | 278 | int32_t frameRate) { |
michael@0 | 279 | CS_LOGV("configureCamera"); |
michael@0 | 280 | Vector<Size> sizes; |
michael@0 | 281 | bool isSetVideoSizeSupportedByCamera = true; |
michael@0 | 282 | getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes); |
michael@0 | 283 | bool isCameraParamChanged = false; |
michael@0 | 284 | if (width != -1 && height != -1) { |
michael@0 | 285 | if (!isVideoSizeSupported(width, height, sizes)) { |
michael@0 | 286 | CS_LOGE("Video dimension (%dx%d) is unsupported", width, height); |
michael@0 | 287 | return BAD_VALUE; |
michael@0 | 288 | } |
michael@0 | 289 | if (isSetVideoSizeSupportedByCamera) { |
michael@0 | 290 | params->setVideoSize(width, height); |
michael@0 | 291 | } else { |
michael@0 | 292 | params->setPreviewSize(width, height); |
michael@0 | 293 | } |
michael@0 | 294 | isCameraParamChanged = true; |
michael@0 | 295 | } else if ((width == -1 && height != -1) || |
michael@0 | 296 | (width != -1 && height == -1)) { |
michael@0 | 297 | // If one and only one of the width and height is -1 |
michael@0 | 298 | // we reject such a request. |
michael@0 | 299 | CS_LOGE("Requested video size (%dx%d) is not supported", width, height); |
michael@0 | 300 | return BAD_VALUE; |
michael@0 | 301 | } else { // width == -1 && height == -1 |
michael@0 | 302 | // Do not configure the camera. |
michael@0 | 303 | // Use the current width and height value setting from the camera. |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | if (frameRate != -1) { |
michael@0 | 307 | CHECK(frameRate > 0 && frameRate <= 120); |
michael@0 | 308 | const char* supportedFrameRates = |
michael@0 | 309 | params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES); |
michael@0 | 310 | CHECK(supportedFrameRates != NULL); |
michael@0 | 311 | CS_LOGV("Supported frame rates: %s", supportedFrameRates); |
michael@0 | 312 | char buf[4]; |
michael@0 | 313 | snprintf(buf, 4, "%d", frameRate); |
michael@0 | 314 | if (strstr(supportedFrameRates, buf) == NULL) { |
michael@0 | 315 | CS_LOGE("Requested frame rate (%d) is not supported: %s", |
michael@0 | 316 | frameRate, supportedFrameRates); |
michael@0 | 317 | return BAD_VALUE; |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | // The frame rate is supported, set the camera to the requested value. |
michael@0 | 321 | params->setPreviewFrameRate(frameRate); |
michael@0 | 322 | isCameraParamChanged = true; |
michael@0 | 323 | } else { // frameRate == -1 |
michael@0 | 324 | // Do not configure the camera. |
michael@0 | 325 | // Use the current frame rate value setting from the camera |
michael@0 | 326 | } |
michael@0 | 327 | |
michael@0 | 328 | if (isCameraParamChanged) { |
michael@0 | 329 | // Either frame rate or frame size needs to be changed. |
michael@0 | 330 | if (OK != mCameraHw->PushParameters(*params)) { |
michael@0 | 331 | CS_LOGE("Could not change settings." |
michael@0 | 332 | " Someone else is using camera?"); |
michael@0 | 333 | return -EBUSY; |
michael@0 | 334 | } |
michael@0 | 335 | } |
michael@0 | 336 | return OK; |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | /* |
michael@0 | 340 | * Check whether the requested video frame size |
michael@0 | 341 | * has been successfully configured or not. If both width and height |
michael@0 | 342 | * are -1, check on the current width and height value setting |
michael@0 | 343 | * is performed. |
michael@0 | 344 | * |
michael@0 | 345 | * @param params CameraParameters to retrieve the information |
michael@0 | 346 | * @param the target video frame width in pixels to check against |
michael@0 | 347 | * @param the target video frame height in pixels to check against |
michael@0 | 348 | * @return OK if no error |
michael@0 | 349 | */ |
michael@0 | 350 | status_t GonkCameraSource::checkVideoSize( |
michael@0 | 351 | const CameraParameters& params, |
michael@0 | 352 | int32_t width, int32_t height) { |
michael@0 | 353 | |
michael@0 | 354 | CS_LOGV("checkVideoSize"); |
michael@0 | 355 | // The actual video size is the same as the preview size |
michael@0 | 356 | // if the camera hal does not support separate video and |
michael@0 | 357 | // preview output. In this case, we retrieve the video |
michael@0 | 358 | // size from preview. |
michael@0 | 359 | int32_t frameWidthActual = -1; |
michael@0 | 360 | int32_t frameHeightActual = -1; |
michael@0 | 361 | Vector<Size> sizes; |
michael@0 | 362 | params.getSupportedVideoSizes(sizes); |
michael@0 | 363 | if (sizes.size() == 0) { |
michael@0 | 364 | // video size is the same as preview size |
michael@0 | 365 | params.getPreviewSize(&frameWidthActual, &frameHeightActual); |
michael@0 | 366 | } else { |
michael@0 | 367 | // video size may not be the same as preview |
michael@0 | 368 | params.getVideoSize(&frameWidthActual, &frameHeightActual); |
michael@0 | 369 | } |
michael@0 | 370 | if (frameWidthActual < 0 || frameHeightActual < 0) { |
michael@0 | 371 | CS_LOGE("Failed to retrieve video frame size (%dx%d)", |
michael@0 | 372 | frameWidthActual, frameHeightActual); |
michael@0 | 373 | return UNKNOWN_ERROR; |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | // Check the actual video frame size against the target/requested |
michael@0 | 377 | // video frame size. |
michael@0 | 378 | if (width != -1 && height != -1) { |
michael@0 | 379 | if (frameWidthActual != width || frameHeightActual != height) { |
michael@0 | 380 | CS_LOGE("Failed to set video frame size to %dx%d. " |
michael@0 | 381 | "The actual video size is %dx%d ", width, height, |
michael@0 | 382 | frameWidthActual, frameHeightActual); |
michael@0 | 383 | return UNKNOWN_ERROR; |
michael@0 | 384 | } |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | // Good now. |
michael@0 | 388 | mVideoSize.width = frameWidthActual; |
michael@0 | 389 | mVideoSize.height = frameHeightActual; |
michael@0 | 390 | return OK; |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | /* |
michael@0 | 394 | * Check the requested frame rate has been successfully configured or not. |
michael@0 | 395 | * If the target frameRate is -1, check on the current frame rate value |
michael@0 | 396 | * setting is performed. |
michael@0 | 397 | * |
michael@0 | 398 | * @param params CameraParameters to retrieve the information |
michael@0 | 399 | * @param the target video frame rate to check against |
michael@0 | 400 | * @return OK if no error. |
michael@0 | 401 | */ |
michael@0 | 402 | status_t GonkCameraSource::checkFrameRate( |
michael@0 | 403 | const CameraParameters& params, |
michael@0 | 404 | int32_t frameRate) { |
michael@0 | 405 | |
michael@0 | 406 | CS_LOGV("checkFrameRate"); |
michael@0 | 407 | int32_t frameRateActual = params.getPreviewFrameRate(); |
michael@0 | 408 | if (frameRateActual < 0) { |
michael@0 | 409 | CS_LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual); |
michael@0 | 410 | return UNKNOWN_ERROR; |
michael@0 | 411 | } |
michael@0 | 412 | |
michael@0 | 413 | // Check the actual video frame rate against the target/requested |
michael@0 | 414 | // video frame rate. |
michael@0 | 415 | if (frameRate != -1 && (frameRateActual - frameRate) != 0) { |
michael@0 | 416 | CS_LOGE("Failed to set preview frame rate to %d fps. The actual " |
michael@0 | 417 | "frame rate is %d", frameRate, frameRateActual); |
michael@0 | 418 | return UNKNOWN_ERROR; |
michael@0 | 419 | } |
michael@0 | 420 | |
michael@0 | 421 | // Good now. |
michael@0 | 422 | mVideoFrameRate = frameRateActual; |
michael@0 | 423 | return OK; |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | /* |
michael@0 | 427 | * Initialize the GonkCameraSource so that it becomes |
michael@0 | 428 | * ready for providing the video input streams as requested. |
michael@0 | 429 | * @param camera the camera object used for the video source |
michael@0 | 430 | * @param cameraId if camera == 0, use camera with this id |
michael@0 | 431 | * as the video source |
michael@0 | 432 | * @param videoSize the target video frame size. If both |
michael@0 | 433 | * width and height in videoSize is -1, use the current |
michael@0 | 434 | * width and heigth settings by the camera |
michael@0 | 435 | * @param frameRate the target frame rate in frames per second. |
michael@0 | 436 | * if it is -1, use the current camera frame rate setting. |
michael@0 | 437 | * @param storeMetaDataInVideoBuffers request to store meta |
michael@0 | 438 | * data or real YUV data in video buffers. Request to |
michael@0 | 439 | * store meta data in video buffers may not be honored |
michael@0 | 440 | * if the source does not support this feature. |
michael@0 | 441 | * |
michael@0 | 442 | * @return OK if no error. |
michael@0 | 443 | */ |
michael@0 | 444 | status_t GonkCameraSource::init( |
michael@0 | 445 | Size videoSize, |
michael@0 | 446 | int32_t frameRate, |
michael@0 | 447 | bool storeMetaDataInVideoBuffers) { |
michael@0 | 448 | |
michael@0 | 449 | CS_LOGV("init"); |
michael@0 | 450 | status_t err = OK; |
michael@0 | 451 | //TODO: need to do something here to check the sanity of camera |
michael@0 | 452 | |
michael@0 | 453 | CameraParameters params; |
michael@0 | 454 | mCameraHw->PullParameters(params); |
michael@0 | 455 | if ((err = isCameraColorFormatSupported(params)) != OK) { |
michael@0 | 456 | return err; |
michael@0 | 457 | } |
michael@0 | 458 | |
michael@0 | 459 | // Set the camera to use the requested video frame size |
michael@0 | 460 | // and/or frame rate. |
michael@0 | 461 | if ((err = configureCamera(¶ms, |
michael@0 | 462 | videoSize.width, videoSize.height, |
michael@0 | 463 | frameRate))) { |
michael@0 | 464 | return err; |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | // Check on video frame size and frame rate. |
michael@0 | 468 | CameraParameters newCameraParams; |
michael@0 | 469 | mCameraHw->PullParameters(newCameraParams); |
michael@0 | 470 | if ((err = checkVideoSize(newCameraParams, |
michael@0 | 471 | videoSize.width, videoSize.height)) != OK) { |
michael@0 | 472 | return err; |
michael@0 | 473 | } |
michael@0 | 474 | if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) { |
michael@0 | 475 | return err; |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | // By default, do not store metadata in video buffers |
michael@0 | 479 | mIsMetaDataStoredInVideoBuffers = false; |
michael@0 | 480 | mCameraHw->StoreMetaDataInBuffers(false); |
michael@0 | 481 | if (storeMetaDataInVideoBuffers) { |
michael@0 | 482 | if (OK == mCameraHw->StoreMetaDataInBuffers(true)) { |
michael@0 | 483 | mIsMetaDataStoredInVideoBuffers = true; |
michael@0 | 484 | } |
michael@0 | 485 | } |
michael@0 | 486 | |
michael@0 | 487 | int64_t glitchDurationUs = (1000000LL / mVideoFrameRate); |
michael@0 | 488 | if (glitchDurationUs > mGlitchDurationThresholdUs) { |
michael@0 | 489 | mGlitchDurationThresholdUs = glitchDurationUs; |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | // XXX: query camera for the stride and slice height |
michael@0 | 493 | // when the capability becomes available. |
michael@0 | 494 | mMeta = new MetaData; |
michael@0 | 495 | mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); |
michael@0 | 496 | mMeta->setInt32(kKeyColorFormat, mColorFormat); |
michael@0 | 497 | mMeta->setInt32(kKeyWidth, mVideoSize.width); |
michael@0 | 498 | mMeta->setInt32(kKeyHeight, mVideoSize.height); |
michael@0 | 499 | mMeta->setInt32(kKeyStride, mVideoSize.width); |
michael@0 | 500 | mMeta->setInt32(kKeySliceHeight, mVideoSize.height); |
michael@0 | 501 | mMeta->setInt32(kKeyFrameRate, mVideoFrameRate); |
michael@0 | 502 | return OK; |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | GonkCameraSource::~GonkCameraSource() { |
michael@0 | 506 | if (mStarted) { |
michael@0 | 507 | reset(); |
michael@0 | 508 | } else if (mInitCheck == OK) { |
michael@0 | 509 | // Camera is initialized but because start() is never called, |
michael@0 | 510 | // the lock on Camera is never released(). This makes sure |
michael@0 | 511 | // Camera's lock is released in this case. |
michael@0 | 512 | // TODO: Don't think I need to do this |
michael@0 | 513 | releaseCamera(); |
michael@0 | 514 | } |
michael@0 | 515 | } |
michael@0 | 516 | |
michael@0 | 517 | int GonkCameraSource::startCameraRecording() { |
michael@0 | 518 | CS_LOGV("startCameraRecording"); |
michael@0 | 519 | return mCameraHw->StartRecording(); |
michael@0 | 520 | } |
michael@0 | 521 | |
michael@0 | 522 | status_t GonkCameraSource::start(MetaData *meta) { |
michael@0 | 523 | int rv; |
michael@0 | 524 | |
michael@0 | 525 | CS_LOGV("start"); |
michael@0 | 526 | CHECK(!mStarted); |
michael@0 | 527 | if (mInitCheck != OK) { |
michael@0 | 528 | CS_LOGE("GonkCameraSource is not initialized yet"); |
michael@0 | 529 | return mInitCheck; |
michael@0 | 530 | } |
michael@0 | 531 | |
michael@0 | 532 | char value[PROPERTY_VALUE_MAX]; |
michael@0 | 533 | if (property_get("media.stagefright.record-stats", value, NULL) |
michael@0 | 534 | && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { |
michael@0 | 535 | mCollectStats = true; |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | mStartTimeUs = 0; |
michael@0 | 539 | mNumInputBuffers = 0; |
michael@0 | 540 | if (meta) { |
michael@0 | 541 | int64_t startTimeUs; |
michael@0 | 542 | if (meta->findInt64(kKeyTime, &startTimeUs)) { |
michael@0 | 543 | mStartTimeUs = startTimeUs; |
michael@0 | 544 | } |
michael@0 | 545 | #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 |
michael@0 | 546 | int32_t nBuffers; |
michael@0 | 547 | if (meta->findInt32(kKeyNumBuffers, &nBuffers)) { |
michael@0 | 548 | CHECK_GT(nBuffers, 0); |
michael@0 | 549 | mNumInputBuffers = nBuffers; |
michael@0 | 550 | } |
michael@0 | 551 | #endif |
michael@0 | 552 | } |
michael@0 | 553 | |
michael@0 | 554 | // Register a listener with GonkCameraHardware so that we can get callbacks |
michael@0 | 555 | mCameraHw->SetListener(new GonkCameraSourceListener(this)); |
michael@0 | 556 | |
michael@0 | 557 | rv = startCameraRecording(); |
michael@0 | 558 | |
michael@0 | 559 | mStarted = (rv == OK); |
michael@0 | 560 | return rv; |
michael@0 | 561 | } |
michael@0 | 562 | |
michael@0 | 563 | void GonkCameraSource::stopCameraRecording() { |
michael@0 | 564 | CS_LOGV("stopCameraRecording"); |
michael@0 | 565 | mCameraHw->StopRecording(); |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | void GonkCameraSource::releaseCamera() { |
michael@0 | 569 | CS_LOGV("releaseCamera"); |
michael@0 | 570 | } |
michael@0 | 571 | |
michael@0 | 572 | status_t GonkCameraSource::reset() { |
michael@0 | 573 | CS_LOGD("reset: E"); |
michael@0 | 574 | Mutex::Autolock autoLock(mLock); |
michael@0 | 575 | mStarted = false; |
michael@0 | 576 | mFrameAvailableCondition.signal(); |
michael@0 | 577 | |
michael@0 | 578 | releaseQueuedFrames(); |
michael@0 | 579 | while (!mFramesBeingEncoded.empty()) { |
michael@0 | 580 | if (NO_ERROR != |
michael@0 | 581 | mFrameCompleteCondition.waitRelative(mLock, |
michael@0 | 582 | mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { |
michael@0 | 583 | CS_LOGW("Timed out waiting for outstanding frames being encoded: %d", |
michael@0 | 584 | mFramesBeingEncoded.size()); |
michael@0 | 585 | } |
michael@0 | 586 | } |
michael@0 | 587 | stopCameraRecording(); |
michael@0 | 588 | releaseCamera(); |
michael@0 | 589 | |
michael@0 | 590 | if (mCollectStats) { |
michael@0 | 591 | CS_LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us", |
michael@0 | 592 | mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, |
michael@0 | 593 | mLastFrameTimestampUs - mFirstFrameTimeUs); |
michael@0 | 594 | } |
michael@0 | 595 | |
michael@0 | 596 | if (mNumGlitches > 0) { |
michael@0 | 597 | CS_LOGW("%d long delays between neighboring video frames", mNumGlitches); |
michael@0 | 598 | } |
michael@0 | 599 | |
michael@0 | 600 | CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); |
michael@0 | 601 | CS_LOGD("reset: X"); |
michael@0 | 602 | return OK; |
michael@0 | 603 | } |
michael@0 | 604 | |
michael@0 | 605 | void GonkCameraSource::releaseRecordingFrame(const sp<IMemory>& frame) { |
michael@0 | 606 | CS_LOGV("releaseRecordingFrame"); |
michael@0 | 607 | mCameraHw->ReleaseRecordingFrame(frame); |
michael@0 | 608 | } |
michael@0 | 609 | |
michael@0 | 610 | void GonkCameraSource::releaseQueuedFrames() { |
michael@0 | 611 | List<sp<IMemory> >::iterator it; |
michael@0 | 612 | while (!mFramesReceived.empty()) { |
michael@0 | 613 | it = mFramesReceived.begin(); |
michael@0 | 614 | releaseRecordingFrame(*it); |
michael@0 | 615 | mFramesReceived.erase(it); |
michael@0 | 616 | ++mNumFramesDropped; |
michael@0 | 617 | } |
michael@0 | 618 | } |
michael@0 | 619 | |
michael@0 | 620 | sp<MetaData> GonkCameraSource::getFormat() { |
michael@0 | 621 | return mMeta; |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | void GonkCameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) { |
michael@0 | 625 | releaseRecordingFrame(frame); |
michael@0 | 626 | } |
michael@0 | 627 | |
michael@0 | 628 | void GonkCameraSource::signalBufferReturned(MediaBuffer *buffer) { |
michael@0 | 629 | CS_LOGV("signalBufferReturned: %p", buffer->data()); |
michael@0 | 630 | Mutex::Autolock autoLock(mLock); |
michael@0 | 631 | for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin(); |
michael@0 | 632 | it != mFramesBeingEncoded.end(); ++it) { |
michael@0 | 633 | if ((*it)->pointer() == buffer->data()) { |
michael@0 | 634 | releaseOneRecordingFrame((*it)); |
michael@0 | 635 | mFramesBeingEncoded.erase(it); |
michael@0 | 636 | ++mNumFramesEncoded; |
michael@0 | 637 | buffer->setObserver(0); |
michael@0 | 638 | buffer->release(); |
michael@0 | 639 | mFrameCompleteCondition.signal(); |
michael@0 | 640 | return; |
michael@0 | 641 | } |
michael@0 | 642 | } |
michael@0 | 643 | CHECK(!"signalBufferReturned: bogus buffer"); |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | status_t GonkCameraSource::read( |
michael@0 | 647 | MediaBuffer **buffer, const ReadOptions *options) { |
michael@0 | 648 | CS_LOGV("read"); |
michael@0 | 649 | |
michael@0 | 650 | *buffer = NULL; |
michael@0 | 651 | |
michael@0 | 652 | int64_t seekTimeUs; |
michael@0 | 653 | ReadOptions::SeekMode mode; |
michael@0 | 654 | if (options && options->getSeekTo(&seekTimeUs, &mode)) { |
michael@0 | 655 | return ERROR_UNSUPPORTED; |
michael@0 | 656 | } |
michael@0 | 657 | |
michael@0 | 658 | sp<IMemory> frame; |
michael@0 | 659 | int64_t frameTime; |
michael@0 | 660 | |
michael@0 | 661 | { |
michael@0 | 662 | Mutex::Autolock autoLock(mLock); |
michael@0 | 663 | while (mStarted && mFramesReceived.empty()) { |
michael@0 | 664 | if (NO_ERROR != |
michael@0 | 665 | mFrameAvailableCondition.waitRelative(mLock, |
michael@0 | 666 | mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) { |
michael@0 | 667 | //TODO: check sanity of camera? |
michael@0 | 668 | CS_LOGW("Timed out waiting for incoming camera video frames: %lld us", |
michael@0 | 669 | mLastFrameTimestampUs); |
michael@0 | 670 | } |
michael@0 | 671 | } |
michael@0 | 672 | if (!mStarted) { |
michael@0 | 673 | return OK; |
michael@0 | 674 | } |
michael@0 | 675 | frame = *mFramesReceived.begin(); |
michael@0 | 676 | mFramesReceived.erase(mFramesReceived.begin()); |
michael@0 | 677 | |
michael@0 | 678 | frameTime = *mFrameTimes.begin(); |
michael@0 | 679 | mFrameTimes.erase(mFrameTimes.begin()); |
michael@0 | 680 | mFramesBeingEncoded.push_back(frame); |
michael@0 | 681 | *buffer = new MediaBuffer(frame->pointer(), frame->size()); |
michael@0 | 682 | (*buffer)->setObserver(this); |
michael@0 | 683 | (*buffer)->add_ref(); |
michael@0 | 684 | (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); |
michael@0 | 685 | } |
michael@0 | 686 | return OK; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | void GonkCameraSource::dataCallbackTimestamp(int64_t timestampUs, |
michael@0 | 690 | int32_t msgType, const sp<IMemory> &data) { |
michael@0 | 691 | CS_LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs); |
michael@0 | 692 | Mutex::Autolock autoLock(mLock); |
michael@0 | 693 | if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) { |
michael@0 | 694 | CS_LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs); |
michael@0 | 695 | releaseOneRecordingFrame(data); |
michael@0 | 696 | return; |
michael@0 | 697 | } |
michael@0 | 698 | |
michael@0 | 699 | if (mNumFramesReceived > 0) { |
michael@0 | 700 | CHECK(timestampUs > mLastFrameTimestampUs); |
michael@0 | 701 | if (timestampUs - mLastFrameTimestampUs > mGlitchDurationThresholdUs) { |
michael@0 | 702 | ++mNumGlitches; |
michael@0 | 703 | } |
michael@0 | 704 | } |
michael@0 | 705 | |
michael@0 | 706 | // May need to skip frame or modify timestamp. Currently implemented |
michael@0 | 707 | // by the subclass CameraSourceTimeLapse. |
michael@0 | 708 | if (skipCurrentFrame(timestampUs)) { |
michael@0 | 709 | releaseOneRecordingFrame(data); |
michael@0 | 710 | return; |
michael@0 | 711 | } |
michael@0 | 712 | |
michael@0 | 713 | mLastFrameTimestampUs = timestampUs; |
michael@0 | 714 | if (mNumFramesReceived == 0) { |
michael@0 | 715 | mFirstFrameTimeUs = timestampUs; |
michael@0 | 716 | // Initial delay |
michael@0 | 717 | if (mStartTimeUs > 0) { |
michael@0 | 718 | if (timestampUs < mStartTimeUs) { |
michael@0 | 719 | // Frame was captured before recording was started |
michael@0 | 720 | // Drop it without updating the statistical data. |
michael@0 | 721 | releaseOneRecordingFrame(data); |
michael@0 | 722 | return; |
michael@0 | 723 | } |
michael@0 | 724 | mStartTimeUs = timestampUs - mStartTimeUs; |
michael@0 | 725 | } |
michael@0 | 726 | } |
michael@0 | 727 | ++mNumFramesReceived; |
michael@0 | 728 | |
michael@0 | 729 | CHECK(data != NULL && data->size() > 0); |
michael@0 | 730 | mFramesReceived.push_back(data); |
michael@0 | 731 | int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs); |
michael@0 | 732 | mFrameTimes.push_back(timeUs); |
michael@0 | 733 | CS_LOGV("initial delay: %lld, current time stamp: %lld", |
michael@0 | 734 | mStartTimeUs, timeUs); |
michael@0 | 735 | mFrameAvailableCondition.signal(); |
michael@0 | 736 | } |
michael@0 | 737 | |
michael@0 | 738 | bool GonkCameraSource::isMetaDataStoredInVideoBuffers() const { |
michael@0 | 739 | CS_LOGV("isMetaDataStoredInVideoBuffers"); |
michael@0 | 740 | return mIsMetaDataStoredInVideoBuffers; |
michael@0 | 741 | } |
michael@0 | 742 | |
michael@0 | 743 | } // namespace android |