2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package android.hardware.camera2.impl;
18 import android.hardware.camera2.CameraAccessException;
19 import android.hardware.camera2.CameraCaptureSession;
20 import android.hardware.camera2.CameraDevice;
21 import android.hardware.camera2.CaptureRequest;
22 import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
23 import android.hardware.camera2.dispatch.BroadcastDispatcher;
24 import android.hardware.camera2.dispatch.Dispatchable;
25 import android.hardware.camera2.dispatch.DuckTypingDispatcher;
26 import android.hardware.camera2.dispatch.HandlerDispatcher;
27 import android.hardware.camera2.dispatch.InvokeDispatcher;
28 import android.hardware.camera2.dispatch.NullDispatcher;
29 import android.hardware.camera2.utils.TaskDrainer;
30 import android.hardware.camera2.utils.TaskSingleDrainer;
31 import android.os.Handler;
32 import android.util.Log;
33 import android.view.Surface;
35 import java.util.Arrays;
36 import java.util.List;
38 import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
39 import static com.android.internal.util.Preconditions.*;
41 public class CameraCaptureSessionImpl extends CameraCaptureSession {
42 private static final String TAG = "CameraCaptureSession";
43 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
45 /** User-specified set of surfaces used as the configuration outputs */
46 private final List<Surface> mOutputs;
48 * User-specified state listener, used for outgoing events; calls to this object will be
49 * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
51 private final CameraCaptureSession.StateListener mStateListener;
52 /** User-specified state handler used for outgoing state listener events */
53 private final Handler mStateHandler;
55 /** Internal camera device; used to translate calls into existing deprecated API */
56 private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
57 /** Internal handler; used for all incoming events to preserve total order */
58 private final Handler mDeviceHandler;
60 /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
61 private final TaskDrainer<Integer> mSequenceDrainer;
62 /** Drain state transitions from ACTIVE -> IDLE */
63 private final TaskSingleDrainer mIdleDrainer;
64 /** Drain state transitions from BUSY -> IDLE */
65 private final TaskSingleDrainer mAbortDrainer;
66 /** Drain the UNCONFIGURED state transition */
67 private final TaskSingleDrainer mUnconfigureDrainer;
69 /** This session is closed; all further calls will throw ISE */
70 private boolean mClosed = false;
71 /** Do not unconfigure if this is set; another session will overwrite configuration */
72 private boolean mSkipUnconfigure = false;
74 /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
75 private boolean mAborting;
78 * Create a new CameraCaptureSession.
80 * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
81 * There must be no pending actions
82 * (e.g. no pending captures, no repeating requests, no flush).</p>
84 CameraCaptureSessionImpl(List<Surface> outputs,
85 CameraCaptureSession.StateListener listener, Handler stateHandler,
86 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
87 Handler deviceStateHandler, boolean configureSuccess) {
88 if (outputs == null || outputs.isEmpty()) {
89 throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
90 } else if (listener == null) {
91 throw new IllegalArgumentException("listener must not be null");
94 // TODO: extra verification of outputs
96 mStateHandler = checkHandler(stateHandler);
97 mStateListener = createUserStateListenerProxy(mStateHandler, listener);
99 mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
100 mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
103 * Use the same handler as the device's StateListener for all the internal coming events
105 * This ensures total ordering between CameraDevice.StateListener and
106 * CameraDevice.CaptureListener events.
108 mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
110 mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
112 mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
114 mUnconfigureDrainer = new TaskSingleDrainer(mDeviceHandler, new UnconfigureDrainListener(),
117 // CameraDevice should call configureOutputs and have it finish before constructing us
119 if (configureSuccess) {
120 mStateListener.onConfigured(this);
121 if (VERBOSE) Log.v(TAG, "ctor - Created session successfully");
123 mStateListener.onConfigureFailed(this);
124 mClosed = true; // do not fire any other callbacks, do not allow any other work
125 Log.e(TAG, "Failed to create capture session; configuration failed");
130 public CameraDevice getDevice() {
135 public synchronized int capture(CaptureRequest request, CaptureListener listener,
136 Handler handler) throws CameraAccessException {
137 if (request == null) {
138 throw new IllegalArgumentException("request must not be null");
142 checkLegalToCapture();
144 handler = checkHandler(handler, listener);
147 Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" +
151 return addPendingSequence(mDeviceImpl.capture(request,
152 createCaptureListenerProxy(handler, listener), mDeviceHandler));
156 public synchronized int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
157 Handler handler) throws CameraAccessException {
158 if (requests == null) {
159 throw new IllegalArgumentException("requests must not be null");
160 } else if (requests.isEmpty()) {
161 throw new IllegalArgumentException("requests must have at least one element");
165 checkLegalToCapture();
167 handler = checkHandler(handler, listener);
170 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
171 Log.v(TAG, "captureBurst - requests " + Arrays.toString(requestArray) + ", listener " +
172 listener + " handler" + "" + handler);
175 return addPendingSequence(mDeviceImpl.captureBurst(requests,
176 createCaptureListenerProxy(handler, listener), mDeviceHandler));
180 public synchronized int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
181 Handler handler) throws CameraAccessException {
182 if (request == null) {
183 throw new IllegalArgumentException("request must not be null");
187 checkLegalToCapture();
189 handler = checkHandler(handler, listener);
192 Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener +
193 " handler" + " " + handler);
196 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
197 createCaptureListenerProxy(handler, listener), mDeviceHandler));
201 public synchronized int setRepeatingBurst(List<CaptureRequest> requests,
202 CaptureListener listener, Handler handler) throws CameraAccessException {
203 if (requests == null) {
204 throw new IllegalArgumentException("requests must not be null");
205 } else if (requests.isEmpty()) {
206 throw new IllegalArgumentException("requests must have at least one element");
210 checkLegalToCapture();
212 handler = checkHandler(handler, listener);
215 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
216 Log.v(TAG, "setRepeatingBurst - requests " + Arrays.toString(requestArray) +
217 ", listener " + listener + " handler" + "" + handler);
220 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
221 createCaptureListenerProxy(handler, listener), mDeviceHandler));
225 public synchronized void stopRepeating() throws CameraAccessException {
229 Log.v(TAG, "stopRepeating");
232 mDeviceImpl.stopRepeating();
236 public synchronized void abortCaptures() throws CameraAccessException {
240 Log.v(TAG, "abortCaptures");
244 Log.w(TAG, "abortCaptures - Session is already aborting; doing nothing");
249 mAbortDrainer.taskStarted();
252 // The next BUSY -> IDLE set of transitions will mark the end of the abort.
256 * Replace this session with another session.
258 * <p>This is an optimization to avoid unconfiguring and then immediately having to
259 * reconfigure again.</p>
261 * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
264 * @see CameraCaptureSession#close
266 synchronized void replaceSessionClose(CameraCaptureSession other) {
268 * In order for creating new sessions to be fast, the new session should be created
269 * before the old session is closed.
271 * Otherwise the old session will always unconfigure if there is no new session to
274 * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
275 * to skip unconfigure if a new session is created before the captures are all drained,
276 * but this would introduce nondeterministic behavior.
279 if (VERBOSE) Log.v(TAG, "replaceSessionClose");
281 // #close was already called explicitly, keep going the slow route
283 if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called");
287 mSkipUnconfigure = true;
292 public synchronized void close() {
295 if (VERBOSE) Log.v(TAG, "close - reentering");
299 if (VERBOSE) Log.v(TAG, "close - first time");
304 * Flush out any repeating request. Since camera is closed, no new requests
305 * can be queued, and eventually the entire request queue will be drained.
307 * If the camera device was already closed, short circuit and do nothing; since
308 * no more internal device callbacks will fire anyway.
310 * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the
311 * camera. Once that's done, fire #onClosed.
314 mDeviceImpl.stopRepeating();
315 } catch (IllegalStateException e) {
316 // OK: Camera device may already be closed, nothing else to do
317 Log.w(TAG, "The camera device was already closed: ", e);
319 // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
320 // or just suppress the ISE only and rely onClosed.
321 // Also skip any of the draining work if this is already closed.
323 // Short-circuit; queue listener immediately and return
324 mStateListener.onClosed(this);
326 } catch (CameraAccessException e) {
327 // OK: close does not throw checked exceptions.
328 Log.e(TAG, "Exception while stopping repeating: ", e);
330 // TODO: call onError instead of onClosed if this happens
333 // If no sequences are pending, fire #onClosed immediately
334 mSequenceDrainer.beginDrain();
338 * Post calls into a CameraCaptureSession.StateListener to the user-specified {@code handler}.
340 private StateListener createUserStateListenerProxy(Handler handler, StateListener listener) {
341 InvokeDispatcher<StateListener> userListenerSink = new InvokeDispatcher<>(listener);
342 HandlerDispatcher<StateListener> handlerPassthrough =
343 new HandlerDispatcher<>(userListenerSink, handler);
345 return new ListenerProxies.SessionStateListenerProxy(handlerPassthrough);
349 * Forward callbacks from
350 * CameraDevice.CaptureListener to the CameraCaptureSession.CaptureListener.
352 * <p>In particular, all calls are automatically split to go both to our own
353 * internal listener, and to the user-specified listener (by transparently posting
354 * to the user-specified handler).</p>
356 * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
358 @SuppressWarnings("deprecation")
359 private CameraDevice.CaptureListener createCaptureListenerProxy(
360 Handler handler, CaptureListener listener) {
361 CameraDevice.CaptureListener localListener = new CameraDevice.CaptureListener() {
363 public void onCaptureSequenceCompleted(CameraDevice camera,
364 int sequenceId, long frameNumber) {
365 finishPendingSequence(sequenceId);
369 public void onCaptureSequenceAborted(CameraDevice camera,
371 finishPendingSequence(sequenceId);
376 * Split the calls from the device listener into local listener and the following chain:
377 * - replace the first CameraDevice arg with a CameraCaptureSession
378 * - duck type from device listener to session listener
379 * - then forward the call to a handler
380 * - then finally invoke the destination method on the session listener object
382 if (listener == null) {
383 // OK: API allows the user to not specify a listener, and the handler may
384 // also be null in that case. Collapse whole dispatch chain to only call the local
386 return localListener;
389 InvokeDispatcher<CameraDevice.CaptureListener> localSink =
390 new InvokeDispatcher<>(localListener);
392 InvokeDispatcher<CaptureListener> userListenerSink =
393 new InvokeDispatcher<>(listener);
394 HandlerDispatcher<CaptureListener> handlerPassthrough =
395 new HandlerDispatcher<>(userListenerSink, handler);
396 DuckTypingDispatcher<CameraDevice.CaptureListener, CaptureListener> duckToSession
397 = new DuckTypingDispatcher<>(handlerPassthrough, CaptureListener.class);
398 ArgumentReplacingDispatcher<CameraDevice.CaptureListener, CameraCaptureSessionImpl>
399 replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
400 /*argumentIndex*/0, this);
402 BroadcastDispatcher<CameraDevice.CaptureListener> broadcaster =
403 new BroadcastDispatcher<CameraDevice.CaptureListener>(
404 replaceDeviceWithSession,
407 return new ListenerProxies.DeviceCaptureListenerProxy(broadcaster);
412 * Create an internal state listener, to be invoked on the mDeviceHandler
414 * <p>It has a few behaviors:
416 * <li>Convert device state changes into session state changes.
417 * <li>Keep track of async tasks that the session began (idle, abort).
421 CameraDevice.StateListener getDeviceStateListener() {
422 final CameraCaptureSession session = this;
424 return new CameraDevice.StateListener() {
425 private boolean mBusy = false;
426 private boolean mActive = false;
429 public void onOpened(CameraDevice camera) {
430 throw new AssertionError("Camera must already be open before creating a session");
434 public void onDisconnected(CameraDevice camera) {
439 public void onError(CameraDevice camera, int error) {
440 // TODO: Handle errors somehow.
441 Log.wtf(TAG, "Got device error " + error);
445 public void onActive(CameraDevice camera) {
446 mIdleDrainer.taskStarted();
449 mStateListener.onActive(session);
453 public void onIdle(CameraDevice camera) {
455 synchronized (session) {
456 isAborting = mAborting;
460 * Check which states we transitioned through:
465 * Note that this is also legal:
466 * (ACTIVE -> BUSY -> IDLE)
468 * and mark those tasks as finished
470 if (mBusy && isAborting) {
471 mAbortDrainer.taskFinished();
473 synchronized (session) {
479 mIdleDrainer.taskFinished();
485 mStateListener.onReady(session);
489 public void onBusy(CameraDevice camera) {
492 // TODO: Queue captures during abort instead of failing them
493 // since the app won't be able to distinguish the two actives
494 Log.w(TAG, "Device is now busy; do not submit new captures (TODO: allow this)");
495 mStateListener.onActive(session);
499 public void onUnconfigured(CameraDevice camera) {
500 synchronized (session) {
501 // Ignore #onUnconfigured before #close is called
503 mUnconfigureDrainer.taskFinished();
512 protected void finalize() throws Throwable {
520 private void checkLegalToCapture() {
522 throw new IllegalStateException(
523 "Session is aborting captures; new captures are not permitted");
527 private void checkNotClosed() {
529 throw new IllegalStateException(
530 "Session has been closed; further changes are illegal.");
535 * Notify the session that a pending capture sequence has just been queued.
537 * <p>During a shutdown/close, the session waits until all pending sessions are finished
538 * before taking any further steps to shut down itself.</p>
540 * @see #finishPendingSequence
542 private int addPendingSequence(int sequenceId) {
543 mSequenceDrainer.taskStarted(sequenceId);
548 * Notify the session that a pending capture sequence is now finished.
550 * <p>During a shutdown/close, once all pending sequences finish, it is safe to
551 * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
553 private void finishPendingSequence(int sequenceId) {
554 mSequenceDrainer.taskFinished(sequenceId);
557 private class SequenceDrainListener implements TaskDrainer.DrainListener {
559 public void onDrained() {
561 * No repeating request is set; and the capture queue has fully drained.
563 * If no captures were queued to begin with, and an abort was queued,
564 * it's still possible to get another BUSY before the last IDLE.
566 * If the camera is already "IDLE" and no aborts are pending,
567 * then the drain immediately finishes.
569 mAbortDrainer.beginDrain();
573 private class AbortDrainListener implements TaskDrainer.DrainListener {
575 public void onDrained() {
576 synchronized (CameraCaptureSessionImpl.this) {
578 * Any queued aborts have now completed.
580 * It's now safe to wait to receive the final "IDLE" event, as the camera device
581 * will no longer again transition to "ACTIVE" by itself.
583 * If the camera is already "IDLE", then the drain immediately finishes.
585 mIdleDrainer.beginDrain();
590 private class IdleDrainListener implements TaskDrainer.DrainListener {
592 public void onDrained() {
593 synchronized (CameraCaptureSessionImpl.this) {
595 * The device is now IDLE, and has settled. It will not transition to
596 * ACTIVE or BUSY again by itself.
598 * It's now safe to unconfigure the outputs and after it's done invoke #onClosed.
600 * This operation is idempotent; a session will not be closed twice.
603 // Fast path: A new capture session has replaced this one; don't unconfigure.
604 if (mSkipUnconfigure) {
605 mStateListener.onClosed(CameraCaptureSessionImpl.this);
609 // Slow path: #close was called explicitly on this session; unconfigure first
612 mUnconfigureDrainer.taskStarted();
613 mDeviceImpl.configureOutputs(null); // begin transition to unconfigured state
614 } catch (CameraAccessException e) {
615 // OK: do not throw checked exceptions.
616 Log.e(TAG, "Exception while configuring outputs: ", e);
618 // TODO: call onError instead of onClosed if this happens
621 mUnconfigureDrainer.beginDrain();
626 private class UnconfigureDrainListener implements TaskDrainer.DrainListener {
628 public void onDrained() {
629 synchronized (CameraCaptureSessionImpl.this) {
630 // The device has finished unconfiguring. It's now fully closed.
631 mStateListener.onClosed(CameraCaptureSessionImpl.this);