1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.android.camera.burst;
16 
17 import android.content.Context;
18 import android.graphics.SurfaceTexture;
19 import android.view.Surface;
20 
21 import com.android.camera.app.OrientationManager.DeviceOrientation;
22 import com.android.camera.async.MainThread;
23 import com.android.camera.burst.BurstController.ImageStreamProperties;
24 import com.android.camera.debug.Log;
25 import com.android.camera.debug.Log.Tag;
26 import com.android.camera.one.OneCamera.Facing;
27 import com.android.camera.session.CaptureSession;
28 import com.android.camera.session.StackSaver;
29 
30 import java.util.Map;
31 import java.util.concurrent.atomic.AtomicReference;
32 
33 /**
34  * Helper to manage burst, listen to burst results and saves media items.
35  * <p/>
36  * The UI feedback is rudimentary in form of a toast that is displayed on start
37  * of the burst and when artifacts are saved. TODO: Move functionality of saving
38  * burst items to a {@link com.android.camera.processing.ProcessingTask} and
39  * change API to use {@link com.android.camera.processing.ProcessingService}.
40  * TODO: Hook UI to the listener.
41  */
42 class BurstFacadeImpl implements BurstFacade {
43     /**
44      * The state of the burst module.
45      */
46     private static enum BurstModuleState {
47         IDLE,
48         RUNNING,
49         STOPPING
50     }
51 
52     private static final Tag TAG = new Tag("BurstFacadeImpl");
53 
54     private static final int DEFAULT_PREVIEW_WIDTH = 320;
55     private static final int DEFAULT_PREVIEW_HEIGHT = 240;
56 
57     private final AtomicReference<BurstModuleState> mBurstModuleState =
58             new AtomicReference<BurstModuleState>(BurstModuleState.IDLE);
59     private final AtomicReference<BurstTaker> mBurstTaker =
60             new AtomicReference<>(null);
61 
62     private final BurstController mBurstController;
63 
64     /** A stack saver for the outstanding burst request. */
65     private StackSaver mActiveStackSaver;
66 
67     /**
68      * Listener for burst controller. Saves the results and interacts with the
69      * UI.
70      */
71     private final BurstResultsListener mBurstResultsListener =
72             new BurstResultsListener() {
73                 @Override
74                 public void onBurstStarted() {
75                 }
76 
77                 @Override
78                 public void onBurstError(Exception error) {
79                     Log.e(TAG, "Exception while running the burst" + error);
80                 }
81 
82                 @Override
83                 public void onBurstCompleted(BurstResult burstResult) {
84                     BurstResultsSaver.saveBurstResultsInBackground(burstResult, mActiveStackSaver,
85                             new Runnable() {
86                         @Override
87                         public void run() {
88                             mBurstModuleState.set(BurstModuleState.IDLE);
89                             }
90                         });
91                 }
92 
93                 @Override
94                 public void onArtifactCountAvailable(
95                         final Map<String, Integer> artifactTypeCount) {
96                     BurstResultsSaver.logArtifactCount(artifactTypeCount);
97                 }
98             };
99 
100     private final OrientationLockController mOrientationLockController;
101     private final BurstReadyStateChangeListener mReadyStateListener;
102 
103     private final AtomicReference<SurfaceTextureContainer> mSurfaceTextureContainer =
104             new AtomicReference<>();
105 
106     /**
107      * Create a new BurstManagerImpl instance.
108      *
109      * @param appContext the application context
110      * @param orientationLockController for locking orientation when burst is
111      *            running.
112      * @param readyStateListener gets called when the ready state of Burst
113      *            changes.
114      */
BurstFacadeImpl(Context appContext, OrientationLockController orientationLockController, BurstReadyStateChangeListener readyStateListener)115     public BurstFacadeImpl(Context appContext,
116             OrientationLockController orientationLockController,
117             BurstReadyStateChangeListener readyStateListener) {
118         mOrientationLockController = orientationLockController;
119         mBurstController = new BurstControllerImpl(appContext);
120         mReadyStateListener = readyStateListener;
121     }
122 
123     @Override
startBurst(CaptureSession.CaptureSessionCreator captureSessionCreator, DeviceOrientation deviceOrientation, Facing cameraFacing, int imageOrientationDegrees)124     public void startBurst(CaptureSession.CaptureSessionCreator captureSessionCreator,
125             DeviceOrientation deviceOrientation,
126             Facing cameraFacing,
127             int imageOrientationDegrees) {
128         MainThread.checkMainThread();
129         if (mBurstTaker.get() != null &&
130                 mBurstModuleState.compareAndSet(BurstModuleState.IDLE,
131                         BurstModuleState.RUNNING)) {
132             // Only create a session if we do start a burst.
133             CaptureSession captureSession = captureSessionCreator.createAndStartEmpty();
134             mActiveStackSaver = captureSession.getStackSaver();
135 
136             mOrientationLockController.lockOrientation();
137             // Disable the shutter button.
138             mReadyStateListener.onBurstReadyStateChanged(false);
139 
140             Log.d(TAG, "Starting burst. Device orientation: " + deviceOrientation.getDegrees()
141                     + " image orientation: " + imageOrientationDegrees);
142             int width = DEFAULT_PREVIEW_WIDTH;
143             int height = DEFAULT_PREVIEW_HEIGHT;
144             if (imageOrientationDegrees % 180 == 90) {
145                 int tmp = width;
146                 width = height;
147                 height = tmp;
148             }
149 
150             ImageStreamProperties imageStreamProperties =
151                     new ImageStreamProperties(width, height,
152                             imageOrientationDegrees, cameraFacing == Facing.FRONT);
153             EvictionHandler evictionHandler =
154                     mBurstController.startBurst(
155                             mSurfaceTextureContainer.get().getSurfaceTexture(),
156                             imageStreamProperties,
157                             mBurstResultsListener,
158                             captureSession);
159 
160             // Start burst.
161             mBurstTaker.get().startBurst(evictionHandler, mBurstController);
162         } else {
163             Log.e(TAG, "Cannot start burst.");
164         }
165     }
166 
167     @Override
stopBurst()168     public boolean stopBurst() {
169         MainThread.checkMainThread();
170             boolean wasStopped = false;
171             if (mBurstModuleState.compareAndSet(BurstModuleState.RUNNING,
172                     BurstModuleState.STOPPING)) {
173                 mBurstTaker.get().stopBurst();
174                 wasStopped = true;
175                 reEnableUI();
176             }
177             return wasStopped;
178     }
179 
180     @Override
getInputSurface()181     public Surface getInputSurface() {
182         return mSurfaceTextureContainer.get().getSurface();
183     }
184 
185     @Override
initialize(SurfaceTexture surfaceTexture)186     public void initialize(SurfaceTexture surfaceTexture) {
187         MainThread.checkMainThread();
188         // TODO: Use preview sizes from Camera API here instead of using the
189         // default.
190         surfaceTexture.setDefaultBufferSize(DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT);
191 
192         // Detach from GL context, to allow frame distributor to attach to the
193         // GL context.
194         surfaceTexture.detachFromGLContext();
195         mSurfaceTextureContainer.set(new SurfaceTextureContainer(surfaceTexture));
196     }
197 
198     @Override
release()199     public void release() {
200         MainThread.checkMainThread();
201         stopBurst();
202         if (mSurfaceTextureContainer.get() != null) {
203             mSurfaceTextureContainer.get().close();
204             mSurfaceTextureContainer.set(null);
205         }
206     }
207 
208     @Override
setBurstTaker(BurstTaker burstTaker)209     public void setBurstTaker(BurstTaker burstTaker) {
210         mBurstTaker.set(burstTaker);
211     }
212 
reEnableUI()213     private void reEnableUI() {
214         MainThread.checkMainThread();
215         mOrientationLockController.unlockOrientation();
216         // Re-enable the shutter button.
217         mReadyStateListener.onBurstReadyStateChanged(true);
218     }
219 }
220