diff --git a/dom/camera/CameraPreferences.cpp b/dom/camera/CameraPreferences.cpp index 1014e4372041..509406e66c0d 100644 --- a/dom/camera/CameraPreferences.cpp +++ b/dom/camera/CameraPreferences.cpp @@ -22,6 +22,11 @@ StaticAutoPtr CameraPreferences::sPrefGonkParameters; nsresult CameraPreferences::sPrefCameraControlMethodErrorOverride = NS_OK; nsresult CameraPreferences::sPrefCameraControlAsyncErrorOverride = NS_OK; +uint32_t CameraPreferences::sPrefCameraControlLowMemoryThresholdMB = 0; + +bool CameraPreferences::sPrefCameraParametersIsLowMemory = false; + +#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT /* static */ nsresult CameraPreferences::UpdatePref(const char* aPref, nsresult& aVal) @@ -33,6 +38,19 @@ CameraPreferences::UpdatePref(const char* aPref, nsresult& aVal) } return rv; } +#endif + +/* static */ +nsresult +CameraPreferences::UpdatePref(const char* aPref, uint32_t& aVal) +{ + uint32_t val; + nsresult rv = Preferences::GetUint(aPref, &val); + if (NS_SUCCEEDED(rv)) { + aVal = val; + } + return rv; +} /* static */ nsresult @@ -46,6 +64,18 @@ CameraPreferences::UpdatePref(const char* aPref, nsACString& aVal) return rv; } +/* static */ +nsresult +CameraPreferences::UpdatePref(const char* aPref, bool& aVal) +{ + bool val; + nsresult rv = Preferences::GetBool(aPref, &val); + if (NS_SUCCEEDED(rv)) { + aVal = val; + } + return rv; +} + /* static */ CameraPreferences::Pref CameraPreferences::sPrefs[] = { { @@ -67,14 +97,24 @@ CameraPreferences::Pref CameraPreferences::sPrefs[] = { #endif { "camera.control.test.method.error", - kPrefValueIsNSResult, + kPrefValueIsNsResult, { &sPrefCameraControlMethodErrorOverride } }, { "camera.control.test.async.error", - kPrefValueIsNSResult, + kPrefValueIsNsResult, { &sPrefCameraControlAsyncErrorOverride } }, + { + "camera.control.test.is_low_memory", + kPrefValueIsBoolean, + { &sPrefCameraParametersIsLowMemory } + }, + { + "camera.control.low_memory_thresholdMB", + kPrefValueIsUint32, + { &sPrefCameraControlLowMemoryThresholdMB } + }, }; /* static */ @@ -104,7 +144,8 @@ CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure) Pref& p = sPrefs[i]; nsresult rv; switch (p.mValueType) { - case kPrefValueIsNSResult: + case kPrefValueIsNsResult: + #ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT { nsresult& v = *p.mValue.mAsNsResult; rv = UpdatePref(aPref, v); @@ -113,6 +154,17 @@ CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure) } } break; + #endif + + case kPrefValueIsUint32: + { + uint32_t& v = *p.mValue.mAsUint32; + rv = UpdatePref(aPref, v); + if (NS_SUCCEEDED(rv)) { + DOM_CAMERA_LOGI("Preference '%s' has changed, %u\n", aPref, v); + } + } + break; case kPrefValueIsCString: { @@ -124,6 +176,17 @@ CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure) } break; + case kPrefValueIsBoolean: + { + bool& v = *p.mValue.mAsBoolean; + rv = UpdatePref(aPref, v); + if (NS_SUCCEEDED(rv)) { + DOM_CAMERA_LOGI("Preference '%s' has changed, %s\n", + aPref, v ? "true" : "false"); + } + } + break; + default: MOZ_ASSERT_UNREACHABLE("Unhandled preference value type!"); return; @@ -211,6 +274,7 @@ CameraPreferences::GetPref(const char* aPref, nsACString& aVal) return true; } +#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT /* static */ bool CameraPreferences::GetPref(const char* aPref, nsresult& aVal) @@ -223,14 +287,14 @@ CameraPreferences::GetPref(const char* aPref, nsresult& aVal) DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref); return false; } - if (sPrefs[i].mValueType != kPrefValueIsNSResult) { + if (sPrefs[i].mValueType != kPrefValueIsNsResult) { DOM_CAMERA_LOGW("Preference '%s' is not an nsresult type\n", aPref); return false; } nsresult v = *sPrefs[i].mValue.mAsNsResult; if (v == NS_OK) { - DOM_CAMERA_LOGI("Preference '%s' is not set\n", aPref); + DOM_CAMERA_LOGW("Preference '%s' is not set\n", aPref); return false; } @@ -238,3 +302,50 @@ CameraPreferences::GetPref(const char* aPref, nsresult& aVal) aVal = v; return true; } +#endif + +/* static */ +bool +CameraPreferences::GetPref(const char* aPref, uint32_t& aVal) +{ + MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()"); + MonitorAutoLock mon(*sPrefMonitor); + + uint32_t i = PrefToIndex(aPref); + if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) { + DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref); + return false; + } + if (sPrefs[i].mValueType != kPrefValueIsUint32) { + DOM_CAMERA_LOGW("Preference '%s' is not a uint32_t type\n", aPref); + return false; + } + + uint32_t v = *sPrefs[i].mValue.mAsUint32; + DOM_CAMERA_LOGI("Preference '%s', got %u\n", aPref, v); + aVal = v; + return true; +} + +/* static */ +bool +CameraPreferences::GetPref(const char* aPref, bool& aVal) +{ + MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()"); + MonitorAutoLock mon(*sPrefMonitor); + + uint32_t i = PrefToIndex(aPref); + if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) { + DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref); + return false; + } + if (sPrefs[i].mValueType != kPrefValueIsBoolean) { + DOM_CAMERA_LOGW("Preference '%s' is not a boolean type\n", aPref); + return false; + } + + bool v = *sPrefs[i].mValue.mAsBoolean; + DOM_CAMERA_LOGI("Preference '%s', got %s\n", aPref, v ? "true" : "false"); + aVal = v; + return true; +} diff --git a/dom/camera/CameraPreferences.h b/dom/camera/CameraPreferences.h index f1a59522ac2a..dc8d9c826680 100644 --- a/dom/camera/CameraPreferences.h +++ b/dom/camera/CameraPreferences.h @@ -8,6 +8,13 @@ #include "nsString.h" +#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) || defined(MOZ_HAVE_CXX11_ENUM_TYPE) +// Older compilers that don't support strongly-typed enums +// just typedef uint32_t to nsresult, which results in conflicting +// overloaded members in CameraPreferences. +#define CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT +#endif + namespace mozilla { template class StaticAutoPtr; @@ -19,19 +26,29 @@ public: static void Shutdown(); static bool GetPref(const char* aPref, nsACString& aVal); +#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT static bool GetPref(const char* aPref, nsresult& aVal); +#endif + static bool GetPref(const char* aPref, uint32_t& aVal); + static bool GetPref(const char* aPref, bool& aVal); protected: static const uint32_t kPrefNotFound = UINT32_MAX; static uint32_t PrefToIndex(const char* aPref); static void PreferenceChanged(const char* aPref, void* aClosure); +#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT static nsresult UpdatePref(const char* aPref, nsresult& aVar); +#endif + static nsresult UpdatePref(const char* aPref, uint32_t& aVar); static nsresult UpdatePref(const char* aPref, nsACString& aVar); + static nsresult UpdatePref(const char* aPref, bool& aVar); enum PrefValueType { - kPrefValueIsNSResult, - kPrefValueIsCString + kPrefValueIsNsResult, + kPrefValueIsUint32, + kPrefValueIsCString, + kPrefValueIsBoolean }; struct Pref { const char* const mPref; @@ -45,6 +62,8 @@ protected: void* mAsVoid; StaticAutoPtr* mAsCString; nsresult* mAsNsResult; + uint32_t* mAsUint32; + bool* mAsBoolean; } mValue; }; static Pref sPrefs[]; @@ -56,6 +75,10 @@ protected: static nsresult sPrefCameraControlMethodErrorOverride; static nsresult sPrefCameraControlAsyncErrorOverride; + static uint32_t sPrefCameraControlLowMemoryThresholdMB; + + static bool sPrefCameraParametersIsLowMemory; + private: // static class only CameraPreferences(); diff --git a/dom/camera/GonkCameraParameters.cpp b/dom/camera/GonkCameraParameters.cpp index dfc4593fe346..3307f3c0b290 100644 --- a/dom/camera/GonkCameraParameters.cpp +++ b/dom/camera/GonkCameraParameters.cpp @@ -16,12 +16,40 @@ #include "GonkCameraParameters.h" #include "camera/CameraParameters.h" +#include "CameraPreferences.h" #include "ICameraControl.h" #include "CameraCommon.h" +#include "mozilla/Hal.h" using namespace mozilla; using namespace android; +/* static */ bool +GonkCameraParameters::IsLowMemoryPlatform() +{ + bool testIsLowMem = false; + CameraPreferences::GetPref("camera.control.test.is_low_memory", testIsLowMem); + if (testIsLowMem) { + NS_WARNING("Forcing low-memory platform camera preferences"); + return true; + } + + uint32_t lowMemoryThresholdBytes = 0; + CameraPreferences::GetPref("camera.control.low_memory_thresholdMB", + lowMemoryThresholdBytes); + lowMemoryThresholdBytes *= 1024 * 1024; + if (lowMemoryThresholdBytes) { + uint32_t totalMemoryBytes = hal::GetTotalSystemMemory(); + if (totalMemoryBytes < lowMemoryThresholdBytes) { + DOM_CAMERA_LOGI("Low-memory platform with %d bytes of RAM (threshold: <%d bytes)\n", + totalMemoryBytes, lowMemoryThresholdBytes); + return true; + } + } + + return false; +} + /* static */ const char* GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey) { @@ -246,7 +274,7 @@ GonkCameraParameters::Initialize() nsString s; nsTArray isoModes; GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes); - for (uint32_t i = 0; i < isoModes.Length(); ++i) { + for (nsTArray::size_type i = 0; i < isoModes.Length(); ++i) { rv = MapIsoFromGonk(isoModes[i].get(), s); if (NS_FAILED(rv)) { DOM_CAMERA_LOGW("Unrecognized ISO mode value '%s'\n", isoModes[i].get()); @@ -256,6 +284,17 @@ GonkCameraParameters::Initialize() mIsoModeMap.Put(s, new nsCString(isoModes[i])); } + GetListAsArray(CAMERA_PARAM_SUPPORTED_SCENEMODES, mSceneModes); + if (IsLowMemoryPlatform()) { + bool hdrRemoved = false; + while (mSceneModes.RemoveElement(NS_LITERAL_STRING("hdr"))) { + hdrRemoved = true; + } + if (hdrRemoved) { + DOM_CAMERA_LOGI("Disabling HDR support due to low memory\n"); + } + } + mInitialized = true; return NS_OK; } @@ -264,16 +303,26 @@ GonkCameraParameters::Initialize() nsresult GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue) { - if (aKey == CAMERA_PARAM_ISOMODE) { - nsAutoCString v; - nsresult rv = MapIsoToGonk(aValue, v); - if (NS_FAILED(rv)) { - return rv; - } - return SetImpl(aKey, v.get()); - } + switch (aKey) { + case CAMERA_PARAM_ISOMODE: + { + nsAutoCString v; + nsresult rv = MapIsoToGonk(aValue, v); + if (NS_FAILED(rv)) { + return rv; + } + return SetImpl(aKey, v.get()); + } - return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get()); + case CAMERA_PARAM_SCENEMODE: + if (mSceneModes.IndexOf(aValue) == nsTArray::NoIndex) { + return NS_ERROR_INVALID_ARG; + } + // fallthrough + + default: + return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get()); + } } nsresult @@ -620,7 +669,7 @@ GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue) nsresult GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue) { - double val; + double val = 0.0; // initialize to keep the compiler happy [-Wmaybe-uninitialized] int index = 0; double focusDistance[3]; const char* s; @@ -840,12 +889,18 @@ GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray& aArray) nsresult GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aValues) { - if (aKey == CAMERA_PARAM_SUPPORTED_ISOMODES) { - aValues = mIsoModes; - return NS_OK; - } + switch (aKey) { + case CAMERA_PARAM_SUPPORTED_ISOMODES: + aValues = mIsoModes; + return NS_OK; - return GetListAsArray(aKey, aValues); + case CAMERA_PARAM_SUPPORTED_SCENEMODES: + aValues = mSceneModes; + return NS_OK; + + default: + return GetListAsArray(aKey, aValues); + } } nsresult diff --git a/dom/camera/GonkCameraParameters.h b/dom/camera/GonkCameraParameters.h index e8193da9fcee..7901c1f1adf4 100644 --- a/dom/camera/GonkCameraParameters.h +++ b/dom/camera/GonkCameraParameters.h @@ -101,6 +101,7 @@ protected: int32_t mExposureCompensationMaxIndex; nsTArray mZoomRatios; nsTArray mIsoModes; + nsTArray mSceneModes; nsClassHashtable mIsoModeMap; // This subclass of android::CameraParameters just gives @@ -224,6 +225,10 @@ protected: // Call once to initialize local cached values used in translating other // arguments between Gecko and Gonk. Always returns NS_OK. nsresult Initialize(); + + // Returns true if we're a memory-constrained platform that requires + // certain features to be disabled; returns false otherwise. + static bool IsLowMemoryPlatform(); }; } // namespace mozilla diff --git a/dom/camera/test/camera_common.js b/dom/camera/test/camera_common.js index 6f0a1c52db65..b50e052ce50b 100644 --- a/dom/camera/test/camera_common.js +++ b/dom/camera/test/camera_common.js @@ -35,6 +35,7 @@ var CameraTest = (function() { const PREF_TEST_ENABLED = "camera.control.test.enabled"; const PREF_TEST_HARDWARE = "camera.control.test.hardware"; const PREF_TEST_EXTRA_PARAMETERS = "camera.control.test.hardware.gonk.parameters"; + const PREF_TEST_FAKE_LOW_MEMORY = "camera.control.test.is_low_memory"; var oldTestEnabled; var oldTestHw; var testMode; @@ -53,6 +54,20 @@ var CameraTest = (function() { SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_EXTRA_PARAMETERS]]}, callback); } + function testHardwareSetFakeLowMemoryPlatform(callback) { + SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_FAKE_LOW_MEMORY, true]]}, function() { + var setParams = SpecialPowers.getBoolPref(PREF_TEST_FAKE_LOW_MEMORY); + ise(setParams, true, "Fake low memory platform"); + if (callback) { + callback(setParams); + } + }); + } + + function testHardwareClearFakeLowMemoryPlatform(callback) { + SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_FAKE_LOW_MEMORY]]}, callback); + } + function testHardwareSet(test, callback) { SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, test]]}, function() { var setTest = SpecialPowers.getCharPref(PREF_TEST_HARDWARE); @@ -88,6 +103,8 @@ var CameraTest = (function() { set: testHardwareSet, setFakeParameters: testHardwareSetFakeParameters, clearFakeParameters: testHardwareClearFakeParameters, + setFakeLowMemoryPlatform: testHardwareSetFakeLowMemoryPlatform, + clearFakeLowMemoryPlatform: testHardwareClearFakeLowMemoryPlatform, done: testHardwareDone }; if (callback) { @@ -122,8 +139,16 @@ var CameraTest = (function() { next(); } } - function cleanUpExtraParameters() { + function cleanUpLowMemoryPlatform() { var next = cleanUpTest; + if (testMode) { + testMode.clearFakeLowMemoryPlatform(next); + } else { + next(); + } + } + function cleanUpExtraParameters() { + var next = cleanUpLowMemoryPlatform; if (testMode) { testMode.clearFakeParameters(next); } else { diff --git a/dom/camera/test/test_camera_fake_parameters.html b/dom/camera/test/test_camera_fake_parameters.html index b1b6dfdc2cc6..9902ccf2ad09 100644 --- a/dom/camera/test/test_camera_fake_parameters.html +++ b/dom/camera/test/test_camera_fake_parameters.html @@ -114,6 +114,61 @@ var tests = [ next(); } }, + { + key: "fake-high-memory-platform", + prep: function setupFakeHighMemoryPlatform(test) { + test.setFakeParameters("scene-mode-values=none,snow,beach,hdr,nothdr", function () { + run(); + }); + }, + test: function testFakeHighMemoryPlatform(cam, cap) { + ok(cap.sceneModes.length == 5, "scene modes length = " + cap.zoomRatios.length); + + // make sure expected values are present and can be set + [ "none", "snow", "beach", "hdr", "nothdr" ].forEach(function(mode) { + ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present"); + cam.sceneMode = mode; + ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set"); + }); + + next(); + } + }, + { + key: "fake-low-memory-platform", + prep: function setupFakeLowMemoryPlatform(test) { + test.setFakeLowMemoryPlatform(function() { + test.setFakeParameters("scene-mode-values=none,hdr,snow,beach,hdr,nothdr", function () { + run(); + }); + }); + }, + test: function testFakeLowMemoryPlatform(cam, cap) { + ok(cap.sceneModes.length == 4, "scene modes length = " + cap.zoomRatios.length); + + // make sure expected values are present and can be set + [ "none", "snow", "beach", "nothdr" ].forEach(function(mode) { + ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present"); + cam.sceneMode = mode; + ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set"); + }); + + // make sure unsupported values have been removed, and can't be set + var sceneMode = cam.sceneMode; + [ "hdr" ].forEach(function(mode) { + ok(cap.sceneModes.indexOf(mode) == -1, "Scene mode '" + mode + "' is not present"); + try { + cam.sceneMode = mode; + } catch(e) { + } + ok(cam.sceneMode != mode, "Scene mode '" + cam.sceneMode + "' is still set, '" + + mode + "' rejected"); + }); + ok(cam.sceneMode == sceneMode, "Scene mode '" + cam.sceneMode + "' is still set"); + + next(); + } + }, { key: "fake-iso", prep: function setupFakeIso(test) { @@ -129,20 +184,14 @@ var tests = [ ok(cap.isoModes.length == 7, "ISO modes length = " + cap.isoModes.length); // make sure we're not leaking any unexpected values formats - ok(cap.isoModes.indexOf("ISO_HJR") == -1, "ISO mode 'ISO_HJR' does not appear"); - ok(cap.isoModes.indexOf("_HJR") == -1, "ISO mode '_HJR' does not appear"); - ok(cap.isoModes.indexOf("HJR") == -1, "ISO mode 'HJR' does not appear"); - ok(cap.isoModes.indexOf("ISO100") == -1, "ISO mode 'ISO100' does not appear"); - ok(cap.isoModes.indexOf("ISO200") == -1, "ISO mode 'ISO200' does not appear"); - ok(cap.isoModes.indexOf("ISO800") == -1, "ISO mode 'ISO800' does not appear"); + [ "ISO_HJR", "_HJR", "HJR", "ISO100", "ISO200", "ISO800" ].forEach(function(iso) { + ok(cap.isoModes.indexOf(iso) == -1, "ISO mode '" + iso + "' does not appear"); + }); // make sure any weird values are dropped entirely - ok(cap.isoModes.indexOf("foo") == -1, "Unknown ISO mode 'foo' is ignored"); - ok(cap.isoModes.indexOf("ISObar") == -1, "Unknown ISO mode 'ISObar' is ignored"); - ok(cap.isoModes.indexOf("bar") == -1, "Unknown ISO mode 'bar' is ignored"); - ok(cap.isoModes.indexOf("ISO150moz") == -1, "Unknown ISO mode 'ISO150moz' is ignored"); - ok(cap.isoModes.indexOf("150moz") == -1, "Unknown ISO mode '150moz' is ignored"); - ok(cap.isoModes.indexOf("150") == -1, "Unknown ISO mode '150' is ignored"); + [ "foo", "ISObar", "bar", "ISO150moz", "150moz", "150" ].forEach(function(iso) { + ok(cap.isoModes.indexOf(iso) == -1, "Unknown ISO mode '" + iso + "' is ignored"); + }); // make sure expected values are present [ "auto", "hjr", "100", "200", "400", "800", "1600" ].forEach(function(iso) { diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 63cc3ba3db23..1800c93b949a 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -4273,3 +4273,11 @@ pref("beacon.enabled", true); // Camera prefs pref("camera.control.autofocus_moving_callback.enabled", true); pref("camera.control.face_detection.enabled", true); +#ifdef MOZ_WIDGET_GONK +// Empirically, this is the value returned by hal::GetTotalSystemMemory() +// when Flame's memory is limited to 512MiB. If the camera stack determines +// it is running on a low memory platform, features that can be reliably +// supported will be disabled. This threshold can be adjusted to suit other +// platforms; and set to 0 to disable the low-memory check altogether. +pref("camera.control.low_memory_thresholdMB", 404); +#endif