CameraDeviceImpl.java revision a45aa0d54937495c30a89b445ced8b686940cc08
1/* 2 * Copyright (C) 2013 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 */ 16 17package android.hardware.camera2.impl; 18 19import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; 20 21import android.hardware.camera2.CameraAccessException; 22import android.hardware.camera2.CameraCaptureSession; 23import android.hardware.camera2.CameraCharacteristics; 24import android.hardware.camera2.CameraDevice; 25import android.hardware.camera2.CaptureRequest; 26import android.hardware.camera2.CaptureResult; 27import android.hardware.camera2.CaptureFailure; 28import android.hardware.camera2.ICameraDeviceCallbacks; 29import android.hardware.camera2.ICameraDeviceUser; 30import android.hardware.camera2.TotalCaptureResult; 31import android.hardware.camera2.params.InputConfiguration; 32import android.hardware.camera2.params.OutputConfiguration; 33import android.hardware.camera2.params.ReprocessFormatsMap; 34import android.hardware.camera2.params.StreamConfigurationMap; 35import android.hardware.camera2.utils.CameraBinderDecorator; 36import android.hardware.camera2.utils.CameraRuntimeException; 37import android.hardware.camera2.utils.LongParcelable; 38import android.os.Handler; 39import android.os.IBinder; 40import android.os.Looper; 41import android.os.RemoteException; 42import android.util.Log; 43import android.util.Size; 44import android.util.SparseArray; 45import android.view.Surface; 46 47import java.util.AbstractMap.SimpleEntry; 48import java.util.ArrayList; 49import java.util.HashMap; 50import java.util.HashSet; 51import java.util.Iterator; 52import java.util.List; 53import java.util.LinkedList; 54import java.util.TreeMap; 55 56/** 57 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate 58 */ 59public class CameraDeviceImpl extends CameraDevice { 60 private final String TAG; 61 private final boolean DEBUG = false; 62 63 private static final int REQUEST_ID_NONE = -1; 64 65 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) 66 private ICameraDeviceUser mRemoteDevice; 67 68 // Lock to synchronize cross-thread access to device public interface 69 final Object mInterfaceLock = new Object(); // access from this class and Session only! 70 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); 71 72 private final StateCallback mDeviceCallback; 73 private volatile StateCallbackKK mSessionStateCallback; 74 private final Handler mDeviceHandler; 75 76 private volatile boolean mClosing = false; 77 private boolean mInError = false; 78 private boolean mIdle = true; 79 80 /** map request IDs to callback/request data */ 81 private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap = 82 new SparseArray<CaptureCallbackHolder>(); 83 84 private int mRepeatingRequestId = REQUEST_ID_NONE; 85 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>(); 86 // Map stream IDs to input/output configurations 87 private SimpleEntry<Integer, InputConfiguration> mConfiguredInput = 88 new SimpleEntry<>(REQUEST_ID_NONE, null); 89 private final SparseArray<OutputConfiguration> mConfiguredOutputs = 90 new SparseArray<>(); 91 92 private final String mCameraId; 93 private final CameraCharacteristics mCharacteristics; 94 private final int mTotalPartialCount; 95 96 /** 97 * A list tracking request and its expected last regular frame number and last reprocess frame 98 * number. Updated when calling ICameraDeviceUser methods. 99 */ 100 private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList = 101 new ArrayList<>(); 102 103 /** 104 * An object tracking received frame numbers. 105 * Updated when receiving callbacks from ICameraDeviceCallbacks. 106 */ 107 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); 108 109 private CameraCaptureSessionImpl mCurrentSession; 110 private int mNextSessionId = 0; 111 112 // Runnables for all state transitions, except error, which needs the 113 // error code argument 114 115 private final Runnable mCallOnOpened = new Runnable() { 116 @Override 117 public void run() { 118 StateCallbackKK sessionCallback = null; 119 synchronized(mInterfaceLock) { 120 if (mRemoteDevice == null) return; // Camera already closed 121 122 sessionCallback = mSessionStateCallback; 123 } 124 if (sessionCallback != null) { 125 sessionCallback.onOpened(CameraDeviceImpl.this); 126 } 127 mDeviceCallback.onOpened(CameraDeviceImpl.this); 128 } 129 }; 130 131 private final Runnable mCallOnUnconfigured = new Runnable() { 132 @Override 133 public void run() { 134 StateCallbackKK sessionCallback = null; 135 synchronized(mInterfaceLock) { 136 if (mRemoteDevice == null) return; // Camera already closed 137 138 sessionCallback = mSessionStateCallback; 139 } 140 if (sessionCallback != null) { 141 sessionCallback.onUnconfigured(CameraDeviceImpl.this); 142 } 143 } 144 }; 145 146 private final Runnable mCallOnActive = new Runnable() { 147 @Override 148 public void run() { 149 StateCallbackKK sessionCallback = null; 150 synchronized(mInterfaceLock) { 151 if (mRemoteDevice == null) return; // Camera already closed 152 153 sessionCallback = mSessionStateCallback; 154 } 155 if (sessionCallback != null) { 156 sessionCallback.onActive(CameraDeviceImpl.this); 157 } 158 } 159 }; 160 161 private final Runnable mCallOnBusy = new Runnable() { 162 @Override 163 public void run() { 164 StateCallbackKK sessionCallback = null; 165 synchronized(mInterfaceLock) { 166 if (mRemoteDevice == null) return; // Camera already closed 167 168 sessionCallback = mSessionStateCallback; 169 } 170 if (sessionCallback != null) { 171 sessionCallback.onBusy(CameraDeviceImpl.this); 172 } 173 } 174 }; 175 176 private final Runnable mCallOnClosed = new Runnable() { 177 private boolean mClosedOnce = false; 178 179 @Override 180 public void run() { 181 if (mClosedOnce) { 182 throw new AssertionError("Don't post #onClosed more than once"); 183 } 184 StateCallbackKK sessionCallback = null; 185 synchronized(mInterfaceLock) { 186 sessionCallback = mSessionStateCallback; 187 } 188 if (sessionCallback != null) { 189 sessionCallback.onClosed(CameraDeviceImpl.this); 190 } 191 mDeviceCallback.onClosed(CameraDeviceImpl.this); 192 mClosedOnce = true; 193 } 194 }; 195 196 private final Runnable mCallOnIdle = new Runnable() { 197 @Override 198 public void run() { 199 StateCallbackKK sessionCallback = null; 200 synchronized(mInterfaceLock) { 201 if (mRemoteDevice == null) return; // Camera already closed 202 203 sessionCallback = mSessionStateCallback; 204 } 205 if (sessionCallback != null) { 206 sessionCallback.onIdle(CameraDeviceImpl.this); 207 } 208 } 209 }; 210 211 private final Runnable mCallOnDisconnected = new Runnable() { 212 @Override 213 public void run() { 214 StateCallbackKK sessionCallback = null; 215 synchronized(mInterfaceLock) { 216 if (mRemoteDevice == null) return; // Camera already closed 217 218 sessionCallback = mSessionStateCallback; 219 } 220 if (sessionCallback != null) { 221 sessionCallback.onDisconnected(CameraDeviceImpl.this); 222 } 223 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 224 } 225 }; 226 227 public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler, 228 CameraCharacteristics characteristics) { 229 if (cameraId == null || callback == null || handler == null || characteristics == null) { 230 throw new IllegalArgumentException("Null argument given"); 231 } 232 mCameraId = cameraId; 233 mDeviceCallback = callback; 234 mDeviceHandler = handler; 235 mCharacteristics = characteristics; 236 237 final int MAX_TAG_LEN = 23; 238 String tag = String.format("CameraDevice-JV-%s", mCameraId); 239 if (tag.length() > MAX_TAG_LEN) { 240 tag = tag.substring(0, MAX_TAG_LEN); 241 } 242 TAG = tag; 243 244 Integer partialCount = 245 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT); 246 if (partialCount == null) { 247 // 1 means partial result is not supported. 248 mTotalPartialCount = 1; 249 } else { 250 mTotalPartialCount = partialCount; 251 } 252 } 253 254 public CameraDeviceCallbacks getCallbacks() { 255 return mCallbacks; 256 } 257 258 public void setRemoteDevice(ICameraDeviceUser remoteDevice) { 259 synchronized(mInterfaceLock) { 260 // TODO: Move from decorator to direct binder-mediated exceptions 261 // If setRemoteFailure already called, do nothing 262 if (mInError) return; 263 264 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice); 265 266 mDeviceHandler.post(mCallOnOpened); 267 mDeviceHandler.post(mCallOnUnconfigured); 268 } 269 } 270 271 /** 272 * Call to indicate failed connection to a remote camera device. 273 * 274 * <p>This places the camera device in the error state and informs the callback. 275 * Use in place of setRemoteDevice() when startup fails.</p> 276 */ 277 public void setRemoteFailure(final CameraRuntimeException failure) { 278 int failureCode = StateCallback.ERROR_CAMERA_DEVICE; 279 boolean failureIsError = true; 280 281 switch (failure.getReason()) { 282 case CameraAccessException.CAMERA_IN_USE: 283 failureCode = StateCallback.ERROR_CAMERA_IN_USE; 284 break; 285 case CameraAccessException.MAX_CAMERAS_IN_USE: 286 failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE; 287 break; 288 case CameraAccessException.CAMERA_DISABLED: 289 failureCode = StateCallback.ERROR_CAMERA_DISABLED; 290 break; 291 case CameraAccessException.CAMERA_DISCONNECTED: 292 failureIsError = false; 293 break; 294 case CameraAccessException.CAMERA_ERROR: 295 failureCode = StateCallback.ERROR_CAMERA_DEVICE; 296 break; 297 default: 298 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason()); 299 break; 300 } 301 final int code = failureCode; 302 final boolean isError = failureIsError; 303 synchronized(mInterfaceLock) { 304 mInError = true; 305 mDeviceHandler.post(new Runnable() { 306 @Override 307 public void run() { 308 if (isError) { 309 mDeviceCallback.onError(CameraDeviceImpl.this, code); 310 } else { 311 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 312 } 313 } 314 }); 315 } 316 } 317 318 @Override 319 public String getId() { 320 return mCameraId; 321 } 322 323 public void configureOutputs(List<Surface> outputs) throws CameraAccessException { 324 // Leave this here for backwards compatibility with older code using this directly 325 ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size()); 326 for (Surface s : outputs) { 327 outputConfigs.add(new OutputConfiguration(s)); 328 } 329 configureStreamsChecked(/*inputConfig*/null, outputConfigs); 330 331 } 332 333 /** 334 * Attempt to configure the input and outputs; the device goes to idle and then configures the 335 * new input and outputs if possible. 336 * 337 * <p>The configuration may gracefully fail, if input configuration is not supported, 338 * if there are too many outputs, if the formats are not supported, or if the sizes for that 339 * format is not supported. In this case this function will return {@code false} and the 340 * unconfigured callback will be fired.</p> 341 * 342 * <p>If the configuration succeeds (with 1 or more outputs with or without an input), 343 * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p> 344 * 345 * @param inputConfig input configuration or {@code null} for no input 346 * @param outputs a list of one or more surfaces, or {@code null} to unconfigure 347 * @return whether or not the configuration was successful 348 * 349 * @throws CameraAccessException if there were any unexpected problems during configuration 350 */ 351 public boolean configureStreamsChecked(InputConfiguration inputConfig, 352 List<OutputConfiguration> outputs) throws CameraAccessException { 353 // Treat a null input the same an empty list 354 if (outputs == null) { 355 outputs = new ArrayList<OutputConfiguration>(); 356 } 357 if (outputs.size() == 0 && inputConfig != null) { 358 throw new IllegalArgumentException("cannot configure an input stream without " + 359 "any output streams"); 360 } 361 362 checkInputConfiguration(inputConfig); 363 364 boolean success = false; 365 366 synchronized(mInterfaceLock) { 367 checkIfCameraClosedOrInError(); 368 // Streams to create 369 HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs); 370 // Streams to delete 371 List<Integer> deleteList = new ArrayList<Integer>(); 372 373 // Determine which streams need to be created, which to be deleted 374 for (int i = 0; i < mConfiguredOutputs.size(); ++i) { 375 int streamId = mConfiguredOutputs.keyAt(i); 376 OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i); 377 378 if (!outputs.contains(outConfig)) { 379 deleteList.add(streamId); 380 } else { 381 addSet.remove(outConfig); // Don't create a stream previously created 382 } 383 } 384 385 mDeviceHandler.post(mCallOnBusy); 386 stopRepeating(); 387 388 try { 389 waitUntilIdle(); 390 391 mRemoteDevice.beginConfigure(); 392 393 // reconfigure the input stream if the input configuration is different. 394 InputConfiguration currentInputConfig = mConfiguredInput.getValue(); 395 if (inputConfig != currentInputConfig && 396 (inputConfig == null || !inputConfig.equals(currentInputConfig))) { 397 if (currentInputConfig != null) { 398 mRemoteDevice.deleteStream(mConfiguredInput.getKey()); 399 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>( 400 REQUEST_ID_NONE, null); 401 } 402 if (inputConfig != null) { 403 int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(), 404 inputConfig.getHeight(), inputConfig.getFormat()); 405 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>( 406 streamId, inputConfig); 407 } 408 } 409 410 // Delete all streams first (to free up HW resources) 411 for (Integer streamId : deleteList) { 412 mRemoteDevice.deleteStream(streamId); 413 mConfiguredOutputs.delete(streamId); 414 } 415 416 // Add all new streams 417 for (OutputConfiguration outConfig : outputs) { 418 if (addSet.contains(outConfig)) { 419 int streamId = mRemoteDevice.createStream(outConfig); 420 mConfiguredOutputs.put(streamId, outConfig); 421 } 422 } 423 424 try { 425 mRemoteDevice.endConfigure(); 426 } 427 catch (IllegalArgumentException e) { 428 // OK. camera service can reject stream config if it's not supported by HAL 429 // This is only the result of a programmer misusing the camera2 api. 430 Log.w(TAG, "Stream configuration failed"); 431 return false; 432 } 433 434 success = true; 435 } catch (CameraRuntimeException e) { 436 if (e.getReason() == CAMERA_IN_USE) { 437 throw new IllegalStateException("The camera is currently busy." + 438 " You must wait until the previous operation completes."); 439 } 440 441 throw e.asChecked(); 442 } catch (RemoteException e) { 443 // impossible 444 return false; 445 } finally { 446 if (success && outputs.size() > 0) { 447 mDeviceHandler.post(mCallOnIdle); 448 } else { 449 // Always return to the 'unconfigured' state if we didn't hit a fatal error 450 mDeviceHandler.post(mCallOnUnconfigured); 451 } 452 } 453 } 454 455 return success; 456 } 457 458 @Override 459 public void createCaptureSession(List<Surface> outputs, 460 CameraCaptureSession.StateCallback callback, Handler handler) 461 throws CameraAccessException { 462 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 463 for (Surface surface : outputs) { 464 outConfigurations.add(new OutputConfiguration(surface)); 465 } 466 createCaptureSessionInternal(null, outConfigurations, callback, handler); 467 } 468 469 @Override 470 public void createCaptureSessionByOutputConfiguration( 471 List<OutputConfiguration> outputConfigurations, 472 CameraCaptureSession.StateCallback callback, Handler handler) 473 throws CameraAccessException { 474 if (DEBUG) { 475 Log.d(TAG, "createCaptureSessionByOutputConfiguration"); 476 } 477 478 createCaptureSessionInternal(null, outputConfigurations, callback, handler); 479 } 480 481 @Override 482 public void createReprocessableCaptureSession(InputConfiguration inputConfig, 483 List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) 484 throws CameraAccessException { 485 if (DEBUG) { 486 Log.d(TAG, "createReprocessableCaptureSession"); 487 } 488 489 if (inputConfig == null) { 490 throw new IllegalArgumentException("inputConfig cannot be null when creating a " + 491 "reprocessable capture session"); 492 } 493 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 494 for (Surface surface : outputs) { 495 outConfigurations.add(new OutputConfiguration(surface)); 496 } 497 createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler); 498 } 499 500 private void createCaptureSessionInternal(InputConfiguration inputConfig, 501 List<OutputConfiguration> outputConfigurations, 502 CameraCaptureSession.StateCallback callback, Handler handler) 503 throws CameraAccessException { 504 synchronized(mInterfaceLock) { 505 if (DEBUG) { 506 Log.d(TAG, "createCaptureSessionInternal"); 507 } 508 509 checkIfCameraClosedOrInError(); 510 511 // Notify current session that it's going away, before starting camera operations 512 // After this call completes, the session is not allowed to call into CameraDeviceImpl 513 if (mCurrentSession != null) { 514 mCurrentSession.replaceSessionClose(); 515 } 516 517 // TODO: dont block for this 518 boolean configureSuccess = true; 519 CameraAccessException pendingException = null; 520 Surface input = null; 521 try { 522 // configure streams and then block until IDLE 523 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations); 524 if (inputConfig != null) { 525 input = new Surface(); 526 mRemoteDevice.getInputSurface(/*out*/input); 527 } 528 } catch (CameraAccessException e) { 529 configureSuccess = false; 530 pendingException = e; 531 input = null; 532 if (DEBUG) { 533 Log.v(TAG, "createCaptureSession - failed with exception ", e); 534 } 535 } catch (RemoteException e) { 536 // impossible 537 return; 538 } 539 540 List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size()); 541 for (OutputConfiguration config : outputConfigurations) { 542 outSurfaces.add(config.getSurface()); 543 } 544 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. 545 CameraCaptureSessionImpl newSession = 546 new CameraCaptureSessionImpl(mNextSessionId++, input, 547 outSurfaces, callback, handler, this, mDeviceHandler, 548 configureSuccess); 549 550 // TODO: wait until current session closes, then create the new session 551 mCurrentSession = newSession; 552 553 if (pendingException != null) { 554 throw pendingException; 555 } 556 557 mSessionStateCallback = mCurrentSession.getDeviceStateCallback(); 558 } 559 } 560 561 /** 562 * For use by backwards-compatibility code only. 563 */ 564 public void setSessionListener(StateCallbackKK sessionCallback) { 565 synchronized(mInterfaceLock) { 566 mSessionStateCallback = sessionCallback; 567 } 568 } 569 570 @Override 571 public CaptureRequest.Builder createCaptureRequest(int templateType) 572 throws CameraAccessException { 573 synchronized(mInterfaceLock) { 574 checkIfCameraClosedOrInError(); 575 576 CameraMetadataNative templatedRequest = new CameraMetadataNative(); 577 578 try { 579 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest); 580 } catch (CameraRuntimeException e) { 581 throw e.asChecked(); 582 } catch (RemoteException e) { 583 // impossible 584 return null; 585 } 586 587 CaptureRequest.Builder builder = new CaptureRequest.Builder( 588 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); 589 590 return builder; 591 } 592 } 593 594 @Override 595 public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult) 596 throws CameraAccessException { 597 synchronized(mInterfaceLock) { 598 checkIfCameraClosedOrInError(); 599 600 CameraMetadataNative resultMetadata = new 601 CameraMetadataNative(inputResult.getNativeCopy()); 602 603 return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true, 604 inputResult.getSessionId()); 605 } 606 } 607 608 public void prepare(Surface surface) throws CameraAccessException { 609 if (surface == null) throw new IllegalArgumentException("Surface is null"); 610 611 synchronized(mInterfaceLock) { 612 int streamId = -1; 613 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 614 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 615 streamId = mConfiguredOutputs.keyAt(i); 616 break; 617 } 618 } 619 if (streamId == -1) { 620 throw new IllegalArgumentException("Surface is not part of this session"); 621 } 622 try { 623 mRemoteDevice.prepare(streamId); 624 } catch (CameraRuntimeException e) { 625 throw e.asChecked(); 626 } catch (RemoteException e) { 627 // impossible 628 return; 629 } 630 } 631 } 632 633 public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) 634 throws CameraAccessException { 635 if (DEBUG) { 636 Log.d(TAG, "calling capture"); 637 } 638 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 639 requestList.add(request); 640 return submitCaptureRequest(requestList, callback, handler, /*streaming*/false); 641 } 642 643 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 644 Handler handler) throws CameraAccessException { 645 if (requests == null || requests.isEmpty()) { 646 throw new IllegalArgumentException("At least one request must be given"); 647 } 648 return submitCaptureRequest(requests, callback, handler, /*streaming*/false); 649 } 650 651 /** 652 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for 653 * starting and stopping repeating request and flushing. 654 * 655 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never 656 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. 657 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last 658 * regular frame number will be added to the list mRequestLastFrameNumbersList.</p> 659 * 660 * @param requestId the request ID of the current repeating request. 661 * 662 * @param lastFrameNumber last frame number returned from binder. 663 */ 664 private void checkEarlyTriggerSequenceComplete( 665 final int requestId, final long lastFrameNumber) { 666 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request 667 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. 668 if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 669 final CaptureCallbackHolder holder; 670 int index = mCaptureCallbackMap.indexOfKey(requestId); 671 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; 672 if (holder != null) { 673 mCaptureCallbackMap.removeAt(index); 674 if (DEBUG) { 675 Log.v(TAG, String.format( 676 "remove holder for requestId %d, " 677 + "because lastFrame is %d.", 678 requestId, lastFrameNumber)); 679 } 680 } 681 682 if (holder != null) { 683 if (DEBUG) { 684 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because" 685 + " request did not reach HAL"); 686 } 687 688 Runnable resultDispatch = new Runnable() { 689 @Override 690 public void run() { 691 if (!CameraDeviceImpl.this.isClosed()) { 692 if (DEBUG) { 693 Log.d(TAG, String.format( 694 "early trigger sequence complete for request %d", 695 requestId)); 696 } 697 holder.getCallback().onCaptureSequenceAborted( 698 CameraDeviceImpl.this, 699 requestId); 700 } 701 } 702 }; 703 holder.getHandler().post(resultDispatch); 704 } else { 705 Log.w(TAG, String.format( 706 "did not register callback to request %d", 707 requestId)); 708 } 709 } else { 710 // This function is only called for regular request so lastFrameNumber is the last 711 // regular frame number. 712 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId, 713 lastFrameNumber)); 714 715 // It is possible that the last frame has already arrived, so we need to check 716 // for sequence completion right away 717 checkAndFireSequenceComplete(); 718 } 719 } 720 721 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, 722 Handler handler, boolean repeating) throws CameraAccessException { 723 724 // Need a valid handler, or current thread needs to have a looper, if 725 // callback is valid 726 handler = checkHandler(handler, callback); 727 728 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 729 for (CaptureRequest request : requestList) { 730 if (request.getTargets().isEmpty()) { 731 throw new IllegalArgumentException( 732 "Each request must have at least one Surface target"); 733 } 734 735 for (Surface surface : request.getTargets()) { 736 if (surface == null) { 737 throw new IllegalArgumentException("Null Surface targets are not allowed"); 738 } 739 } 740 } 741 742 synchronized(mInterfaceLock) { 743 checkIfCameraClosedOrInError(); 744 int requestId; 745 746 if (repeating) { 747 stopRepeating(); 748 } 749 750 LongParcelable lastFrameNumberRef = new LongParcelable(); 751 try { 752 requestId = mRemoteDevice.submitRequestList(requestList, repeating, 753 /*out*/lastFrameNumberRef); 754 if (DEBUG) { 755 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber()); 756 } 757 } catch (CameraRuntimeException e) { 758 throw e.asChecked(); 759 } catch (RemoteException e) { 760 // impossible 761 return -1; 762 } 763 764 if (callback != null) { 765 mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback, 766 requestList, handler, repeating, mNextSessionId - 1)); 767 } else { 768 if (DEBUG) { 769 Log.d(TAG, "Listen for request " + requestId + " is null"); 770 } 771 } 772 773 long lastFrameNumber = lastFrameNumberRef.getNumber(); 774 775 if (repeating) { 776 if (mRepeatingRequestId != REQUEST_ID_NONE) { 777 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 778 } 779 mRepeatingRequestId = requestId; 780 } else { 781 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList, 782 requestId, lastFrameNumber)); 783 } 784 785 if (mIdle) { 786 mDeviceHandler.post(mCallOnActive); 787 } 788 mIdle = false; 789 790 return requestId; 791 } 792 } 793 794 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 795 Handler handler) throws CameraAccessException { 796 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 797 requestList.add(request); 798 return submitCaptureRequest(requestList, callback, handler, /*streaming*/true); 799 } 800 801 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, 802 Handler handler) throws CameraAccessException { 803 if (requests == null || requests.isEmpty()) { 804 throw new IllegalArgumentException("At least one request must be given"); 805 } 806 return submitCaptureRequest(requests, callback, handler, /*streaming*/true); 807 } 808 809 public void stopRepeating() throws CameraAccessException { 810 811 synchronized(mInterfaceLock) { 812 checkIfCameraClosedOrInError(); 813 if (mRepeatingRequestId != REQUEST_ID_NONE) { 814 815 int requestId = mRepeatingRequestId; 816 mRepeatingRequestId = REQUEST_ID_NONE; 817 818 // Queue for deletion after in-flight requests finish 819 if (mCaptureCallbackMap.get(requestId) != null) { 820 mRepeatingRequestIdDeletedList.add(requestId); 821 } 822 823 try { 824 LongParcelable lastFrameNumberRef = new LongParcelable(); 825 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef); 826 long lastFrameNumber = lastFrameNumberRef.getNumber(); 827 828 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber); 829 830 } catch (CameraRuntimeException e) { 831 throw e.asChecked(); 832 } catch (RemoteException e) { 833 // impossible 834 return; 835 } 836 } 837 } 838 } 839 840 private void waitUntilIdle() throws CameraAccessException { 841 842 synchronized(mInterfaceLock) { 843 checkIfCameraClosedOrInError(); 844 845 if (mRepeatingRequestId != REQUEST_ID_NONE) { 846 throw new IllegalStateException("Active repeating request ongoing"); 847 } 848 try { 849 mRemoteDevice.waitUntilIdle(); 850 } catch (CameraRuntimeException e) { 851 throw e.asChecked(); 852 } catch (RemoteException e) { 853 // impossible 854 return; 855 } 856 } 857 } 858 859 public void flush() throws CameraAccessException { 860 synchronized(mInterfaceLock) { 861 checkIfCameraClosedOrInError(); 862 863 mDeviceHandler.post(mCallOnBusy); 864 865 // If already idle, just do a busy->idle transition immediately, don't actually 866 // flush. 867 if (mIdle) { 868 mDeviceHandler.post(mCallOnIdle); 869 return; 870 } 871 try { 872 LongParcelable lastFrameNumberRef = new LongParcelable(); 873 mRemoteDevice.flush(/*out*/lastFrameNumberRef); 874 if (mRepeatingRequestId != REQUEST_ID_NONE) { 875 long lastFrameNumber = lastFrameNumberRef.getNumber(); 876 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 877 mRepeatingRequestId = REQUEST_ID_NONE; 878 } 879 } catch (CameraRuntimeException e) { 880 throw e.asChecked(); 881 } catch (RemoteException e) { 882 // impossible 883 return; 884 } 885 } 886 } 887 888 @Override 889 public void close() { 890 synchronized (mInterfaceLock) { 891 try { 892 if (mRemoteDevice != null) { 893 mRemoteDevice.disconnect(); 894 } 895 } catch (CameraRuntimeException e) { 896 Log.e(TAG, "Exception while closing: ", e.asChecked()); 897 } catch (RemoteException e) { 898 // impossible 899 } 900 901 // Only want to fire the onClosed callback once; 902 // either a normal close where the remote device is valid 903 // or a close after a startup error (no remote device but in error state) 904 if (mRemoteDevice != null || mInError) { 905 mDeviceHandler.post(mCallOnClosed); 906 } 907 908 mRemoteDevice = null; 909 } 910 } 911 912 @Override 913 protected void finalize() throws Throwable { 914 try { 915 close(); 916 } 917 finally { 918 super.finalize(); 919 } 920 } 921 922 private void checkInputConfiguration(InputConfiguration inputConfig) { 923 if (inputConfig != null) { 924 StreamConfigurationMap configMap = mCharacteristics.get( 925 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 926 927 int[] inputFormats = configMap.getInputFormats(); 928 boolean validFormat = false; 929 for (int format : inputFormats) { 930 if (format == inputConfig.getFormat()) { 931 validFormat = true; 932 } 933 } 934 935 if (validFormat == false) { 936 throw new IllegalArgumentException("input format " + inputConfig.getFormat() + 937 " is not valid"); 938 } 939 940 boolean validSize = false; 941 Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat()); 942 for (Size s : inputSizes) { 943 if (inputConfig.getWidth() == s.getWidth() && 944 inputConfig.getHeight() == s.getHeight()) { 945 validSize = true; 946 } 947 } 948 949 if (validSize == false) { 950 throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" + 951 inputConfig.getHeight() + " is not valid"); 952 } 953 } 954 } 955 956 /** 957 * <p>A callback for tracking the progress of a {@link CaptureRequest} 958 * submitted to the camera device.</p> 959 * 960 */ 961 public static abstract class CaptureCallback { 962 963 /** 964 * This constant is used to indicate that no images were captured for 965 * the request. 966 * 967 * @hide 968 */ 969 public static final int NO_FRAMES_CAPTURED = -1; 970 971 /** 972 * This method is called when the camera device has started capturing 973 * the output image for the request, at the beginning of image exposure. 974 * 975 * @see android.media.MediaActionSound 976 */ 977 public void onCaptureStarted(CameraDevice camera, 978 CaptureRequest request, long timestamp, long frameNumber) { 979 // default empty implementation 980 } 981 982 /** 983 * This method is called when some results from an image capture are 984 * available. 985 * 986 * @hide 987 */ 988 public void onCapturePartial(CameraDevice camera, 989 CaptureRequest request, CaptureResult result) { 990 // default empty implementation 991 } 992 993 /** 994 * This method is called when an image capture makes partial forward progress; some 995 * (but not all) results from an image capture are available. 996 * 997 */ 998 public void onCaptureProgressed(CameraDevice camera, 999 CaptureRequest request, CaptureResult partialResult) { 1000 // default empty implementation 1001 } 1002 1003 /** 1004 * This method is called when an image capture has fully completed and all the 1005 * result metadata is available. 1006 */ 1007 public void onCaptureCompleted(CameraDevice camera, 1008 CaptureRequest request, TotalCaptureResult result) { 1009 // default empty implementation 1010 } 1011 1012 /** 1013 * This method is called instead of {@link #onCaptureCompleted} when the 1014 * camera device failed to produce a {@link CaptureResult} for the 1015 * request. 1016 */ 1017 public void onCaptureFailed(CameraDevice camera, 1018 CaptureRequest request, CaptureFailure failure) { 1019 // default empty implementation 1020 } 1021 1022 /** 1023 * This method is called independently of the others in CaptureCallback, 1024 * when a capture sequence finishes and all {@link CaptureResult} 1025 * or {@link CaptureFailure} for it have been returned via this callback. 1026 */ 1027 public void onCaptureSequenceCompleted(CameraDevice camera, 1028 int sequenceId, long frameNumber) { 1029 // default empty implementation 1030 } 1031 1032 /** 1033 * This method is called independently of the others in CaptureCallback, 1034 * when a capture sequence aborts before any {@link CaptureResult} 1035 * or {@link CaptureFailure} for it have been returned via this callback. 1036 */ 1037 public void onCaptureSequenceAborted(CameraDevice camera, 1038 int sequenceId) { 1039 // default empty implementation 1040 } 1041 } 1042 1043 /** 1044 * A callback for notifications about the state of a camera device, adding in the callbacks that 1045 * were part of the earlier KK API design, but now only used internally. 1046 */ 1047 public static abstract class StateCallbackKK extends StateCallback { 1048 /** 1049 * The method called when a camera device has no outputs configured. 1050 * 1051 */ 1052 public void onUnconfigured(CameraDevice camera) { 1053 // Default empty implementation 1054 } 1055 1056 /** 1057 * The method called when a camera device begins processing 1058 * {@link CaptureRequest capture requests}. 1059 * 1060 */ 1061 public void onActive(CameraDevice camera) { 1062 // Default empty implementation 1063 } 1064 1065 /** 1066 * The method called when a camera device is busy. 1067 * 1068 */ 1069 public void onBusy(CameraDevice camera) { 1070 // Default empty implementation 1071 } 1072 1073 /** 1074 * The method called when a camera device has finished processing all 1075 * submitted capture requests and has reached an idle state. 1076 * 1077 */ 1078 public void onIdle(CameraDevice camera) { 1079 // Default empty implementation 1080 } 1081 1082 /** 1083 * The method called when the camera device has finished preparing 1084 * an output Surface 1085 */ 1086 public void onSurfacePrepared(Surface surface) { 1087 // Default empty implementation 1088 } 1089 } 1090 1091 static class CaptureCallbackHolder { 1092 1093 private final boolean mRepeating; 1094 private final CaptureCallback mCallback; 1095 private final List<CaptureRequest> mRequestList; 1096 private final Handler mHandler; 1097 private final int mSessionId; 1098 1099 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, 1100 Handler handler, boolean repeating, int sessionId) { 1101 if (callback == null || handler == null) { 1102 throw new UnsupportedOperationException( 1103 "Must have a valid handler and a valid callback"); 1104 } 1105 mRepeating = repeating; 1106 mHandler = handler; 1107 mRequestList = new ArrayList<CaptureRequest>(requestList); 1108 mCallback = callback; 1109 mSessionId = sessionId; 1110 } 1111 1112 public boolean isRepeating() { 1113 return mRepeating; 1114 } 1115 1116 public CaptureCallback getCallback() { 1117 return mCallback; 1118 } 1119 1120 public CaptureRequest getRequest(int subsequenceId) { 1121 if (subsequenceId >= mRequestList.size()) { 1122 throw new IllegalArgumentException( 1123 String.format( 1124 "Requested subsequenceId %d is larger than request list size %d.", 1125 subsequenceId, mRequestList.size())); 1126 } else { 1127 if (subsequenceId < 0) { 1128 throw new IllegalArgumentException(String.format( 1129 "Requested subsequenceId %d is negative", subsequenceId)); 1130 } else { 1131 return mRequestList.get(subsequenceId); 1132 } 1133 } 1134 } 1135 1136 public CaptureRequest getRequest() { 1137 return getRequest(0); 1138 } 1139 1140 public Handler getHandler() { 1141 return mHandler; 1142 } 1143 1144 public int getSessionId() { 1145 return mSessionId; 1146 } 1147 } 1148 1149 /** 1150 * This class holds a capture ID and its expected last regular frame number and last reprocess 1151 * frame number. 1152 */ 1153 static class RequestLastFrameNumbersHolder { 1154 // request ID 1155 private final int mRequestId; 1156 // The last regular frame number for this request ID. It's 1157 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request. 1158 private final long mLastRegularFrameNumber; 1159 // The last reprocess frame number for this request ID. It's 1160 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request. 1161 private final long mLastReprocessFrameNumber; 1162 1163 /** 1164 * Create a request-last-frame-numbers holder with a list of requests, request ID, and 1165 * the last frame number returned by camera service. 1166 */ 1167 public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId, 1168 long lastFrameNumber) { 1169 long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1170 long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1171 long frameNumber = lastFrameNumber; 1172 1173 if (lastFrameNumber < requestList.size() - 1) { 1174 throw new IllegalArgumentException("lastFrameNumber: " + lastFrameNumber + 1175 " should be at least " + (requestList.size() - 1) + " for the number of " + 1176 " requests in the list: " + requestList.size()); 1177 } 1178 1179 // find the last regular frame number and the last reprocess frame number 1180 for (int i = requestList.size() - 1; i >= 0; i--) { 1181 CaptureRequest request = requestList.get(i); 1182 if (request.isReprocess() && lastReprocessFrameNumber == 1183 CaptureCallback.NO_FRAMES_CAPTURED) { 1184 lastReprocessFrameNumber = frameNumber; 1185 } else if (!request.isReprocess() && lastRegularFrameNumber == 1186 CaptureCallback.NO_FRAMES_CAPTURED) { 1187 lastRegularFrameNumber = frameNumber; 1188 } 1189 1190 if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED && 1191 lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { 1192 break; 1193 } 1194 1195 frameNumber--; 1196 } 1197 1198 mLastRegularFrameNumber = lastRegularFrameNumber; 1199 mLastReprocessFrameNumber = lastReprocessFrameNumber; 1200 mRequestId = requestId; 1201 } 1202 1203 /** 1204 * Create a request-last-frame-numbers holder with a request ID and last regular frame 1205 * number. 1206 */ 1207 public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) { 1208 mLastRegularFrameNumber = lastRegularFrameNumber; 1209 mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1210 mRequestId = requestId; 1211 } 1212 1213 /** 1214 * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1215 * it contains no regular request. 1216 */ 1217 public long getLastRegularFrameNumber() { 1218 return mLastRegularFrameNumber; 1219 } 1220 1221 /** 1222 * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1223 * it contains no reprocess request. 1224 */ 1225 public long getLastReprocessFrameNumber() { 1226 return mLastReprocessFrameNumber; 1227 } 1228 1229 /** 1230 * Return the last frame number overall. 1231 */ 1232 public long getLastFrameNumber() { 1233 return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber); 1234 } 1235 1236 /** 1237 * Return the request ID. 1238 */ 1239 public int getRequestId() { 1240 return mRequestId; 1241 } 1242 } 1243 1244 /** 1245 * This class tracks the last frame number for submitted requests. 1246 */ 1247 public class FrameNumberTracker { 1248 1249 private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1250 private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1251 /** the skipped frame numbers that belong to regular results */ 1252 private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>(); 1253 /** the skipped frame numbers that belong to reprocess results */ 1254 private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>(); 1255 /** frame number -> is reprocess */ 1256 private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>(); 1257 /** Map frame numbers to list of partial results */ 1258 private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); 1259 1260 private void update() { 1261 Iterator iter = mFutureErrorMap.entrySet().iterator(); 1262 while (iter.hasNext()) { 1263 TreeMap.Entry pair = (TreeMap.Entry)iter.next(); 1264 Long errorFrameNumber = (Long)pair.getKey(); 1265 Boolean reprocess = (Boolean)pair.getValue(); 1266 Boolean removeError = true; 1267 if (reprocess) { 1268 if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) { 1269 mCompletedReprocessFrameNumber = errorFrameNumber; 1270 } else if (mSkippedReprocessFrameNumbers.isEmpty() != true && 1271 errorFrameNumber == mSkippedReprocessFrameNumbers.element()) { 1272 mCompletedReprocessFrameNumber = errorFrameNumber; 1273 mSkippedReprocessFrameNumbers.remove(); 1274 } else { 1275 removeError = false; 1276 } 1277 } else { 1278 if (errorFrameNumber == mCompletedFrameNumber + 1) { 1279 mCompletedFrameNumber = errorFrameNumber; 1280 } else if (mSkippedRegularFrameNumbers.isEmpty() != true && 1281 errorFrameNumber == mSkippedRegularFrameNumbers.element()) { 1282 mCompletedFrameNumber = errorFrameNumber; 1283 mSkippedRegularFrameNumbers.remove(); 1284 } else { 1285 removeError = false; 1286 } 1287 } 1288 if (removeError) { 1289 iter.remove(); 1290 } 1291 } 1292 } 1293 1294 /** 1295 * This function is called every time when a result or an error is received. 1296 * @param frameNumber the frame number corresponding to the result or error 1297 * @param isError true if it is an error, false if it is not an error 1298 * @param isReprocess true if it is a reprocess result, false if it is a regular result. 1299 */ 1300 public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) { 1301 if (isError) { 1302 mFutureErrorMap.put(frameNumber, isReprocess); 1303 } else { 1304 try { 1305 if (isReprocess) { 1306 updateCompletedReprocessFrameNumber(frameNumber); 1307 } else { 1308 updateCompletedFrameNumber(frameNumber); 1309 } 1310 } catch (IllegalArgumentException e) { 1311 Log.e(TAG, e.getMessage()); 1312 } 1313 } 1314 update(); 1315 } 1316 1317 /** 1318 * This function is called every time a result has been completed. 1319 * 1320 * <p>It keeps a track of all the partial results already created for a particular 1321 * frame number.</p> 1322 * 1323 * @param frameNumber the frame number corresponding to the result 1324 * @param result the total or partial result 1325 * @param partial {@true} if the result is partial, {@code false} if total 1326 * @param isReprocess true if it is a reprocess result, false if it is a regular result. 1327 */ 1328 public void updateTracker(long frameNumber, CaptureResult result, boolean partial, 1329 boolean isReprocess) { 1330 if (!partial) { 1331 // Update the total result's frame status as being successful 1332 updateTracker(frameNumber, /*isError*/false, isReprocess); 1333 // Don't keep a list of total results, we don't need to track them 1334 return; 1335 } 1336 1337 if (result == null) { 1338 // Do not record blank results; this also means there will be no total result 1339 // so it doesn't matter that the partials were not recorded 1340 return; 1341 } 1342 1343 // Partial results must be aggregated in-order for that frame number 1344 List<CaptureResult> partials = mPartialResults.get(frameNumber); 1345 if (partials == null) { 1346 partials = new ArrayList<>(); 1347 mPartialResults.put(frameNumber, partials); 1348 } 1349 1350 partials.add(result); 1351 } 1352 1353 /** 1354 * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. 1355 * 1356 * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} 1357 * is called again with new partials for that frame number).</p> 1358 * 1359 * @param frameNumber the frame number corresponding to the result 1360 * @return a list of partial results for that frame with at least 1 element, 1361 * or {@code null} if there were no partials recorded for that frame 1362 */ 1363 public List<CaptureResult> popPartialResults(long frameNumber) { 1364 return mPartialResults.remove(frameNumber); 1365 } 1366 1367 public long getCompletedFrameNumber() { 1368 return mCompletedFrameNumber; 1369 } 1370 1371 public long getCompletedReprocessFrameNumber() { 1372 return mCompletedReprocessFrameNumber; 1373 } 1374 1375 /** 1376 * Update the completed frame number for regular results. 1377 * 1378 * It validates that all previous frames have arrived except for reprocess frames. 1379 * 1380 * If there is a gap since previous regular frame number, assume the frames in the gap are 1381 * reprocess frames and store them in the skipped reprocess frame number queue to check 1382 * against when reprocess frames arrive. 1383 */ 1384 private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException { 1385 if (frameNumber <= mCompletedFrameNumber) { 1386 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); 1387 } else if (frameNumber <= mCompletedReprocessFrameNumber) { 1388 // if frame number is smaller than completed reprocess frame number, 1389 // it must be the head of mSkippedRegularFrameNumbers 1390 if (mSkippedRegularFrameNumbers.isEmpty() == true || 1391 frameNumber < mSkippedRegularFrameNumbers.element()) { 1392 throw new IllegalArgumentException("frame number " + frameNumber + 1393 " is a repeat"); 1394 } else if (frameNumber > mSkippedRegularFrameNumbers.element()) { 1395 throw new IllegalArgumentException("frame number " + frameNumber + 1396 " comes out of order. Expecting " + 1397 mSkippedRegularFrameNumbers.element()); 1398 } 1399 // frame number matches the head of the skipped frame number queue. 1400 mSkippedRegularFrameNumbers.remove(); 1401 } else { 1402 // there is a gap of unseen frame numbers which should belong to reprocess result 1403 // put all the skipped frame numbers in the queue 1404 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1; 1405 i < frameNumber; i++) { 1406 mSkippedReprocessFrameNumbers.add(i); 1407 } 1408 } 1409 1410 mCompletedFrameNumber = frameNumber; 1411 } 1412 1413 /** 1414 * Update the completed frame number for reprocess results. 1415 * 1416 * It validates that all previous frames have arrived except for regular frames. 1417 * 1418 * If there is a gap since previous reprocess frame number, assume the frames in the gap are 1419 * regular frames and store them in the skipped regular frame number queue to check 1420 * against when regular frames arrive. 1421 */ 1422 private void updateCompletedReprocessFrameNumber(long frameNumber) 1423 throws IllegalArgumentException { 1424 if (frameNumber < mCompletedReprocessFrameNumber) { 1425 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); 1426 } else if (frameNumber < mCompletedFrameNumber) { 1427 // if reprocess frame number is smaller than completed regular frame number, 1428 // it must be the head of the skipped reprocess frame number queue. 1429 if (mSkippedReprocessFrameNumbers.isEmpty() == true || 1430 frameNumber < mSkippedReprocessFrameNumbers.element()) { 1431 throw new IllegalArgumentException("frame number " + frameNumber + 1432 " is a repeat"); 1433 } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) { 1434 throw new IllegalArgumentException("frame number " + frameNumber + 1435 " comes out of order. Expecting " + 1436 mSkippedReprocessFrameNumbers.element()); 1437 } 1438 // frame number matches the head of the skipped frame number queue. 1439 mSkippedReprocessFrameNumbers.remove(); 1440 } else { 1441 // put all the skipped frame numbers in the queue 1442 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1; 1443 i < frameNumber; i++) { 1444 mSkippedRegularFrameNumbers.add(i); 1445 } 1446 } 1447 mCompletedReprocessFrameNumber = frameNumber; 1448 } 1449 } 1450 1451 private void checkAndFireSequenceComplete() { 1452 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 1453 long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); 1454 boolean isReprocess = false; 1455 Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator(); 1456 while (iter.hasNext()) { 1457 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 1458 boolean sequenceCompleted = false; 1459 final int requestId = requestLastFrameNumbers.getRequestId(); 1460 final CaptureCallbackHolder holder; 1461 synchronized(mInterfaceLock) { 1462 if (mRemoteDevice == null) { 1463 Log.w(TAG, "Camera closed while checking sequences"); 1464 return; 1465 } 1466 1467 int index = mCaptureCallbackMap.indexOfKey(requestId); 1468 holder = (index >= 0) ? 1469 mCaptureCallbackMap.valueAt(index) : null; 1470 if (holder != null) { 1471 long lastRegularFrameNumber = 1472 requestLastFrameNumbers.getLastRegularFrameNumber(); 1473 long lastReprocessFrameNumber = 1474 requestLastFrameNumbers.getLastReprocessFrameNumber(); 1475 1476 // check if it's okay to remove request from mCaptureCallbackMap 1477 if (lastRegularFrameNumber <= completedFrameNumber && 1478 lastReprocessFrameNumber <= completedReprocessFrameNumber) { 1479 sequenceCompleted = true; 1480 mCaptureCallbackMap.removeAt(index); 1481 if (DEBUG) { 1482 Log.v(TAG, String.format( 1483 "Remove holder for requestId %d, because lastRegularFrame %d " + 1484 "is <= %d and lastReprocessFrame %d is <= %d", requestId, 1485 lastRegularFrameNumber, completedFrameNumber, 1486 lastReprocessFrameNumber, completedReprocessFrameNumber)); 1487 } 1488 } 1489 } 1490 } 1491 1492 // If no callback is registered for this requestId or sequence completed, remove it 1493 // from the frame number->request pair because it's not needed anymore. 1494 if (holder == null || sequenceCompleted) { 1495 iter.remove(); 1496 } 1497 1498 // Call onCaptureSequenceCompleted 1499 if (sequenceCompleted) { 1500 Runnable resultDispatch = new Runnable() { 1501 @Override 1502 public void run() { 1503 if (!CameraDeviceImpl.this.isClosed()){ 1504 if (DEBUG) { 1505 Log.d(TAG, String.format( 1506 "fire sequence complete for request %d", 1507 requestId)); 1508 } 1509 1510 holder.getCallback().onCaptureSequenceCompleted( 1511 CameraDeviceImpl.this, 1512 requestId, 1513 requestLastFrameNumbers.getLastFrameNumber()); 1514 } 1515 } 1516 }; 1517 holder.getHandler().post(resultDispatch); 1518 } 1519 } 1520 } 1521 1522 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 1523 // 1524 // Constants below need to be kept up-to-date with 1525 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h 1526 // 1527 1528 // 1529 // Error codes for onCameraError 1530 // 1531 1532 /** 1533 * Camera has been disconnected 1534 */ 1535 public static final int ERROR_CAMERA_DISCONNECTED = 0; 1536 /** 1537 * Camera has encountered a device-level error 1538 * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE 1539 */ 1540 public static final int ERROR_CAMERA_DEVICE = 1; 1541 /** 1542 * Camera has encountered a service-level error 1543 * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE 1544 */ 1545 public static final int ERROR_CAMERA_SERVICE = 2; 1546 /** 1547 * Camera has encountered an error processing a single request. 1548 */ 1549 public static final int ERROR_CAMERA_REQUEST = 3; 1550 /** 1551 * Camera has encountered an error producing metadata for a single capture 1552 */ 1553 public static final int ERROR_CAMERA_RESULT = 4; 1554 /** 1555 * Camera has encountered an error producing an image buffer for a single capture 1556 */ 1557 public static final int ERROR_CAMERA_BUFFER = 5; 1558 1559 @Override 1560 public IBinder asBinder() { 1561 return this; 1562 } 1563 1564 @Override 1565 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 1566 if (DEBUG) { 1567 Log.d(TAG, String.format( 1568 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", 1569 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), 1570 resultExtras.getSubsequenceId())); 1571 } 1572 1573 synchronized(mInterfaceLock) { 1574 if (mRemoteDevice == null) { 1575 return; // Camera already closed 1576 } 1577 1578 switch (errorCode) { 1579 case ERROR_CAMERA_DISCONNECTED: 1580 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); 1581 break; 1582 default: 1583 Log.e(TAG, "Unknown error from camera device: " + errorCode); 1584 // no break 1585 case ERROR_CAMERA_DEVICE: 1586 case ERROR_CAMERA_SERVICE: 1587 mInError = true; 1588 Runnable r = new Runnable() { 1589 @Override 1590 public void run() { 1591 if (!CameraDeviceImpl.this.isClosed()) { 1592 mDeviceCallback.onError(CameraDeviceImpl.this, errorCode); 1593 } 1594 } 1595 }; 1596 CameraDeviceImpl.this.mDeviceHandler.post(r); 1597 break; 1598 case ERROR_CAMERA_REQUEST: 1599 case ERROR_CAMERA_RESULT: 1600 case ERROR_CAMERA_BUFFER: 1601 onCaptureErrorLocked(errorCode, resultExtras); 1602 break; 1603 } 1604 } 1605 } 1606 1607 @Override 1608 public void onDeviceIdle() { 1609 if (DEBUG) { 1610 Log.d(TAG, "Camera now idle"); 1611 } 1612 synchronized(mInterfaceLock) { 1613 if (mRemoteDevice == null) return; // Camera already closed 1614 1615 if (!CameraDeviceImpl.this.mIdle) { 1616 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle); 1617 } 1618 CameraDeviceImpl.this.mIdle = true; 1619 } 1620 } 1621 1622 @Override 1623 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 1624 int requestId = resultExtras.getRequestId(); 1625 final long frameNumber = resultExtras.getFrameNumber(); 1626 1627 if (DEBUG) { 1628 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber); 1629 } 1630 final CaptureCallbackHolder holder; 1631 1632 synchronized(mInterfaceLock) { 1633 if (mRemoteDevice == null) return; // Camera already closed 1634 1635 // Get the callback for this frame ID, if there is one 1636 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1637 1638 if (holder == null) { 1639 return; 1640 } 1641 1642 if (isClosed()) return; 1643 1644 // Dispatch capture start notice 1645 holder.getHandler().post( 1646 new Runnable() { 1647 @Override 1648 public void run() { 1649 if (!CameraDeviceImpl.this.isClosed()) { 1650 holder.getCallback().onCaptureStarted( 1651 CameraDeviceImpl.this, 1652 holder.getRequest(resultExtras.getSubsequenceId()), 1653 timestamp, frameNumber); 1654 } 1655 } 1656 }); 1657 1658 } 1659 } 1660 1661 @Override 1662 public void onResultReceived(CameraMetadataNative result, 1663 CaptureResultExtras resultExtras) throws RemoteException { 1664 1665 int requestId = resultExtras.getRequestId(); 1666 long frameNumber = resultExtras.getFrameNumber(); 1667 1668 if (DEBUG) { 1669 Log.v(TAG, "Received result frame " + frameNumber + " for id " 1670 + requestId); 1671 } 1672 1673 synchronized(mInterfaceLock) { 1674 if (mRemoteDevice == null) return; // Camera already closed 1675 1676 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 1677 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 1678 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 1679 1680 final CaptureCallbackHolder holder = 1681 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1682 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 1683 1684 boolean isPartialResult = 1685 (resultExtras.getPartialResultCount() < mTotalPartialCount); 1686 boolean isReprocess = request.isReprocess(); 1687 1688 // Check if we have a callback for this 1689 if (holder == null) { 1690 if (DEBUG) { 1691 Log.d(TAG, 1692 "holder is null, early return at frame " 1693 + frameNumber); 1694 } 1695 1696 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 1697 isReprocess); 1698 1699 return; 1700 } 1701 1702 if (isClosed()) { 1703 if (DEBUG) { 1704 Log.d(TAG, 1705 "camera is closed, early return at frame " 1706 + frameNumber); 1707 } 1708 1709 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 1710 isReprocess); 1711 return; 1712 } 1713 1714 1715 Runnable resultDispatch = null; 1716 1717 CaptureResult finalResult; 1718 1719 // Either send a partial result or the final capture completed result 1720 if (isPartialResult) { 1721 final CaptureResult resultAsCapture = 1722 new CaptureResult(result, request, resultExtras); 1723 1724 // Partial result 1725 resultDispatch = new Runnable() { 1726 @Override 1727 public void run() { 1728 if (!CameraDeviceImpl.this.isClosed()){ 1729 holder.getCallback().onCaptureProgressed( 1730 CameraDeviceImpl.this, 1731 request, 1732 resultAsCapture); 1733 } 1734 } 1735 }; 1736 1737 finalResult = resultAsCapture; 1738 } else { 1739 List<CaptureResult> partialResults = 1740 mFrameNumberTracker.popPartialResults(frameNumber); 1741 1742 final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result, 1743 request, resultExtras, partialResults, holder.getSessionId()); 1744 1745 // Final capture result 1746 resultDispatch = new Runnable() { 1747 @Override 1748 public void run() { 1749 if (!CameraDeviceImpl.this.isClosed()){ 1750 holder.getCallback().onCaptureCompleted( 1751 CameraDeviceImpl.this, 1752 request, 1753 resultAsCapture); 1754 } 1755 } 1756 }; 1757 1758 finalResult = resultAsCapture; 1759 } 1760 1761 holder.getHandler().post(resultDispatch); 1762 1763 // Collect the partials for a total result; or mark the frame as totally completed 1764 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, 1765 isReprocess); 1766 1767 // Fire onCaptureSequenceCompleted 1768 if (!isPartialResult) { 1769 checkAndFireSequenceComplete(); 1770 } 1771 } 1772 } 1773 1774 @Override 1775 public void onPrepared(int streamId) { 1776 final OutputConfiguration output; 1777 final StateCallbackKK sessionCallback; 1778 1779 if (DEBUG) { 1780 Log.v(TAG, "Stream " + streamId + " is prepared"); 1781 } 1782 1783 synchronized(mInterfaceLock) { 1784 output = mConfiguredOutputs.get(streamId); 1785 sessionCallback = mSessionStateCallback; 1786 } 1787 1788 if (sessionCallback == null) return; 1789 1790 if (output == null) { 1791 Log.w(TAG, "onPrepared invoked for unknown output Surface"); 1792 return; 1793 } 1794 final Surface surface = output.getSurface(); 1795 1796 sessionCallback.onSurfacePrepared(surface); 1797 } 1798 1799 /** 1800 * Called by onDeviceError for handling single-capture failures. 1801 */ 1802 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { 1803 1804 final int requestId = resultExtras.getRequestId(); 1805 final int subsequenceId = resultExtras.getSubsequenceId(); 1806 final long frameNumber = resultExtras.getFrameNumber(); 1807 final CaptureCallbackHolder holder = 1808 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1809 1810 final CaptureRequest request = holder.getRequest(subsequenceId); 1811 1812 // No way to report buffer errors right now 1813 if (errorCode == ERROR_CAMERA_BUFFER) { 1814 Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber)); 1815 return; 1816 } 1817 1818 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); 1819 1820 // This is only approximate - exact handling needs the camera service and HAL to 1821 // disambiguate between request failures to due abort and due to real errors. 1822 // For now, assume that if the session believes we're mid-abort, then the error 1823 // is due to abort. 1824 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? 1825 CaptureFailure.REASON_FLUSHED : 1826 CaptureFailure.REASON_ERROR; 1827 1828 final CaptureFailure failure = new CaptureFailure( 1829 request, 1830 reason, 1831 /*dropped*/ mayHaveBuffers, 1832 requestId, 1833 frameNumber); 1834 1835 Runnable failureDispatch = new Runnable() { 1836 @Override 1837 public void run() { 1838 if (!CameraDeviceImpl.this.isClosed()){ 1839 holder.getCallback().onCaptureFailed( 1840 CameraDeviceImpl.this, 1841 request, 1842 failure); 1843 } 1844 } 1845 }; 1846 holder.getHandler().post(failureDispatch); 1847 1848 // Fire onCaptureSequenceCompleted if appropriate 1849 if (DEBUG) { 1850 Log.v(TAG, String.format("got error frame %d", frameNumber)); 1851 } 1852 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess()); 1853 checkAndFireSequenceComplete(); 1854 } 1855 1856 } // public class CameraDeviceCallbacks 1857 1858 /** 1859 * Default handler management. 1860 * 1861 * <p> 1862 * If handler is null, get the current thread's 1863 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}. 1864 * </p> 1865 */ 1866 static Handler checkHandler(Handler handler) { 1867 if (handler == null) { 1868 Looper looper = Looper.myLooper(); 1869 if (looper == null) { 1870 throw new IllegalArgumentException( 1871 "No handler given, and current thread has no looper!"); 1872 } 1873 handler = new Handler(looper); 1874 } 1875 return handler; 1876 } 1877 1878 /** 1879 * Default handler management, conditional on there being a callback. 1880 * 1881 * <p>If the callback isn't null, check the handler, otherwise pass it through.</p> 1882 */ 1883 static <T> Handler checkHandler(Handler handler, T callback) { 1884 if (callback != null) { 1885 return checkHandler(handler); 1886 } 1887 return handler; 1888 } 1889 1890 private void checkIfCameraClosedOrInError() throws CameraAccessException { 1891 if (mRemoteDevice == null) { 1892 throw new IllegalStateException("CameraDevice was already closed"); 1893 } 1894 if (mInError) { 1895 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, 1896 "The camera device has encountered a serious error"); 1897 } 1898 } 1899 1900 /** Whether the camera device has started to close (may not yet have finished) */ 1901 private boolean isClosed() { 1902 return mClosing; 1903 } 1904 1905 private CameraCharacteristics getCharacteristics() { 1906 return mCharacteristics; 1907 } 1908 1909 @Override 1910 public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs, 1911 android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler) 1912 throws CameraAccessException { 1913 // TODO: to be implemented 1914 throw new UnsupportedOperationException("To be implemented!!!!"); 1915 } 1916 1917 @Override 1918 public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request) 1919 throws CameraAccessException { 1920 throw new UnsupportedOperationException("To be implemented!!!!"); 1921 } 1922} 1923