CameraCaptureSessionImpl.java revision 7875a886b8397684c5fac3dd022d19de9874b436
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16package android.hardware.camera2.impl; 17 18import android.hardware.camera2.CameraAccessException; 19import android.hardware.camera2.CameraCaptureSession; 20import android.hardware.camera2.CameraDevice; 21import android.hardware.camera2.CaptureRequest; 22import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher; 23import android.hardware.camera2.dispatch.BroadcastDispatcher; 24import android.hardware.camera2.dispatch.Dispatchable; 25import android.hardware.camera2.dispatch.DuckTypingDispatcher; 26import android.hardware.camera2.dispatch.HandlerDispatcher; 27import android.hardware.camera2.dispatch.InvokeDispatcher; 28import android.hardware.camera2.dispatch.NullDispatcher; 29import android.hardware.camera2.utils.TaskDrainer; 30import android.hardware.camera2.utils.TaskSingleDrainer; 31import android.os.Handler; 32import android.util.Log; 33import android.view.Surface; 34 35import java.util.Arrays; 36import java.util.List; 37 38import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler; 39import static com.android.internal.util.Preconditions.*; 40 41public class CameraCaptureSessionImpl extends CameraCaptureSession { 42 private static final String TAG = "CameraCaptureSession"; 43 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 44 45 /** User-specified set of surfaces used as the configuration outputs */ 46 private final List<Surface> mOutputs; 47 /** 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}. 50 */ 51 private final CameraCaptureSession.StateListener mStateListener; 52 /** User-specified state handler used for outgoing state listener events */ 53 private final Handler mStateHandler; 54 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; 59 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; 68 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; 73 74 /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */ 75 private boolean mAborting; 76 77 /** 78 * Create a new CameraCaptureSession. 79 * 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> 83 */ 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"); 92 } 93 94 // TODO: extra verification of outputs 95 mOutputs = outputs; 96 mStateHandler = checkHandler(stateHandler); 97 mStateListener = createUserStateListenerProxy(mStateHandler, listener); 98 99 mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null"); 100 mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null"); 101 102 /* 103 * Use the same handler as the device's StateListener for all the internal coming events 104 * 105 * This ensures total ordering between CameraDevice.StateListener and 106 * CameraDevice.CaptureListener events. 107 */ 108 mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(), 109 /*name*/"seq"); 110 mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(), 111 /*name*/"idle"); 112 mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(), 113 /*name*/"abort"); 114 mUnconfigureDrainer = new TaskSingleDrainer(mDeviceHandler, new UnconfigureDrainListener(), 115 /*name*/"unconf"); 116 117 // CameraDevice should call configureOutputs and have it finish before constructing us 118 119 if (configureSuccess) { 120 mStateListener.onConfigured(this); 121 if (VERBOSE) Log.v(TAG, "ctor - Created session successfully"); 122 } else { 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"); 126 } 127 } 128 129 @Override 130 public CameraDevice getDevice() { 131 return mDeviceImpl; 132 } 133 134 @Override 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"); 139 } 140 141 checkNotClosed(); 142 checkLegalToCapture(); 143 144 handler = checkHandler(handler, listener); 145 146 if (VERBOSE) { 147 Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" + 148 " " + handler); 149 } 150 151 return addPendingSequence(mDeviceImpl.capture(request, 152 createCaptureListenerProxy(handler, listener), mDeviceHandler)); 153 } 154 155 @Override 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"); 162 } 163 164 checkNotClosed(); 165 checkLegalToCapture(); 166 167 handler = checkHandler(handler, listener); 168 169 if (VERBOSE) { 170 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); 171 Log.v(TAG, "captureBurst - requests " + Arrays.toString(requestArray) + ", listener " + 172 listener + " handler" + "" + handler); 173 } 174 175 return addPendingSequence(mDeviceImpl.captureBurst(requests, 176 createCaptureListenerProxy(handler, listener), mDeviceHandler)); 177 } 178 179 @Override 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"); 184 } 185 186 checkNotClosed(); 187 checkLegalToCapture(); 188 189 handler = checkHandler(handler, listener); 190 191 if (VERBOSE) { 192 Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener + 193 " handler" + " " + handler); 194 } 195 196 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request, 197 createCaptureListenerProxy(handler, listener), mDeviceHandler)); 198 } 199 200 @Override 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"); 207 } 208 209 checkNotClosed(); 210 checkLegalToCapture(); 211 212 handler = checkHandler(handler, listener); 213 214 if (VERBOSE) { 215 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); 216 Log.v(TAG, "setRepeatingBurst - requests " + Arrays.toString(requestArray) + 217 ", listener " + listener + " handler" + "" + handler); 218 } 219 220 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests, 221 createCaptureListenerProxy(handler, listener), mDeviceHandler)); 222 } 223 224 @Override 225 public synchronized void stopRepeating() throws CameraAccessException { 226 checkNotClosed(); 227 228 if (VERBOSE) { 229 Log.v(TAG, "stopRepeating"); 230 } 231 232 mDeviceImpl.stopRepeating(); 233 } 234 235 @Override 236 public synchronized void abortCaptures() throws CameraAccessException { 237 checkNotClosed(); 238 239 if (VERBOSE) { 240 Log.v(TAG, "abortCaptures"); 241 } 242 243 if (mAborting) { 244 Log.w(TAG, "abortCaptures - Session is already aborting; doing nothing"); 245 return; 246 } 247 248 mAborting = true; 249 mAbortDrainer.taskStarted(); 250 251 mDeviceImpl.flush(); 252 // The next BUSY -> IDLE set of transitions will mark the end of the abort. 253 } 254 255 /** 256 * Replace this session with another session. 257 * 258 * <p>This is an optimization to avoid unconfiguring and then immediately having to 259 * reconfigure again.</p> 260 * 261 * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped. 262 * <p> 263 * 264 * @see CameraCaptureSession#close 265 */ 266 synchronized void replaceSessionClose(CameraCaptureSession other) { 267 /* 268 * In order for creating new sessions to be fast, the new session should be created 269 * before the old session is closed. 270 * 271 * Otherwise the old session will always unconfigure if there is no new session to 272 * replace it. 273 * 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. 277 */ 278 279 if (VERBOSE) Log.v(TAG, "replaceSessionClose"); 280 281 // #close was already called explicitly, keep going the slow route 282 if (mClosed) { 283 if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called"); 284 return; 285 } 286 287 mSkipUnconfigure = true; 288 close(); 289 } 290 291 @Override 292 public synchronized void close() { 293 294 if (mClosed) { 295 if (VERBOSE) Log.v(TAG, "close - reentering"); 296 return; 297 } 298 299 if (VERBOSE) Log.v(TAG, "close - first time"); 300 301 mClosed = true; 302 303 /* 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. 306 * 307 * If the camera device was already closed, short circuit and do nothing; since 308 * no more internal device callbacks will fire anyway. 309 * 310 * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the 311 * camera. Once that's done, fire #onClosed. 312 */ 313 try { 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); 318 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. 322 323 // Short-circuit; queue listener immediately and return 324 mStateListener.onClosed(this); 325 return; 326 } catch (CameraAccessException e) { 327 // OK: close does not throw checked exceptions. 328 Log.e(TAG, "Exception while stopping repeating: ", e); 329 330 // TODO: call onError instead of onClosed if this happens 331 } 332 333 // If no sequences are pending, fire #onClosed immediately 334 mSequenceDrainer.beginDrain(); 335 } 336 337 /** 338 * Post calls into a CameraCaptureSession.StateListener to the user-specified {@code handler}. 339 */ 340 private StateListener createUserStateListenerProxy(Handler handler, StateListener listener) { 341 InvokeDispatcher<StateListener> userListenerSink = new InvokeDispatcher<>(listener); 342 HandlerDispatcher<StateListener> handlerPassthrough = 343 new HandlerDispatcher<>(userListenerSink, handler); 344 345 return new ListenerProxies.SessionStateListenerProxy(handlerPassthrough); 346 } 347 348 /** 349 * Forward callbacks from 350 * CameraDevice.CaptureListener to the CameraCaptureSession.CaptureListener. 351 * 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> 355 * 356 * <p>When a capture sequence finishes, update the pending checked sequences set.</p> 357 */ 358 @SuppressWarnings("deprecation") 359 private CameraDevice.CaptureListener createCaptureListenerProxy( 360 Handler handler, CaptureListener listener) { 361 CameraDevice.CaptureListener localListener = new CameraDevice.CaptureListener() { 362 @Override 363 public void onCaptureSequenceCompleted(CameraDevice camera, 364 int sequenceId, long frameNumber) { 365 finishPendingSequence(sequenceId); 366 } 367 368 @Override 369 public void onCaptureSequenceAborted(CameraDevice camera, 370 int sequenceId) { 371 finishPendingSequence(sequenceId); 372 } 373 }; 374 375 /* 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 381 */ 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 385 // listener 386 return localListener; 387 } 388 389 InvokeDispatcher<CameraDevice.CaptureListener> localSink = 390 new InvokeDispatcher<>(localListener); 391 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); 401 402 BroadcastDispatcher<CameraDevice.CaptureListener> broadcaster = 403 new BroadcastDispatcher<CameraDevice.CaptureListener>( 404 replaceDeviceWithSession, 405 localSink); 406 407 return new ListenerProxies.DeviceCaptureListenerProxy(broadcaster); 408 } 409 410 /** 411 * 412 * Create an internal state listener, to be invoked on the mDeviceHandler 413 * 414 * <p>It has a few behaviors: 415 * <ul> 416 * <li>Convert device state changes into session state changes. 417 * <li>Keep track of async tasks that the session began (idle, abort). 418 * </ul> 419 * </p> 420 * */ 421 CameraDevice.StateListener getDeviceStateListener() { 422 final CameraCaptureSession session = this; 423 424 return new CameraDevice.StateListener() { 425 private boolean mBusy = false; 426 private boolean mActive = false; 427 428 @Override 429 public void onOpened(CameraDevice camera) { 430 throw new AssertionError("Camera must already be open before creating a session"); 431 } 432 433 @Override 434 public void onDisconnected(CameraDevice camera) { 435 close(); 436 } 437 438 @Override 439 public void onError(CameraDevice camera, int error) { 440 // TODO: Handle errors somehow. 441 Log.wtf(TAG, "Got device error " + error); 442 } 443 444 @Override 445 public void onActive(CameraDevice camera) { 446 mIdleDrainer.taskStarted(); 447 mActive = true; 448 449 mStateListener.onActive(session); 450 } 451 452 @Override 453 public void onIdle(CameraDevice camera) { 454 boolean isAborting; 455 synchronized (session) { 456 isAborting = mAborting; 457 } 458 459 /* 460 * Check which states we transitioned through: 461 * 462 * (ACTIVE -> IDLE) 463 * (BUSY -> IDLE) 464 * 465 * Note that this is also legal: 466 * (ACTIVE -> BUSY -> IDLE) 467 * 468 * and mark those tasks as finished 469 */ 470 if (mBusy && isAborting) { 471 mAbortDrainer.taskFinished(); 472 473 synchronized (session) { 474 mAborting = false; 475 } 476 } 477 478 if (mActive) { 479 mIdleDrainer.taskFinished(); 480 } 481 482 mBusy = false; 483 mActive = false; 484 485 mStateListener.onReady(session); 486 } 487 488 @Override 489 public void onBusy(CameraDevice camera) { 490 mBusy = true; 491 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); 496 } 497 498 @Override 499 public void onUnconfigured(CameraDevice camera) { 500 synchronized (session) { 501 // Ignore #onUnconfigured before #close is called 502 if (mClosed) { 503 mUnconfigureDrainer.taskFinished(); 504 } 505 } 506 } 507 }; 508 509 } 510 511 @Override 512 protected void finalize() throws Throwable { 513 try { 514 close(); 515 } finally { 516 super.finalize(); 517 } 518 } 519 520 private void checkLegalToCapture() { 521 if (mAborting) { 522 throw new IllegalStateException( 523 "Session is aborting captures; new captures are not permitted"); 524 } 525 } 526 527 private void checkNotClosed() { 528 if (mClosed) { 529 throw new IllegalStateException( 530 "Session has been closed; further changes are illegal."); 531 } 532 } 533 534 /** 535 * Notify the session that a pending capture sequence has just been queued. 536 * 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> 539 * 540 * @see #finishPendingSequence 541 */ 542 private int addPendingSequence(int sequenceId) { 543 mSequenceDrainer.taskStarted(sequenceId); 544 return sequenceId; 545 } 546 547 /** 548 * Notify the session that a pending capture sequence is now finished. 549 * 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> 552 */ 553 private void finishPendingSequence(int sequenceId) { 554 mSequenceDrainer.taskFinished(sequenceId); 555 } 556 557 private class SequenceDrainListener implements TaskDrainer.DrainListener { 558 @Override 559 public void onDrained() { 560 /* 561 * No repeating request is set; and the capture queue has fully drained. 562 * 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. 565 * 566 * If the camera is already "IDLE" and no aborts are pending, 567 * then the drain immediately finishes. 568 */ 569 mAbortDrainer.beginDrain(); 570 } 571 } 572 573 private class AbortDrainListener implements TaskDrainer.DrainListener { 574 @Override 575 public void onDrained() { 576 synchronized (CameraCaptureSessionImpl.this) { 577 /* 578 * Any queued aborts have now completed. 579 * 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. 582 * 583 * If the camera is already "IDLE", then the drain immediately finishes. 584 */ 585 mIdleDrainer.beginDrain(); 586 } 587 } 588 } 589 590 private class IdleDrainListener implements TaskDrainer.DrainListener { 591 @Override 592 public void onDrained() { 593 synchronized (CameraCaptureSessionImpl.this) { 594 /* 595 * The device is now IDLE, and has settled. It will not transition to 596 * ACTIVE or BUSY again by itself. 597 * 598 * It's now safe to unconfigure the outputs and after it's done invoke #onClosed. 599 * 600 * This operation is idempotent; a session will not be closed twice. 601 */ 602 603 // Fast path: A new capture session has replaced this one; don't unconfigure. 604 if (mSkipUnconfigure) { 605 mStateListener.onClosed(CameraCaptureSessionImpl.this); 606 return; 607 } 608 609 // Slow path: #close was called explicitly on this session; unconfigure first 610 611 try { 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); 617 618 // TODO: call onError instead of onClosed if this happens 619 } 620 621 mUnconfigureDrainer.beginDrain(); 622 } 623 } 624 } 625 626 private class UnconfigureDrainListener implements TaskDrainer.DrainListener { 627 @Override 628 public void onDrained() { 629 synchronized (CameraCaptureSessionImpl.this) { 630 // The device has finished unconfiguring. It's now fully closed. 631 mStateListener.onClosed(CameraCaptureSessionImpl.this); 632 } 633 } 634 } 635} 636