Bug 1036637 - Disable HDR on low-memory platforms. r=dhylands, a=2.0+
authorMike Habicher <mikeh@mozilla.com>
Tue, 15 Jul 2014 11:19:00 -0400
changeset 209097 48868fc69e456431c45326e475f4c4f277e101c3
parent 209096 a3386f2441d615e80785b8a96da462b83a61f71e
child 209098 b244b8b50d911bf34905a451e8392ed7b502c05f
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands, 2
bugs1036637
milestone32.0a2
Bug 1036637 - Disable HDR on low-memory platforms. r=dhylands, a=2.0+
dom/camera/GonkCameraParameters.cpp
dom/camera/GonkCameraParameters.h
dom/camera/test/camera_common.js
dom/camera/test/test_camera_fake_parameters.html
modules/libpref/src/init/all.js
--- a/dom/camera/GonkCameraParameters.cpp
+++ b/dom/camera/GonkCameraParameters.cpp
@@ -11,22 +11,49 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkCameraParameters.h"
 #include "camera/CameraParameters.h"
+#include "mozilla/Preferences.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
+#include "mozilla/Hal.h"
 
 using namespace mozilla;
 using namespace android;
 
+/* static */ bool
+GonkCameraParameters::IsLowMemoryPlatform()
+{
+  bool testIsLowMem =
+    mozilla::Preferences::GetBool("camera.control.test.is_low_memory");
+  if (testIsLowMem) {
+    NS_WARNING("Forcing low-memory platform camera preferences");
+    return true;
+  }
+
+  uint32_t lowMemoryThresholdBytes =
+    mozilla::Preferences::GetUint("camera.control.low_memory_thresholdMB");
+  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)
 {
   switch (aKey) {
     case CAMERA_PARAM_PREVIEWSIZE:
       return KEY_PREVIEW_SIZE;
     case CAMERA_PARAM_PREVIEWFORMAT:
       return KEY_PREVIEW_FORMAT;
@@ -236,42 +263,63 @@ GonkCameraParameters::Initialize()
     *mZoomRatios.AppendElement() = 100;
   }
 
   // The return code from GetListAsArray() doesn't matter. If it fails,
   // the isoModes array will be empty, and the subsequent loop won't
   // execute.
   nsTArray<nsCString> isoModes;
   GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes);
-  for (uint32_t i = 0; i < isoModes.Length(); ++i) {
+  for (nsTArray<nsCString>::size_type i = 0; i < isoModes.Length(); ++i) {
     nsString v;
     rv = MapIsoFromGonk(isoModes[i].get(), v);
     if (NS_SUCCEEDED(rv)) {
       *mIsoModes.AppendElement() = v;
     }
   }
 
+  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;
 }
 
 // Handle nsAStrings
 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());
+      }
+
+    case CAMERA_PARAM_SCENEMODE:
+      if (mSceneModes.IndexOf(aValue) == nsTArray<nsString>::NoIndex) {
+        return NS_ERROR_INVALID_ARG;
+      }
+      // fallthrough
+
+    default:
+      return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
   }
