summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorI-Jong Lin <ijonglin@google.com>2015-01-15 11:24:27 -0800
committerI-Jong Lin <ijonglin@google.com>2015-01-22 15:38:59 -0800
commit4dc301a073dab22b9bc12e0b846530d3a80bf8f7 (patch)
tree79ece66e3f5c48a1cf29685afa8e0bd6958a16a7
parentf0eda77c4651a436420d8b9a958f8f83e1e89800 (diff)
downloadandroid_packages_apps_Camera2-4dc301a073dab22b9bc12e0b846530d3a80bf8f7.tar.gz
android_packages_apps_Camera2-4dc301a073dab22b9bc12e0b846530d3a80bf8f7.tar.bz2
android_packages_apps_Camera2-4dc301a073dab22b9bc12e0b846530d3a80bf8f7.zip
Image Backend Integration w/ Simple Camera
Wiring for cameras that only deliver compressed JPEG artifacts. This CL was tested by enabling it on the Nexus 5. Currently, the code in this CL is NOT exposed in any known Nexus Camera model. The two unexposed bugs are as follows: an camera initialization bug on Nexus 4, and a cross-device functional issue with the SimpleCameraOneFactory object in that it doesn't request a JPEG compression artifact from the HAL with zero orientation. Bug: 18908116 Change-Id: If7080c4e8e52a329fb9a6fed52f7c31541758afb
-rw-r--r--src/com/android/camera/CaptureModule.java190
-rw-r--r--src/com/android/camera/module/ModulesInfo.java1
-rw-r--r--src/com/android/camera/one/OneCameraManager.java26
-rw-r--r--src/com/android/camera/one/v1/OneCameraManagerImpl.java6
-rw-r--r--src/com/android/camera/one/v2/OneCameraManagerImpl.java24
-rw-r--r--src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java190
-rw-r--r--src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java7
-rw-r--r--src/com/android/camera/processing/imagebackend/ImageBackend.java37
-rw-r--r--src/com/android/camera/processing/imagebackend/ImageConsumer.java43
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java35
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java139
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java18
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java31
-rw-r--r--src/com/android/camera/util/ApiHelper.java2
-rw-r--r--src_pd/com/android/camera/one/v2/OneCameraCreator.java10
15 files changed, 522 insertions, 237 deletions
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index c65d029ab..44428b1c6 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -59,12 +59,8 @@ import com.android.camera.one.OneCameraAccessException;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraManager;
import com.android.camera.one.v2.OneCameraManagerImpl;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
-import com.android.camera.one.v2.imagesaver.YuvImageBackendImageSaver;
import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.one.v2.photo.ImageRotationCalculatorImpl;
-import com.android.camera.processing.ProcessingServiceManager;
-import com.android.camera.processing.imagebackend.ImageBackend;
import com.android.camera.remote.RemoteCameraModule;
import com.android.camera.session.CaptureSession;
import com.android.camera.settings.Keys;
@@ -252,7 +248,8 @@ public class CaptureModule extends CameraModule implements
mMainHandler.post(new Runnable() {
@Override
public void run() {
- mAppController.getServices().getRemoteShutterListener().onPictureTaken(jpegImage);
+ mAppController.getServices().getRemoteShutterListener()
+ .onPictureTaken(jpegImage);
}
});
}
@@ -1182,15 +1179,10 @@ public class CaptureModule extends CameraModule implements
return;
}
- // Create the image saver.
- // Used to rotate images the right way based on the sensor used
- // for taking the image.
+ // Derive objects necessary for camera creation.
MainThread mainThread = MainThread.create();
ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl
.from(cameraCharacteristics);
- ImageBackend imageBackend = ProcessingServiceManager.getImageBackendInstance();
- ImageSaver.Builder imageSaverBuilder = new YuvImageBackendImageSaver(
- mainThread, imageRotationCalculator, imageBackend);
// Only enable HDR on the back camera
boolean useHdr = mHdrEnabled && mCameraFacing == Facing.BACK;
@@ -1203,96 +1195,96 @@ public class CaptureModule extends CameraModule implements
return;
}
- mCameraManager.open(mCameraFacing, useHdr, mPictureSize, imageSaverBuilder,
- new OpenCallback() {
- @Override
- public void onFailure() {
- Log.e(TAG, "Could not open camera.");
- mCamera = null;
- mCameraOpenCloseLock.release();
- mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
- }
-
- @Override
- public void onCameraClosed() {
- mCamera = null;
- mBurstController.onCameraDetached();
- mCameraOpenCloseLock.release();
- }
-
- @Override
- public void onCameraOpened(final OneCamera camera) {
- Log.d(TAG, "onCameraOpened: " + camera);
- mCamera = camera;
- mBurstController.onCameraAttached(mCamera);
- updatePreviewBufferDimension();
-
- // If the surface texture is not destroyed, it may have
- // the last frame lingering. We need to hold off setting
- // transform until preview is started.
- updateFrameDistributorBufferSize();
- mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
- Log.d(TAG, "starting preview ...");
-
- // TODO: make mFocusController final and remove null
- // check.
- if (mFocusController != null) {
- camera.setFocusDistanceListener(mFocusController);
- }
-
- // TODO: Consider rolling these two calls into one.
- camera.startPreview(new Surface(getPreviewSurfaceTexture()),
- new CaptureReadyCallback() {
- @Override
- public void onSetupFailed() {
- // We must release this lock here,
- // before posting to the main handler
- // since we may be blocked in pause(),
- // getting ready to close the camera.
- mCameraOpenCloseLock.release();
- Log.e(TAG, "Could not set up preview.");
- mMainHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mCamera == null) {
- Log.d(TAG, "Camera closed, aborting.");
- return;
+ mCameraManager.open(mCameraFacing, useHdr, mPictureSize,
+ new OpenCallback() {
+ @Override
+ public void onFailure() {
+ Log.e(TAG, "Could not open camera.");
+ mCamera = null;
+ mCameraOpenCloseLock.release();
+ mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
+ }
+
+ @Override
+ public void onCameraClosed() {
+ mCamera = null;
+ mBurstController.onCameraDetached();
+ mCameraOpenCloseLock.release();
+ }
+
+ @Override
+ public void onCameraOpened(final OneCamera camera) {
+ Log.d(TAG, "onCameraOpened: " + camera);
+ mCamera = camera;
+ mBurstController.onCameraAttached(mCamera);
+ updatePreviewBufferDimension();
+
+ // If the surface texture is not destroyed, it may have
+ // the last frame lingering. We need to hold off setting
+ // transform until preview is started.
+ updateFrameDistributorBufferSize();
+ mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
+ Log.d(TAG, "starting preview ...");
+
+ // TODO: make mFocusController final and remove null
+ // check.
+ if (mFocusController != null) {
+ camera.setFocusDistanceListener(mFocusController);
+ }
+
+ // TODO: Consider rolling these two calls into one.
+ camera.startPreview(new Surface(getPreviewSurfaceTexture()),
+ new CaptureReadyCallback() {
+ @Override
+ public void onSetupFailed() {
+ // We must release this lock here,
+ // before posting to the main handler
+ // since we may be blocked in pause(),
+ // getting ready to close the camera.
+ mCameraOpenCloseLock.release();
+ Log.e(TAG, "Could not set up preview.");
+ mMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mCamera == null) {
+ Log.d(TAG, "Camera closed, aborting.");
+ return;
+ }
+ mCamera.close();
+ mCamera = null;
+ // TODO: Show an error message
+ // and exit.
}
- mCamera.close();
- mCamera = null;
- // TODO: Show an error message
- // and exit.
- }
- });
- }
-
- @Override
- public void onReadyForCapture() {
- // We must release this lock here,
- // before posting to the main handler
- // since we may be blocked in pause(),
- // getting ready to close the camera.
- mCameraOpenCloseLock.release();
- mMainHandler.post(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, "Ready for capture.");
- if (mCamera == null) {
- Log.d(TAG, "Camera closed, aborting.");
- return;
+ });
+ }
+
+ @Override
+ public void onReadyForCapture() {
+ // We must release this lock here,
+ // before posting to the main handler
+ // since we may be blocked in pause(),
+ // getting ready to close the camera.
+ mCameraOpenCloseLock.release();
+ mMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "Ready for capture.");
+ if (mCamera == null) {
+ Log.d(TAG, "Camera closed, aborting.");
+ return;
+ }
+ onPreviewStarted();
+ // Enable zooming after preview
+ // has started.
+ mUI.initializeZoom(mCamera.getMaxZoom());
+ mCamera.setFocusStateListener(CaptureModule.this);
+ mCamera.setReadyStateChangedListener(CaptureModule.this);
}
- onPreviewStarted();
- // Enable zooming after preview
- // has started.
- mUI.initializeZoom(mCamera.getMaxZoom());
- mCamera.setFocusStateListener(CaptureModule.this);
- mCamera.setReadyStateChangedListener(CaptureModule.this);
- }
- });
- }
- });
- }
- }, mCameraHandler);
+ });
+ }
+ });
+ }
+ }, mCameraHandler, mainThread, imageRotationCalculator);
}
private void closeCamera() {
diff --git a/src/com/android/camera/module/ModulesInfo.java b/src/com/android/camera/module/ModulesInfo.java
index a8ecb68c9..327df75c8 100644
--- a/src/com/android/camera/module/ModulesInfo.java
+++ b/src/com/android/camera/module/ModulesInfo.java
@@ -81,6 +81,7 @@ public class ModulesInfo {
@Override
public ModuleController createModule(AppController app) {
+ Log.v(TAG, "EnableCaptureModule = " + ENABLE_CAPTURE_MODULE);
return ENABLE_CAPTURE_MODULE ? new CaptureModule(app) : new PhotoModule(app);
}
});
diff --git a/src/com/android/camera/one/OneCameraManager.java b/src/com/android/camera/one/OneCameraManager.java
index 9a4fe0c65..d23e5a37f 100644
--- a/src/com/android/camera/one/OneCameraManager.java
+++ b/src/com/android/camera/one/OneCameraManager.java
@@ -16,18 +16,19 @@
package com.android.camera.one;
-import com.google.common.base.Optional;
-
import android.os.Handler;
import android.util.DisplayMetrics;
import com.android.camera.CameraActivity;
+import com.android.camera.async.MainThread;
import com.android.camera.debug.Log.Tag;
import com.android.camera.one.OneCamera.Facing;
import com.android.camera.one.OneCamera.OpenCallback;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.Size;
+import com.google.common.base.Optional;
+
/**
* The camera manager is responsible for instantiating {@link OneCamera}
* instances.
@@ -38,7 +39,7 @@ public abstract class OneCameraManager {
/**
* Attempts to open the camera facing the given direction with the given
* capture size.
- *
+ * <p>
* Exactly one call will always be made to a single method in the provided
* {@link OpenCallback}.
*
@@ -47,13 +48,16 @@ public abstract class OneCameraManager {
* @param enableHdr if an HDR feature exists, open a camera that supports it
* @param captureSize the capture size. This must be one of the supported
* sizes.
- * @param imageSaverBuilder the builder that will be used to create {@link ImageSaver}.
* @param callback this listener is called when the camera was opened or
* when it failed to open.
* @param handler the handler on which callback methods are invoked.
+ * @param mainThread Main thread executor
+ * @param imageRotationCalculator Image rotation calculator required for
+ * Camera Factory initialization
*/
public abstract void open(Facing facing, boolean enableHdr, Size captureSize,
- ImageSaver.Builder imageSaverBuilder, OpenCallback callback, Handler handler);
+ OpenCallback callback, Handler handler,
+ MainThread mainThread, final ImageRotationCalculator imageRotationCalculator);
// TODO: Move this to OneCameraCharacteristics class.
/**
@@ -62,13 +66,13 @@ public abstract class OneCameraManager {
public abstract boolean hasCameraFacing(Facing facing);
/**
- * Retrieve the characteristics for the camera facing at the given direction. The first camera
- * found in the given direction will be chosen.
+ * Retrieve the characteristics for the camera facing at the given
+ * direction. The first camera found in the given direction will be chosen.
*
* @param facing The facing direction of the camera.
- * @return A #{link com.android.camera.one.OneCameraCharacteristics} object to provide camera
- * characteristics information. Returns null if there is no camera facing the given
- * direction.
+ * @return A #{link com.android.camera.one.OneCameraCharacteristics} object
+ * to provide camera characteristics information. Returns null if
+ * there is no camera facing the given direction.
*/
public abstract OneCameraCharacteristics getCameraCharacteristics(Facing facing)
throws OneCameraAccessException;
diff --git a/src/com/android/camera/one/v1/OneCameraManagerImpl.java b/src/com/android/camera/one/v1/OneCameraManagerImpl.java
index 87b541905..4fa6be7e4 100644
--- a/src/com/android/camera/one/v1/OneCameraManagerImpl.java
+++ b/src/com/android/camera/one/v1/OneCameraManagerImpl.java
@@ -22,6 +22,7 @@ import android.hardware.Camera;
import android.os.Handler;
import com.android.camera.CameraActivity;
+import com.android.camera.async.MainThread;
import com.android.camera.debug.Log;
import com.android.camera.one.OneCamera.Facing;
import com.android.camera.one.OneCamera.OpenCallback;
@@ -29,6 +30,7 @@ import com.android.camera.one.OneCameraAccessException;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraManager;
import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.Size;
/**
@@ -93,7 +95,9 @@ public class OneCameraManagerImpl extends OneCameraManager {
@Override
public void open(Facing facing, boolean enableHdr, Size pictureSize,
- ImageSaver.Builder imageSaverBuilder, OpenCallback callback, Handler handler) {
+ OpenCallback callback, Handler handler,
+ MainThread mainThread,
+ ImageRotationCalculator imageRotationCalculator) {
throw new RuntimeException("Not implemented yet.");
}
diff --git a/src/com/android/camera/one/v2/OneCameraManagerImpl.java b/src/com/android/camera/one/v2/OneCameraManagerImpl.java
index 340a6dfb3..6b3754b2b 100644
--- a/src/com/android/camera/one/v2/OneCameraManagerImpl.java
+++ b/src/com/android/camera/one/v2/OneCameraManagerImpl.java
@@ -28,6 +28,7 @@ import android.util.DisplayMetrics;
import com.android.camera.CameraActivity;
import com.android.camera.SoundPlayer;
import com.android.camera.app.AppController;
+import com.android.camera.async.MainThread;
import com.android.camera.debug.Log;
import com.android.camera.debug.Log.Tag;
import com.android.camera.one.OneCamera;
@@ -36,10 +37,11 @@ import com.android.camera.one.OneCamera.OpenCallback;
import com.android.camera.one.OneCameraAccessException;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraManager;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.AndroidServices;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.Size;
+
import com.google.common.base.Optional;
/**
@@ -55,7 +57,8 @@ public class OneCameraManagerImpl extends OneCameraManager {
private final DisplayMetrics mDisplayMetrics;
private final SoundPlayer mSoundPlayer;
- public static Optional<OneCameraManager> create(CameraActivity activity, DisplayMetrics displayMetrics) {
+ public static Optional<OneCameraManager> create(CameraActivity activity,
+ DisplayMetrics displayMetrics) {
if (!ApiHelper.HAS_CAMERA_2_API) {
return Optional.absent();
}
@@ -92,14 +95,16 @@ public class OneCameraManagerImpl extends OneCameraManager {
@Override
public void open(Facing facing, final boolean useHdr, final Size pictureSize,
- final ImageSaver.Builder imageSaverBuilder, final OpenCallback openCallback, Handler handler) {
+ final OpenCallback openCallback,
+ Handler handler, final MainThread mainThread,
+ final ImageRotationCalculator imageRotationCalculator) {
try {
final String cameraId = getCameraId(facing);
Log.i(TAG, "Opening Camera ID " + cameraId);
mCameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
// We may get multiple calls to StateCallback, but only the
// first callback indicates the status of the camera-opening
- // operation. For example, we may receive onOpened() and later
+ // operation. For example, we may receive onOpened() and later
// onClosed(), but only the first should be relayed to
// openCallback.
private boolean isFirstCallback = true;
@@ -139,10 +144,12 @@ public class OneCameraManagerImpl extends OneCameraManager {
try {
CameraCharacteristics characteristics = mCameraManager
.getCameraCharacteristics(device.getId());
- // TODO: Set boolean based on whether HDR+ is enabled.
+ // TODO: Set boolean based on whether HDR+ is
+ // enabled.
OneCamera oneCamera = OneCameraCreator.create(mAppController, useHdr,
- device, characteristics, pictureSize, imageSaverBuilder,
- mMaxMemoryMB, mDisplayMetrics, mSoundPlayer);
+ device, characteristics, pictureSize,
+ mMaxMemoryMB, mDisplayMetrics, mSoundPlayer,
+ mainThread, imageRotationCalculator);
openCallback.onCameraOpened(oneCamera);
} catch (CameraAccessException e) {
Log.d(TAG, "Could not get camera characteristics");
@@ -181,7 +188,8 @@ public class OneCameraManagerImpl extends OneCameraManager {
throws OneCameraAccessException {
String cameraId = getCameraId(facing);
try {
- CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
+ CameraCharacteristics characteristics = mCameraManager
+ .getCameraCharacteristics(cameraId);
return new OneCameraCharacteristicsImpl(characteristics);
} catch (CameraAccessException ex) {
throw new OneCameraAccessException("Unable to get camera characteristics", ex);
diff --git a/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java b/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java
new file mode 100644
index 000000000..ad2b8ebef
--- /dev/null
+++ b/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * 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.
+ */
+
+package com.android.camera.one.v2.imagesaver;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+
+import com.android.camera.app.OrientationManager;
+import com.android.camera.async.MainThread;
+import com.android.camera.debug.Log;
+import com.android.camera.one.OneCamera;
+import com.android.camera.one.v2.camera2proxy.ImageProxy;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
+import com.android.camera.processing.imagebackend.ImageBackend;
+import com.android.camera.processing.imagebackend.ImageConsumer;
+import com.android.camera.processing.imagebackend.ImageProcessorListener;
+import com.android.camera.processing.imagebackend.ImageProcessorProxyListener;
+import com.android.camera.processing.imagebackend.ImageToProcess;
+import com.android.camera.processing.imagebackend.TaskImageContainer;
+import com.android.camera.session.CaptureSession;
+
+import com.google.common.base.Optional;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.ParametersAreNonnullByDefault;
+
+/**
+ * Wires up the ImageBackend task submission process to save JPEG images. Camera
+ * delivers a JPEG-compressed full-size image. This class does very little work
+ * and just routes this image artifact as the thumbnail and to remote devices.
+ */
+public class JpegImageBackendImageSaver implements ImageSaver.Builder {
+ private static Log.Tag TAG = new Log.Tag("JpegImgBESaver");
+
+ @ParametersAreNonnullByDefault
+ private static class ImageSaverImpl implements SingleImageSaver {
+ private final MainThread mExecutor;
+ private final CaptureSession mSession;
+ private final OrientationManager.DeviceOrientation mImageRotation;
+ private final ImageBackend mImageBackend;
+ private final ImageProcessorListener mPreviewListener;
+
+ public ImageSaverImpl(MainThread executor,
+ CaptureSession session, OrientationManager.DeviceOrientation imageRotation,
+ ImageBackend imageBackend, ImageProcessorListener previewListener) {
+ mExecutor = executor;
+ mSession = session;
+ mImageRotation = imageRotation;
+ mImageBackend = imageBackend;
+ mPreviewListener = previewListener;
+ }
+
+ @Override
+ public void saveAndCloseImage(ImageProxy image, Optional<ImageProxy> thumbnail) {
+ // TODO: Use thumbnail to speed up RGB thumbnail creation whenever
+ // possible. For now, just close it.
+ if (thumbnail.isPresent()) {
+ thumbnail.get().close();
+ }
+ final ImageProcessorProxyListener listenerProxy = mImageBackend.getProxyListener();
+
+ listenerProxy.registerListener(mPreviewListener, image);
+
+ Set<ImageConsumer.ImageTaskFlags> taskFlagsSet = new HashSet<>();
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE);
+
+ try {
+ mImageBackend.receiveImage(new ImageToProcess(image, mImageRotation),
+ mExecutor, taskFlagsSet, mSession);
+ } catch (InterruptedException e) {
+ // Impossible exception because receiveImage is nonblocking
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private static class PreviewListener implements ImageProcessorListener {
+ private final MainThread mExecutor;
+ private final ImageProcessorProxyListener mListenerProxy;
+ private final CaptureSession mSession;
+ private final OrientationManager.DeviceOrientation mImageRotation;
+ private final OneCamera.PictureSaverCallback mPictureSaverCallback;
+
+ private PreviewListener(MainThread executor,
+ ImageProcessorProxyListener listenerProxy, CaptureSession session,
+ OrientationManager.DeviceOrientation imageRotation,
+ OneCamera.PictureSaverCallback pictureSaverCallback) {
+ mExecutor = executor;
+ mListenerProxy = listenerProxy;
+ mSession = session;
+ mImageRotation = imageRotation;
+ mPictureSaverCallback = pictureSaverCallback;
+ }
+
+ @Override
+ public void onStart(TaskImageContainer.TaskInfo task) {
+ if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) {
+ mSession.startEmpty();
+ }
+ }
+
+ @Override
+ public void onResultCompressed(TaskImageContainer.TaskInfo task,
+ TaskImageContainer.CompressedPayload payload) {
+ if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) {
+ // Just start the thumbnail now, since there's no earlier event.
+ mPictureSaverCallback.onThumbnailProcessingBegun();
+ final Bitmap bitmap = BitmapFactory.decodeByteArray(payload.data, 0,
+ payload.data.length);
+ // Send image to disk saver.
+ mPictureSaverCallback.onThumbnailAvailable(bitmap, mImageRotation.getDegrees());
+ // Send image to remote devices
+ mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data);
+ }
+
+ }
+
+ @Override
+ public void onResultUncompressed(TaskImageContainer.TaskInfo task,
+ TaskImageContainer.UncompressedPayload payload) {
+ // Do Nothing
+ }
+
+ @Override
+ public void onResultUri(TaskImageContainer.TaskInfo task, Uri uri) {
+ // Remove yourself from the listener after JPEG save.
+ // TODO: This should really be done by the ImageBackend to guarantee
+ // ordering, since technically this could happen out of order.
+ mListenerProxy.unregisterListener(this);
+ }
+ }
+
+ private final MainThread mExecutor;
+ private final ImageRotationCalculator mImageRotationCalculator;
+ private final ImageBackend mImageBackend;
+
+ /**
+ * Constructor
+ *
+ * @param executor Executor to run listener events on the ImageBackend
+ * @param imageRotationCalculator the image rotation calculator to determine
+ */
+ public JpegImageBackendImageSaver(MainThread executor,
+ ImageRotationCalculator imageRotationCalculator,
+ ImageBackend imageBackend) {
+ mExecutor = executor;
+ mImageRotationCalculator = imageRotationCalculator;
+ mImageBackend = imageBackend;
+ }
+
+ /**
+ * Builder for the Zsl/ImageBackend Interface
+ *
+ * @return Instantiated interface object
+ */
+ @Override
+ public ImageSaver build(
+ @Nonnull OneCamera.PictureSaverCallback pictureSaverCallback,
+ @Nonnull OrientationManager.DeviceOrientation orientation,
+ @Nonnull CaptureSession session) {
+ final OrientationManager.DeviceOrientation imageRotation = mImageRotationCalculator
+ .toImageRotation(orientation);
+
+ ImageProcessorProxyListener proxyListener = mImageBackend.getProxyListener();
+
+ PreviewListener previewListener = new PreviewListener(mExecutor,
+ proxyListener, session, imageRotation, pictureSaverCallback);
+ return new MostRecentImageSaver(new ImageSaverImpl(mExecutor, session,
+ imageRotation, mImageBackend, previewListener));
+ }
+}
diff --git a/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java b/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
index 1f1dd95b3..e7a10fb3b 100644
--- a/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
+++ b/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
@@ -74,9 +74,10 @@ public class YuvImageBackendImageSaver implements ImageSaver.Builder {
listenerProxy.registerListener(mPreviewListener, image);
Set<ImageConsumer.ImageTaskFlags> taskFlagsSet = new HashSet<>();
- taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CONVERT_IMAGE_TO_RGB_PREVIEW);
- taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_IMAGE_TO_JPEG);
- taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_IMAGE_ON_RELEASE);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CREATE_EARLY_FILMSTRIP_PREVIEW);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CONVERT_TO_RGB_PREVIEW);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE);
try {
mImageBackend.receiveImage(new ImageToProcess(image, mImageRotation),
diff --git a/src/com/android/camera/processing/imagebackend/ImageBackend.java b/src/com/android/camera/processing/imagebackend/ImageBackend.java
index 3630e90e9..7ba442cec 100644
--- a/src/com/android/camera/processing/imagebackend/ImageBackend.java
+++ b/src/com/android/camera/processing/imagebackend/ImageBackend.java
@@ -16,13 +16,10 @@
package com.android.camera.processing.imagebackend;
-import com.android.camera.app.CameraAppUI;
import com.android.camera.debug.Log;
-import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.session.CaptureSession;
import com.android.camera.util.Size;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -92,7 +89,7 @@ public class ImageBackend implements ImageConsumer, ImageTaskManager {
* Approximate viewable size (in pixels) for the fast thumbnail in the
* current UX definition of the product. Note that these values will be the
* minimum size of FAST_THUMBNAIL target for the
- * CONVERT_IMAGE_TO_RGB_PREVIEW task.
+ * CONVERT_TO_RGB_PREVIEW task.
*/
private final static Size FAST_THUMBNAIL_TARGET_SIZE = new Size(160, 100);
@@ -100,7 +97,7 @@ public class ImageBackend implements ImageConsumer, ImageTaskManager {
* A standard viewable size (in pixels) for the filmstrip thumbnail in the
* current UX definition of the product. Note that this size is the minimum
* size for the Preview on the filmstrip associated with
- * COMPRESS_IMAGE_TO_JPEG task.
+ * COMPRESS_TO_JPEG_AND_WRITE_TO_DISK task.
*/
private final static Size FILMSTRIP_THUMBNAIL_TARGET_SIZE = new Size(512, 384);
@@ -369,31 +366,29 @@ public class ImageBackend implements ImageConsumer, ImageTaskManager {
// Now add the pre-mixed versions of the tasks.
- if (processingFlags.contains(ImageTaskFlags.COMPRESS_IMAGE_TO_JPEG)
- || processingFlags.contains(ImageTaskFlags.WRITE_IMAGE_TO_DISK)) {
- // Add this type of task to the appropriate queue.
- // tasksToExecute.add(new TaskCompressImageToJpeg(img, executor, this, session));
- tasksToExecute.add(new TaskPreviewChainedJpeg(img, executor, this, session,
- FILMSTRIP_THUMBNAIL_TARGET_SIZE));
+ if (processingFlags.contains(ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK)) {
+ if (processingFlags.contains(ImageTaskFlags.CREATE_EARLY_FILMSTRIP_PREVIEW)) {
+ // Request job that creates both filmstrip thumbnail from YUV,
+ // JPEG compression of the YUV Image, and writes the result to disk
+ tasksToExecute.add(new TaskPreviewChainedJpeg(img, executor, this, session,
+ FILMSTRIP_THUMBNAIL_TARGET_SIZE));
+ } else {
+ // Request job that only does JPEG compression and writes the result to disk
+ tasksToExecute.add(new TaskCompressImageToJpeg(img, executor, this, session));
+ }
}
- if (processingFlags.contains(ImageTaskFlags.CONVERT_IMAGE_TO_RGB_PREVIEW)) {
- // Add this type of task to the appropriate queue.
+ if (processingFlags.contains(ImageTaskFlags.CONVERT_TO_RGB_PREVIEW)) {
+ // Add an additional type of task to the appropriate queue.
tasksToExecute.add(new TaskConvertImageToRGBPreview(img, executor,
this, TaskImageContainer.ProcessingPriority.FAST, session,
FAST_THUMBNAIL_TARGET_SIZE,
TaskConvertImageToRGBPreview.ThumbnailShape.SQUARE_ASPECT_CIRCULAR_INSET));
}
- if (processingFlags.contains(ImageTaskFlags.WRITE_IMAGE_TO_DISK)) {
- // Add this type of task to the appropriate queue.
- // Has a dependency as well on the result JPEG_COMPRESSION
- // TODO: Put disk writing implementation within the framework.
- }
-
receiveImage(img, tasksToExecute,
- processingFlags.contains(ImageTaskFlags.BLOCK_UNTIL_IMAGE_RELEASE),
- processingFlags.contains(ImageTaskFlags.CLOSE_IMAGE_ON_RELEASE));
+ processingFlags.contains(ImageTaskFlags.BLOCK_UNTIL_ALL_TASKS_RELEASE),
+ processingFlags.contains(ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE));
return true;
}
diff --git a/src/com/android/camera/processing/imagebackend/ImageConsumer.java b/src/com/android/camera/processing/imagebackend/ImageConsumer.java
index 16e523536..43a5369e0 100644
--- a/src/com/android/camera/processing/imagebackend/ImageConsumer.java
+++ b/src/com/android/camera/processing/imagebackend/ImageConsumer.java
@@ -26,20 +26,45 @@ import java.util.concurrent.Executor;
* object that merely delivers images to the backend. After the tasks and
* Android image is submitted to the ImageConsumer, the responsibility to close
* on the Android image object as early as possible is transferred to the
- * implementation. Whether an image can be submitted again for process is up to
- * the implementation of the consumer. For integration of Camera Application,
- * we now pass in the CaptureSession in order to properly update filmstrip and UI.
+ * implementation. Whether an image can be submitted again for process is up to
+ * the implementation of the consumer. For integration of Camera Application, we
+ * now pass in the CaptureSession in order to properly update filmstrip and UI.
*/
public interface ImageConsumer {
/**
+ * ImageTaskFlags specifies the current tasks that will be run with an
+ * image.
+ * <ol>
+ * <li>CREATE_EARLY_FILMSTRIP_PREVIEW: Subsamples a YUV Image and converts
+ * it to an ARGB Image with nearly similar aspect ratio. ONLY Valid when
+ * specified with COMPRESS_TO_JPEG_AND_WRITE_TO_DISK. Otherwise, ignored.</li>
+ * <li>COMPRESS_TO_JPEG_AND_WRITE_TO_DISK: Compresses an YUV/JPEG Image to
+ * JPEG (when necessary), delivers the compressed artifact via the listener,
+ * and writes it to disk.</li>
+ * <li>CONVERT_TO_RGB_PREVIEW: Subsamples a YUV Image and converts the
+ * uncompressed output to ARGB image inset within a circle</li>
+ * <li>BLOCK_UNTIL_ALL_TASKS_RELEASE: Block on ReceiveImage call until image
+ * is released.</li>
+ * <li>CLOSE_ON_ALL_TASKS_RELEASE: Close the ImageProxy on ReceiveImage Call
+ * when all tasks release their image</li>
+ * </ol>
+ */
+ public enum ImageTaskFlags {
+ CREATE_EARLY_FILMSTRIP_PREVIEW,
+ COMPRESS_TO_JPEG_AND_WRITE_TO_DISK,
+ CONVERT_TO_RGB_PREVIEW,
+ BLOCK_UNTIL_ALL_TASKS_RELEASE,
+ CLOSE_ON_ALL_TASKS_RELEASE
+ }
+
+ /**
* Provides the basic functionality of camera processing via an easy-to-use
* method call.
*
* @param img The Image to be Processed
* @param executor The executor on which to execute events and image close
- * @param processingFlags Bit vector comprised of logically ORed TASK_FLAG*
- * constants
+ * @param processingFlags {@see ImageTaskFlags}
*/
public boolean receiveImage(ImageToProcess img, Executor executor,
Set<ImageTaskFlags> processingFlags, CaptureSession captureSession)
@@ -102,12 +127,4 @@ public interface ImageConsumer {
*/
public ImageProcessorProxyListener getProxyListener();
- // Current jobs that should be able to be tagged to an image.
- public enum ImageTaskFlags {
- COMPRESS_IMAGE_TO_JPEG,
- CONVERT_IMAGE_TO_RGB_PREVIEW,
- WRITE_IMAGE_TO_DISK,
- BLOCK_UNTIL_IMAGE_RELEASE,
- CLOSE_IMAGE_ON_RELEASE
- }
}
diff --git a/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java b/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java
index 6e4cf4fd0..61b2c878d 100644
--- a/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java
+++ b/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java
@@ -52,23 +52,26 @@ class TaskChainedCompressImageToJpeg extends TaskJpegEncode {
img.proxy.getHeight(), img.proxy.getFormat());
final TaskImage resultImage = new TaskImage(mImage.rotation, img.proxy.getWidth(),
img.proxy.getHeight(), ImageFormat.JPEG);
-
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
-
+ byte[] dataCopy;
int[] strides = new int[3];
- // Do the byte copy
- strides[0] = planeList.get(0).getRowStride()
- / planeList.get(0).getPixelStride();
- strides[1] = planeList.get(1).getRowStride()
- / planeList.get(1).getPixelStride();
- strides[2] = 2 * planeList.get(2).getRowStride()
- / planeList.get(2).getPixelStride();
-
- // TODO: For performance, use a cache subsystem for buffer reuse.
- byte[] dataCopy = convertYUV420ImageToPackedNV21(img.proxy);
-
- // Release the image now that you have a usable copy
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+
+ try {
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
+
+ // Do the byte copy
+ strides[0] = planeList.get(0).getRowStride()
+ / planeList.get(0).getPixelStride();
+ strides[1] = planeList.get(1).getRowStride()
+ / planeList.get(1).getPixelStride();
+ strides[2] = 2 * planeList.get(2).getRowStride()
+ / planeList.get(2).getPixelStride();
+
+ // TODO: For performance, use a cache subsystem for buffer reuse.
+ dataCopy = convertYUV420ImageToPackedNV21(img.proxy);
+ } finally {
+ // Release the image now that you have a usable copy
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ }
final byte[] chainedDataCopy = dataCopy;
final int[] chainedStrides = strides;
diff --git a/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java b/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java
index ea69440cf..a13e596af 100644
--- a/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java
+++ b/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java
@@ -22,6 +22,7 @@ import android.net.Uri;
import com.android.camera.app.MediaSaver;
import com.android.camera.app.OrientationManager.DeviceOrientation;
import com.android.camera.exif.ExifInterface;
+import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.session.CaptureSession;
import com.android.camera.util.JpegUtilNative;
import com.android.camera.util.Size;
@@ -31,12 +32,12 @@ import java.util.concurrent.Executor;
/**
* Implements the conversion of a YUV_420_888 image to compressed JPEG byte
- * array, using the native implementation of the Camera Application.
- * <p>
- * TODO: Instead of setting the orientation in EXIF, actually rotate the image
- * here and set a rotation of 0.
+ * array, using the native implementation of the Camera Application. If the
+ * image is already JPEG, then it passes it through properly with the assumption
+ * that the JPEG is already encoded in the proper orientation.
*/
public class TaskCompressImageToJpeg extends TaskJpegEncode {
+
private static final int DEFAULT_JPEG_COMPRESSION_QUALITY = 90;
/**
@@ -48,56 +49,100 @@ public class TaskCompressImageToJpeg extends TaskJpegEncode {
* @param captureSession Handler for UI/Disk events
*/
TaskCompressImageToJpeg(ImageToProcess image, Executor executor,
- ImageTaskManager imageTaskManager, CaptureSession captureSession) {
+ ImageTaskManager imageTaskManager,
+ CaptureSession captureSession) {
super(image, executor, imageTaskManager, ProcessingPriority.SLOW, captureSession);
}
- private void logWrapper(String message) {
- // Do nothing.
+ /**
+ * Wraps the static call to JpegUtilNative for testability. {@see
+ * JpegUtilNative#compressJpegFromYUV420Image}
+ */
+ public int compressJpegFromYUV420Image(ImageProxy img, ByteBuffer outBuf, int quality,
+ int degrees) {
+ return JpegUtilNative.compressJpegFromYUV420Image(img, outBuf, quality, degrees);
}
@Override
public void run() {
ImageToProcess img = mImage;
+ if (img.rotation != DeviceOrientation.CLOCKWISE_0
+ && img.proxy.getFormat() == ImageFormat.JPEG) {
+ // TODO: Ensure the capture for SimpleOneCameraFactory is always
+ // at zero rotation
+ // To avoid suboptimal rotation implementation, the JPEG should
+ // come with zero orientation. Any post-rotation of JPEG would be
+ // REALLY sub-optimal.
+
+ // Release image references on error
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+
+ throw new IllegalStateException(
+ "Image Rotation for JPEG should be zero, but is actually "
+ + img.rotation.getDegrees());
+ }
final TaskImage inputImage = new TaskImage(
- img.rotation, img.proxy.getWidth(), img.proxy.getHeight(), img.proxy.getFormat());
-
- // Resulting image will be rotated so that viewers won't have to rotate.
- // That's why the resulting image will have 0 rotation.
- Size resultSize = getImageSizeForOrientation(img.proxy.getWidth(), img.proxy.getHeight(),
+ img.rotation, img.proxy.getWidth(), img.proxy.getHeight(),
+ img.proxy.getFormat());
+ Size resultSize = getImageSizeForOrientation(img.proxy.getWidth(),
+ img.proxy.getHeight(),
img.rotation);
final TaskImage resultImage = new TaskImage(
DeviceOrientation.CLOCKWISE_0, resultSize.getWidth(), resultSize.getHeight(),
ImageFormat.JPEG);
-
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
- logWrapper("TIMER_END Full-size YUV buffer available, w=" + img.proxy.getWidth() + " h="
- + img.proxy.getHeight() + " of format " + img.proxy.getFormat()
- + " (35==YUV_420_888)");
-
- ByteBuffer compressedData = ByteBuffer.allocateDirect(3 * resultImage.width
- * resultImage.height);
-
- // If Orientation is UNKNOWN, treat input image orientation as
- // CLOCKWISE_0.
- int numBytes = JpegUtilNative.compressJpegFromYUV420Image(
- img.proxy, compressedData, DEFAULT_JPEG_COMPRESSION_QUALITY,
- (inputImage.orientation == DeviceOrientation.UNKNOWN) ? 0 : inputImage.orientation
- .getDegrees());
-
- if (numBytes < 0) {
- throw new RuntimeException("Error compressing jpeg.");
+ byte[] writeOut;
+ int numBytes;
+
+ try {
+ // Resulting image will be rotated so that viewers won't have to
+ // rotate. That's why the resulting image will have 0 rotation.
+
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
+ logWrapper("TIMER_END Full-size YUV buffer available, w=" + img.proxy.getWidth()
+ + " h="
+ + img.proxy.getHeight() + " of format " + img.proxy.getFormat()
+ + " (35==YUV_420_888)");
+
+ ByteBuffer compressedData;
+ switch (inputImage.format) {
+ case ImageFormat.JPEG:
+ compressedData = img.proxy.getPlanes().get(0).getBuffer();
+ numBytes = compressedData.capacity();
+ break;
+ case ImageFormat.YUV_420_888:
+ compressedData = ByteBuffer.allocateDirect(3 * resultImage.width
+ * resultImage.height);
+
+ // If Orientation is UNKNOWN, treat input image orientation
+ // as CLOCKWISE_0.
+ numBytes = compressJpegFromYUV420Image(
+ img.proxy, compressedData, DEFAULT_JPEG_COMPRESSION_QUALITY,
+ (inputImage.orientation == DeviceOrientation.UNKNOWN) ? 0
+ : inputImage.orientation
+ .getDegrees());
+
+ if (numBytes < 0) {
+ throw new RuntimeException("Error compressing jpeg.");
+ }
+ compressedData.limit(numBytes);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported input image format for TaskCompressImageToJpeg");
+ }
+
+ writeOut = new byte[numBytes];
+ compressedData.get(writeOut);
+ compressedData.rewind();
+ } finally {
+ // Release the image now that you have a usable copy in local memory
+ // Or you failed to process
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
}
- compressedData.limit(numBytes);
- byte[] writeOut = new byte[numBytes];
- compressedData.get(writeOut);
- compressedData.rewind();
-
- // Release the image now that you have a usable copy
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
- onJpegEncodeDone(mId, inputImage, resultImage, writeOut, TaskInfo.Destination.FINAL_IMAGE);
+ onJpegEncodeDone(mId, inputImage, resultImage, writeOut,
+ TaskInfo.Destination.FINAL_IMAGE);
// TODO: the app actually crashes here on a race condition:
// TaskCompressImageToJpeg might complete before
@@ -113,7 +158,23 @@ public class TaskCompressImageToJpeg extends TaskJpegEncode {
});
}
- private static ExifInterface createExif(TaskImage image) {
+ /**
+ * Wraps a possible log message to be overridden for testability purposes.
+ *
+ * @param message
+ */
+ protected void logWrapper(String message) {
+ // Do nothing.
+ }
+
+ /**
+ * Wraps EXIF Interface for JPEG Metadata creation. Can be overridden for
+ * testing
+ *
+ * @param image Metadata for a jpeg image to create EXIF Interface
+ * @return the created Exif Interface
+ */
+ protected ExifInterface createExif(TaskImage image) {
ExifInterface exif = new ExifInterface();
exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_X_DIMENSION, image.width));
exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_Y_DIMENSION, image.height));
diff --git a/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java b/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java
index b355fad70..e4d50a0af 100644
--- a/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java
+++ b/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java
@@ -740,16 +740,20 @@ public class TaskConvertImageToRGBPreview extends TaskImageContainer {
new Size(inputImage.width, inputImage.height),
mTargetSize);
final TaskImage resultImage = calculateResultImage(img, subsample);
+ final int[] convertedImage;
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.FAST_THUMBNAIL);
+ try {
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.FAST_THUMBNAIL);
- logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
- / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
- + subsample);
+ logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
+ / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
+ + subsample);
- final int[] convertedImage = runSelectedConversion(img.proxy, subsample);
- // Signal backend that reference has been released
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ convertedImage = runSelectedConversion(img.proxy, subsample);
+ } finally {
+ // Signal backend that reference has been released
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ }
onPreviewDone(resultImage, inputImage, convertedImage, TaskInfo.Destination.FAST_THUMBNAIL);
}
diff --git a/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java b/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java
index 25a1f27a7..7d41d243c 100644
--- a/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java
+++ b/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java
@@ -27,8 +27,6 @@ import java.util.concurrent.Executor;
* inscribed in a circle.
*/
public class TaskPreviewChainedJpeg extends TaskConvertImageToRGBPreview {
- protected final static Log.Tag TAG = new Log.Tag("TaskPreviewChainedJpeg");
-
/**
* Constructor
*
@@ -46,7 +44,8 @@ public class TaskPreviewChainedJpeg extends TaskConvertImageToRGBPreview {
}
public void logWrapper(String message) {
- Log.v(TAG, message);
+ // final Log.Tag TAG = new Log.Tag("TaskPreviewChainedJpeg");
+ // Log.v(TAG, message);
}
@Override
@@ -58,22 +57,26 @@ public class TaskPreviewChainedJpeg extends TaskConvertImageToRGBPreview {
new Size(inputImage.width, inputImage.height),
mTargetSize);
final TaskImage resultImage = calculateResultImage(img, subsample);
+ final int[] convertedImage;
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.INTERMEDIATE_THUMBNAIL);
+ try {
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.INTERMEDIATE_THUMBNAIL);
- logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
- / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
- + subsample);
+ logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
+ / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
+ + subsample);
- final int[] convertedImage = runSelectedConversion(img.proxy,subsample);
+ convertedImage = runSelectedConversion(img.proxy, subsample);
- // Chain JPEG task
- TaskImageContainer jpegTask = new TaskCompressImageToJpeg(img, mExecutor,
- mImageTaskManager, mSession);
- mImageTaskManager.appendTasks(img, jpegTask);
+ // Chain JPEG task
+ TaskImageContainer jpegTask = new TaskCompressImageToJpeg(img, mExecutor,
+ mImageTaskManager, mSession);
+ mImageTaskManager.appendTasks(img, jpegTask);
+ } finally {
+ // Signal backend that reference has been released
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ }
- // Signal backend that reference has been released
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
onPreviewDone(resultImage, inputImage, convertedImage,
TaskInfo.Destination.INTERMEDIATE_THUMBNAIL);
}
diff --git a/src/com/android/camera/util/ApiHelper.java b/src/com/android/camera/util/ApiHelper.java
index 67a67a8c7..37209a636 100644
--- a/src/com/android/camera/util/ApiHelper.java
+++ b/src/com/android/camera/util/ApiHelper.java
@@ -97,6 +97,6 @@ public class ApiHelper {
public static boolean isLOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
- || "L".equals(Build.VERSION.CODENAME);
+ || "L".equals(Build.VERSION.CODENAME) || "LOLLIPOP".equals(Build.VERSION.CODENAME);
}
}
diff --git a/src_pd/com/android/camera/one/v2/OneCameraCreator.java b/src_pd/com/android/camera/one/v2/OneCameraCreator.java
index bf6b419dd..a1736ec31 100644
--- a/src_pd/com/android/camera/one/v2/OneCameraCreator.java
+++ b/src_pd/com/android/camera/one/v2/OneCameraCreator.java
@@ -20,17 +20,19 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.util.DisplayMetrics;
-import com.android.camera.app.AppController;
import com.android.camera.SoundPlayer;
+import com.android.camera.app.AppController;
+import com.android.camera.async.MainThread;
import com.android.camera.one.OneCamera;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.Size;
public class OneCameraCreator {
public static OneCamera create(AppController context, boolean useHdr, CameraDevice device,
CameraCharacteristics characteristics, Size pictureSize,
- ImageSaver.Builder imageSaverBuilder, int maxMemoryMB,
- DisplayMetrics displayMetrics, SoundPlayer soundPlayer) {
+ int maxMemoryMB,
+ DisplayMetrics displayMetrics, SoundPlayer soundPlayer,
+ MainThread mainThread, ImageRotationCalculator imageRotationCalculator) {
// TODO: Might want to switch current camera to vendor HDR.
return new OneCameraImpl(device, characteristics, pictureSize);
}