diff options
author | Shashi Shekhar <shashishekhar@google.com> | 2015-01-16 10:16:25 -0800 |
---|---|---|
committer | Shashi Shekhar <shashishekhar@google.com> | 2015-02-11 17:24:13 -0800 |
commit | b4b27f2c7738cd5e3f31a281d66a33f4698810b3 (patch) | |
tree | 1f5c3f83cee9632816724d718817ef0947815f48 | |
parent | f27cf91653d6be413e14c362a0887d3bf1f5ca50 (diff) | |
download | android_packages_apps_Camera2-b4b27f2c7738cd5e3f31a281d66a33f4698810b3.tar.gz android_packages_apps_Camera2-b4b27f2c7738cd5e3f31a281d66a33f4698810b3.tar.bz2 android_packages_apps_Camera2-b4b27f2c7738cd5e3f31a281d66a33f4698810b3.zip |
Port burst to use FrameServer.
Port burst to frameserver
- simplifies burst interfaces
- removes burst methods from OneCamera.
- locks orientation when burst is running.
- Deletes most old code for burst integration.
- Removes frame distributor preview forwarding, instead frames are
streamed using a surface texture the Surface for which is passed
as a output target to the create capture session.
- Deletes FrameDistributor, now the low-res frames are streamed to a new
surface specifically setup for burst.
Change-Id: I50f2955bb48610a7e284f9609cd70b8e4e1f1059
38 files changed, 1124 insertions, 2363 deletions
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 3d92a7891..9c555b94b 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -39,10 +39,12 @@ import com.android.camera.app.AppController; import com.android.camera.app.CameraAppUI; import com.android.camera.app.CameraAppUI.BottomBarUISpec; import com.android.camera.app.LocationManager; +import com.android.camera.app.OrientationManager.DeviceOrientation; import com.android.camera.async.MainThread; import com.android.camera.burst.BurstFacade; import com.android.camera.burst.BurstFacadeFactory; import com.android.camera.burst.BurstReadyStateChangeListener; +import com.android.camera.burst.OrientationLockController; import com.android.camera.debug.DebugPropertyHelper; import com.android.camera.debug.Log; import com.android.camera.debug.Log.Tag; @@ -78,8 +80,6 @@ import com.android.camera.util.Size; import com.android.camera2.R; import com.android.ex.camera2.portability.CameraAgent.CameraProxy; -import java.io.File; -import java.io.IOException; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -139,6 +139,7 @@ public class CaptureModule extends CameraModule implements private Facing mCameraFacing; /** Whether HDR is currently enabled. */ private boolean mHdrEnabled = false; + private final Object mSurfaceTextureLock = new Object(); private FocusController mFocusController; private OneCameraCharacteristics mCameraCharacteristics; @@ -172,7 +173,6 @@ public class CaptureModule extends CameraModule implements int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { int width = right - left; int height = bottom - top; - mBurstController.setPreviewConsumerSize(width, height); updatePreviewTransform(width, height, false); } @@ -216,13 +216,18 @@ public class CaptureModule extends CameraModule implements // Force to re-apply transform matrix here as a workaround for // b/11168275 updatePreviewTransform(width, height, true); - initSurfaceTextureConsumer(surface, width, height); + synchronized (mSurfaceTextureLock) { + mPreviewSurfaceTexture = surface; + } + reopenCamera(); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { Log.d(TAG, "onSurfaceTextureDestroyed"); - mBurstController.setSurfaceTexture(null, 0, 0); + synchronized (mSurfaceTextureLock) { + mPreviewSurfaceTexture = null; + } closeCamera(); return true; } @@ -230,7 +235,7 @@ public class CaptureModule extends CameraModule implements @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { Log.d(TAG, "onSurfaceTextureSizeChanged"); - updateFrameDistributorBufferSize(); + updatePreviewBufferSize(); } @Override @@ -323,6 +328,8 @@ public class CaptureModule extends CameraModule implements /** The current preview transformation matrix. */ private Matrix mPreviewTranformationMatrix = new Matrix(); + /** The surface texture for the preview. */ + private SurfaceTexture mPreviewSurfaceTexture; /** The burst manager for controlling the burst. */ private final BurstFacade mBurstController; @@ -343,15 +350,26 @@ public class CaptureModule extends CameraModule implements mStickyGcamCamera = stickyHdr; mLocationManager = mAppController.getLocationManager(); - mBurstController = BurstFacadeFactory.create(mContext, mAppController - .getOrientationManager(), new BurstReadyStateChangeListener() { - @Override - public void onBurstReadyStateChanged(boolean ready) { - // TODO: This needs to take into account the state of the whole - // system, not just burst. - mAppController.setShutterEnabled(ready); - } - }); + mBurstController = BurstFacadeFactory.create(mContext, + new OrientationLockController() { + @Override + public void unlockOrientation() { + mAppController.getOrientationManager().unlockOrientation(); + } + + @Override + public void lockOrientation() { + mAppController.getOrientationManager().lockOrientation(); + } + }, + new BurstReadyStateChangeListener() { + @Override + public void onBurstReadyStateChanged(boolean ready) { + // TODO: This needs to take into account the state of + // the whole system, not just burst. + mAppController.setShutterEnabled(ready); + } + }); mMediaActionSound = new MediaActionSound(); } @@ -383,12 +401,6 @@ public class CaptureModule extends CameraModule implements FocusSound focusSound = new FocusSound(mSoundPlayer, R.raw.material_camera_focus); mFocusController = new FocusController(mUI.getFocusRing(), focusSound, mMainThread); - // Set the preview texture from UI for the SurfaceTextureConsumer. - mBurstController.setSurfaceTexture( - mAppController.getCameraAppUI().getSurfaceTexture(), - mAppController.getCameraAppUI().getSurfaceWidth(), - mAppController.getCameraAppUI().getSurfaceHeight()); - mHeadingSensor = new HeadingSensor(AndroidServices.instance().provideSensorManager()); View cancelButton = activity.findViewById(R.id.shutter_cancel_button); @@ -404,13 +416,22 @@ public class CaptureModule extends CameraModule implements @Override public void onShutterButtonLongPressed() { - File tempSessionDataDirectory; try { - tempSessionDataDirectory = getServices().getCaptureSessionManager() - .getSessionDirectory(BURST_SESSIONS_DIR); CaptureSession session = createAndStartCaptureSession(); - mBurstController.startBurst(session, tempSessionDataDirectory); - } catch (IOException e) { + + OneCameraCharacteristics cameraCharacteristics; + cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraFacing); + DeviceOrientation deviceOrientation = mAppController.getOrientationManager() + .getDeviceOrientation(); + ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl + .from(mAppController.getOrientationManager(), cameraCharacteristics); + + mBurstController.startBurst(session, + deviceOrientation, + mCamera.getDirection(), + imageRotationCalculator.toImageRotation().getDegrees()); + + } catch (OneCameraAccessException e) { Log.e(TAG, "Cannot start burst", e); return; } @@ -530,14 +551,13 @@ public class CaptureModule extends CameraModule implements } private void initSurfaceTextureConsumer() { - mBurstController.initializeSurfaceTextureConsumer( - mAppController.getCameraAppUI().getSurfaceWidth(), - mAppController.getCameraAppUI().getSurfaceHeight()); - reopenCamera(); - } - - private void initSurfaceTextureConsumer(SurfaceTexture surfaceTexture, int width, int height) { - mBurstController.initializeSurfaceTextureConsumer(surfaceTexture, width, height); + synchronized (mSurfaceTextureLock) { + if (mPreviewSurfaceTexture != null) { + mPreviewSurfaceTexture.setDefaultBufferSize( + mAppController.getCameraAppUI().getSurfaceWidth(), + mAppController.getCameraAppUI().getSurfaceHeight()); + } + } reopenCamera(); } @@ -550,11 +570,18 @@ public class CaptureModule extends CameraModule implements } private SurfaceTexture getPreviewSurfaceTexture() { - return mBurstController.getInputSurfaceTexture(); + synchronized (mSurfaceTextureLock) { + return mPreviewSurfaceTexture; + } } - private void updateFrameDistributorBufferSize() { - mBurstController.updatePreviewBufferSize(mPreviewBufferWidth, mPreviewBufferHeight); + private void updatePreviewBufferSize() { + synchronized (mSurfaceTextureLock) { + if (mPreviewSurfaceTexture != null) { + mPreviewSurfaceTexture.setDefaultBufferSize(mPreviewBufferWidth, + mPreviewBufferHeight); + } + } } @Override @@ -568,9 +595,8 @@ public class CaptureModule extends CameraModule implements mAppController.addPreviewAreaSizeChangedListener(mPreviewAreaChangedListener); mAppController.addPreviewAreaSizeChangedListener(mUI); mAppController.getCameraAppUI().onChangeCamera(); - mBurstController.initializeAndStartFrameDistributor(); - updateFrameDistributorBufferSize(); getServices().getRemoteShutterListener().onModuleReady(this); + mBurstController.initialize(new SurfaceTexture(0)); // TODO: Check if we can really take a photo right now (memory, camera // state, ... ). mAppController.getCameraAppUI().enableModeOptions(); @@ -607,11 +633,10 @@ public class CaptureModule extends CameraModule implements mAppController.removePreviewAreaSizeChangedListener(mUI); mAppController.removePreviewAreaSizeChangedListener(mPreviewAreaChangedListener); getServices().getRemoteShutterListener().onModuleExit(); - mBurstController.stopBurst(); + mBurstController.release(); cancelCountDown(); closeCamera(); resetTextureBufferSize(); - mBurstController.closeFrameDistributor(); mSoundPlayer.unloadSound(R.raw.timer_final_second); mSoundPlayer.unloadSound(R.raw.timer_increment); } @@ -626,7 +651,6 @@ public class CaptureModule extends CameraModule implements @Override public void onLayoutOrientationChanged(boolean isLandscape) { Log.d(TAG, "onLayoutOrientationChanged"); - mBurstController.stopBurst(); } @Override @@ -1145,7 +1169,7 @@ public class CaptureModule extends CameraModule implements Size previewBufferSize = mCamera.pickPreviewSize(mPictureSize, mContext); mPreviewBufferWidth = previewBufferSize.getWidth(); mPreviewBufferHeight = previewBufferSize.getHeight(); - updateFrameDistributorBufferSize(); + updatePreviewBufferSize(); } /** @@ -1209,7 +1233,6 @@ public class CaptureModule extends CameraModule implements @Override public void onCameraClosed() { mCamera = null; - mBurstController.onCameraDetached(); mCameraOpenCloseLock.release(); } @@ -1217,13 +1240,12 @@ public class CaptureModule extends CameraModule implements 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(); + updatePreviewBufferSize(); mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED; Log.d(TAG, "starting preview ..."); @@ -1290,7 +1312,7 @@ public class CaptureModule extends CameraModule implements } }); } - }, mCameraHandler, mainThread, imageRotationCalculator); + }, mCameraHandler, mainThread, imageRotationCalculator, mBurstController); } private void closeCamera() { @@ -1347,6 +1369,6 @@ public class CaptureModule extends CameraModule implements // SurfaceTexture must have these buffer sizes reset manually. Otherwise // the SurfaceTexture cannot be transformed by matrix set on the // TextureView. - updateFrameDistributorBufferSize(); + updatePreviewBufferSize(); } } diff --git a/src/com/android/camera/burst/BurstCaptureCommand.java b/src/com/android/camera/burst/BurstCaptureCommand.java new file mode 100644 index 000000000..91da26e00 --- /dev/null +++ b/src/com/android/camera/burst/BurstCaptureCommand.java @@ -0,0 +1,176 @@ +/* + * 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.burst; + +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.TotalCaptureResult; +import android.view.Surface; + +import com.android.camera.async.BufferQueue; +import com.android.camera.async.BufferQueue.BufferQueueClosedException; +import com.android.camera.async.Lifetime; +import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException; +import com.android.camera.one.v2.camera2proxy.ImageProxy; +import com.android.camera.one.v2.commands.CameraCommand; +import com.android.camera.one.v2.core.CaptureStream; +import com.android.camera.one.v2.core.FrameServer; +import com.android.camera.one.v2.core.Request; +import com.android.camera.one.v2.core.RequestBuilder; +import com.android.camera.one.v2.core.ResourceAcquisitionFailedException; +import com.android.camera.one.v2.core.ResponseListener; +import com.android.camera.one.v2.sharedimagereader.ManagedImageReader; +import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageStream; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class BurstCaptureCommand implements CameraCommand { + /** + * Template to use for the burst capture. + */ + private static final int BURST_TEMPLATE_TYPE = CameraDevice.TEMPLATE_VIDEO_SNAPSHOT; + + private final FrameServer mFrameServer; + private final RequestBuilder.Factory mBuilderFactory; + private final ManagedImageReader mManagedImageReader; + private final Surface mBurstInputSurface; + private final EvictionHandler mBurstEvictionHandler; + private final BurstController mBurstController; + private final Runnable mRestorePreviewCommand; + /** + * The max images supported by the {@link ImageStream}. + */ + private final int mMaxImageCount; + private final Lifetime mBurstLifetime; + + /** + * Initializes a new burst capture command. + * + * @param frameServer the {@link FrameServer} instance for creating session + * @param builder factory to use for creating the {@link Request} for burst + * capture + * @param managedImageReader the factory to use for creating a stream of + * images + * @param burstInputSurface the input surface to use for streaming preview + * frames to burst + * @param lifetime the lifetime of the burst, the burst stops capturing + * images once the lifetime is closed + * @param burstEvictionHandler the eviction handler to use for + * {@link RingBuffer} + * @param burstController the burst controller + * @param restorePreviewCommand the command to run to restore the preview, + * once burst capture is complete + * @param maxImageCount the maximum number of images supported by the image + * reader + */ + public BurstCaptureCommand(FrameServer frameServer, RequestBuilder.Factory builder, + ManagedImageReader managedImageReader, Surface burstInputSurface, + Lifetime lifetime, + EvictionHandler burstEvictionHandler, + BurstController burstController, + Runnable restorePreviewCommand, + int maxImageCount) { + mFrameServer = frameServer; + mBuilderFactory = builder; + mManagedImageReader = managedImageReader; + mBurstInputSurface = burstInputSurface; + mBurstLifetime = lifetime; + mBurstEvictionHandler = burstEvictionHandler; + mBurstController = burstController; + mRestorePreviewCommand = restorePreviewCommand; + mMaxImageCount = maxImageCount; + } + + @Override + public void run() throws InterruptedException, CameraAccessException, + CameraCaptureSessionClosedException, ResourceAcquisitionFailedException { + List<ImageProxy> capturedImages = new ArrayList<>(); + try (FrameServer.Session session = mFrameServer.createExclusiveSession()) { + // Create a ring buffer and with the passed burst eviction + // handler and insert images in it from the image stream. + // The ring buffer size is one less than the image count. + int ringBufferSize = mMaxImageCount - 1; + try (RingBuffer ringBuffer = new RingBuffer(ringBufferSize, + mBurstEvictionHandler)) { + try (ImageStream imageStream = + mManagedImageReader.createStream(mMaxImageCount)) { + mBurstLifetime.add(imageStream); + + // Use the video snapshot template for the burst. + RequestBuilder photoRequest = + mBuilderFactory.create(BURST_TEMPLATE_TYPE); + photoRequest.setParam(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO); + + photoRequest.addStream(imageStream); + // Hook up the camera stream to burst input surface. + photoRequest.addStream(new CaptureStream() { + @Override + public Surface bind(BufferQueue<Long> timestamps) + throws InterruptedException, + ResourceAcquisitionFailedException { + return mBurstInputSurface; + } + }); + + // Hook the response listener to invoke eviction handler + // frame capture result. + photoRequest.addResponseListener(new ResponseListener() { + @Override + public void onCompleted(TotalCaptureResult result) { + final long timestamp = result.get(TotalCaptureResult.SENSOR_TIMESTAMP); + mBurstEvictionHandler.onFrameCaptureResultAvailable(timestamp, result); + } + }); + session.submitRequest(Arrays.asList(photoRequest.build()), + FrameServer.RequestType.REPEATING); + + try { + while (!imageStream.isClosed()) { + ringBuffer.insertImage( + imageStream.getNext()); + } + } catch (BufferQueueClosedException e) { + // This is normal. the image stream was closed. + } + } finally { + // Burst was completed call remove the images from the ring + // buffer. + capturedImages = ringBuffer.getAndRemoveAllImages(); + } + } + } finally { + // Make sure the images are closed after the callback. + try (Lifetime capturedImagesLifetime = new Lifetime()) { + for (ImageProxy image : capturedImages) { + capturedImagesLifetime.add(image); + } + mBurstController.processBurstResults(capturedImages); + } finally { + // Switch back to the old request. + mRestorePreviewCommand.run(); + } + } + } + +} diff --git a/src/com/android/camera/burst/BurstConfiguration.java b/src/com/android/camera/burst/BurstConfiguration.java deleted file mode 100644 index 08688e33d..000000000 --- a/src/com/android/camera/burst/BurstConfiguration.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2014 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.burst; - -import android.hardware.camera2.TotalCaptureResult; - -/** - * The burst configuration parameters. - */ -public interface BurstConfiguration { - /** - * The eviction strategy of the internal Camera image buffer. - * <p/> - * For a burst the Camera maintains an internal image buffer. This image - * buffer has limited memory and old images need to be evicted for storing - * new images. The eviction handler encapsulates the eviction strategy that - * the Camera uses to evict frames. - */ - public interface EvictionHandler { - /** - * Return the timestamp of the image that should be dropped. - * <p/> - * This method is called when the internal image buffer is out of - * capacity and needs to drop an image from the buffer. - * <p/> - * This should return one of the timestamps passed into - * {@link #onFrameInserted(long, TotalCaptureResult)}, and which has not - * yet been dropped. - * - * @return the timestamp of the frame to drop. - */ - long selectFrameToDrop(); - - /** - * Called when the capture result for a frame is available. - * - * @param timestamp the timestamp of the frame, this frame may or may - * not be present in the image buffer. - * @param captureResult the capture result of the image. - */ - void onFrameCaptureResultAvailable(long timestamp, - TotalCaptureResult captureResult); - - /** - * Called when an image is inserted in the image buffer. - * - * @param timestamp the timestamp of the inserted frame in the image - * buffer. - */ - void onFrameInserted(long timestamp); - - /** - * Called when a frame is dropped from the image buffer. - * - * @param timestamp the timestamp of the dropped frame. - */ - void onFrameDropped(long timestamp); - } - - /** - * Return the eviction handler associated with this instance of burst. - */ - public EvictionHandler getEvictionHandler(); -} diff --git a/src/com/android/camera/burst/BurstController.java b/src/com/android/camera/burst/BurstController.java index c501906b6..da89d4c0e 100644 --- a/src/com/android/camera/burst/BurstController.java +++ b/src/com/android/camera/burst/BurstController.java @@ -14,7 +14,11 @@ package com.android.camera.burst; -import com.android.camera.gl.FrameDistributor.FrameConsumer; +import android.graphics.SurfaceTexture; + +import com.android.camera.one.v2.camera2proxy.ImageProxy; + +import java.util.List; /** * Controls the interactions with burst. @@ -27,19 +31,15 @@ import com.android.camera.gl.FrameDistributor.FrameConsumer; * module retrieves results from the internal camera buffer and can do post * processing on the results. * <p/> - * Camera hooks up the frame consumer for the burst module returned by - * {@link #getPreviewFrameConsumer()} and initializes the burst module by - * calling {@link #startBurst()}. The returned configuration for initialized - * burst module contains the eviction strategy for the internal camera buffer. - * This {@link BurstConfiguration#getEvictionHandler()} is then used by camera - * to decide which frames to keep and which to reject. + * Camera initializes the burst module by calling {@link #startBurst(SurfaceTexture, + * ImageStreamProperties, BurstResultsListener)}. The returned eviction strategy + * is used by the internal camera buffer to decide which frames to keep and + * which to reject. * <p/> - * Once burst finishes, camera calls the {@link #stopBurst(ResultsAccessor)} to - * let the burst module retrieve burst results from the internal buffer. Results - * of burst can be extracted by calling the - * {@link ResultsAccessor#extractImage(long)} method. Once extraction is - * finished the burst module should call {@link ResultsAccessor#close()} method - * to let camera free resources used by burst. + * Once burst finishes, camera calls the {@link #processBurstResults(List)} to + * let the burst module retrieve burst results from the internal buffer. Once + * {@link #processBurstResults(List)} completes all resources allocated for the + * burst are freed. * <p/> * Once post processing is complete, the burst module returns the final results * by calling {@link BurstResultsListener#onBurstCompleted(BurstResult)} method. @@ -47,46 +47,68 @@ import com.android.camera.gl.FrameDistributor.FrameConsumer; interface BurstController { /** - * Starts the burst. - * - * @return the configuration of burst that can be used to control the - * ongoing burst. + * Properties of the image stream. */ - public BurstConfiguration startBurst(); + public static class ImageStreamProperties { + private final int width; + private final int height; + private final int imageRotation; + private final boolean isMirrored; - /** - * Stops the burst. - * - * @param resultsAccessor an instance of results accessor that can be used - * to query the results of the burst. - */ - public void stopBurst(ResultsAccessor resultsAccessor); + public ImageStreamProperties(int width, int height, + int imageRotation, + boolean isMirrored) { + this.width = width; + this.height = height; + this.imageRotation = imageRotation; + this.isMirrored = isMirrored; + } - /** - * Called when size of the preview changes. - * <p> - * Preview size can change in case of rotation or switching cameras. - * - * @param width the width of the preview. - * @param height the height of the preview. - */ - public void onPreviewSizeChanged(int width, int height); + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getImageRotation() { + return imageRotation; + } + + public boolean isMirrored() { + return isMirrored; + } + } /** - * Called when the orientation of the preview changes. + * Starts the burst. + * <p/> + * Takes a SurfaceTexture that is not attached to any context (call + * {@link android.graphics.SurfaceTexture#detachFromGLContext()} before + * passing it here. Can register as a frame available listener by calling + * {@link SurfaceTexture#setOnFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener, + * android.os.Handler)}. * - * @param orientation orientation of preview in degrees. - * @param isMirrored true if preview is mirrored. + * @param surfaceTexture the SurfaceTexture for the low-res image stream. + * This surface should not be attached to any GL context. + * @param imageStreamProperties the properties of the low-res image stream. + * @param burstResultsListener the listener for burst results. + * @return the configuration of burst that can be used to control the + * ongoing burst. */ - public void onOrientationChanged(int orientation, boolean isMirrored); + public EvictionHandler startBurst(SurfaceTexture surfaceTexture, + ImageStreamProperties imageStreamProperties, + BurstResultsListener burstResultsListener); /** - * Get the consumer for preview frames. + * Stops the burst. * <p/> - * Burst module streams preview frames and selects "good" frames by - * analyzing preview frames. Preview frames should have exact timestamps as - * the high-res images held in the internal image buffer. + * + * @param capturedImages list of images captured from the burst. All images + * are closed after this call completes. If implementations need + * to access images after this call they need to make a copy of + * images before returning. */ - public FrameConsumer getPreviewFrameConsumer(); - + public void processBurstResults(List<ImageProxy> capturedImages); } diff --git a/src/com/android/camera/burst/BurstFacade.java b/src/com/android/camera/burst/BurstFacade.java index f1e19afb0..6cda927db 100644 --- a/src/com/android/camera/burst/BurstFacade.java +++ b/src/com/android/camera/burst/BurstFacade.java @@ -17,37 +17,46 @@ package com.android.camera.burst; import android.graphics.SurfaceTexture; +import android.view.Surface; -import com.android.camera.one.OneCamera; +import com.android.camera.app.OrientationManager.DeviceOrientation; +import com.android.camera.one.OneCamera.Facing; import com.android.camera.session.CaptureSession; -import java.io.File; - /** - * Facade for {@link BurstController} provides a simpler interface. + * Facade for the entire burst acquisition pipeline. Provides a simplified + * interface over the {@link BurstController}. + * <p/> + * The expected usage of BurstFacade can be described by the regular expression + * "<code>initialize (startBurst stopBurst)* release</code>". That is there can + * be multiple calls to + * {@link #startBurst(CaptureSession, DeviceOrientation, Facing, int)} and + * {@link #stopBurst()} between {@link #initialize(SurfaceTexture)} and + * {@link #release()} calls. */ public interface BurstFacade { - /** - * Called when camera is available. - * - * @param camera an instance of {@link OneCamera} that is used to start or - * stop the burst. - */ - public void onCameraAttached(OneCamera camera); /** - * Called when camera becomes unavailable. + * Starts the burst. + * + * @param captureSession the capture session to use for this burst + * @param deviceOrientation the orientation of the device + * @param cameraFacing the camera facing + * @param imageOrientationDegrees the orientation of captured image in + * degrees */ - public void onCameraDetached(); + public void startBurst(CaptureSession captureSession, + DeviceOrientation deviceOrientation, + Facing cameraFacing, + int imageOrientationDegrees); /** - * Starts the burst. + * Stops the burst. * - * @param captureSession the capture session to use for this burst. - * @param tempSessionDirectory a directory in which temporary data can be - * put. + * @return Whether a burst was actually stopped. Returns false if no burst + * was running at the time. */ - public void startBurst(CaptureSession captureSession, File tempSessionDirectory); + public boolean stopBurst(); /** * @return Whether this burst controller is ready to start another burst. @@ -55,42 +64,34 @@ public interface BurstFacade { public boolean isReady(); /** - * Stops the burst. + * Initialize resources and use the provided {@link SurfaceTexture} for + * streaming low-res preview frames for the burst. * - * @return Whether a burst was actually stopped. Returns false if no burst - * was running at the time. + * @param surfaceTexture to use for streaming */ - public boolean stopBurst(); - - /** Sets the surface texture from which frames will be consumed. */ - public void setSurfaceTexture(SurfaceTexture surfaceTexture, int width, int height); + public void initialize(SurfaceTexture surfaceTexture); /** - * Initializes the surface texture consumer with the current surface - * texture and its dimensions. + * Release any resources used by the burst. + * <p/> + * {@link #initialize(SurfaceTexture)} should be called in order to start + * capturing bursts again. */ - public void initializeSurfaceTextureConsumer(int surfaceWidth, int surfaceHeight); + public void release(); /** - * Initializes the surface texture consumer with the give surface - * texture and dimensions. + * Returns the input surface for preview stream used by burst module. + * <p/> + * This is an instance of {@link Surface} that is created for the passed in + * surface texture {@link #initialize(SurfaceTexture)}. */ - public void initializeSurfaceTextureConsumer(SurfaceTexture surfaceTexture, int surfaceWidth, - int surfaceHeight); - - /** Updates the size of the preview buffer. */ - public void updatePreviewBufferSize(int width, int height); - - /** Initializes and starts the frame distributor. */ - public void initializeAndStartFrameDistributor(); - - /** Closed the frame distributor. */ - public void closeFrameDistributor(); - - /** Returns the frame distributor's input surface texture. */ - public SurfaceTexture getInputSurfaceTexture(); - - /** Sets the preview size (in pixels) of the preview consumer. */ - public void setPreviewConsumerSize(int width, int height); + public Surface getInputSurface(); + /** + * Sets an instance of {@link BurstTaker}. + * <p/> + * The instance of {@link BurstTaker} is available only when the capture + * session with Camera is complete. + */ + public void setBurstTaker(BurstTaker burstTaker); } diff --git a/src/com/android/camera/burst/BurstFacadeFactory.java b/src/com/android/camera/burst/BurstFacadeFactory.java index 993409269..cd97ed096 100644 --- a/src/com/android/camera/burst/BurstFacadeFactory.java +++ b/src/com/android/camera/burst/BurstFacadeFactory.java @@ -18,15 +18,12 @@ package com.android.camera.burst; import android.content.Context; import android.graphics.SurfaceTexture; +import android.view.Surface; -import com.android.camera.app.OrientationManager; -import com.android.camera.gl.FrameDistributorWrapper; -import com.android.camera.gl.SurfaceTextureConsumer; -import com.android.camera.one.OneCamera; +import com.android.camera.app.OrientationManager.DeviceOrientation; +import com.android.camera.one.OneCamera.Facing; import com.android.camera.session.CaptureSession; -import java.io.File; - /** * Factory for creating burst manager objects. */ @@ -42,18 +39,10 @@ public class BurstFacadeFactory { * burst is not enabled. */ private static class BurstFacadeStub implements BurstFacade { - private SurfaceTexture mPreviewSurfaceTexture; - - @Override - public void onCameraAttached(OneCamera camera) { - } - - @Override - public void onCameraDetached() { - } - @Override - public void startBurst(CaptureSession captureSession, File tempSessionDirectory) { + public void startBurst(CaptureSession captureSession, + DeviceOrientation deviceOrientation, Facing cameraFacing, + int imageOrientationDegrees) { } @Override @@ -67,44 +56,18 @@ public class BurstFacadeFactory { } @Override - public void setSurfaceTexture(SurfaceTexture surfaceTexture, int width, int height) { - mPreviewSurfaceTexture = surfaceTexture; - } - - @Override - public void initializeSurfaceTextureConsumer(int surfaceWidth, int surfaceHeight) { - } - - @Override - public void initializeSurfaceTextureConsumer(SurfaceTexture surfaceTexture, int - surfaceWidth, int surfaceHeight) { - mPreviewSurfaceTexture = surfaceTexture; - - } - - @Override - public void updatePreviewBufferSize(int width, int height) { - if (mPreviewSurfaceTexture != null) { - mPreviewSurfaceTexture.setDefaultBufferSize(width, height); - } - } - - @Override - public void initializeAndStartFrameDistributor() { - } + public void initialize(SurfaceTexture surfaceTexture) {} @Override - public void closeFrameDistributor() { - } + public void release() {} @Override - public SurfaceTexture getInputSurfaceTexture() { - return mPreviewSurfaceTexture; + public Surface getInputSurface() { + return null; } @Override - public void setPreviewConsumerSize(int width, int height) { - } + public void setBurstTaker(BurstTaker burstTaker) {} } /** @@ -112,16 +75,16 @@ public class BurstFacadeFactory { * * @param appContext the Android application context which is passes through * to the burst controller. - * @param orientationManager for querying orientation of burst. + * @param orientationController for locking orientation when burst is running. * @param readyStateListener gets called when the ready state of Burst * changes. */ - public static BurstFacade create(Context appContext, OrientationManager orientationManager, + public static BurstFacade create(Context appContext, + OrientationLockController orientationController, BurstReadyStateChangeListener readyStateListener) { if (BurstControllerImpl.isBurstModeSupported(appContext)) { - BurstFacade burstController = new BurstFacadeImpl(appContext, orientationManager, - readyStateListener, - new FrameDistributorWrapper(), new SurfaceTextureConsumer()); + BurstFacade burstController = new BurstFacadeImpl(appContext, orientationController, + readyStateListener); ToastingBurstFacadeDecorator.BurstToaster toaster = new ToastingBurstFacadeDecorator.BurstToaster(appContext); return new ToastingBurstFacadeDecorator(burstController, toaster); diff --git a/src/com/android/camera/burst/BurstFacadeImpl.java b/src/com/android/camera/burst/BurstFacadeImpl.java index 811195632..253d50d9d 100644 --- a/src/com/android/camera/burst/BurstFacadeImpl.java +++ b/src/com/android/camera/burst/BurstFacadeImpl.java @@ -16,33 +16,18 @@ package com.android.camera.burst; import android.content.Context; import android.graphics.SurfaceTexture; -import android.location.Location; -import android.os.AsyncTask; -import android.text.TextUtils; +import android.view.Surface; -import com.android.camera.app.OrientationManager; import com.android.camera.app.OrientationManager.DeviceOrientation; -import com.android.camera.app.OrientationManager.OnOrientationChangeListener; -import com.android.camera.data.FilmstripItemData; +import com.android.camera.async.MainThread; +import com.android.camera.burst.BurstController.ImageStreamProperties; import com.android.camera.debug.Log; import com.android.camera.debug.Log.Tag; -import com.android.camera.exif.ExifInterface; -import com.android.camera.gl.FrameDistributor.FrameConsumer; -import com.android.camera.gl.FrameDistributorWrapper; -import com.android.camera.gl.SurfaceTextureConsumer; -import com.android.camera.one.OneCamera; -import com.android.camera.one.OneCamera.BurstParameters; -import com.android.camera.one.OneCamera.BurstResultsCallback; import com.android.camera.one.OneCamera.Facing; import com.android.camera.session.CaptureSession; import com.android.camera.session.StackSaver; -import java.io.File; -import java.util.ArrayList; -import java.util.List; import java.util.Map; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** @@ -66,41 +51,19 @@ class BurstFacadeImpl implements BurstFacade { private static final Tag TAG = new Tag("BurstFacadeImpl"); - /** - * The format string of burst media item file name (without extension). - * <p/> - * An media item file name has the following format: "Burst_" + artifact - * type + "_" + index of artifact + "_" + index of media item + "_" + - * timestamp - */ - private static final String MEDIA_ITEM_FILENAME_FORMAT_STRING = "Burst_%s_%d_%d_%d"; - - private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); + private static final int DEFAULT_PREVIEW_WIDTH = 320; + private static final int DEFAULT_PREVIEW_HEIGHT = 240; private final AtomicReference<BurstModuleState> mBurstModuleState = new AtomicReference<BurstModuleState>(BurstModuleState.IDLE); - - /** Lock to protect starting and stopping of the burst. */ - private final Object mStartStopBurstLock = new Object(); + private final AtomicReference<BurstTaker> mBurstTaker = + new AtomicReference<>(null); private final BurstController mBurstController; - /** - * Results callback that is invoked by camera when results are available. - */ - private final BurstResultsCallback mBurstExtractsResultsCallback = new BurstResultsCallback() { - @Override - public void onBurstComplete(ResultsAccessor resultAccessor) { - // Pass the results accessor to the controller. - mBurstController.stopBurst(resultAccessor); - } - }; /** A stack saver for the outstanding burst request. */ private StackSaver mActiveStackSaver; - /** The image orientation of the active burst. */ - private int mImageOrientation; - /** * Listener for burst controller. Saves the results and interacts with the * UI. @@ -114,113 +77,88 @@ class BurstFacadeImpl implements BurstFacade { @Override public void onBurstError(Exception error) { Log.e(TAG, "Exception while running the burst" + error); - mActiveStackSaver = null; - - // Transition to idle state, ready to take another burst. - mBurstModuleState.set(BurstModuleState.IDLE); - - // Re-enable the shutter button. - mReadyStateListener.onBurstReadyStateChanged(true); + reEnableUI(); } @Override public void onBurstCompleted(BurstResult burstResult) { - saveBurstResultAndEnableShutterButton(burstResult, mActiveStackSaver); + BurstResultsSaver.saveBurstResultsInBackground(burstResult, mActiveStackSaver, + new Runnable() { + @Override + public void run() { + reEnableUI(); + } + }); } @Override public void onArtifactCountAvailable( final Map<String, Integer> artifactTypeCount) { - logArtifactCount(artifactTypeCount); + BurstResultsSaver.logArtifactCount(artifactTypeCount); } }; - private final OrientationManager.OnOrientationChangeListener mOrientationChangeListener = - new OnOrientationChangeListener() { - @Override - public void onOrientationChanged(OrientationManager orientationManager, - DeviceOrientation orientation) { - mBurstController.onOrientationChanged(orientation.getDegrees(), - mCamera.getDirection() == Facing.FRONT); - } - }; - - /** Camera instance for starting/stopping the burst. */ - private OneCamera mCamera; - - private final OrientationManager mOrientationManager; + private final OrientationLockController mOrientationLockController; private final BurstReadyStateChangeListener mReadyStateListener; - /** Used to distribute camera frames to consumers. */ - private final FrameDistributorWrapper mFrameDistributor; - - /** The frame consumer that renders frames to the preview. */ - private final SurfaceTextureConsumer mPreviewConsumer; + private final AtomicReference<SurfaceTextureContainer> mSurfaceTextureContainer = + new AtomicReference<>(); /** * Create a new BurstManagerImpl instance. * - * @param appContext the Android application context. - * @param orientationManager orientationManager + * @param appContext the application context + * @param orientationLockController for locking orientation when burst is + * running. * @param readyStateListener gets called when the ready state of Burst * changes. */ - public BurstFacadeImpl(Context appContext, OrientationManager orientationManager, - BurstReadyStateChangeListener readyStateListener, - FrameDistributorWrapper frameDistributor, SurfaceTextureConsumer previewConsumer) { - mOrientationManager = orientationManager; - mBurstController = new BurstControllerImpl(appContext, mBurstResultsListener); + public BurstFacadeImpl(Context appContext, + OrientationLockController orientationLockController, + BurstReadyStateChangeListener readyStateListener) { + mOrientationLockController = orientationLockController; + mBurstController = new BurstControllerImpl(appContext); mReadyStateListener = readyStateListener; - mFrameDistributor = frameDistributor; - mPreviewConsumer = previewConsumer; } @Override - public void onCameraAttached(OneCamera camera) { - synchronized (mStartStopBurstLock) { - mCamera = camera; - } - } - - @Override - public void onCameraDetached() { - synchronized (mStartStopBurstLock) { - mCamera = null; - } - } - - @Override - public void startBurst(CaptureSession captureSession, File tempSessionDirectory) { - synchronized (mStartStopBurstLock) { - if (mCamera != null && - mBurstModuleState.compareAndSet(BurstModuleState.IDLE, - BurstModuleState.RUNNING)) { - Log.d(TAG, "Starting burst."); - Location location = captureSession.getLocation(); - - mOrientationManager.addOnOrientationChangeListener(mOrientationChangeListener); - int orientation = mOrientationManager.getDeviceOrientation().getDegrees(); - mBurstController.onOrientationChanged(orientation, - mCamera.getDirection() == Facing.FRONT); - - BurstConfiguration burstConfig = mBurstController.startBurst(); - BurstParameters params = new BurstParameters(captureSession.getTitle(), - orientation, location, tempSessionDirectory, burstConfig, - mBurstExtractsResultsCallback); - - if (mActiveStackSaver != null) { - throw new IllegalStateException( - "Cannot start a burst while another is in progress."); - } - mActiveStackSaver = captureSession.getStackSaver(); - mImageOrientation = mOrientationManager.getDeviceOrientation().getDegrees(); - - // Disable the shutter button. - mReadyStateListener.onBurstReadyStateChanged(false); - - // Start burst. - mCamera.startBurst(params, captureSession); + public void startBurst(CaptureSession captureSession, + DeviceOrientation deviceOrientation, + Facing cameraFacing, + int imageOrientationDegrees) { + MainThread.checkMainThread(); + if (mBurstTaker.get() != null && + mBurstModuleState.compareAndSet(BurstModuleState.IDLE, + BurstModuleState.RUNNING)) { + mActiveStackSaver = captureSession.getStackSaver(); + + mOrientationLockController.lockOrientation(); + // Disable the shutter button. + mReadyStateListener.onBurstReadyStateChanged(false); + + Log.d(TAG, "Starting burst. Device orientation: " + deviceOrientation.getDegrees() + + " image orientation: " + imageOrientationDegrees); + int width = DEFAULT_PREVIEW_WIDTH; + int height = DEFAULT_PREVIEW_HEIGHT; + if (imageOrientationDegrees % 180 == 90) { + int tmp = width; + width = height; + height = tmp; } + + ImageStreamProperties imageStreamProperties = + new ImageStreamProperties(width, height, + imageOrientationDegrees, cameraFacing == Facing.FRONT); + EvictionHandler evictionHandler = + mBurstController.startBurst( + mSurfaceTextureContainer.get().getSurfaceTexture(), + imageStreamProperties, + mBurstResultsListener); + + // Start burst. + mBurstTaker.get().startBurst(evictionHandler, mBurstController); + } else { + Log.e(TAG, "Cannot start burst."); } } @@ -231,221 +169,54 @@ class BurstFacadeImpl implements BurstFacade { @Override public boolean stopBurst() { - synchronized (mStartStopBurstLock) { + MainThread.checkMainThread(); boolean wasStopped = false; if (mBurstModuleState.compareAndSet(BurstModuleState.RUNNING, BurstModuleState.STOPPING)) { - mOrientationManager.removeOnOrientationChangeListener(mOrientationChangeListener); - if (mCamera != null) { - mCamera.stopBurst(); - wasStopped = true; - } + mBurstTaker.get().stopBurst(); + wasStopped = true; } return wasStopped; - } - } - - @Override - public void setSurfaceTexture(SurfaceTexture surfaceTexture, int width, int height) { - mPreviewConsumer.setSurfaceTexture(surfaceTexture, width, height); } @Override - public void initializeSurfaceTextureConsumer(int surfaceWidth, int surfaceHeight) { - initializeSurfaceTextureConsumer(mPreviewConsumer.getSurfaceTexture(), surfaceWidth, - surfaceHeight); + public Surface getInputSurface() { + return mSurfaceTextureContainer.get().getSurface(); } @Override - public void initializeSurfaceTextureConsumer(SurfaceTexture surface, int surfaceWidth, - int surfaceHeight) { - if (surface == null) { - return; - } + public void initialize(SurfaceTexture surfaceTexture) { + MainThread.checkMainThread(); + // TODO: Use preview sizes from Camera API here instead of using the + // default. + surfaceTexture.setDefaultBufferSize(DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT); - if (mPreviewConsumer.getSurfaceTexture() != surface) { - mPreviewConsumer.setSurfaceTexture(surface, surfaceWidth, surfaceHeight); - } else if (mPreviewConsumer.getWidth() != surfaceWidth - || mPreviewConsumer.getHeight() != surfaceHeight) { - mPreviewConsumer.setSize(surfaceWidth, surfaceHeight); - } + // Detach from GL context, to allow frame distributor to attach to the + // GL context. + surfaceTexture.detachFromGLContext(); + mSurfaceTextureContainer.set(new SurfaceTextureContainer(surfaceTexture)); } @Override - public void initializeAndStartFrameDistributor() { - // Currently, there is only one consumer to FrameDistributor for - // rendering the frames to the preview texture. - List<FrameConsumer> frameConsumers = new ArrayList<>(); - frameConsumers.add(mBurstController.getPreviewFrameConsumer()); - frameConsumers.add(mPreviewConsumer); - mFrameDistributor.start(frameConsumers); - } - - @Override - public void updatePreviewBufferSize(int width, int height) { - mFrameDistributor.updatePreviewBufferSize(width, height); - } - - @Override - public void closeFrameDistributor() { - mFrameDistributor.close(); - } - - @Override - public SurfaceTexture getInputSurfaceTexture() { - if (mFrameDistributor != null) { - return mFrameDistributor.getInputSurfaceTexture(); - } else { - return null; + public void release() { + MainThread.checkMainThread(); + stopBurst(); + if (mSurfaceTextureContainer.get() != null) { + mSurfaceTextureContainer.get().close(); + mSurfaceTextureContainer.set(null); } } @Override - public void setPreviewConsumerSize(int previewWidth, int previewHeight) { - mPreviewConsumer.setSize(previewWidth, previewHeight); - } - - /** - * Generates sequential timestamp with 1 second difference. - */ - private static class SequentialTimestampGenerator { - private long mSeedTimestampMillis; - - /** - * New instance of generator. - * - * @param seedTimestampMillis the timestamp in milliseconds for - * initializing the generator. - */ - public SequentialTimestampGenerator(long seedTimestampMillis) { - mSeedTimestampMillis = seedTimestampMillis; - } - - /** - * Returns the next timestamp. - */ - public synchronized long getNextTimestampMillis() { - mSeedTimestampMillis += TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS); - return mSeedTimestampMillis; - } - } - - /** - * Saves the burst result and on completion re-enables the shutter button. - * - * @param burstResult the result of the burst. - */ - private void saveBurstResultAndEnableShutterButton(final BurstResult burstResult, - final StackSaver stackSaver) { - Log.i(TAG, "Saving results of of the burst."); - - AsyncTask<Void, String, Void> saveTask = - new AsyncTask<Void, String, Void>() { - @Override - protected Void doInBackground(Void... arg0) { - // The timestamp with which a media item is saved - // determines its place in the film strip. The newer - // items appear first. - // We save artifacts and their corresponding media - // items sequentially in the desired order. The order - // of the artifacts is implicitly defined by - // burstResult.getTypes() and the media items inside the - // artifacts are assumed to be sorted in ascending order - // by timestamps. - // We create a timestamp-generator that generates - // timestamps in order and use it to save timestamps. - SequentialTimestampGenerator timestampGen = - new SequentialTimestampGenerator(System.currentTimeMillis()); - for (String artifactType : burstResult.getTypes()) { - publishProgress(artifactType); - saveArtifacts(stackSaver, burstResult, artifactType, - timestampGen); - } - return null; - } - - @Override - protected void onPostExecute(Void result) { - mBurstModuleState.set(BurstModuleState.IDLE); - mActiveStackSaver = null; - // Re-enable the shutter button. - mReadyStateListener.onBurstReadyStateChanged(true); - } - - @Override - protected void onProgressUpdate(String... artifactTypes) { - logProgressUpdate(artifactTypes, burstResult); - } - }; - saveTask.execute(null, null, null); - } - - /** - * Save individual artifacts for bursts. - */ - private void saveArtifacts(final StackSaver stackSaver, final BurstResult burstResult, - final String artifactType, SequentialTimestampGenerator timestampGenerator) { - List<BurstArtifact> artifactList = burstResult.getArtifactsByType(artifactType); - for (int artifactIndex = 0; artifactIndex < artifactList.size(); artifactIndex++) { - List<BurstMediaItem> mediaItems = artifactList.get(artifactIndex).getMediaItems(); - for (int index = 0; index < mediaItems.size(); index++) { - saveBurstMediaItem(stackSaver, mediaItems.get(index), - artifactType, artifactIndex + 1, index + 1, timestampGenerator); - } - } + public void setBurstTaker(BurstTaker burstTaker) { + mBurstTaker.set(burstTaker); } - private void saveBurstMediaItem(StackSaver stackSaver, - BurstMediaItem mediaItem, - String artifactType, - int artifactIndex, - int index, - SequentialTimestampGenerator timestampGenerator) { - // Use ordered timestamp for saving the media item, this way media - // items appear to be in the correct order when user swipes to the - // film strip. - long timestamp = timestampGenerator.getNextTimestampMillis(); - final String title = String.format(MEDIA_ITEM_FILENAME_FORMAT_STRING, - artifactType, artifactIndex, index, mediaItem.getTimestamp()); - String mimeType = mediaItem.getMimeType(); - ExifInterface exif = null; - if (FilmstripItemData.MIME_TYPE_JPEG.equals(mimeType)) { - exif = new ExifInterface(); - exif.addDateTimeStampTag( - ExifInterface.TAG_DATE_TIME, - timestamp, - UTC_TIMEZONE); - } - - stackSaver.saveStackedImage(mediaItem.getData(), - title, - mediaItem.getWidth(), - mediaItem.getHeight(), - mImageOrientation, - exif, - timestamp, - mimeType); - } - - private void logArtifactCount(final Map<String, Integer> artifactTypeCount) { - final String prefix = "Finished burst. Creating "; - List<String> artifactDescription = new ArrayList<String>(); - for (Map.Entry<String, Integer> entry : artifactTypeCount.entrySet()) { - artifactDescription.add(entry.getValue() + " " + entry.getKey()); - } - - String message = prefix + TextUtils.join(" and ", artifactDescription) + "."; - Log.d(TAG, message); - } - - private void logProgressUpdate(String[] artifactTypes, BurstResult burstResult) { - for (String artifactType : artifactTypes) { - List<BurstArtifact> artifacts = - burstResult.getArtifactsByType(artifactType); - if (!artifacts.isEmpty()) { - Log.d(TAG, "Saving " + artifacts.size() - + " " + artifactType + "s."); - } - } + private void reEnableUI() { + MainThread.checkMainThread(); + mBurstModuleState.set(BurstModuleState.IDLE); + mOrientationLockController.unlockOrientation(); + // Re-enable the shutter button. + mReadyStateListener.onBurstReadyStateChanged(true); } } diff --git a/src/com/android/camera/burst/BurstImage.java b/src/com/android/camera/burst/BurstImage.java deleted file mode 100644 index cc5d4fdc7..000000000 --- a/src/com/android/camera/burst/BurstImage.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2014 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.burst; - -import android.hardware.camera2.TotalCaptureResult; - -/** - * Holds image data and meta-data for a single image that is part of a burst. - * <p/> - * Bursts are an ordered collection of images. This class holds the data for one - * of these images. - */ -public class BurstImage { - /** - * The bytes of image that can be decoded by - * {@link android.graphics.BitmapFactory#decodeByteArray(byte[], - * int, int, android.graphics.BitmapFactory.Options)}. - */ - // TODO: Fix this and use a URI here for saved image. - public byte[] data; - - /** - * The timestamp of the image measured in nanoseconds. - * <p/> - * The timestamp is monotonically increasing. It is mostly useful for - * determining time offsets between images in burst. The zero-point and the - * value of the timestamp cannot be in general compared between two - * different bursts. - */ - public long timestamp; - - /** - * The width of the image in pixels. - */ - public int width; - - /** - * The height of the image in pixels. - */ - public int height; - - /** - * The capture result associated with the image. - */ - public TotalCaptureResult captureResult; -} diff --git a/src/com/android/camera/burst/BurstResultsSaver.java b/src/com/android/camera/burst/BurstResultsSaver.java new file mode 100644 index 000000000..104931700 --- /dev/null +++ b/src/com/android/camera/burst/BurstResultsSaver.java @@ -0,0 +1,188 @@ +/* + * 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.burst; + +import android.os.AsyncTask; +import android.text.TextUtils; + +import com.android.camera.data.FilmstripItemData; +import com.android.camera.debug.Log; +import com.android.camera.debug.Log.Tag; +import com.android.camera.exif.ExifInterface; +import com.android.camera.session.StackSaver; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +class BurstResultsSaver { + private static final Tag TAG = new Tag("BurstResultsSaver"); + + /** + * The format string of burst media item file name (without extension). + * <p/> + * An media item file name has the following format: "Burst_" + artifact + * type + "_" + index of artifact + "_" + index of media item + "_" + + * timestamp + */ + private static final String MEDIA_ITEM_FILENAME_FORMAT_STRING = "Burst_%s_%d_%d_%d"; + private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); + + /** + * Generates sequential timestamp with 1 second difference. + */ + private static class SequentialTimestampGenerator { + private long mSeedTimestampMillis; + + /** + * New instance of generator. + * + * @param seedTimestampMillis the timestamp in milliseconds for + * initializing the generator. + */ + public SequentialTimestampGenerator(long seedTimestampMillis) { + mSeedTimestampMillis = seedTimestampMillis; + } + + /** + * Returns the next timestamp. + */ + public synchronized long getNextTimestampMillis() { + mSeedTimestampMillis += TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS); + return mSeedTimestampMillis; + } + } + + public static void logArtifactCount(final Map<String, Integer> artifactTypeCount) { + final String prefix = "Finished burst. Creating "; + List<String> artifactDescription = new ArrayList<String>(); + for (Map.Entry<String, Integer> entry : artifactTypeCount.entrySet()) { + artifactDescription.add(entry.getValue() + " " + entry.getKey()); + } + + String message = prefix + TextUtils.join(" and ", artifactDescription) + "."; + Log.d(TAG, message); + } + + /** + * Saves the burst result and on completion re-enables the shutter button. + * + * @param burstResult the result of the burst. + */ + public static void saveBurstResultsInBackground(final BurstResult burstResult, + final StackSaver stackSaver, final Runnable onCompletetionCallback) { + Log.i(TAG, "Saving results of of the burst."); + + AsyncTask<Void, String, Void> saveTask = + new AsyncTask<Void, String, Void>() { + @Override + protected Void doInBackground(Void... arg0) { + // The timestamp with which a media item is saved + // determines its place in the film strip. The newer + // items appear first. + // We save artifacts and their corresponding media + // items sequentially in the desired order. The order + // of the artifacts is implicitly defined by + // burstResult.getTypes() and the media items inside the + // artifacts are assumed to be sorted in ascending order + // by timestamps. + // We create a timestamp-generator that generates + // timestamps in order and use it to save timestamps. + SequentialTimestampGenerator timestampGen = + new SequentialTimestampGenerator(System.currentTimeMillis()); + for (String artifactType : burstResult.getTypes()) { + publishProgress(artifactType); + saveArtifacts(stackSaver, burstResult, artifactType, + timestampGen); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + onCompletetionCallback.run(); + } + + @Override + protected void onProgressUpdate(String... artifactTypes) { + logProgressUpdate(artifactTypes, burstResult); + } + }; + saveTask.execute(null, null, null); + } + + /** + * Save individual artifacts for bursts. + */ + private static void saveArtifacts(final StackSaver stackSaver, final BurstResult burstResult, + final String artifactType, SequentialTimestampGenerator timestampGenerator) { + List<BurstArtifact> artifactList = burstResult.getArtifactsByType(artifactType); + for (int artifactIndex = 0; artifactIndex < artifactList.size(); artifactIndex++) { + List<BurstMediaItem> mediaItems = artifactList.get(artifactIndex).getMediaItems(); + for (int index = 0; index < mediaItems.size(); index++) { + saveBurstMediaItem(stackSaver, mediaItems.get(index), + artifactType, artifactIndex + 1, index + 1, timestampGenerator); + } + } + } + + private static void saveBurstMediaItem(StackSaver stackSaver, + BurstMediaItem mediaItem, + String artifactType, + int artifactIndex, + int index, + SequentialTimestampGenerator timestampGenerator) { + // Use ordered timestamp for saving the media item, this way media + // items appear to be in the correct order when user swipes to the + // film strip. + long timestamp = timestampGenerator.getNextTimestampMillis(); + final String title = String.format(MEDIA_ITEM_FILENAME_FORMAT_STRING, + artifactType, artifactIndex, index, mediaItem.getTimestamp()); + String mimeType = mediaItem.getMimeType(); + ExifInterface exif = null; + if (FilmstripItemData.MIME_TYPE_JPEG.equals(mimeType)) { + exif = new ExifInterface(); + exif.addDateTimeStampTag( + ExifInterface.TAG_DATE_TIME, + timestamp, + UTC_TIMEZONE); + } + + stackSaver.saveStackedImage(mediaItem.getData(), + title, + mediaItem.getWidth(), + mediaItem.getHeight(), + 0, // Artifacts returned from burst have upright orientation. + exif, + timestamp, + mimeType); + } + + private static void logProgressUpdate(String[] artifactTypes, BurstResult burstResult) { + for (String artifactType : artifactTypes) { + List<BurstArtifact> artifacts = + burstResult.getArtifactsByType(artifactType); + if (!artifacts.isEmpty()) { + Log.d(TAG, "Saving " + artifacts.size() + + " " + artifactType + "s."); + } + } + } + +} diff --git a/src/com/android/camera/burst/BurstTaker.java b/src/com/android/camera/burst/BurstTaker.java new file mode 100644 index 000000000..d819b1637 --- /dev/null +++ b/src/com/android/camera/burst/BurstTaker.java @@ -0,0 +1,41 @@ +/* + * 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.burst; + +import com.android.camera.burst.EvictionHandler; + +import javax.annotation.ParametersAreNonnullByDefault; + +/** + * Helper for taking bursts. + */ +@ParametersAreNonnullByDefault +public interface BurstTaker { + /** + * Start the burst. + * + * @param evictionHandler the strategy to use for evicting frames from the + * internal ring buffer. + * @param burstController the instance for {@link BurstController}. + */ + public void startBurst(EvictionHandler evictionHandler, BurstController burstController); + + /** + * Stop the burst. + */ + public void stopBurst(); +} diff --git a/src/com/android/camera/burst/BurstTakerImpl.java b/src/com/android/camera/burst/BurstTakerImpl.java new file mode 100644 index 000000000..cf9d1e071 --- /dev/null +++ b/src/com/android/camera/burst/BurstTakerImpl.java @@ -0,0 +1,105 @@ +/* + * 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.burst; + +import android.view.Surface; + +import com.android.camera.async.Lifetime; +import com.android.camera.async.MainThread; +import com.android.camera.one.v2.commands.CameraCommandExecutor; +import com.android.camera.one.v2.core.FrameServer; +import com.android.camera.one.v2.core.Request; +import com.android.camera.one.v2.core.RequestBuilder; +import com.android.camera.one.v2.sharedimagereader.ManagedImageReader; +import com.google.common.base.Preconditions; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class BurstTakerImpl implements BurstTaker { + + private final CameraCommandExecutor mCameraCommandExecutor; + private final FrameServer mFrameServer; + private final ManagedImageReader mImageFactory; + private final RequestBuilder.Factory mRequestBuilder; + private final Surface mBurstInputSurface; + private final Runnable mRestorePreviewCommand; + private final int mMaxImageCount; + /** + * The lifetime of the burst, the burst stops capturing images once the + * lifetime is closed + */ + @Nullable + private Lifetime mBurstLifetime; + + /** + * Creates an instance for burst taker. + * <p/> + * + * @param cameraCommandExecutor the executor to use for executing + * {@link BurstCaptureCommand} + * @param frameServer the {@link FrameServer} instance for creating session + * @param builder factory to use for creating the {@link Request} for burst + * capture + * @param imageFactory the factory to use for creating a stream of images + * @param burstInputSurface the input surface to use for streaming preview + * frames to burst + * @param restorePreviewCommand the command to run to restore the preview, + * once burst capture is complete + * @param maxImageCount the maximum number of images supported by the image + * reader + */ + public BurstTakerImpl(CameraCommandExecutor cameraCommandExecutor, + FrameServer frameServer, RequestBuilder.Factory builder, + ManagedImageReader imageFactory, Surface burstInputSurface, + Runnable restorePreviewCommand, + int maxImageCount) { + mCameraCommandExecutor = cameraCommandExecutor; + mFrameServer = frameServer; + mRequestBuilder = builder; + mImageFactory = imageFactory; + mBurstInputSurface = burstInputSurface; + mRestorePreviewCommand = restorePreviewCommand; + mMaxImageCount = maxImageCount; + } + + @Override + public void startBurst(EvictionHandler evictionHandler, + BurstController burstController) { + MainThread.checkMainThread(); + Preconditions.checkState(mBurstLifetime == null, + "Burst cannot be started, while another is running."); + mBurstLifetime = new Lifetime(); + BurstCaptureCommand burstCommand = new BurstCaptureCommand( + mFrameServer, mRequestBuilder, + mImageFactory, mBurstInputSurface, + mBurstLifetime, evictionHandler, burstController, mRestorePreviewCommand, + mMaxImageCount); + + mCameraCommandExecutor.execute(burstCommand); + } + + @Override + public synchronized void stopBurst() { + MainThread.checkMainThread(); + if (mBurstLifetime != null) { + mBurstLifetime.close(); + mBurstLifetime = null; + } + } +} diff --git a/src/com/android/camera/burst/EvictionHandler.java b/src/com/android/camera/burst/EvictionHandler.java new file mode 100644 index 000000000..18ff772dc --- /dev/null +++ b/src/com/android/camera/burst/EvictionHandler.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 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.burst; + +import android.hardware.camera2.TotalCaptureResult; + +/** + * The eviction strategy of the internal Camera image buffer. + * <p/> + * For a burst the Camera maintains an internal image buffer. This image buffer + * has limited memory and old images need to be evicted for storing new images. + * The eviction handler encapsulates the eviction strategy that the Camera uses + * to evict frames. + */ +public interface EvictionHandler { + /** + * Return the timestamp of the image that should be dropped. + * <p/> + * This method is called when the internal image buffer is out of capacity + * and needs to drop an image from the buffer. + * <p/> + * This should return one of the timestamps passed into + * {@link #onFrameInserted(long)}, and which has not yet + * been dropped. + * + * @return the timestamp of the frame to drop. + */ + long selectFrameToDrop(); + + /** + * Called when the capture result for a frame is available. + * + * @param timestamp the timestamp of the frame, this frame may or may not be + * present in the image buffer. + * @param captureResult the capture result of the image. + */ + void onFrameCaptureResultAvailable(long timestamp, + TotalCaptureResult captureResult); + + /** + * Called when an image is inserted in the image buffer. + * + * @param timestamp the timestamp of the inserted frame in the image buffer. + */ + void onFrameInserted(long timestamp); + + /** + * Called when a frame is dropped from the image buffer. + * + * @param timestamp the timestamp of the dropped frame. + */ + void onFrameDropped(long timestamp); +} diff --git a/src/com/android/camera/burst/OrientationLockController.java b/src/com/android/camera/burst/OrientationLockController.java new file mode 100644 index 000000000..a9ead861b --- /dev/null +++ b/src/com/android/camera/burst/OrientationLockController.java @@ -0,0 +1,34 @@ +/* + * 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.burst; + +/** + * Controls the framework the orientation lock. + */ +public interface OrientationLockController { + /** + * Lock the framework orientation to the current device orientation + * rotation. + */ + void lockOrientation(); + + /** + * Unlock the framework orientation, so it can change when the device + * rotates. + */ + void unlockOrientation(); +} diff --git a/src/com/android/camera/burst/ResultsAccessor.java b/src/com/android/camera/burst/ResultsAccessor.java deleted file mode 100644 index be2904c99..000000000 --- a/src/com/android/camera/burst/ResultsAccessor.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2014 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.burst; - -import java.util.concurrent.Future; - -/** - * Accessor that can be used for retrieving high-res burst images from the - * internal camera image buffer. - * <p/> - * On completion of a burst Camera hands over an instance of - * {@link ResultsAccessor} to the burst module by calling the - * {@link BurstControllerImpl#stopBurst(ResultsAccessor)} method. The burst - * module can then retrieve images from the internal buffer by calling - * {@link #extractImage(long)} with the timestamp of each image. - * <p/> - * When burst module has completed retrieving the high-res images from the - * buffer it should call {@link #close()} to let camera free the internal buffer - * and resources used by the burst. - */ -public interface ResultsAccessor extends AutoCloseable { - /** - * Extract image which has the given timestamp. - * <p/> - * Extracting an image is an expensive operation and is done asynchronously. - * This method returns a <code>Future<BurstImage></code> which can be used - * to retrieve the image. If the provided timestamp is not a result of burst - * or internal buffer of Camera does not have the image, the - * {@link Future#get()} will return null. Camera frees up resources related - * to the timestamp after the first call. Calling this method with the same - * timestamp will result in a null image. - * - * @param timestamp the timestamp of the image to be extracted - * @return the future for BurstImage. - */ - Future<BurstImage> extractImage(long timestamp); - - /** - * Called when results extraction has been completed and the Camera can free - * up resources related to results of burst. - * <p/> - * No further images can be extracted after calling this method. Any - * computations for extracting images are cancelled. - */ - @Override - void close(); -} diff --git a/src/com/android/camera/burst/RingBuffer.java b/src/com/android/camera/burst/RingBuffer.java new file mode 100644 index 000000000..5831b7c82 --- /dev/null +++ b/src/com/android/camera/burst/RingBuffer.java @@ -0,0 +1,103 @@ +/* + * 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.burst; + +import android.support.v4.util.LongSparseArray; + +import com.android.camera.async.SafeCloseable; +import com.android.camera.burst.EvictionHandler; +import com.android.camera.one.v2.camera2proxy.ImageProxy; + +import java.util.ArrayList; +import java.util.List; + +/** + * A RingBuffer that is used during burst capture. It takes a + * {@link EvictionHandler} instance and uses it to evict frames when the ring + * buffer runs out of capacity. + */ +class RingBuffer implements SafeCloseable { + private final int mMaxCapacity; + private final EvictionHandler mEvictionHandler; + private final LongSparseArray<ImageProxy> mImages = new LongSparseArray<>(); + + /** + * Create a new ring buffer instance. + * + * @param maxCapacity the maximum number of images in the ring buffer. + * @param evictionHandler + */ + public RingBuffer(int maxCapacity, EvictionHandler evictionHandler) { + mMaxCapacity = maxCapacity; + mEvictionHandler = evictionHandler; + } + + /** + * Insert an image in the ring buffer, evicting any frames if necessary. + * + * @param image the image to be inserted. + */ + public synchronized void insertImage(ImageProxy image) { + long timestamp = image.getTimestamp(); + if (mImages.get(timestamp) != null) { + image.close(); + return; + } + // Add image to ring buffer so it can be closed in case eviction + // handler throws. + addImage(image); + mEvictionHandler.onFrameInserted(timestamp); + if (mImages.size() > mMaxCapacity) { + long selectFrameToDrop = mEvictionHandler.selectFrameToDrop(); + removeAndCloseImage(selectFrameToDrop); + mEvictionHandler.onFrameDropped(selectFrameToDrop); + } + } + + /** + * Returns all images present in the ring buffer. + */ + public synchronized List<ImageProxy> getAndRemoveAllImages() { + List<ImageProxy> allImages = new ArrayList<ImageProxy>(mImages.size()); + for (int i = 0; i < mImages.size(); i++) { + allImages.add(mImages.valueAt(i)); + } + mImages.clear(); + return allImages; + } + + /** + * Closes the ring buffer and any images in the ring buffer. + */ + @Override + public synchronized void close() { + for (int i = 0; i < mImages.size(); i++) { + mImages.valueAt(i).close(); + } + mImages.clear(); + } + + private synchronized void removeAndCloseImage(long timestampNs) { + ImageProxy imageProxy = mImages.get(timestampNs); + imageProxy.close(); + mImages.remove(timestampNs); + } + + private synchronized void addImage(ImageProxy image) { + mImages.put(image.getTimestamp(), image); + } +} diff --git a/src/com/android/camera/burst/SurfaceTextureContainer.java b/src/com/android/camera/burst/SurfaceTextureContainer.java new file mode 100644 index 000000000..3fdd12392 --- /dev/null +++ b/src/com/android/camera/burst/SurfaceTextureContainer.java @@ -0,0 +1,49 @@ +/* + * 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.burst; + +import android.graphics.SurfaceTexture; +import android.view.Surface; + +import com.android.camera.async.SafeCloseable; + +/** + * A container that stores the {@link SurfaceTexture} and the stored + * {@link Surface} instance together. + */ +public class SurfaceTextureContainer implements SafeCloseable { + private final SurfaceTexture mSurfaceTexture; + private final Surface mSurface; + + public SurfaceTextureContainer(SurfaceTexture surfaceTexture) { + mSurfaceTexture = surfaceTexture; + mSurface = new Surface(mSurfaceTexture); + } + + public SurfaceTexture getSurfaceTexture() { + return mSurfaceTexture; + } + + public Surface getSurface() { + return mSurface; + } + + @Override + public void close() { + mSurfaceTexture.release(); + } +} diff --git a/src/com/android/camera/burst/ToastingBurstFacadeDecorator.java b/src/com/android/camera/burst/ToastingBurstFacadeDecorator.java index ecebde499..86043ab7c 100644 --- a/src/com/android/camera/burst/ToastingBurstFacadeDecorator.java +++ b/src/com/android/camera/burst/ToastingBurstFacadeDecorator.java @@ -18,15 +18,15 @@ package com.android.camera.burst; import android.content.Context; import android.graphics.SurfaceTexture; +import android.view.Surface; import android.widget.Toast; -import com.android.camera.one.OneCamera; +import com.android.camera.app.OrientationManager.DeviceOrientation; +import com.android.camera.one.OneCamera.Facing; import com.android.camera.session.CaptureSession; -import java.io.File; - /** - * A simle decorator for a {@link BurstFacade} that shows toasts for when a + * A simple decorator for a {@link BurstFacade} that shows toasts for when a * burst starts or stops. * <p> * This class can simply be removed once we have proper UI for this. @@ -70,19 +70,11 @@ public class ToastingBurstFacadeDecorator implements BurstFacade { } @Override - public void onCameraAttached(OneCamera camera) { - mBurstFacade.onCameraAttached(camera); - } - - @Override - public void onCameraDetached() { - mBurstFacade.onCameraDetached(); - } - - @Override - public void startBurst(CaptureSession captureSession, File tempSessionDirectory) { + public void startBurst(CaptureSession captureSession, + DeviceOrientation deviceOrientation, Facing cameraFacing, int imageOrientationDegrees) { mToaster.showToastBurstStarted(); - mBurstFacade.startBurst(captureSession, tempSessionDirectory); + mBurstFacade.startBurst(captureSession, + deviceOrientation, cameraFacing, imageOrientationDegrees); } @Override @@ -102,43 +94,22 @@ public class ToastingBurstFacadeDecorator implements BurstFacade { } @Override - public void setSurfaceTexture(SurfaceTexture surfaceTexture, int width, int height) { - mBurstFacade.setSurfaceTexture(surfaceTexture, width, height); - } - - @Override - public void initializeSurfaceTextureConsumer(int surfaceWidth, int surfaceHeight) { - mBurstFacade.initializeSurfaceTextureConsumer(surfaceWidth, surfaceHeight); - } - - @Override - public void initializeSurfaceTextureConsumer(SurfaceTexture surfaceTexture, int surfaceWidth, - int surfaceHeight) { - mBurstFacade.initializeSurfaceTextureConsumer(surfaceTexture, surfaceWidth, surfaceHeight); - } - - @Override - public void updatePreviewBufferSize(int width, int height) { - mBurstFacade.updatePreviewBufferSize(width, height); - } - - @Override - public void initializeAndStartFrameDistributor() { - mBurstFacade.initializeAndStartFrameDistributor(); + public void initialize(SurfaceTexture surfaceTexture) { + mBurstFacade.initialize(surfaceTexture); } @Override - public void closeFrameDistributor() { - mBurstFacade.closeFrameDistributor(); + public void release() { + mBurstFacade.release(); } @Override - public SurfaceTexture getInputSurfaceTexture() { - return mBurstFacade.getInputSurfaceTexture(); + public Surface getInputSurface() { + return mBurstFacade.getInputSurface(); } @Override - public void setPreviewConsumerSize(int width, int height) { - mBurstFacade.setPreviewConsumerSize(width, height); + public void setBurstTaker(BurstTaker burstTaker) { + mBurstFacade.setBurstTaker(burstTaker); } } diff --git a/src/com/android/camera/gl/CopyShader.java b/src/com/android/camera/gl/CopyShader.java deleted file mode 100644 index cff3d3cd3..000000000 --- a/src/com/android/camera/gl/CopyShader.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2014 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.gl; - -import android.opengl.GLES11Ext; -import android.opengl.GLES20; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -/** - * Allows copying a GL texture to a {@link RenderTarget}. - */ -// TODO: Document this class a bit more and add a test for the class. -public class CopyShader { - - private static final String VERTEX_SHADER = - "attribute vec4 a_position;\n" + - "attribute vec2 a_texcoord;\n" + - "varying vec2 v_texcoord;\n" + - "void main() {\n" + - " gl_Position = a_position;\n" + - " v_texcoord = a_texcoord;\n" + - "}\n"; - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" + - "}\n"; - - private static final String FRAGMENT_SHADER_EXTERNAL = - "#extension GL_OES_EGL_image_external : require\n" + - "precision mediump float;\n" + - "uniform samplerExternalOES tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" + - "}\n"; - - private static final float[] SOURCE_COORDS = new float[] { - 0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f }; - private static final float[] TARGET_COORDS = new float[] { - -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f }; - - private final int mProgram; - private final int mTextureTarget; - - private final FloatBuffer mSourceCoords; - private final FloatBuffer mTargetCoords; - - private final int mSourceAttrib; - private final int mTargetAttrib; - - private final int mTextureUniform; - - private CopyShader(int program, int textureTarget) { - mProgram = program; - mTextureTarget = textureTarget; - mSourceCoords = readFloats(SOURCE_COORDS); - mTargetCoords = readFloats(TARGET_COORDS); - mSourceAttrib = GLES20.glGetAttribLocation(mProgram, "a_texcoord"); - mTargetAttrib = GLES20.glGetAttribLocation(mProgram, "a_position"); - mTextureUniform = GLES20.glGetUniformLocation(mProgram, "tex_sampler"); - } - - /** - * Compiles a new shader that is valid in the current context. - * - * @return a new shader instance that is valid in the current context - */ - public static CopyShader compileNewShader() { - return new CopyShader(createProgram(VERTEX_SHADER, FRAGMENT_SHADER), GLES20.GL_TEXTURE); - } - - /** - * Compiles a new shader that binds textures as GL_TEXTURE_EXTERNAL_OES. - * - * @return a new shader instance that is valid in the current context - */ - public static CopyShader compileNewExternalShader() { - return new CopyShader(createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXTERNAL), - GLES11Ext.GL_TEXTURE_EXTERNAL_OES); - } - - /** - * Sets the 4x4 transform matrix to apply when copying the texture. - * <p/> - * Note: Non-affine components of the transformation are ignored. - * - * @param matrix a 16-length float array containing the transform matrix in - * column-major order. - */ - public void setTransform(float[] matrix) { - /** - * Multiply transformation matrix by column vectors (s,t, 0, 1) for - * coordinates {(0,0),(1,0), (0,1), (1,1) } and store (s,t) values of - * the resulting matrix in column-major order. - */ - float[] coords = new float[] { - matrix[12], matrix[13], - matrix[0] + matrix[12], - matrix[1] + matrix[13], - matrix[4] + matrix[12], - matrix[5] + matrix[13], - matrix[0] + matrix[4] + matrix[12], - matrix[1] + matrix[5] + matrix[13] - }; - mSourceCoords.put(coords).position(0); - } - - /** - * Renders the specified texture to the specified target. - * - * @param texName name of a valid texture - * @param target to render into - * @param width of output - * @param height of output - */ - public void renderTextureToTarget(int texName, RenderTarget target, int width, int height) { - useProgram(); - focusTarget(target, width, height); - assignAttribute(mSourceAttrib, mSourceCoords); - assignAttribute(mTargetAttrib, mTargetCoords); - bindTexture(mTextureUniform, texName, mTextureTarget); - render(); - } - - /** - * Releases the current shader. - * <p> - * This must be called in the shader's GL thread. - */ - public void release() { - GLES20.glDeleteProgram(mProgram); - } - - private void focusTarget(RenderTarget target, int width, int height) { - target.focus(); - GLES20.glViewport(0, 0, width, height); - GLToolbox.checkGlError("focus"); - } - - private void useProgram() { - GLES20.glUseProgram(mProgram); - GLToolbox.checkGlError("glUseProgram"); - } - - private void render() { - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLToolbox.checkGlError("glDrawArrays"); - } - - private void bindTexture(int uniformName, int texName, int texTarget) { - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(texTarget, texName); - GLES20.glUniform1i(uniformName, 0); - GLToolbox.checkGlError( - "bindTexture(" + uniformName + "," + texName + "," + texTarget + ")"); - } - - private void assignAttribute(int index, FloatBuffer values) { - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); - GLES20.glVertexAttribPointer(index, 2, GLES20.GL_FLOAT, false, 8, values); - GLES20.glEnableVertexAttribArray(index); - GLToolbox.checkGlError("glVertexAttribPointer(" + index + ")"); - } - - private static FloatBuffer readFloats(float[] values) { - FloatBuffer result = ByteBuffer.allocateDirect(values.length * 4) - .order(ByteOrder.nativeOrder()).asFloatBuffer(); - result.put(values).position(0); - return result; - } - - private static int loadShader(int shaderType, String source) { - int shader = GLES20.glCreateShader(shaderType); - if (shader != 0) { - GLES20.glShaderSource(shader, source); - GLES20.glCompileShader(shader); - int[] compiled = new int[1]; - GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); - if (compiled[0] == 0) { - String info = GLES20.glGetShaderInfoLog(shader); - GLES20.glDeleteShader(shader); - shader = 0; - throw new RuntimeException("Could not compile shader " + shaderType + ":" + info); - } - } - return shader; - } - - private static int createProgram(String vertexSource, String fragmentSource) { - int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); - if (vertexShader == 0) { - throw new RuntimeException("Could not create shader-program as vertex shader " - + "could not be compiled!"); - } - int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); - if (pixelShader == 0) { - throw new RuntimeException("Could not create shader-program as fragment shader " - + "could not be compiled!"); - } - - int program = GLES20.glCreateProgram(); - if (program != 0) { - GLES20.glAttachShader(program, vertexShader); - GLToolbox.checkGlError("glAttachShader"); - GLES20.glAttachShader(program, pixelShader); - GLToolbox.checkGlError("glAttachShader"); - GLES20.glLinkProgram(program); - int[] linkStatus = new int[1]; - GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); - if (linkStatus[0] != GLES20.GL_TRUE) { - String info = GLES20.glGetProgramInfoLog(program); - GLES20.glDeleteProgram(program); - program = 0; - throw new RuntimeException("Could not link program: " + info); - } - } - - GLES20.glDeleteShader(vertexShader); - GLES20.glDeleteShader(pixelShader); - - return program; - } -} diff --git a/src/com/android/camera/gl/FrameDistributor.java b/src/com/android/camera/gl/FrameDistributor.java deleted file mode 100644 index 1ecb5bf28..000000000 --- a/src/com/android/camera/gl/FrameDistributor.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2014 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.gl; - -import android.graphics.SurfaceTexture; - -/** - * Distributes frames from a {@link SurfaceTexture} to multiple consumers. - * <p> - * Frames are distributed as OpenGL textures, that can be used for further - * processing. This is a flexible approach that allows processing both using GL - * methods (e.g. shaders) and CPU methods by sending frames through an - * {@link ImageReader}. - * <p> - * Consumers receive {@link FrameConsumer#onNewFrameAvailable(FrameDistributor)} - * callbacks for new frames. Each consumer can grab the most current frame from - * its GL thread by calling {@link #acquireNextFrame(int, float[])}. After - * accessing the data, the consumer needs to call {@link #releaseFrame()}. - */ -public interface FrameDistributor { - /** - * Consumes frames streamed from a distributor. - */ - public interface FrameConsumer { - /** - * Called when frame processing is about to start. - * <p> - * You can use this to do any setup required to process frames. Note - * that this is called on the distributor's thread. - */ - public void onStart(); - - /** - * Called when a new frame is available for processing. - * <p> - * Note that as this is called on the frameProducer's thread, you should - * typically not call {@code acquireNextFrame()} from this callback. - * Instead, call it from your GL thread, after receiving this callback. - * When you are done processing the frame, you must call - * {@code releaseFrame()}. - * - * @param frameDistributor that issued the callback - * @param timestampNs timestamp in nanoseconds of the available frame. - */ - public void onNewFrameAvailable(FrameDistributor frameDistributor, long timestampNs); - - /** - * Called when frame processing is about to stop. - * <p> - * You can use this to release any resources that your consumer uses. - * You must reinstate resources when {@link #onStart()} is called again. - */ - public void onStop(); - } - - /** - * Acquire the next available frame. - * <p> - * Call this after having received a - * {@link FrameConsumer#onNewFrameAvailable(FrameDistributor)} callback. You - * must call this from the thread in which your texture name is current and - * valid. The texture will be filled with the frame data and must be bound - * using GL_TEXTURE_EXTERNAL_OES. It must be a valid texture name created - * with {@code glGenTextures()} . You must call {@link #releaseFrame()} as - * soon as you are done processing this frame. All other consumers are - * blocked from processing frames until you have released it, so any - * processing should be done as efficiently as possible. - * - * @param textureName to fill with image data. Must be a valid texture name. - * @param transform of the frame that needs to be applied for an upright - * image. - * @return the timestamp in nanoseconds of the frame. - */ - public long acquireNextFrame(int textureName, float[] transform); - - /** - * Release the currently acquired frame. - */ - public void releaseFrame(); - - /** - * Get the {@link RenderTarget} that the producer uses for GL operations. - * <p> - * You should rarely need to use this method. It is used exclusively by - * consumers that reuse the FrameProducer's EGL context, and must be handled - * with great care. - * - * @return the RenderTarget used by the FrameProducer. - */ - public RenderTarget getRenderTarget(); -} diff --git a/src/com/android/camera/gl/FrameDistributorImpl.java b/src/com/android/camera/gl/FrameDistributorImpl.java deleted file mode 100644 index 54998aca0..000000000 --- a/src/com/android/camera/gl/FrameDistributorImpl.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2014 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.gl; - -import android.graphics.SurfaceTexture; -import android.graphics.SurfaceTexture.OnFrameAvailableListener; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.util.Size; - -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Implementation of {@link FrameDistributor}. - * <p> - * The typical way to use this is as follows: - * <ol> - * <li>Create a new instance and add all the {@link FrameConsumer} instances. - * </li> - * <li>Call {@link #start()} on the distributor.</li> - * <li>Obtain the {@link SurfaceTexture} used by the distributor and hook it up - * to your producer e.g. a Camera instance.</li> - * <li>Your consumers now start receiving - * {@link FrameConsumer#onNewFrameAvailable(FrameDistributor)} callbacks for new - * frames.</li> - * <li>Each consumer grabs the most current frame from its GL thread by calling - * {@link #acquireNextFrame(int, float[])}.</li> - * <li>After accessing the data, the consumer calls {@link #releaseFrame()}. - * </li> - * <li>When all processing is complete, call {@link #stop()}.</li> - * </ol> - */ -class FrameDistributorImpl implements FrameDistributor, AutoCloseable { - - private final DistributionHandler mDistributionHandler; - - private final HandlerThread mDistributionThread; - - private static class DistributionHandler extends Handler implements OnFrameAvailableListener { - - public static final int MSG_SETUP = 1; - public static final int MSG_RELEASE = 2; - public static final int MSG_UPDATE_SURFACE = 3; - public static final int MSG_UPDATE_PREVIEW_BUFFER_SIZE = 4; - - private static final int DEFAULT_SURFACE_BUFFER_WIDTH = 1440; - private static final int DEFAULT_SURFACE_BUFFER_HEIGHT = 1080; - - private final FrameDistributorImpl mDistributor; - - private final AtomicBoolean mNewFrame = new AtomicBoolean(false); - - final ConditionVariable mCommandDoneCondition = new ConditionVariable(true); - - private final Lock mSurfaceTextureAccessLock = new ReentrantLock(); - - private final List<FrameConsumer> mConsumers; - - private SurfaceTexture mSurfaceTexture; - - private long mTimestamp; - - private int mTexture; - - private RenderTarget mServerTarget; - - private boolean mIsSetup = false; - - public DistributionHandler(FrameDistributorImpl distributor, Looper looper, - List<FrameConsumer> consumers) { - super(looper); - mDistributor = distributor; - mConsumers = consumers; - } - - @Override - public void handleMessage(Message message) { - try { - switch (message.what) { - case MSG_SETUP: - setup(); - break; - case MSG_UPDATE_SURFACE: - updateSurfaceTexture(); - break; - case MSG_RELEASE: - release(); - break; - case MSG_UPDATE_PREVIEW_BUFFER_SIZE: - updatePreviewBufferSize((Size) message.obj); - break; - default: - throw new IllegalStateException("Unknown message: " + message + "!"); - } - } finally { - mCommandDoneCondition.open(); - } - } - - private synchronized void setup() { - if (!mIsSetup) { - mServerTarget = RenderTarget.newTarget(1, 1); - mServerTarget.focus(); - mSurfaceTexture = createSurfaceTexture(); - informListenersOfStart(); - mIsSetup = true; - } - } - - private synchronized void release() { - if (mIsSetup) { - // Notify listeners - informListenersOfStop(); - - // Release our resources - mServerTarget.close(); - mServerTarget = null; - mSurfaceTexture.release(); - mSurfaceTexture = null; - GLToolbox.deleteTexture(mTexture); - - // It is VERY important we unfocus the current EGL context, as - // the SurfaceTextures - // will not properly detach if this is not done. - RenderTarget.focusNone(); - - // Update internal state - mNewFrame.set(false); - mIsSetup = false; - } - } - - private void updateSurfaceTexture() { - if (mIsSetup) { - mSurfaceTextureAccessLock.lock(); - mSurfaceTexture.attachToGLContext(mTexture); - mSurfaceTexture.updateTexImage(); - mSurfaceTexture.detachFromGLContext(); - mSurfaceTextureAccessLock.unlock(); - mTimestamp = mSurfaceTexture.getTimestamp(); - informListenersOfNewFrame(mTimestamp); - } - } - - private void updatePreviewBufferSize(Size size) { - if (size != null && mIsSetup) { - mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight()); - } - } - - public void postMessageType(int kind) { - mCommandDoneCondition.close(); - sendMessage(Message.obtain(this, kind)); - } - - public void postMessageType(int kind, Object params) { - mCommandDoneCondition.close(); - sendMessage(Message.obtain(this, kind, params)); - } - - private void informListenersOfStart() { - synchronized (mConsumers) { - for (FrameConsumer consumer : mConsumers) { - consumer.onStart(); - } - } - } - - private void informListenersOfNewFrame(long timestamp) { - synchronized (mConsumers) { - for (FrameConsumer consumer : mConsumers) { - consumer.onNewFrameAvailable(mDistributor, timestamp); - } - } - } - - private void informListenersOfStop() { - synchronized (mConsumers) { - for (FrameConsumer consumer : mConsumers) { - consumer.onStop(); - } - } - } - - public long acquireNextFrame(int textureName, float[] transform) { - if (transform == null || transform.length != 16) { - throw new - IllegalArgumentException("acquireNextFrame: invalid transform array."); - } - mSurfaceTextureAccessLock.lock(); - mSurfaceTexture.attachToGLContext(textureName); - mSurfaceTexture.getTransformMatrix(transform); - - return mTimestamp; - } - - public void releaseFrame() { - mSurfaceTexture.detachFromGLContext(); - mSurfaceTextureAccessLock.unlock(); - } - - public synchronized SurfaceTexture getSurfaceTexture() { - return mSurfaceTexture; - } - - public synchronized RenderTarget getRenderTarget() { - return mServerTarget; - } - - @Override - public void onFrameAvailable(SurfaceTexture surfaceTexture) { - postMessageType(MSG_UPDATE_SURFACE); - } - - private SurfaceTexture createSurfaceTexture() { - mTexture = GLToolbox.generateTexture(); - SurfaceTexture surfaceTexture = new SurfaceTexture(mTexture); - surfaceTexture.setDefaultBufferSize(DEFAULT_SURFACE_BUFFER_WIDTH, - DEFAULT_SURFACE_BUFFER_HEIGHT); - surfaceTexture.setOnFrameAvailableListener(this); - surfaceTexture.detachFromGLContext(); - return surfaceTexture; - } - - } - - /** - * Creates a new distributor with the specified consumers. - * - * @param consumers list of consumers that will process incoming frames. - */ - public FrameDistributorImpl(List<FrameConsumer> consumers) { - mDistributionThread = new HandlerThread("FrameDistributor"); - mDistributionThread.start(); - mDistributionHandler = new DistributionHandler(this, mDistributionThread.getLooper(), - consumers); - } - - /** - * Start processing frames and sending them to consumers. - */ - public void start() { - mDistributionHandler.postMessageType(DistributionHandler.MSG_SETUP); - } - - /** - * Stop processing frames and release any resources required for doing so. - */ - public void stop() { - mDistributionHandler.postMessageType(DistributionHandler.MSG_RELEASE); - } - - /** - * Wait until the current start/stop command has finished executing. - * <p> - * Use this command if you need to make sure that the distributor has fully - * started or stopped. - */ - public void waitForCommand() { - mDistributionHandler.mCommandDoneCondition.block(); - } - - /** - * Close the current distributor and release its resources. - * <p> - * You must not use the distributor after calling this method. - */ - @Override - public void close() { - stop(); - mDistributionThread.quitSafely(); - } - - @Override - public long acquireNextFrame(int textureName, float[] transform) { - return mDistributionHandler.acquireNextFrame(textureName, transform); - } - - @Override - public void releaseFrame() { - mDistributionHandler.releaseFrame(); - } - - /** - * Get the {@link SurfaceTexture} whose frames will be distributed. - * <p> - * You must call this after distribution has started with a call to - * {@link #start()}. - * - * @return the input SurfaceTexture or null, if none is yet available. - */ - public SurfaceTexture getInputSurfaceTexture() { - return mDistributionHandler.getSurfaceTexture(); - } - - /** - * Get the {@link RenderTarget} that the distributor uses for GL operations. - * <p> - * You should rarely need to use this method. It is used exclusively by - * consumers that reuse the FrameDistributor's EGL context, and must be - * handled with great care. - * - * @return the RenderTarget used by the FrameDistributor. - */ - @Override - public RenderTarget getRenderTarget() { - return mDistributionHandler.getRenderTarget(); - } - - /** - * Update the default buffer size of the input {@link SurfaceTexture}. - * - * @param width the new value of width of the preview buffer. - * @param height the new value of height of the preview buffer. - */ - public void updatePreviewBufferSize(int width, int height) { - mDistributionHandler.postMessageType(DistributionHandler.MSG_UPDATE_PREVIEW_BUFFER_SIZE, - new Size(width, height)); - } -} diff --git a/src/com/android/camera/gl/FrameDistributorWrapper.java b/src/com/android/camera/gl/FrameDistributorWrapper.java deleted file mode 100644 index 687d16a48..000000000 --- a/src/com/android/camera/gl/FrameDistributorWrapper.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2014 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.gl; - -import android.graphics.SurfaceTexture; -import android.os.Looper; - -import com.android.camera.gl.FrameDistributor.FrameConsumer; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -/** - * A wrapper class for {@link FrameDistributorImpl} that provides thread safe - * access to the frame distributor. - */ -public class FrameDistributorWrapper implements AutoCloseable { - private final AtomicReference<FrameDistributorImpl> mFrameDistributor = - new AtomicReference<FrameDistributorImpl>(); - - /** - * Start processing frames and sending them to consumers. - * <p/> - * Can only be called from the main thread. - * - * @param consumers list of consumers that will process incoming frames. - */ - public void start(List<FrameConsumer> consumers) { - assertOnMainThread(); - if (mFrameDistributor.get() != null) { - throw new IllegalStateException("FrameDistributorWrapper: start called before close."); - } else { - FrameDistributorImpl distributor = new FrameDistributorImpl(consumers); - mFrameDistributor.set(distributor); - distributor.start(); - distributor.waitForCommand(); - } - } - - /** - * Get the {@link SurfaceTexture} whose frames will be distributed. - * <p> - * You must call this after distribution has started with a call to - * {@link #start(List)}. - * - * @return the input SurfaceTexture or null, if none is yet available. - */ - public SurfaceTexture getInputSurfaceTexture() { - FrameDistributorImpl distributor = mFrameDistributor.get(); - return (distributor != null) ? distributor.getInputSurfaceTexture() : null; - } - - /** - * Update the default buffer size of the input {@link SurfaceTexture}. - * - * @param width the new value of width of the preview buffer. - * @param height the new value of height of the preview buffer. - */ - public void updatePreviewBufferSize(int width, int height) { - FrameDistributorImpl distributor = mFrameDistributor.get(); - if (distributor != null) { - distributor.updatePreviewBufferSize(width, height); - } - } - - /** - * Close the current distributor and release its resources. - * <p> - * Can only be called from the main thread. - */ - @Override - public void close() { - assertOnMainThread(); - if (mFrameDistributor.get() != null) { - mFrameDistributor.get().close(); - mFrameDistributor.set(null); - } else { - throw new IllegalStateException("FrameDistributorWrapper: close called before start."); - } - } - - private static void assertOnMainThread() { - if (Looper.getMainLooper().getThread() != Thread.currentThread()) { - throw new IllegalStateException("Must be called on the main thread."); - } - } -} diff --git a/src/com/android/camera/gl/GLToolbox.java b/src/com/android/camera/gl/GLToolbox.java deleted file mode 100644 index 581f811ea..000000000 --- a/src/com/android/camera/gl/GLToolbox.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2014 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.gl; - -import android.opengl.GLES20; -import android.os.Looper; - -/** - * Collection of utility functions used for GL operations. - */ -public class GLToolbox { - - public static int textureNone() { - return 0; - } - - public static boolean isTexture(int texId) { - return GLES20.glIsTexture(texId); - } - - public static void deleteTexture(int texId) { - int[] textures = new int[] { - texId }; - assertNonUiThread("glDeleteTextures"); - GLES20.glDeleteTextures(1, textures, 0); - checkGlError("glDeleteTextures"); - } - - public static void deleteFbo(int fboId) { - int[] fbos = new int[] { - fboId }; - assertNonUiThread("glDeleteFramebuffers"); - GLES20.glDeleteFramebuffers(1, fbos, 0); - checkGlError("glDeleteFramebuffers"); - } - - public static int generateTexture() { - int[] textures = new int[1]; - GLES20.glGenTextures(1, textures, 0); - checkGlError("glGenTextures"); - return textures[0]; - } - - public static int generateFbo() { - int[] fbos = new int[1]; - GLES20.glGenFramebuffers(1, fbos, 0); - checkGlError("glGenFramebuffers"); - return fbos[0]; - } - - public static int attachedTexture(int fboId) { - int[] params = new int[1]; - GLES20.glGetFramebufferAttachmentParameteriv( - GLES20.GL_FRAMEBUFFER, - GLES20.GL_COLOR_ATTACHMENT0, - GLES20.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - params, 0); - checkGlError("glGetFramebufferAttachmentParameteriv"); - return params[0]; - } - - public static void attachTextureToFbo(int texId, int fboId) { - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId); - GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, - GLES20.GL_COLOR_ATTACHMENT0, - GLES20.GL_TEXTURE_2D, - texId, - 0); - checkGlError("glFramebufferTexture2D"); - } - - public static void setDefaultTexParams() { - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, - GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - checkGlError("glTexParameteri"); - } - - public static void checkGlError(String operation) { - int error; - while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { - throw new RuntimeException("GL Operation '" + operation + "' caused error " - + Integer.toHexString(error) + "!"); - } - } - - /** - * Make sure we are not operating in the UI thread. It is often tricky to - * track down bugs that happen when issuing GL commands in the UI thread. - * This is especially true when releasing GL resources. Often this will - * cause errors much later on. Therefore we make sure we do not do these - * dangerous operations on the UI thread. - */ - private static void assertNonUiThread(String operation) { - if (Looper.getMainLooper().getThread() == Thread.currentThread()) { - throw new RuntimeException("Attempting to perform GL operation '" + operation - + "' on UI thread!"); - } - } -} diff --git a/src/com/android/camera/gl/RenderTarget.java b/src/com/android/camera/gl/RenderTarget.java deleted file mode 100644 index d126fa1df..000000000 --- a/src/com/android/camera/gl/RenderTarget.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (C) 2014 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.gl; - -import android.graphics.SurfaceTexture; -import android.opengl.GLES20; -import android.view.Surface; -import android.view.SurfaceHolder; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; - -/** - * Encapsulates a target into which a GL operation can draw into. - * <p> - * A RenderTarget can take on many forms, such as an offscreen buffer, an FBO - * attached to a texture, or a SurfaceTexture target. Regardless of output type, - * once a RenderTarget is focused, any issued OpenGL draw commands will be - * rasterized into that target. - * <p> - * Note, that this class is a simplified version of the MFF's - * {@code RenderTarget} class. - */ -// TODO: Add a test for the class. -public class RenderTarget implements AutoCloseable { - - private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - private static final int EGL_OPENGL_ES2_BIT = 4; - - /** The cached EGLConfig instance. */ - private static EGLConfig mEglConfig = null; - - /** The display for which the EGLConfig was chosen. We expect only one. */ - private static EGLDisplay mConfiguredDisplay; - - private final EGL10 mEgl; - private final EGLDisplay mDisplay; - private final EGLContext mContext; - private final EGLSurface mSurface; - private final int mFbo; - - private final boolean mOwnsContext; - private final boolean mOwnsSurface; - - private static int sRedSize = 8; - private static int sGreenSize = 8; - private static int sBlueSize = 8; - private static int sAlphaSize = 8; - private static int sDepthSize = 0; - private static int sStencilSize = 0; - - public static RenderTarget newTarget(int width, int height) { - EGL10 egl = (EGL10) EGLContext.getEGL(); - EGLDisplay eglDisplay = createDefaultDisplay(egl); - EGLConfig eglConfig = chooseEglConfig(egl, eglDisplay); - EGLContext eglContext = createContext(egl, eglDisplay, eglConfig); - EGLSurface eglSurface = createSurface(egl, eglDisplay, width, height); - RenderTarget result = new RenderTarget(eglDisplay, eglContext, eglSurface, 0, true, true); - return result; - } - - public RenderTarget forTexture(int texName, int texTarget, int width, int height) { - // NOTE: We do not need to lookup any previous bindings of this texture - // to an FBO, as - // multiple FBOs to a single texture is valid. - int fbo = GLToolbox.generateFbo(); - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo); - GLToolbox.checkGlError("glBindFramebuffer"); - GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, - GLES20.GL_COLOR_ATTACHMENT0, - texName, - texTarget, - 0); - GLToolbox.checkGlError("glFramebufferTexture2D"); - return new RenderTarget(mDisplay, mContext, surface(), fbo, false, false); - } - - public RenderTarget forSurfaceHolder(SurfaceHolder surfaceHolder) { - EGLConfig eglConfig = chooseEglConfig(mEgl, mDisplay); - EGLSurface eglSurf = mEgl.eglCreateWindowSurface(mDisplay, eglConfig, surfaceHolder, null); - checkEglError(mEgl, "eglCreateWindowSurface"); - checkSurface(mEgl, eglSurf); - RenderTarget result = new RenderTarget(mDisplay, mContext, eglSurf, 0, false, true); - return result; - } - - public RenderTarget forSurfaceTexture(SurfaceTexture surfaceTexture) { - EGLConfig eglConfig = chooseEglConfig(mEgl, mDisplay); - EGLSurface eglSurf = mEgl.eglCreateWindowSurface(mDisplay, eglConfig, surfaceTexture, null); - checkEglError(mEgl, "eglCreateWindowSurface"); - checkSurface(mEgl, eglSurf); - RenderTarget result = new RenderTarget(mDisplay, mContext, eglSurf, 0, false, true); - return result; - } - - public RenderTarget forSurface(Surface surface) { - EGLConfig eglConfig = chooseEglConfig(mEgl, mDisplay); - EGLSurface eglSurf = mEgl.eglCreateWindowSurface(mDisplay, eglConfig, surface, null); - checkEglError(mEgl, "eglCreateWindowSurface"); - checkSurface(mEgl, eglSurf); - RenderTarget result = new RenderTarget(mDisplay, mContext, eglSurf, 0, false, true); - return result; - } - - public static void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, - int depthSize, int stencilSize) { - sRedSize = redSize; - sGreenSize = greenSize; - sBlueSize = blueSize; - sAlphaSize = alphaSize; - sDepthSize = depthSize; - sStencilSize = stencilSize; - } - - public void focus() { - mEgl.eglMakeCurrent(mDisplay, surface(), surface(), mContext); - if (getCurrentFbo() != mFbo) { - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFbo); - GLToolbox.checkGlError("glBindFramebuffer"); - } - } - - public static void focusNone() { - EGL10 egl = (EGL10) EGLContext.getEGL(); - egl.eglMakeCurrent(egl.eglGetCurrentDisplay(), - EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_CONTEXT); - checkEglError(egl, "eglMakeCurrent"); - } - - public void swapBuffers() { - mEgl.eglSwapBuffers(mDisplay, surface()); - } - - public EGLContext getContext() { - return mContext; - } - - @Override - public void close() { - if (mOwnsContext) { - mEgl.eglDestroyContext(mDisplay, mContext); - } - if (mOwnsSurface) { - mEgl.eglDestroySurface(mDisplay, mSurface); - } - if (mFbo != 0) { - GLToolbox.deleteFbo(mFbo); - } - } - - @Override - public String toString() { - return "RenderTarget(" + mDisplay + ", " + mContext + ", " + mSurface + ", " + mFbo + ")"; - } - - private static EGLConfig chooseEglConfig(EGL10 egl, EGLDisplay display) { - if (mEglConfig == null || !display.equals(mConfiguredDisplay)) { - int[] configsCount = new int[1]; - EGLConfig[] configs = new EGLConfig[1]; - int[] configSpec = getDesiredConfig(); - if (!egl.eglChooseConfig(display, configSpec, configs, 1, configsCount)) { - throw new IllegalArgumentException("EGL Error: eglChooseConfig failed " + - getEGLErrorString(egl)); - } else if (configsCount[0] > 0) { - mEglConfig = configs[0]; - mConfiguredDisplay = display; - } - } - return mEglConfig; - } - - private static int[] getDesiredConfig() { - return new int[] { - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_RED_SIZE, sRedSize, - EGL10.EGL_GREEN_SIZE, sGreenSize, - EGL10.EGL_BLUE_SIZE, sBlueSize, - EGL10.EGL_ALPHA_SIZE, sAlphaSize, - EGL10.EGL_DEPTH_SIZE, sDepthSize, - EGL10.EGL_STENCIL_SIZE, sStencilSize, - EGL10.EGL_NONE - }; - } - - private RenderTarget(EGLDisplay display, EGLContext context, EGLSurface surface, int fbo, - boolean ownsContext, boolean ownsSurface) { - mEgl = (EGL10) EGLContext.getEGL(); - mDisplay = display; - mContext = context; - mSurface = surface; - mFbo = fbo; - mOwnsContext = ownsContext; - mOwnsSurface = ownsSurface; - } - - private EGLSurface surface() { - return mSurface; - } - - private static void initEgl(EGL10 egl, EGLDisplay display) { - int[] version = new int[2]; - if (!egl.eglInitialize(display, version)) { - throw new RuntimeException("EGL Error: eglInitialize failed " + getEGLErrorString(egl)); - } - } - - private static EGLDisplay createDefaultDisplay(EGL10 egl) { - EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - checkDisplay(egl, display); - initEgl(egl, display); - return display; - } - - private static EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { - int[] attrib_list = { - EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - EGLContext ctxt = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrib_list); - checkContext(egl, ctxt); - return ctxt; - } - - private static EGLSurface createSurface(EGL10 egl, EGLDisplay display, int width, int height) { - EGLConfig eglConfig = chooseEglConfig(egl, display); - int[] attribs = { - EGL10.EGL_WIDTH, width, EGL10.EGL_HEIGHT, height, EGL10.EGL_NONE }; - return egl.eglCreatePbufferSurface(display, eglConfig, attribs); - } - - private static int getCurrentFbo() { - int[] result = new int[1]; - GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0); - return result[0]; - } - - private static void checkDisplay(EGL10 egl, EGLDisplay display) { - if (display == EGL10.EGL_NO_DISPLAY) { - throw new RuntimeException("EGL Error: Bad display: " + getEGLErrorString(egl)); - } - } - - private static void checkContext(EGL10 egl, EGLContext context) { - if (context == EGL10.EGL_NO_CONTEXT) { - throw new RuntimeException("EGL Error: Bad context: " + getEGLErrorString(egl)); - } - } - - private static void checkSurface(EGL10 egl, EGLSurface surface) { - if (surface == EGL10.EGL_NO_SURFACE) { - throw new RuntimeException("EGL Error: Bad surface: " + getEGLErrorString(egl)); - } - } - - private static void checkEglError(EGL10 egl, String command) { - int error = egl.eglGetError(); - if (error != EGL10.EGL_SUCCESS) { - throw new RuntimeException("Error executing " + command + "! EGL error = 0x" - + Integer.toHexString(error)); - } - } - - private static String getEGLErrorString(EGL10 egl) { - int eglError = egl.eglGetError(); - return "EGL Error 0x" + Integer.toHexString(eglError); - } - -} diff --git a/src/com/android/camera/gl/SurfaceTextureConsumer.java b/src/com/android/camera/gl/SurfaceTextureConsumer.java deleted file mode 100644 index 096f5e066..000000000 --- a/src/com/android/camera/gl/SurfaceTextureConsumer.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2014 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.gl; - -import android.graphics.SurfaceTexture; - -import com.android.camera.debug.Log; -import com.android.camera.debug.Log.Tag; -import com.android.camera.gl.FrameDistributor; -import com.android.camera.gl.FrameDistributor.FrameConsumer; - -/** - * Consumes frames from a {@link FrameDistributor} and passes them into a - * SurfaceTexture. - */ -// TODO: Document this class a bit more and add a test for this class. -public class SurfaceTextureConsumer implements FrameConsumer { - private static final Tag TAG = new Tag("SurfaceTexConsumer"); - - private SurfaceTexture mSurfaceTexture; - private final float[] mTransform = new float[16]; - private CopyShader mCopyShader; - private RenderTarget mTarget; - private int mWidth; - private int mHeight; - - @Override - public synchronized void onStart() {} - - @Override - public synchronized void onNewFrameAvailable(FrameDistributor frameDistributor, - long timestampNs) { - if (mSurfaceTexture == null) { - Log.d(TAG, "Ignoring frame without a SurfaceTexture!"); - return; - } - if (mTarget == null) { - mTarget = frameDistributor.getRenderTarget().forSurfaceTexture(mSurfaceTexture); - } - renderFrameToTarget(frameDistributor); - } - - @Override - public synchronized void onStop() { - releaseResources(); - } - - public synchronized SurfaceTexture getSurfaceTexture() { - return mSurfaceTexture; - } - - public synchronized void setSurfaceTexture(SurfaceTexture surfaceTexture, - int width, - int height) { - mSurfaceTexture = surfaceTexture; - mWidth = width; - mHeight = height; - releaseResources(); - } - - public synchronized void setSize(int width, int height) { - mWidth = width; - mHeight = height; - } - - public synchronized void setDefaultBufferSize(int width, int height) { - if (mSurfaceTexture != null) { - mSurfaceTexture.setDefaultBufferSize(width, height); - } - } - - public synchronized int getWidth() { - return mWidth; - } - - public synchronized int getHeight() { - return mHeight; - } - - private void releaseResources() { - if (mTarget != null) { - mTarget.close(); - mTarget = null; - } - if (mCopyShader != null) { - mCopyShader.release(); - mCopyShader = null; - } - } - - private void renderFrameToTarget(FrameDistributor frameDistributor) { - CopyShader shader = getCopyShader(); - int texture = GLToolbox.generateTexture(); - frameDistributor.acquireNextFrame(texture, mTransform); - try { - shader.setTransform(mTransform); - shader.renderTextureToTarget(texture, mTarget, mWidth, mHeight); - } finally { - frameDistributor.releaseFrame(); - GLToolbox.deleteTexture(texture); - } - mTarget.swapBuffers(); - } - - private CopyShader getCopyShader() { - if (mCopyShader == null) { - mCopyShader = CopyShader.compileNewExternalShader(); - } - return mCopyShader; - } - -} diff --git a/src/com/android/camera/one/AbstractOneCamera.java b/src/com/android/camera/one/AbstractOneCamera.java index b9514b96f..b059d8034 100644 --- a/src/com/android/camera/one/AbstractOneCamera.java +++ b/src/com/android/camera/one/AbstractOneCamera.java @@ -122,14 +122,4 @@ public abstract class AbstractOneCamera implements OneCamera { public void setZoom(float zoom) { // If not implemented, no-op. } - - @Override - public void startBurst(BurstParameters params, CaptureSession session) { - throw new UnsupportedOperationException("Not implemented yet."); - } - - @Override - public void stopBurst() { - throw new UnsupportedOperationException("Not implemented yet."); - } } diff --git a/src/com/android/camera/one/OneCamera.java b/src/com/android/camera/one/OneCamera.java index 49fb2c0a9..aa6aab5f9 100644 --- a/src/com/android/camera/one/OneCamera.java +++ b/src/com/android/camera/one/OneCamera.java @@ -22,8 +22,6 @@ import android.location.Location; import android.net.Uri; import android.view.Surface; -import com.android.camera.burst.BurstConfiguration; -import com.android.camera.burst.ResultsAccessor; import com.android.camera.session.CaptureSession; import com.android.camera.settings.SettingsManager; import com.android.camera.util.Size; @@ -350,29 +348,6 @@ public interface OneCamera { } } - /** - * The callback to be invoked when results are available. - */ - public interface BurstResultsCallback { - void onBurstComplete(ResultsAccessor resultAccessor); - } - - /** - * Parameters to be given to burst requests. - */ - public static class BurstParameters extends CaptureParameters { - /** The title/filename (without suffix) for this capture. */ - public final BurstConfiguration burstConfiguration; - public final BurstResultsCallback callback; - - public BurstParameters(String title, int orientation, Location location, - File debugDataFolder, BurstConfiguration burstConfiguration, - BurstResultsCallback callback) { - super(title, orientation, location, debugDataFolder); - this.burstConfiguration = burstConfiguration; - this.callback = callback; - } - } /** * Meters and triggers auto focus scan with ROI around tap point. @@ -394,20 +369,6 @@ public interface OneCamera { public void takePicture(PhotoCaptureParameters params, CaptureSession session); /** - * Call this to take a burst. - * - * @param params parameters for taking burst. - * @param session the capture session for this burst. - */ - - public void startBurst(BurstParameters params, CaptureSession session); - - /** - * Call this to stop taking burst. - */ - public void stopBurst(); - - /** * Sets or replaces a listener that is called whenever the focus state of * the camera changes. */ @@ -427,7 +388,7 @@ public interface OneCamera { /** * Starts a preview stream and renders it to the given surface. - * + * * @param Surface the surface on which to render preview frames * @param listener */ diff --git a/src/com/android/camera/one/OneCameraManager.java b/src/com/android/camera/one/OneCameraManager.java index 405b581d5..36953b23e 100644 --- a/src/com/android/camera/one/OneCameraManager.java +++ b/src/com/android/camera/one/OneCameraManager.java @@ -21,6 +21,7 @@ import android.util.DisplayMetrics; import com.android.camera.CameraActivity; import com.android.camera.async.MainThread; +import com.android.camera.burst.BurstFacade; import com.android.camera.debug.Log.Tag; import com.android.camera.one.OneCamera.Facing; import com.android.camera.one.OneCamera.OpenCallback; @@ -53,10 +54,12 @@ public abstract class OneCameraManager { * @param mainThread Main thread executor * @param imageRotationCalculator Image rotation calculator required for * Camera Factory initialization + * @param burstController the burst facade to configure */ public abstract void open(Facing facing, boolean enableHdr, Size captureSize, OpenCallback callback, Handler handler, - MainThread mainThread, final ImageRotationCalculator imageRotationCalculator); + MainThread mainThread, final ImageRotationCalculator imageRotationCalculator, + BurstFacade burstController); // TODO: Move this to OneCameraCharacteristics class. /** diff --git a/src/com/android/camera/one/v1/OneCameraManagerImpl.java b/src/com/android/camera/one/v1/OneCameraManagerImpl.java index e2fbc59f9..ac6073267 100644 --- a/src/com/android/camera/one/v1/OneCameraManagerImpl.java +++ b/src/com/android/camera/one/v1/OneCameraManagerImpl.java @@ -21,6 +21,7 @@ import android.os.Handler; import com.android.camera.CameraActivity; import com.android.camera.async.MainThread; +import com.android.camera.burst.BurstFacade; import com.android.camera.debug.Log; import com.android.camera.one.OneCamera.Facing; import com.android.camera.one.OneCamera.OpenCallback; @@ -94,7 +95,7 @@ public class OneCameraManagerImpl extends OneCameraManager { public void open(Facing facing, boolean enableHdr, Size pictureSize, OpenCallback callback, Handler handler, MainThread mainThread, - ImageRotationCalculator imageRotationCalculator) { + ImageRotationCalculator imageRotationCalculator, BurstFacade burstController) { throw new RuntimeException("Not implemented yet."); } diff --git a/src/com/android/camera/one/v2/ImageCaptureManager.java b/src/com/android/camera/one/v2/ImageCaptureManager.java index 09b3e180a..38b968e21 100644 --- a/src/com/android/camera/one/v2/ImageCaptureManager.java +++ b/src/com/android/camera/one/v2/ImageCaptureManager.java @@ -29,7 +29,6 @@ import android.os.Handler; import android.os.SystemClock; import android.util.Pair; -import com.android.camera.burst.BurstConfiguration.EvictionHandler; import com.android.camera.debug.Log; import com.android.camera.debug.Log.Tag; import com.android.camera.util.ConcurrentSharedRingBuffer; @@ -45,9 +44,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; /** * Implements {@link android.media.ImageReader.OnImageAvailableListener} and @@ -233,35 +230,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im } } - /** - * A stub implementation of eviction handler that returns -1 as timestamp of - * the frame to be dropped. - * <p/> - * This forces the ring buffer to use its default eviction strategy. - */ - private static class DefaultEvictionHandler implements EvictionHandler { - @Override - public long selectFrameToDrop() { - return -1; - } - - @Override - public void onFrameCaptureResultAvailable(long timestamp, - TotalCaptureResult captureResult) { - } - - @Override - public void onFrameInserted(long timestamp) { - } - - @Override - public void onFrameDropped(long timestamp) { - } - } - - private static final EvictionHandler DEFAULT_EVICTION_HANDLER = - new DefaultEvictionHandler(); - private static final Tag TAG = new Tag("ZSLImageListener"); /** @@ -319,10 +287,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im */ private final Executor mImageCaptureListenerExecutor; - private final AtomicReference<EvictionHandler> mEvictionHandler = - new AtomicReference<EvictionHandler>(DEFAULT_EVICTION_HANDLER); - private final AtomicBoolean mIsCapturingBurst = new AtomicBoolean(false); - /** * The set of constraints which must be satisfied for a newly acquired image * to be captured and sent to {@link #mPendingImageCaptureCallback}. null if @@ -353,12 +317,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im mMetadataChangeListeners = new ConcurrentHashMap<Key<?>, Set<MetadataChangeListener>>(); /** - * The lock for guarding installation and uninstallation of burst eviction - * handler. - */ - private final Object mBurstLock = new Object(); - - /** * @param maxImages the maximum number of images provided by the * {@link ImageReader}. This must be greater than 2. * @param listenerHandler the handler on which to invoke listeners. Note @@ -440,7 +398,7 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im final TotalCaptureResult result) { final long timestamp = result.get(TotalCaptureResult.SENSOR_TIMESTAMP); - updateMetadataChangeListeners(result); + updateMetadataChangeListeners(result); // Detect camera thread stall. long now = SystemClock.uptimeMillis(); @@ -474,7 +432,7 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im final Object oldValue = (oldEntry != null) ? oldEntry.second : null; boolean newerValueAlreadyExists = oldEntry != null - && frameNumber < oldEntry.first; + && frameNumber < oldEntry.first; if (newerValueAlreadyExists) { continue; } @@ -489,12 +447,12 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im } for (final MetadataChangeListener listener : - mMetadataChangeListeners.get(key)) { + mMetadataChangeListeners.get(key)) { mListenerHandler.post(new Runnable() { @Override public void run() { listener.onImageMetadataChange(key, oldValue, newValue, - result); + result); } }); } @@ -502,18 +460,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im } private boolean doMetaDataSwap(final TotalCaptureResult newMetadata, final long timestamp) { - mEvictionHandler.get().onFrameCaptureResultAvailable(timestamp, newMetadata); - - if (mIsCapturingBurst.get()) { - // In case of burst we do not swap metadata in the ring buffer. This - // is to avoid the following scenario. If image for frame with - // timestamp A arrives first and the eviction handler decides to - // evict timestamp A. But when metadata for timestamp A arrives - // the eviction handler chooses to keep timestamp A. In this case - // the image for A will never be available. - return false; - } - return mCapturedImageBuffer.swapLeast(timestamp, new SwapTask<CapturedImage>() { @Override @@ -547,7 +493,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im new SwapTask<CapturedImage>() { @Override public CapturedImage create() { - mEvictionHandler.get().onFrameInserted(newImage.getTimestamp()); CapturedImage image = new CapturedImage(); image.addImage(newImage); return image; @@ -555,9 +500,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im @Override public CapturedImage swap(CapturedImage oldElement) { - mEvictionHandler.get().onFrameInserted(newImage.getTimestamp()); - long timestamp = oldElement.tryGetTimestamp(); - mEvictionHandler.get().onFrameDropped(timestamp); oldElement.reset(); CapturedImage image = new CapturedImage(); image.addImage(newImage); @@ -566,17 +508,12 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im @Override public void update(CapturedImage existingElement) { - mEvictionHandler.get().onFrameInserted(newImage.getTimestamp()); existingElement.addImage(newImage); } @Override public long getSwapKey() { - final long toDropTimestamp = mEvictionHandler.get().selectFrameToDrop(); - if (toDropTimestamp > 0) { - mCapturedImageBuffer.releaseIfPinned(toDropTimestamp); - } - return toDropTimestamp; + return -1; } }); } @@ -605,11 +542,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im if (DEBUG_PRINT_OPEN_IMAGE_COUNT) { Log.v(TAG, "Closed an image. Number of open images = " + numOpenImages); } - } else { - if (mIsCapturingBurst.get()) { - // In case of burst we pin every image. - mCapturedImageBuffer.tryPin(timestamp); - } } tryExecutePendingCaptureRequest(timestamp); @@ -808,57 +740,6 @@ public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback im } /** - * Sets a new burst eviction handler for the internal buffer. - * <p/> - * Also clears the buffer. If there was an old burst eviction handler - * already installed this method will throw an exception. - * - * @param evictionHandler the handler to install on the internal image - * buffer. - */ - public void setBurstEvictionHandler(EvictionHandler evictionHandler) { - if (evictionHandler == null) { - throw new IllegalArgumentException("setBurstEvictionHandler: evictionHandler is null."); - } - synchronized (mBurstLock) { - if (mIsCapturingBurst.compareAndSet(false, true)) { - if (!mEvictionHandler.compareAndSet(DEFAULT_EVICTION_HANDLER, - evictionHandler)) { - throw new IllegalStateException( - "Trying to set eviction handler before restoring the original."); - } else { - clearCapturedImageBuffer(0); - } - } else { - throw new IllegalStateException("Trying to start burst when it was already running."); - } - } - } - - /** - * Removes the burst eviction handler from the buffer. - */ - public void resetEvictionHandler() { - synchronized (mBurstLock) { - mEvictionHandler.set(DEFAULT_EVICTION_HANDLER); - } - } - - /** - * Clears the underlying buffer and reset the eviction handler. - */ - public void resetCaptureState() { - synchronized (mBurstLock) { - if (mIsCapturingBurst.compareAndSet(true, false)) { - // By default the image buffer has 1 slot that is reserved for - // unpinned elements. - clearCapturedImageBuffer(1); - mEvictionHandler.set(DEFAULT_EVICTION_HANDLER); - } - } - } - - /** * Clear the buffer and reserves <code>unpinnedReservedSlots</code> in the buffer. * * @param unpinnedReservedSlots the number of unpinned slots that are never diff --git a/src/com/android/camera/one/v2/OneCameraFactory.java b/src/com/android/camera/one/v2/OneCameraFactory.java index e7b57e460..1d72dffca 100644 --- a/src/com/android/camera/one/v2/OneCameraFactory.java +++ b/src/com/android/camera/one/v2/OneCameraFactory.java @@ -18,6 +18,7 @@ package com.android.camera.one.v2; import com.android.camera.async.MainThread; import com.android.camera.async.Observable; +import com.android.camera.burst.BurstFacade; import com.android.camera.one.OneCamera; import com.android.camera.one.OneCameraCharacteristics; import com.android.camera.one.v2.camera2proxy.CameraDeviceProxy; @@ -30,5 +31,6 @@ public interface OneCameraFactory { MainThread mainThread, Size pictureSize, ImageSaver.Builder imageSaverBuilder, - Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting); + Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting, + BurstFacade burstController); } diff --git a/src/com/android/camera/one/v2/OneCameraManagerImpl.java b/src/com/android/camera/one/v2/OneCameraManagerImpl.java index 6b3754b2b..2879bf763 100644 --- a/src/com/android/camera/one/v2/OneCameraManagerImpl.java +++ b/src/com/android/camera/one/v2/OneCameraManagerImpl.java @@ -29,6 +29,7 @@ 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.burst.BurstFacade; import com.android.camera.debug.Log; import com.android.camera.debug.Log.Tag; import com.android.camera.one.OneCamera; @@ -97,7 +98,8 @@ public class OneCameraManagerImpl extends OneCameraManager { public void open(Facing facing, final boolean useHdr, final Size pictureSize, final OpenCallback openCallback, Handler handler, final MainThread mainThread, - final ImageRotationCalculator imageRotationCalculator) { + final ImageRotationCalculator imageRotationCalculator, + final BurstFacade burstController) { try { final String cameraId = getCameraId(facing); Log.i(TAG, "Opening Camera ID " + cameraId); @@ -149,7 +151,7 @@ public class OneCameraManagerImpl extends OneCameraManager { OneCamera oneCamera = OneCameraCreator.create(mAppController, useHdr, device, characteristics, pictureSize, mMaxMemoryMB, mDisplayMetrics, mSoundPlayer, - mainThread, imageRotationCalculator); + mainThread, imageRotationCalculator, burstController); openCallback.onCameraOpened(oneCamera); } catch (CameraAccessException e) { Log.d(TAG, "Could not get camera characteristics"); diff --git a/src/com/android/camera/one/v2/OneCameraZslImpl.java b/src/com/android/camera/one/v2/OneCameraZslImpl.java index 0a57aecde..c326c02ca 100644 --- a/src/com/android/camera/one/v2/OneCameraZslImpl.java +++ b/src/com/android/camera/one/v2/OneCameraZslImpl.java @@ -44,8 +44,6 @@ import android.view.Surface; import com.android.camera.CaptureModuleUtil; import com.android.camera.app.MediaSaver.OnMediaSavedListener; -import com.android.camera.burst.BurstImage; -import com.android.camera.burst.ResultsAccessor; import com.android.camera.debug.Log; import com.android.camera.debug.Log.Tag; import com.android.camera.exif.ExifInterface; @@ -59,6 +57,7 @@ import com.android.camera.one.Settings3A; import com.android.camera.one.v2.ImageCaptureManager.ImageCaptureListener; import com.android.camera.one.v2.ImageCaptureManager.MetadataChangeListener; import com.android.camera.one.v2.camera2proxy.AndroidImageProxy; +import com.android.camera.one.v2.camera2proxy.ImageProxy; import com.android.camera.session.CaptureSession; import com.android.camera.util.CameraUtil; import com.android.camera.util.JpegUtilNative; @@ -196,9 +195,6 @@ public class OneCameraZslImpl extends AbstractOneCamera { private MediaActionSound mMediaActionSound = new MediaActionSound(); - private final AtomicReference<BurstParameters> - mBurstParams = new AtomicReference<BurstParameters>(); - /** * Ready state (typically displayed by the UI shutter-button) depends on two * things:<br> @@ -1098,73 +1094,4 @@ public class OneCameraZslImpl extends AbstractOneCamera { private Rect cropRegionForZoom(float zoom) { return AutoFocusHelper.cropRegionForZoom(mCharacteristics, zoom); } - - @Override - public void startBurst(BurstParameters params, CaptureSession session) { - if (!mBurstParams.compareAndSet(null, params)) { - throw new IllegalStateException( - "Attempting to start burst, when burst is already running."); - } - // Overwrite repeating capture request with one specific to capturing Bursts. - sendRepeatingBurstCaptureRequest(); - mCaptureManager.setBurstEvictionHandler(params. - burstConfiguration.getEvictionHandler()); - } - - private class ImageExtractor implements ResultsAccessor { - private final int mOrientation; - - public ImageExtractor(int orientation) { - mOrientation = orientation; - } - - @Override - public Future<BurstImage> extractImage(final long timestampToExtract) { - final Pair<Image, TotalCaptureResult> pinnedImageData = - mCaptureManager.tryCapturePinnedImage(timestampToExtract); - return mImageSaverThreadPool.submit(new Callable<BurstImage>() { - - @Override - public BurstImage call() throws Exception { - BurstImage burstImage = null; - Image image = pinnedImageData.first; - if (image != null) { - burstImage = new BurstImage(); - int degrees = CameraUtil.getJpegRotation(mOrientation, mCharacteristics); - Size size = getImageSizeForOrientation(image.getWidth(), - image.getHeight(), - degrees); - burstImage.width = size.getWidth(); - burstImage.height = size.getHeight(); - burstImage.data = acquireJpegBytes(image, - degrees); - burstImage.captureResult = pinnedImageData.second; - burstImage.timestamp = timestampToExtract; - } else { - Log.e(TAG, "Failed to extract burst image for timestamp: " - + timestampToExtract); - } - return burstImage; - } - }); - } - - @Override - public void close() { - mCaptureManager.resetCaptureState(); - } - } - - @Override - public void stopBurst() { - if (mBurstParams.get() == null) { - throw new IllegalStateException("Burst parameters should not be null."); - } - mCaptureManager.resetEvictionHandler(); - mBurstParams.get().callback.onBurstComplete( - new ImageExtractor(mBurstParams.get().orientation)); - mBurstParams.set(null); - // Restore original repeating request associated with taking camera picture. - sendRepeatingCaptureRequest(); - } } diff --git a/src/com/android/camera/one/v2/SimpleOneCameraFactory.java b/src/com/android/camera/one/v2/SimpleOneCameraFactory.java index 9cf6192ee..f3baa0520 100644 --- a/src/com/android/camera/one/v2/SimpleOneCameraFactory.java +++ b/src/com/android/camera/one/v2/SimpleOneCameraFactory.java @@ -29,6 +29,7 @@ import com.android.camera.async.MainThread; import com.android.camera.async.Observable; import com.android.camera.async.Observables; import com.android.camera.async.Updatable; +import com.android.camera.burst.BurstFacade; import com.android.camera.debug.Loggers; import com.android.camera.one.OneCamera; import com.android.camera.one.OneCameraCharacteristics; @@ -91,7 +92,8 @@ public class SimpleOneCameraFactory implements OneCameraFactory { public OneCamera createOneCamera(final CameraDeviceProxy device, final OneCameraCharacteristics characteristics, final MainThread mainExecutor, Size pictureSize, final ImageSaver.Builder imageSaverBuilder, - final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting) { + final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting, + final BurstFacade burstFacade) { final Lifetime lifetime = new Lifetime(); final ImageReaderProxy imageReader = new CloseWhenDoneImageReader(new LoggingImageReader( diff --git a/src/com/android/camera/one/v2/ZslOneCameraFactory.java b/src/com/android/camera/one/v2/ZslOneCameraFactory.java index 3c13f77df..72a713767 100644 --- a/src/com/android/camera/one/v2/ZslOneCameraFactory.java +++ b/src/com/android/camera/one/v2/ZslOneCameraFactory.java @@ -30,6 +30,9 @@ import com.android.camera.async.Updatable; import com.android.camera.debug.Log.Tag; import com.android.camera.debug.Logger; import com.android.camera.debug.Loggers; +import com.android.camera.burst.BurstFacade; +import com.android.camera.burst.BurstTaker; +import com.android.camera.burst.BurstTakerImpl; import com.android.camera.one.OneCamera; import com.android.camera.one.OneCameraCharacteristics; import com.android.camera.one.v2.camera2proxy.AndroidImageReaderProxy; @@ -101,7 +104,8 @@ public class ZslOneCameraFactory implements OneCameraFactory { final OneCameraCharacteristics characteristics, final MainThread mainThread, Size pictureSize, final ImageSaver.Builder imageSaverBuilder, - final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting) { + final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting, + final BurstFacade burstFacade) { final Lifetime lifetime = new Lifetime(); final ImageReaderProxy imageReader = new CloseWhenDoneImageReader( @@ -114,6 +118,9 @@ public class ZslOneCameraFactory implements OneCameraFactory { List<Surface> outputSurfaces = new ArrayList<>(); outputSurfaces.add(imageReader.getSurface()); + if (burstFacade.getInputSurface() != null) { + outputSurfaces.add(burstFacade.getInputSurface()); + } /** * Finishes constructing the camera when prerequisites, e.g. the preview @@ -183,6 +190,14 @@ public class ZslOneCameraFactory implements OneCameraFactory { sharedImageReaderFactory.provideSharedImageReader(), sharedImageReaderFactory.provideZSLStream(), sharedImageReaderFactory.provideMetadataPool(), flashSetting); + BurstTaker burstTaker = new BurstTakerImpl(cameraCommandExecutor, frameServer, + rootBuilder, sharedImageReaderFactory.provideSharedImageReader(), + burstFacade.getInputSurface(),basicCameraFactory.providePreviewStarter(), + // ImageReader#acquireLatestImage() requires two images as the margin so + // specify that as the maximum number of images that can be used by burst. + mMaxImageCount - 2); + + burstFacade.setBurstTaker(burstTaker); basicCameraFactory.providePreviewStarter().run(); diff --git a/src/com/android/camera/one/v2/initialization/GenericOneCameraImpl.java b/src/com/android/camera/one/v2/initialization/GenericOneCameraImpl.java index 32c2cb16e..957ad0e4a 100644 --- a/src/com/android/camera/one/v2/initialization/GenericOneCameraImpl.java +++ b/src/com/android/camera/one/v2/initialization/GenericOneCameraImpl.java @@ -95,16 +95,6 @@ class GenericOneCameraImpl implements OneCamera { } @Override - public void startBurst(BurstParameters params, CaptureSession session) { - // TODO delete from OneCamera interface - } - - @Override - public void stopBurst() { - // TODO delete from OneCamera interface - } - - @Override public void setFocusStateListener(final FocusStateListener listener) { mAFStateListenable.setCallback(new Callback<Integer>() { @Override diff --git a/src/com/android/camera/one/v2/sharedimagereader/ZslSharedImageReaderFactory.java b/src/com/android/camera/one/v2/sharedimagereader/ZslSharedImageReaderFactory.java index 310eed2ed..d9f4c8aa6 100644 --- a/src/com/android/camera/one/v2/sharedimagereader/ZslSharedImageReaderFactory.java +++ b/src/com/android/camera/one/v2/sharedimagereader/ZslSharedImageReaderFactory.java @@ -24,7 +24,6 @@ import com.android.camera.async.Lifetime; import com.android.camera.async.Observable; import com.android.camera.async.Updatable; import com.android.camera.one.v2.camera2proxy.ImageReaderProxy; -import com.android.camera.one.v2.core.CaptureStream; import com.android.camera.one.v2.core.ResponseListener; import com.android.camera.one.v2.core.ResponseListeners; import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageDistributor; diff --git a/src_pd/com/android/camera/burst/BurstControllerImpl.java b/src_pd/com/android/camera/burst/BurstControllerImpl.java index 0c8156446..dfbc030eb 100644 --- a/src_pd/com/android/camera/burst/BurstControllerImpl.java +++ b/src_pd/com/android/camera/burst/BurstControllerImpl.java @@ -15,22 +15,21 @@ package com.android.camera.burst; import android.content.Context; +import android.graphics.SurfaceTexture; -import com.android.camera.gl.FrameDistributor.FrameConsumer; +import com.android.camera.burst.BurstResultsListener; +import com.android.camera.burst.EvictionHandler; +import com.android.camera.burst.BurstController.ImageStreamProperties; +import com.android.camera.one.v2.camera2proxy.ImageProxy; + +import java.util.List; /** * Stub implementation for burst controller. */ class BurstControllerImpl implements BurstController { - /** - * Create a new BurstController. - * - * @param context the context of the application. - * @param resultsListener listener for listening to burst events. - */ - public BurstControllerImpl(Context context, BurstResultsListener resultsListener) { - } + public BurstControllerImpl(Context context) {} /** * Returns true if burst mode is supported by camera. */ @@ -39,25 +38,14 @@ class BurstControllerImpl implements BurstController { } @Override - public BurstConfiguration startBurst() { + public EvictionHandler startBurst(SurfaceTexture surfaceTexture, + ImageStreamProperties imageStreamProperties, + BurstResultsListener resultsListener) { return null; } @Override - public void stopBurst(ResultsAccessor resultsAccessor) { + public void processBurstResults(List<ImageProxy> capturedImages) { // no op } - - @Override - public void onPreviewSizeChanged(int width, int height) { - } - - @Override - public void onOrientationChanged(int orientation, boolean isMirrored) { - } - - @Override - public FrameConsumer getPreviewFrameConsumer() { - throw new IllegalStateException("Not implemented."); - } } diff --git a/src_pd/com/android/camera/one/v2/OneCameraCreator.java b/src_pd/com/android/camera/one/v2/OneCameraCreator.java index a1736ec31..7d67e2f8f 100644 --- a/src_pd/com/android/camera/one/v2/OneCameraCreator.java +++ b/src_pd/com/android/camera/one/v2/OneCameraCreator.java @@ -23,6 +23,7 @@ import android.util.DisplayMetrics; import com.android.camera.SoundPlayer; import com.android.camera.app.AppController; import com.android.camera.async.MainThread; +import com.android.camera.burst.BurstFacade; import com.android.camera.one.OneCamera; import com.android.camera.one.v2.photo.ImageRotationCalculator; import com.android.camera.util.Size; @@ -32,7 +33,8 @@ public class OneCameraCreator { CameraCharacteristics characteristics, Size pictureSize, int maxMemoryMB, DisplayMetrics displayMetrics, SoundPlayer soundPlayer, - MainThread mainThread, ImageRotationCalculator imageRotationCalculator) { + MainThread mainThread, ImageRotationCalculator imageRotationCalculator, + BurstFacade burstController) { // TODO: Might want to switch current camera to vendor HDR. return new OneCameraImpl(device, characteristics, pictureSize); } |