Consolidated all the Android LuminanceSource classes into one file. Either a device...
[zxing.git] / android / src / com / google / zxing / client / android / CameraManager.java
index 9ac5f47..8b7ae73 100755 (executable)
@@ -23,6 +23,7 @@ import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.Camera;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.util.Log;
@@ -60,6 +61,7 @@ final class CameraManager {
   private boolean previewing;
   private int previewFormat;
   private String previewFormatString;
+  private boolean useOneShotPreviewCallback;
 
   /**
    * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
@@ -67,6 +69,9 @@ final class CameraManager {
    */
   private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
     public void onPreviewFrame(byte[] data, Camera camera) {
+      if (!useOneShotPreviewCallback) {
+        camera.setPreviewCallback(null);
+      }
       if (previewHandler != null) {
         Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
             cameraResolution.y, data);
@@ -115,6 +120,16 @@ final class CameraManager {
     camera = null;
     initialized = false;
     previewing = false;
+
+    // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
+    // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
+    // the more efficient one shot callback, as the older one can swamp the system and cause it
+    // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
+    if (Integer.parseInt(Build.VERSION.SDK) <= Build.VERSION_CODES.CUPCAKE) {
+      useOneShotPreviewCallback = false;
+    } else {
+      useOneShotPreviewCallback = true;
+    }
   }
 
   /**
@@ -162,6 +177,9 @@ final class CameraManager {
    */
   public void stopPreview() {
     if (camera != null && previewing) {
+      if (!useOneShotPreviewCallback) {
+        camera.setPreviewCallback(null);
+      }
       camera.stopPreview();
       previewHandler = null;
       autoFocusHandler = null;
@@ -181,7 +199,11 @@ final class CameraManager {
     if (camera != null && previewing) {
       previewHandler = handler;
       previewMessage = message;
-      camera.setOneShotPreviewCallback(previewCallback);
+      if (useOneShotPreviewCallback) {
+        camera.setOneShotPreviewCallback(previewCallback);
+      } else {
+        camera.setPreviewCallback(previewCallback);
+      }
     }
   }
 
@@ -257,24 +279,31 @@ final class CameraManager {
    * @param data A preview frame.
    * @param width The width of the image.
    * @param height The height of the image.
-   * @return A BaseLuminanceSource subclass.
+   * @return A PlanarYUVLuminanceSource instance.
    */
-  public BaseLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
+  public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
     Rect rect = getFramingRect();
     switch (previewFormat) {
+      // This is the standard Android format which all devices are REQUIRED to support.
+      // In theory, it's the only one we should ever care about.
       case PixelFormat.YCbCr_420_SP:
+        return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
+            rect.width(), rect.height());
+      // This format has never been seen in the wild, but is compatible as we only care
+      // about the Y channel, so allow it.
       case PixelFormat.YCbCr_422_SP:
         return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
             rect.width(), rect.height());
       default:
-        // There's no PixelFormat constant for this buffer format yet.
-        if (previewFormatString.equals("yuv422i-yuyv")) {
-          return new InterleavedYUV422LuminanceSource(data, width, height, rect.left, rect.top,
-              rect.width(), rect.height());
+        // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
+        // Fortunately, it too has all the Y data up front, so we can read it.
+        if (previewFormatString.equals("yuv420p")) {
+          return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
+            rect.width(), rect.height());
         }
-        break;
     }
-    return null;
+    throw new IllegalArgumentException("Unsupported picture format: " +
+        previewFormat + '/' + previewFormatString);
   }
 
   /**
@@ -289,7 +318,7 @@ final class CameraManager {
     Log.v(TAG, "Default preview size: " + size.width + ", " + size.height);
     previewFormat = parameters.getPreviewFormat();
     previewFormatString = parameters.get("preview-format");
-    Log.v(TAG, "Default preview format: " + previewFormat);
+    Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
 
     // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
     // TODO: A better solution would be to request the supported preview resolutions