-
-  return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue)
 {
   const char* val;
   nsresult rv = GetImpl(aKey, val);
   if (NS_FAILED(rv)) {
@@ -608,17 +656,17 @@ GonkCameraParameters::SetTranslated(uint
   }
 
   return SetImpl(aKey, 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;
   nsresult rv;
 
   switch (aKey) {
     case CAMERA_PARAM_ZOOM:
       rv = GetImpl(aKey, index);
@@ -828,22 +876,28 @@ GonkCameraParameters::GetListAsArray(uin
   }
 
   return NS_OK;
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues)
 {
-  if (aKey == CAMERA_PARAM_SUPPORTED_ISOMODES) {
-    aValues = mIsoModes;
-    return NS_OK;
+  switch (aKey) {
+    case CAMERA_PARAM_SUPPORTED_ISOMODES:
+      aValues = mIsoModes;
+      return NS_OK;
+
+    case CAMERA_PARAM_SUPPORTED_SCENEMODES:
+      aValues = mSceneModes;
+      return NS_OK;
+
+    default:
+      return GetListAsArray(aKey, aValues);
   }
-
-  return GetListAsArray(aKey, aValues);
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<double>& aValues)
 {
   if (aKey == CAMERA_PARAM_SUPPORTED_ZOOMRATIOS) {
     aValues.Clear();
     for (uint32_t i = 0; i < mZoomRatios.Length(); ++i) {
--- a/dom/camera/GonkCameraParameters.h
+++ b/dom/camera/GonkCameraParameters.h
@@ -95,16 +95,17 @@ protected:
   bool mInitialized;
 
   // Required internal properties
   double mExposureCompensationStep;
   int32_t mExposureCompensationMinIndex;
   int32_t mExposureCompensationMaxIndex;
   nsTArray<int> mZoomRatios;
   nsTArray<nsString> mIsoModes;
+  nsTArray<nsString> mSceneModes;
 
   // This subclass of android::CameraParameters just gives
   // all of the AOSP getters and setters the same signature.
   class Parameters : public android::CameraParameters
   {
   public:
     using android::CameraParameters::set;
     using android::CameraParameters::get;
@@ -217,13 +218,17 @@ protected:
   //  - NS_OK on success;
   //  - NS_ERROR_INVALID_ARG if the 'aIso' argument is not a valid form.
   nsresult MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut);
   nsresult MapIsoFromGonk(const char* aIso, nsAString& aIsoOut);
 
   // 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
 
 #endif // DOM_CAMERA_GONKCAMERAPARAMETERS_H
--- a/dom/camera/test/camera_common.js
+++ b/dom/camera/test/camera_common.js
@@ -30,16 +30,17 @@ var CameraTest = (function() {
    *
    * This means (of course) that neither the key not the value tokens can
    * contain either equals signs or semicolons. The test shim doesn't enforce
    * this so that we can test getting junk from the camera library as well.
    */
   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;
 
   function testHardwareSetFakeParameters(parameters, callback) {
     SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_EXTRA_PARAMETERS, parameters]]}, function() {
       var setParams = SpecialPowers.getCharPref(PREF_TEST_EXTRA_PARAMETERS);
       ise(setParams, parameters, "Extra test parameters '" + setParams + "'");
@@ -48,16 +49,30 @@ var CameraTest = (function() {
       }
     });
   }
 
   function testHardwareClearFakeParameters(callback) {
     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);
       ise(setTest, test, "Test subtype set to " + setTest);
       if (callback) {
         callback(setTest);
       }
     });
@@ -83,16 +98,18 @@ var CameraTest = (function() {
       if (setMode === "hardware") {
         try {
           oldTestHw = SpecialPowers.getCharPref(PREF_TEST_HARDWARE);
         } catch(e) { }
         testMode = {
           set: testHardwareSet,
           setFakeParameters: testHardwareSetFakeParameters,
           clearFakeParameters: testHardwareClearFakeParameters,
+          setFakeLowMemoryPlatform: testHardwareSetFakeLowMemoryPlatform,
+          clearFakeLowMemoryPlatform: testHardwareClearFakeLowMemoryPlatform,
           done: testHardwareDone
         };
         if (callback) {
           callback(testMode);
         }
       }
     });
   }
@@ -117,18 +134,26 @@ var CameraTest = (function() {
       var next = cleanUpTestEnabled;
       if (testMode) {
         testMode.done(next);
         testMode = null;
       } else {
         next();
       }
     }
+    function cleanUpLowMemoryPlatform() {
+      var next = cleanUpTest;
+      if (testMode) {
+        testMode.clearFakeLowMemoryPlatform(next);
+      } else {
+        next();
+      }
+    }
     function cleanUpExtraParameters() {
-      var next = cleanUpTest;
+      var next = cleanUpLowMemoryPlatform;
       if (testMode) {
         testMode.clearFakeParameters(next);
       } else {
         next();
       }
     }
 
     cleanUpExtraParameters();
--- a/dom/camera/test/test_camera_fake_parameters.html
+++ b/dom/camera/test/test_camera_fake_parameters.html
@@ -110,16 +110,71 @@ var tests = [
     },
     test: function testFakeZoomOutOfOrder(cam, cap) {
       ok(cap.zoomRatios.length == 1, "zoom ratios length = " + cap.zoomRatios.length);
       ok(cap.zoomRatios[0] == 1.0, "only supported zoom = " + cap.zoomRatios[0] + "x");
       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) {
       // we should recognize 'auto', 'hjr', and numeric modes; anything else
       // from the driver is ignored, which this test also verifies.
       test.setFakeParameters(
         "iso=auto;iso-values=auto,ISO_HJR,ISO100,foo,ISObar,ISO200,ISO400,ISO800,ISO1600",
         function () {
         run();
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -4227,8 +4227,16 @@ pref("image.mozsamplesize.enabled", fals
 // play nicely with Firefox OS apps yet.
 #ifndef MOZ_WIDGET_GONK
 pref("beacon.enabled", true);
 #endif
 
 // 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