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;
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
*/
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);
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;
+ }
}
/**
*/
public void stopPreview() {
if (camera != null && previewing) {
+ if (!useOneShotPreviewCallback) {
+ camera.setPreviewCallback(null);
+ }
camera.stopPreview();
previewHandler = null;
autoFocusHandler = null;
if (camera != null && previewing) {
previewHandler = handler;
previewMessage = message;
- camera.setOneShotPreviewCallback(previewCallback);
+ if (useOneShotPreviewCallback) {
+ camera.setOneShotPreviewCallback(previewCallback);
+ } else {
+ camera.setPreviewCallback(previewCallback);
+ }
}
}
* @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);
}
/**
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