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 */ 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 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 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 181 public Surface getInputSurface() { 182 return mSurfaceTextureContainer.get().getSurface(); 183 } 184 185 @Override 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 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 209 public void setBurstTaker(BurstTaker burstTaker) { 210 mBurstTaker.set(burstTaker); 211 } 212 213 private void reEnableUI() { 214 MainThread.checkMainThread(); 215 mOrientationLockController.unlockOrientation(); 216 // Re-enable the shutter button. 217 mReadyStateListener.onBurstReadyStateChanged(true); 218 } 219 } 220