michael@0: /* michael@0: * Copyright (C) 2013-2014 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 "GonkCameraParameters.h" michael@0: #include "camera/CameraParameters.h" michael@0: #include "ICameraControl.h" michael@0: #include "CameraCommon.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace android; michael@0: michael@0: /* static */ const char* michael@0: GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey) michael@0: { michael@0: switch (aKey) { michael@0: case CAMERA_PARAM_PREVIEWSIZE: michael@0: return KEY_PREVIEW_SIZE; michael@0: case CAMERA_PARAM_PREVIEWFORMAT: michael@0: return KEY_PREVIEW_FORMAT; michael@0: case CAMERA_PARAM_PREVIEWFRAMERATE: michael@0: return KEY_PREVIEW_FRAME_RATE; michael@0: case CAMERA_PARAM_EFFECT: michael@0: return KEY_EFFECT; michael@0: case CAMERA_PARAM_WHITEBALANCE: michael@0: return KEY_WHITE_BALANCE; michael@0: case CAMERA_PARAM_SCENEMODE: michael@0: return KEY_SCENE_MODE; michael@0: case CAMERA_PARAM_FLASHMODE: michael@0: return KEY_FLASH_MODE; michael@0: case CAMERA_PARAM_FOCUSMODE: michael@0: return KEY_FOCUS_MODE; michael@0: case CAMERA_PARAM_ZOOM: michael@0: return KEY_ZOOM; michael@0: case CAMERA_PARAM_METERINGAREAS: michael@0: return KEY_METERING_AREAS; michael@0: case CAMERA_PARAM_FOCUSAREAS: michael@0: return KEY_FOCUS_AREAS; michael@0: case CAMERA_PARAM_FOCALLENGTH: michael@0: return KEY_FOCAL_LENGTH; michael@0: case CAMERA_PARAM_FOCUSDISTANCENEAR: michael@0: return KEY_FOCUS_DISTANCES; michael@0: case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM: michael@0: return KEY_FOCUS_DISTANCES; michael@0: case CAMERA_PARAM_FOCUSDISTANCEFAR: michael@0: return KEY_FOCUS_DISTANCES; michael@0: case CAMERA_PARAM_EXPOSURECOMPENSATION: michael@0: return KEY_EXPOSURE_COMPENSATION; michael@0: case CAMERA_PARAM_THUMBNAILQUALITY: michael@0: return KEY_JPEG_THUMBNAIL_QUALITY; michael@0: case CAMERA_PARAM_PICTURE_SIZE: michael@0: return KEY_PICTURE_SIZE; michael@0: case CAMERA_PARAM_PICTURE_FILEFORMAT: michael@0: return KEY_PICTURE_FORMAT; michael@0: case CAMERA_PARAM_PICTURE_ROTATION: michael@0: return KEY_ROTATION; michael@0: case CAMERA_PARAM_PICTURE_DATETIME: michael@0: // Not every platform defines a KEY_EXIF_DATETIME; michael@0: // for those that don't, we use the raw string key, and if the platform michael@0: // doesn't support it, it will be ignored. michael@0: // michael@0: // See bug 832494. michael@0: return "exif-datetime"; michael@0: case CAMERA_PARAM_VIDEOSIZE: michael@0: return KEY_VIDEO_SIZE; michael@0: case CAMERA_PARAM_ISOMODE: michael@0: // Not every platform defines KEY_ISO_MODE; michael@0: // for those that don't, we use the raw string key. michael@0: return "iso"; michael@0: case CAMERA_PARAM_LUMINANCE: michael@0: return "luminance-condition"; michael@0: case CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE: michael@0: // Not every platform defines KEY_QC_HDR_NEED_1X; michael@0: // for those that don't, we use the raw string key. michael@0: return "hdr-need-1x"; michael@0: michael@0: case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES: michael@0: return KEY_SUPPORTED_PREVIEW_SIZES; michael@0: case CAMERA_PARAM_SUPPORTED_PICTURESIZES: michael@0: return KEY_SUPPORTED_PICTURE_SIZES; michael@0: case CAMERA_PARAM_SUPPORTED_VIDEOSIZES: michael@0: return KEY_SUPPORTED_VIDEO_SIZES; michael@0: case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS: michael@0: return KEY_SUPPORTED_PICTURE_FORMATS; michael@0: case CAMERA_PARAM_SUPPORTED_WHITEBALANCES: michael@0: return KEY_SUPPORTED_WHITE_BALANCE; michael@0: case CAMERA_PARAM_SUPPORTED_SCENEMODES: michael@0: return KEY_SUPPORTED_SCENE_MODES; michael@0: case CAMERA_PARAM_SUPPORTED_EFFECTS: michael@0: return KEY_SUPPORTED_EFFECTS; michael@0: case CAMERA_PARAM_SUPPORTED_FLASHMODES: michael@0: return KEY_SUPPORTED_FLASH_MODES; michael@0: case CAMERA_PARAM_SUPPORTED_FOCUSMODES: michael@0: return KEY_SUPPORTED_FOCUS_MODES; michael@0: case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS: michael@0: return KEY_MAX_NUM_FOCUS_AREAS; michael@0: case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS: michael@0: return KEY_MAX_NUM_METERING_AREAS; michael@0: case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION: michael@0: return KEY_MIN_EXPOSURE_COMPENSATION; michael@0: case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION: michael@0: return KEY_MAX_EXPOSURE_COMPENSATION; michael@0: case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP: michael@0: return KEY_EXPOSURE_COMPENSATION_STEP; michael@0: case CAMERA_PARAM_SUPPORTED_ZOOM: michael@0: return KEY_ZOOM_SUPPORTED; michael@0: case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS: michael@0: return KEY_ZOOM_RATIOS; michael@0: case CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES: michael@0: return KEY_MAX_NUM_DETECTED_FACES_HW; michael@0: case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES: michael@0: return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES; michael@0: case CAMERA_PARAM_SUPPORTED_ISOMODES: michael@0: // Not every platform defines KEY_SUPPORTED_ISO_MODES; michael@0: // for those that don't, we use the raw string key. michael@0: return "iso-values"; michael@0: default: michael@0: DOM_CAMERA_LOGE("Unhandled camera parameter value %u\n", aKey); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: GonkCameraParameters::GonkCameraParameters() michael@0: : mLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraParameters.Lock")) michael@0: , mDirty(false) michael@0: , mInitialized(false) michael@0: { michael@0: MOZ_COUNT_CTOR(GonkCameraParameters); michael@0: if (!mLock) { michael@0: MOZ_CRASH("OOM getting new PRRWLock"); michael@0: } michael@0: } michael@0: michael@0: GonkCameraParameters::~GonkCameraParameters() michael@0: { michael@0: MOZ_COUNT_DTOR(GonkCameraParameters); michael@0: if (mLock) { michael@0: PR_DestroyRWLock(mLock); michael@0: mLock = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut) michael@0: { michael@0: if (aIso.EqualsASCII("hjr")) { michael@0: aIsoOut = "ISO_HJR"; michael@0: } else if (aIso.EqualsASCII("auto")) { michael@0: aIsoOut = "auto"; michael@0: } else { michael@0: nsAutoCString v = NS_LossyConvertUTF16toASCII(aIso); michael@0: unsigned int iso; michael@0: if (sscanf(v.get(), "%u", &iso) != 1) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: aIsoOut = nsPrintfCString("ISO%u", iso); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::MapIsoFromGonk(const char* aIso, nsAString& aIsoOut) michael@0: { michael@0: if (strcmp(aIso, "ISO_HJR") == 0) { michael@0: aIsoOut.AssignASCII("hjr"); michael@0: } else if (strcmp(aIso, "auto") == 0) { michael@0: aIsoOut.AssignASCII("auto"); michael@0: } else { michael@0: unsigned int iso; michael@0: if (sscanf(aIso, "ISO%u", &iso) != 1) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: aIsoOut.AppendInt(iso); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Any members that need to be initialized on the first parameter pull michael@0: // need to get handled in here. michael@0: nsresult michael@0: GonkCameraParameters::Initialize() michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = GetImpl(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mExposureCompensationMin); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to initialize minimum exposure compensation"); michael@0: mExposureCompensationMin = 0; michael@0: } michael@0: rv = GetImpl(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to initialize exposure compensation step size"); michael@0: mExposureCompensationStep = 0; michael@0: } michael@0: michael@0: rv = GetListAsArray(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios); michael@0: if (NS_FAILED(rv)) { michael@0: // zoom is not supported michael@0: mZoomRatios.Clear(); michael@0: } michael@0: for (uint32_t i = 1; i < mZoomRatios.Length(); ++i) { michael@0: // Make sure the camera gave us a properly sorted zoom ratio list! michael@0: if (mZoomRatios[i] < mZoomRatios[i - 1]) { michael@0: NS_WARNING("Zoom ratios list is out of order, discarding"); michael@0: DOM_CAMERA_LOGE("zoom[%d]=%fx < zoom[%d]=%fx is out of order\n", michael@0: i, mZoomRatios[i] / 100.0, i - 1, mZoomRatios[i - 1] / 100.0); michael@0: mZoomRatios.Clear(); michael@0: break; michael@0: } michael@0: } michael@0: if (mZoomRatios.Length() == 0) { michael@0: // Always report that we support at least 1.0x zoom. michael@0: *mZoomRatios.AppendElement() = 100; michael@0: } michael@0: michael@0: // The return code from GetListAsArray() doesn't matter. If it fails, michael@0: // the isoModes array will be empty, and the subsequent loop won't michael@0: // execute. michael@0: nsTArray isoModes; michael@0: GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes); michael@0: for (uint32_t i = 0; i < isoModes.Length(); ++i) { michael@0: nsString v; michael@0: rv = MapIsoFromGonk(isoModes[i].get(), v); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *mIsoModes.AppendElement() = v; michael@0: } michael@0: } michael@0: michael@0: mInitialized = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handle nsAStrings michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue) michael@0: { michael@0: if (aKey == CAMERA_PARAM_ISOMODE) { michael@0: nsAutoCString v; michael@0: nsresult rv = MapIsoToGonk(aValue, v); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: return SetImpl(aKey, v.get()); michael@0: } michael@0: michael@0: return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get()); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue) michael@0: { michael@0: const char* val; michael@0: nsresult rv = GetImpl(aKey, val); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: if (aKey == CAMERA_PARAM_ISOMODE) { michael@0: rv = MapIsoFromGonk(val, aValue); michael@0: } else if(val) { michael@0: aValue.AssignASCII(val); michael@0: } else { michael@0: aValue.Truncate(0); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: // Handle ICameraControl::Sizes michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize) michael@0: { michael@0: if (aSize.width > INT_MAX || aSize.height > INT_MAX) { michael@0: // AOSP can only handle signed ints. michael@0: DOM_CAMERA_LOGE("Camera parameter aKey=%d out of bounds (width=%u, height=%u)\n", michael@0: aSize.width, aSize.height); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: switch (aKey) { michael@0: case CAMERA_PARAM_THUMBNAILSIZE: michael@0: // This is a special case--for some reason the thumbnail size michael@0: // is accessed as two separate values instead of a tuple. michael@0: // XXXmikeh - make this restore the original values on error michael@0: rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast(aSize.width)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast(aSize.height)); michael@0: } michael@0: break; michael@0: michael@0: case CAMERA_PARAM_VIDEOSIZE: michael@0: // "record-size" is probably deprecated in later ICS; michael@0: // might need to set "video-size" instead of "record-size"; michael@0: // for the time being, set both. See bug 795332. michael@0: rv = SetImpl("record-size", nsPrintfCString("%ux%u", aSize.width, aSize.height).get()); michael@0: if (NS_FAILED(rv)) { michael@0: break; michael@0: } michael@0: // intentional fallthrough michael@0: michael@0: default: michael@0: rv = SetImpl(aKey, nsPrintfCString("%ux%u", aSize.width, aSize.height).get()); michael@0: break; michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, ICameraControl::Size& aSize) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (aKey == CAMERA_PARAM_THUMBNAILSIZE) { michael@0: int width; michael@0: int height; michael@0: michael@0: rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, width); michael@0: if (NS_FAILED(rv) || width < 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, height); michael@0: if (NS_FAILED(rv) || height < 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aSize.width = static_cast(width); michael@0: aSize.height = static_cast(height); michael@0: return NS_OK; michael@0: } michael@0: michael@0: const char* value; michael@0: rv = GetImpl(aKey, value); michael@0: if (NS_FAILED(rv) || !value || *value == '\0') { michael@0: DOM_CAMERA_LOGW("Camera parameter aKey=%d not available (0x%x)\n", aKey, rv); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: if (sscanf(value, "%ux%u", &aSize.width, &aSize.height) != 2) { michael@0: DOM_CAMERA_LOGE("Camera parameter aKey=%d size tuple '%s' is invalid\n", aKey, value); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handle arrays of ICameraControl::Regions michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const nsTArray& aRegions) michael@0: { michael@0: uint32_t length = aRegions.Length(); michael@0: michael@0: if (!length) { michael@0: // This tells the camera driver to revert to automatic regioning. michael@0: return SetImpl(aKey, "(0,0,0,0,0)"); michael@0: } michael@0: michael@0: nsCString s; michael@0: michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: const ICameraControl::Region* r = &aRegions[i]; michael@0: s.AppendPrintf("(%d,%d,%d,%d,%d),", r->top, r->left, r->bottom, r->right, r->weight); michael@0: } michael@0: michael@0: // remove the trailing comma michael@0: s.Trim(",", false, true, true); michael@0: michael@0: return SetImpl(aKey, s.get()); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aRegions) michael@0: { michael@0: aRegions.Clear(); michael@0: michael@0: const char* value; michael@0: nsresult rv = GetImpl(aKey, value); michael@0: if (NS_FAILED(rv) || !value || *value == '\0') { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: const char* p = value; michael@0: uint32_t count = 1; michael@0: michael@0: // count the number of regions in the string michael@0: while ((p = strstr(p, "),("))) { michael@0: ++count; michael@0: p += 3; michael@0: } michael@0: michael@0: aRegions.SetCapacity(count); michael@0: ICameraControl::Region* r; michael@0: michael@0: // parse all of the region sets michael@0: uint32_t i; michael@0: for (i = 0, p = value; p && i < count; ++i, p = strchr(p + 1, '(')) { michael@0: r = aRegions.AppendElement(); michael@0: if (sscanf(p, "(%d,%d,%d,%d,%u)", &r->top, &r->left, &r->bottom, &r->right, &r->weight) != 5) { michael@0: DOM_CAMERA_LOGE("%s:%d : region tuple has bad format: '%s'\n", __func__, __LINE__, p); michael@0: aRegions.Clear(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handle ICameraControl::Positions michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition) michael@0: { michael@0: MOZ_ASSERT(aKey == CAMERA_PARAM_PICTURE_LOCATION); michael@0: michael@0: // Add any specified location information -- we don't care if these fail. michael@0: if (!isnan(aPosition.latitude)) { michael@0: DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aPosition.latitude); michael@0: SetImpl(Parameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aPosition.latitude).get()); michael@0: } michael@0: if (!isnan(aPosition.longitude)) { michael@0: DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aPosition.longitude); michael@0: SetImpl(Parameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aPosition.longitude).get()); michael@0: } michael@0: if (!isnan(aPosition.altitude)) { michael@0: DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aPosition.altitude); michael@0: SetImpl(Parameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aPosition.altitude).get()); michael@0: } michael@0: if (!isnan(aPosition.timestamp)) { michael@0: DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aPosition.timestamp); michael@0: SetImpl(Parameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aPosition.timestamp).get()); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handle int64_ts michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const int64_t& aValue) michael@0: { michael@0: switch (aKey) { michael@0: case CAMERA_PARAM_PICTURE_DATETIME: michael@0: { michael@0: // Add the non-GPS timestamp. The EXIF date/time field is formatted as michael@0: // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time michael@0: // is meant to be stored as a local time. Since we are given seconds from michael@0: // Epoch GMT, we use localtime_r() to handle the conversion. michael@0: time_t time = aValue; michael@0: if (time != aValue) { michael@0: DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aValue); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: struct tm t; michael@0: if (!localtime_r(&time, &t)) { michael@0: DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno)); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: char dateTime[20]; michael@0: if (!strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) { michael@0: DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime); michael@0: michael@0: return SetImpl(CAMERA_PARAM_PICTURE_DATETIME, dateTime); michael@0: } michael@0: michael@0: case CAMERA_PARAM_ISOMODE: michael@0: { michael@0: if (aValue > INT32_MAX) { michael@0: DOM_CAMERA_LOGW("Can't set ISO mode = %lld, too big\n", aValue); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsString s; michael@0: s.AppendInt(aValue); michael@0: return SetTranslated(CAMERA_PARAM_ISOMODE, s); michael@0: } michael@0: } michael@0: michael@0: // You can't actually pass 64-bit parameters to Gonk. :( michael@0: int32_t v = static_cast(aValue); michael@0: if (static_cast(v) != aValue) { michael@0: return NS_ERROR_INVALID_ARG;; michael@0: } michael@0: return SetImpl(aKey, v); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, int64_t& aValue) michael@0: { michael@0: int val; michael@0: nsresult rv = GetImpl(aKey, val); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: aValue = val; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handle doubles michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue) michael@0: { michael@0: int index; michael@0: int value; michael@0: michael@0: switch (aKey) { michael@0: case CAMERA_PARAM_EXPOSURECOMPENSATION: michael@0: if (mExposureCompensationStep == 0) { michael@0: DOM_CAMERA_LOGE("Exposure compensation not supported, can't set %f\n", aValue); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: /** michael@0: * Convert from real value to a Gonk index, round michael@0: * to the nearest step; index is 1-based. michael@0: */ michael@0: index = michael@0: (aValue - mExposureCompensationMin + mExposureCompensationStep / 2) / michael@0: mExposureCompensationStep + 1; michael@0: DOM_CAMERA_LOGI("Exposure compensation = %f --> index = %d\n", aValue, index); michael@0: return SetImpl(CAMERA_PARAM_EXPOSURECOMPENSATION, index); michael@0: michael@0: case CAMERA_PARAM_ZOOM: michael@0: { michael@0: /** michael@0: * Convert from a real zoom multipler (e.g. 2.5x) to michael@0: * the index of the nearest supported value. michael@0: */ michael@0: value = aValue * 100.0; michael@0: michael@0: if (value <= mZoomRatios[0]) { michael@0: index = 0; michael@0: } else if (value >= mZoomRatios.LastElement()) { michael@0: index = mZoomRatios.Length() - 1; michael@0: } else { michael@0: // mZoomRatios is sorted, so we can binary search it michael@0: int bottom = 0; michael@0: int top = mZoomRatios.Length() - 1; michael@0: michael@0: while (top >= bottom) { michael@0: index = (top + bottom) / 2; michael@0: if (value == mZoomRatios[index]) { michael@0: // exact match michael@0: break; michael@0: } michael@0: if (value > mZoomRatios[index] && value < mZoomRatios[index + 1]) { michael@0: // the specified zoom value lies in this interval michael@0: break; michael@0: } michael@0: if (value > mZoomRatios[index]) { michael@0: bottom = index + 1; michael@0: } else { michael@0: top = index - 1; michael@0: } michael@0: } michael@0: } michael@0: DOM_CAMERA_LOGI("Zoom = %fx --> index = %d\n", aValue, index); michael@0: } michael@0: return SetImpl(CAMERA_PARAM_ZOOM, index); michael@0: } michael@0: michael@0: return SetImpl(aKey, aValue); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue) michael@0: { michael@0: double val; michael@0: int index = 0; michael@0: double focusDistance[3]; michael@0: const char* s; michael@0: nsresult rv; michael@0: michael@0: switch (aKey) { michael@0: case CAMERA_PARAM_ZOOM: michael@0: rv = GetImpl(aKey, index); michael@0: if (NS_SUCCEEDED(rv) && index >= 0) { michael@0: val = mZoomRatios[index] / 100.0; michael@0: } else { michael@0: // return 1x when zooming is not supported michael@0: val = 1.0; michael@0: rv = NS_OK; michael@0: } michael@0: break; michael@0: michael@0: /** michael@0: * The gonk camera parameters API only exposes one focus distance property michael@0: * that contains "Near,Optimum,Far" distances, in metres, where 'Far' may michael@0: * be 'Infinity'. michael@0: */ michael@0: case CAMERA_PARAM_FOCUSDISTANCEFAR: michael@0: ++index; michael@0: // intentional fallthrough michael@0: michael@0: case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM: michael@0: ++index; michael@0: // intentional fallthrough michael@0: michael@0: case CAMERA_PARAM_FOCUSDISTANCENEAR: michael@0: rv = GetImpl(aKey, s); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (sscanf(s, "%lf,%lf,%lf", &focusDistance[0], &focusDistance[1], &focusDistance[2]) == 3) { michael@0: val = focusDistance[index]; michael@0: } else { michael@0: val = 0.0; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case CAMERA_PARAM_EXPOSURECOMPENSATION: michael@0: rv = GetImpl(aKey, index); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (!index) { michael@0: // NaN indicates automatic exposure compensation michael@0: val = NAN; michael@0: } else { michael@0: val = (index - 1) * mExposureCompensationStep + mExposureCompensationMin; michael@0: DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index, val); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: rv = GetImpl(aKey, val); michael@0: break; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: aValue = val; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: // Handle ints michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const int& aValue) michael@0: { michael@0: return SetImpl(aKey, aValue); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, int& aValue) michael@0: { michael@0: return GetImpl(aKey, aValue); michael@0: } michael@0: michael@0: // Handle uint32_ts -- Gonk only speaks int michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const uint32_t& aValue) michael@0: { michael@0: if (aValue > INT_MAX) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: int val = static_cast(aValue); michael@0: return SetImpl(aKey, val); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, uint32_t& aValue) michael@0: { michael@0: int val; michael@0: nsresult rv = GetImpl(aKey, val); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: if (val < 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aValue = val; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handle bools michael@0: nsresult michael@0: GonkCameraParameters::SetTranslated(uint32_t aKey, const bool& aValue) michael@0: { michael@0: return SetImpl(aKey, aValue); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, bool& aValue) michael@0: { michael@0: return GetImpl(aKey, aValue); michael@0: } michael@0: michael@0: nsresult michael@0: ParseItem(const char* aStart, const char* aEnd, ICameraControl::Size* aItem) michael@0: { michael@0: if (sscanf(aStart, "%ux%u", &aItem->width, &aItem->height) == 2) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: DOM_CAMERA_LOGE("Size tuple has bad format: '%s'\n", __func__, __LINE__, aStart); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult michael@0: ParseItem(const char* aStart, const char* aEnd, nsAString* aItem) michael@0: { michael@0: if (aEnd) { michael@0: aItem->AssignASCII(aStart, aEnd - aStart); michael@0: } else { michael@0: aItem->AssignASCII(aStart); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ParseItem(const char* aStart, const char* aEnd, nsACString* aItem) michael@0: { michael@0: if (aEnd) { michael@0: aItem->AssignASCII(aStart, aEnd - aStart); michael@0: } else { michael@0: aItem->AssignASCII(aStart); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ParseItem(const char* aStart, const char* aEnd, double* aItem) michael@0: { michael@0: if (sscanf(aStart, "%lf", aItem) == 1) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult michael@0: ParseItem(const char* aStart, const char* aEnd, int* aItem) michael@0: { michael@0: if (sscanf(aStart, "%d", aItem) == 1) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: template nsresult michael@0: GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray& aArray) michael@0: { michael@0: const char* p; michael@0: nsresult rv = GetImpl(aKey, p); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: aArray.Clear(); michael@0: michael@0: // If there is no value available, just return the empty array. michael@0: if (!p) { michael@0: DOM_CAMERA_LOGI("Camera parameter %d not available (value is null)\n", aKey); michael@0: return NS_OK; michael@0: } michael@0: if (*p == '\0') { michael@0: DOM_CAMERA_LOGI("Camera parameter %d not available (value is empty string)\n", aKey); michael@0: return NS_OK; michael@0: } michael@0: michael@0: const char* comma; michael@0: michael@0: while (p) { michael@0: T* v = aArray.AppendElement(); michael@0: if (!v) { michael@0: aArray.Clear(); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: comma = strchr(p, ','); michael@0: if (comma != p) { michael@0: rv = ParseItem(p, comma, v); michael@0: if (NS_FAILED(rv)) { michael@0: aArray.Clear(); michael@0: return rv; michael@0: } michael@0: p = comma; michael@0: } michael@0: if (p) { michael@0: ++p; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aValues) michael@0: { michael@0: if (aKey == CAMERA_PARAM_SUPPORTED_ISOMODES) { michael@0: aValues = mIsoModes; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return GetListAsArray(aKey, aValues); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aValues) michael@0: { michael@0: if (aKey == CAMERA_PARAM_SUPPORTED_ZOOMRATIOS) { michael@0: aValues.Clear(); michael@0: for (uint32_t i = 0; i < mZoomRatios.Length(); ++i) { michael@0: *aValues.AppendElement() = mZoomRatios[i] / 100.0; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: return GetListAsArray(aKey, aValues); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aSizes) michael@0: { michael@0: return GetListAsArray(aKey, aSizes); michael@0: } michael@0: