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