1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.camera2.legacy; 18 19 import android.graphics.SurfaceTexture; 20 import android.hardware.Camera; 21 import android.hardware.camera2.CameraCharacteristics; 22 import android.hardware.camera2.CaptureRequest; 23 import android.hardware.camera2.impl.CameraDeviceImpl; 24 import android.hardware.camera2.utils.SubmitInfo; 25 import android.hardware.camera2.utils.SizeAreaComparator; 26 import android.hardware.camera2.impl.CameraMetadataNative; 27 import android.os.ConditionVariable; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.os.SystemClock; 31 import android.util.Log; 32 import android.util.MutableLong; 33 import android.util.Pair; 34 import android.util.Size; 35 import android.view.Surface; 36 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Collection; 40 import java.util.Collections; 41 import java.util.Iterator; 42 import java.util.List; 43 import java.util.concurrent.TimeUnit; 44 import java.util.concurrent.atomic.AtomicBoolean; 45 46 import static com.android.internal.util.Preconditions.*; 47 48 /** 49 * This class executes requests to the {@link Camera}. 50 * 51 * <p> 52 * The main components of this class are: 53 * - A message queue of requests to the {@link Camera}. 54 * - A thread that consumes requests to the {@link Camera} and executes them. 55 * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s. 56 * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations. 57 * </p> 58 */ 59 @SuppressWarnings("deprecation") 60 public class RequestThreadManager { 61 private final String TAG; 62 private final int mCameraId; 63 private final RequestHandlerThread mRequestThread; 64 65 private static final boolean DEBUG = false; 66 // For slightly more spammy messages that will get repeated every frame 67 private static final boolean VERBOSE = false; 68 private Camera mCamera; 69 private final CameraCharacteristics mCharacteristics; 70 71 private final CameraDeviceState mDeviceState; 72 private final CaptureCollector mCaptureCollector; 73 private final LegacyFocusStateMapper mFocusStateMapper; 74 private final LegacyFaceDetectMapper mFaceDetectMapper; 75 76 private static final int MSG_CONFIGURE_OUTPUTS = 1; 77 private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2; 78 private static final int MSG_CLEANUP = 3; 79 80 private static final int MAX_IN_FLIGHT_REQUESTS = 2; 81 82 private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms 83 private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2) 84 private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT; 85 86 private static final float ASPECT_RATIO_TOLERANCE = 0.01f; 87 private boolean mPreviewRunning = false; 88 89 private final List<Surface> mPreviewOutputs = new ArrayList<>(); 90 private final List<Surface> mCallbackOutputs = new ArrayList<>(); 91 private GLThreadManager mGLThreadManager; 92 private SurfaceTexture mPreviewTexture; 93 private Camera.Parameters mParams; 94 95 private final List<Long> mJpegSurfaceIds = new ArrayList<>(); 96 97 private Size mIntermediateBufferSize; 98 99 private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds); 100 private LegacyRequest mLastRequest = null; 101 private SurfaceTexture mDummyTexture; 102 private Surface mDummySurface; 103 104 private final Object mIdleLock = new Object(); 105 private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview"); 106 private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests"); 107 108 private final AtomicBoolean mQuit = new AtomicBoolean(false); 109 110 // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write 111 // limitations for (b/17379185). 112 private static final boolean USE_BLOB_FORMAT_OVERRIDE = true; 113 114 /** 115 * Container object for Configure messages. 116 */ 117 private static class ConfigureHolder { 118 public final ConditionVariable condition; 119 public final Collection<Pair<Surface, Size>> surfaces; 120 ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface, Size>> surfaces)121 public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface, 122 Size>> surfaces) { 123 this.condition = condition; 124 this.surfaces = surfaces; 125 } 126 } 127 128 /** 129 * Counter class used to calculate and log the current FPS of frame production. 130 */ 131 public static class FpsCounter { 132 //TODO: Hook this up to SystTrace? 133 private static final String TAG = "FpsCounter"; 134 private int mFrameCount = 0; 135 private long mLastTime = 0; 136 private long mLastPrintTime = 0; 137 private double mLastFps = 0; 138 private final String mStreamType; 139 private static final long NANO_PER_SECOND = 1000000000; //ns 140 FpsCounter(String streamType)141 public FpsCounter(String streamType) { 142 mStreamType = streamType; 143 } 144 countFrame()145 public synchronized void countFrame() { 146 mFrameCount++; 147 long nextTime = SystemClock.elapsedRealtimeNanos(); 148 if (mLastTime == 0) { 149 mLastTime = nextTime; 150 } 151 if (nextTime > mLastTime + NANO_PER_SECOND) { 152 long elapsed = nextTime - mLastTime; 153 mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed); 154 mFrameCount = 0; 155 mLastTime = nextTime; 156 } 157 } 158 checkFps()159 public synchronized double checkFps() { 160 return mLastFps; 161 } 162 staggeredLog()163 public synchronized void staggeredLog() { 164 if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) { 165 mLastPrintTime = mLastTime; 166 Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps ); 167 } 168 } 169 countAndLog()170 public synchronized void countAndLog() { 171 countFrame(); 172 staggeredLog(); 173 } 174 } 175 /** 176 * Fake preview for jpeg captures when there is no active preview 177 */ createDummySurface()178 private void createDummySurface() { 179 if (mDummyTexture == null || mDummySurface == null) { 180 mDummyTexture = new SurfaceTexture(/*ignored*/0); 181 // TODO: use smallest default sizes 182 mDummyTexture.setDefaultBufferSize(640, 480); 183 mDummySurface = new Surface(mDummyTexture); 184 } 185 } 186 187 private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() { 188 @Override 189 public void onError(int i, Camera camera) { 190 switch(i) { 191 case Camera.CAMERA_ERROR_EVICTED: { 192 flush(); 193 mDeviceState.setError( 194 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED); 195 } break; 196 default: { 197 Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback"); 198 mDeviceState.setError( 199 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 200 } break; 201 } 202 } 203 }; 204 205 private final ConditionVariable mReceivedJpeg = new ConditionVariable(false); 206 207 private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() { 208 @Override 209 public void onPictureTaken(byte[] data, Camera camera) { 210 Log.i(TAG, "Received jpeg."); 211 Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced(); 212 if (captureInfo == null || captureInfo.first == null) { 213 Log.e(TAG, "Dropping jpeg frame."); 214 return; 215 } 216 RequestHolder holder = captureInfo.first; 217 long timestamp = captureInfo.second; 218 for (Surface s : holder.getHolderTargets()) { 219 try { 220 if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) { 221 Log.i(TAG, "Producing jpeg buffer..."); 222 223 int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize(); 224 totalSize = (totalSize + 3) & ~0x3; // round up to nearest octonibble 225 LegacyCameraDevice.setNextTimestamp(s, timestamp); 226 227 if (USE_BLOB_FORMAT_OVERRIDE) { 228 // Override to RGBA_8888 format. 229 LegacyCameraDevice.setSurfaceFormat(s, 230 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888); 231 232 int dimen = (int) Math.ceil(Math.sqrt(totalSize)); 233 dimen = (dimen + 0xf) & ~0xf; // round up to nearest multiple of 16 234 LegacyCameraDevice.setSurfaceDimens(s, dimen, dimen); 235 LegacyCameraDevice.produceFrame(s, data, dimen, dimen, 236 CameraMetadataNative.NATIVE_JPEG_FORMAT); 237 } else { 238 LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1); 239 LegacyCameraDevice.produceFrame(s, data, totalSize, /*height*/1, 240 CameraMetadataNative.NATIVE_JPEG_FORMAT); 241 } 242 } 243 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 244 Log.w(TAG, "Surface abandoned, dropping frame. ", e); 245 } 246 } 247 248 mReceivedJpeg.open(); 249 } 250 }; 251 252 private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() { 253 @Override 254 public void onShutter() { 255 mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos()); 256 } 257 }; 258 259 private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback = 260 new SurfaceTexture.OnFrameAvailableListener() { 261 @Override 262 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 263 if (DEBUG) { 264 mPrevCounter.countAndLog(); 265 } 266 mGLThreadManager.queueNewFrame(); 267 } 268 }; 269 stopPreview()270 private void stopPreview() { 271 if (VERBOSE) { 272 Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning); 273 } 274 if (mPreviewRunning) { 275 mCamera.stopPreview(); 276 mPreviewRunning = false; 277 } 278 } 279 startPreview()280 private void startPreview() { 281 if (VERBOSE) { 282 Log.v(TAG, "startPreview - preview running? " + mPreviewRunning); 283 } 284 if (!mPreviewRunning) { 285 // XX: CameraClient:;startPreview is not getting called after a stop 286 mCamera.startPreview(); 287 mPreviewRunning = true; 288 } 289 } 290 doJpegCapturePrepare(RequestHolder request)291 private void doJpegCapturePrepare(RequestHolder request) throws IOException { 292 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning); 293 294 if (!mPreviewRunning) { 295 if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface"); 296 297 createDummySurface(); 298 mCamera.setPreviewTexture(mDummyTexture); 299 startPreview(); 300 } 301 } 302 doJpegCapture(RequestHolder request)303 private void doJpegCapture(RequestHolder request) { 304 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare"); 305 306 mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback); 307 mPreviewRunning = false; 308 } 309 doPreviewCapture(RequestHolder request)310 private void doPreviewCapture(RequestHolder request) throws IOException { 311 if (VERBOSE) { 312 Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning); 313 } 314 315 if (mPreviewRunning) { 316 return; // Already running 317 } 318 319 if (mPreviewTexture == null) { 320 throw new IllegalStateException( 321 "Preview capture called with no preview surfaces configured."); 322 } 323 324 mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(), 325 mIntermediateBufferSize.getHeight()); 326 mCamera.setPreviewTexture(mPreviewTexture); 327 328 startPreview(); 329 } 330 configureOutputs(Collection<Pair<Surface, Size>> outputs)331 private void configureOutputs(Collection<Pair<Surface, Size>> outputs) { 332 if (DEBUG) { 333 String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces"); 334 Log.d(TAG, "configureOutputs with " + outputsStr); 335 } 336 337 try { 338 stopPreview(); 339 } catch (RuntimeException e) { 340 Log.e(TAG, "Received device exception in configure call: ", e); 341 mDeviceState.setError( 342 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 343 return; 344 } 345 346 /* 347 * Try to release the previous preview's surface texture earlier if we end up 348 * using a different one; this also reduces the likelihood of getting into a deadlock 349 * when disconnecting from the old previous texture at a later time. 350 */ 351 try { 352 mCamera.setPreviewTexture(/*surfaceTexture*/null); 353 } catch (IOException e) { 354 Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e); 355 } catch (RuntimeException e) { 356 Log.e(TAG, "Received device exception in configure call: ", e); 357 mDeviceState.setError( 358 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 359 return; 360 } 361 362 if (mGLThreadManager != null) { 363 mGLThreadManager.waitUntilStarted(); 364 mGLThreadManager.ignoreNewFrames(); 365 mGLThreadManager.waitUntilIdle(); 366 } 367 resetJpegSurfaceFormats(mCallbackOutputs); 368 369 for (Surface s : mCallbackOutputs) { 370 try { 371 LegacyCameraDevice.disconnectSurface(s); 372 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 373 Log.w(TAG, "Surface abandoned, skipping...", e); 374 } 375 } 376 mPreviewOutputs.clear(); 377 mCallbackOutputs.clear(); 378 mJpegSurfaceIds.clear(); 379 mPreviewTexture = null; 380 381 List<Size> previewOutputSizes = new ArrayList<>(); 382 List<Size> callbackOutputSizes = new ArrayList<>(); 383 384 int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING); 385 int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 386 if (outputs != null) { 387 for (Pair<Surface, Size> outPair : outputs) { 388 Surface s = outPair.first; 389 Size outSize = outPair.second; 390 try { 391 int format = LegacyCameraDevice.detectSurfaceType(s); 392 LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation); 393 switch (format) { 394 case CameraMetadataNative.NATIVE_JPEG_FORMAT: 395 if (USE_BLOB_FORMAT_OVERRIDE) { 396 // Override to RGBA_8888 format. 397 LegacyCameraDevice.setSurfaceFormat(s, 398 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888); 399 } 400 mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s)); 401 mCallbackOutputs.add(s); 402 callbackOutputSizes.add(outSize); 403 404 // LegacyCameraDevice is the producer of JPEG output surfaces 405 // so LegacyCameraDevice needs to connect to the surfaces. 406 LegacyCameraDevice.connectSurface(s); 407 break; 408 default: 409 LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice. 410 NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); 411 mPreviewOutputs.add(s); 412 previewOutputSizes.add(outSize); 413 break; 414 } 415 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 416 Log.w(TAG, "Surface abandoned, skipping...", e); 417 } 418 } 419 } 420 try { 421 mParams = mCamera.getParameters(); 422 } catch (RuntimeException e) { 423 Log.e(TAG, "Received device exception: ", e); 424 mDeviceState.setError( 425 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 426 return; 427 } 428 429 List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange(); 430 int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges); 431 if (DEBUG) { 432 Log.d(TAG, "doPreviewCapture - Selected range [" + 433 bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," + 434 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]"); 435 } 436 mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], 437 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); 438 439 Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, 440 callbackOutputSizes, mParams); 441 442 if (previewOutputSizes.size() > 0) { 443 444 Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes); 445 446 // Find largest jpeg dimension - assume to have the same aspect ratio as sensor. 447 Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams); 448 449 Size chosenJpegDimen = (smallestSupportedJpegSize != null) ? smallestSupportedJpegSize 450 : largestJpegDimen; 451 452 List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList( 453 mParams.getSupportedPreviewSizes()); 454 455 // Use smallest preview dimension with same aspect ratio as sensor that is >= than all 456 // of the configured output dimensions. If none exists, fall back to using the largest 457 // supported preview size. 458 long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth(); 459 Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes); 460 for (Size s : supportedPreviewSizes) { 461 long currArea = s.getWidth() * s.getHeight(); 462 long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight(); 463 if (checkAspectRatiosMatch(chosenJpegDimen, s) && (currArea < bestArea && 464 currArea >= largestOutputArea)) { 465 bestPreviewDimen = s; 466 } 467 } 468 469 mIntermediateBufferSize = bestPreviewDimen; 470 mParams.setPreviewSize(mIntermediateBufferSize.getWidth(), 471 mIntermediateBufferSize.getHeight()); 472 473 if (DEBUG) { 474 Log.d(TAG, "Intermediate buffer selected with dimens: " + 475 bestPreviewDimen.toString()); 476 } 477 } else { 478 mIntermediateBufferSize = null; 479 if (DEBUG) { 480 Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured"); 481 } 482 } 483 484 if (smallestSupportedJpegSize != null) { 485 /* 486 * Set takePicture size to the smallest supported JPEG size large enough 487 * to scale/crop out of for the bounding rectangle of the configured JPEG sizes. 488 */ 489 490 Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize); 491 mParams.setPictureSize( 492 smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight()); 493 } 494 495 // TODO: Detect and optimize single-output paths here to skip stream teeing. 496 if (mGLThreadManager == null) { 497 mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState); 498 mGLThreadManager.start(); 499 } 500 mGLThreadManager.waitUntilStarted(); 501 List<Pair<Surface, Size>> previews = new ArrayList<>(); 502 Iterator<Size> previewSizeIter = previewOutputSizes.iterator(); 503 for (Surface p : mPreviewOutputs) { 504 previews.add(new Pair<>(p, previewSizeIter.next())); 505 } 506 mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector); 507 mGLThreadManager.allowNewFrames(); 508 mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture(); 509 if (mPreviewTexture != null) { 510 mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback); 511 } 512 513 try { 514 mCamera.setParameters(mParams); 515 } catch (RuntimeException e) { 516 Log.e(TAG, "Received device exception while configuring: ", e); 517 mDeviceState.setError( 518 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 519 520 } 521 } 522 resetJpegSurfaceFormats(Collection<Surface> surfaces)523 private void resetJpegSurfaceFormats(Collection<Surface> surfaces) { 524 if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) { 525 return; 526 } 527 for(Surface s : surfaces) { 528 if (s == null || !s.isValid()) { 529 Log.w(TAG, "Jpeg surface is invalid, skipping..."); 530 continue; 531 } 532 try { 533 LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB); 534 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 535 Log.w(TAG, "Surface abandoned, skipping...", e); 536 } 537 } 538 } 539 540 /** 541 * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger 542 * than all of the configured {@code JPEG} outputs (by both width and height). 543 * 544 * <p>If multiple supported JPEG sizes are larger, select the smallest of them which 545 * still satisfies the above constraint.</p> 546 * 547 * <p>As a result, the returned size is guaranteed to be usable without needing 548 * to upscale any of the outputs. If only one {@code JPEG} surface is used, 549 * then no scaling/cropping is necessary between the taken picture and 550 * the {@code JPEG} output surface.</p> 551 * 552 * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats 553 * @param params api1 parameters (used for reading only) 554 * 555 * @return a size large enough to fit all of the configured {@code JPEG} outputs, or 556 * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG} 557 * surfaces. 558 */ calculatePictureSize( List<Surface> callbackOutputs, List<Size> callbackSizes, Camera.Parameters params)559 private Size calculatePictureSize( List<Surface> callbackOutputs, 560 List<Size> callbackSizes, Camera.Parameters params) { 561 /* 562 * Find the largest JPEG size (if any), from the configured outputs: 563 * - the api1 picture size should be set to the smallest legal size that's at least as large 564 * as the largest configured JPEG size 565 */ 566 if (callbackOutputs.size() != callbackSizes.size()) { 567 throw new IllegalStateException("Input collections must be same length"); 568 } 569 List<Size> configuredJpegSizes = new ArrayList<>(); 570 Iterator<Size> sizeIterator = callbackSizes.iterator(); 571 for (Surface callbackSurface : callbackOutputs) { 572 Size jpegSize = sizeIterator.next(); 573 if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) { 574 continue; // Ignore non-JPEG callback formats 575 } 576 577 configuredJpegSizes.add(jpegSize); 578 } 579 if (!configuredJpegSizes.isEmpty()) { 580 /* 581 * Find the largest configured JPEG width, and height, independently 582 * of the rest. 583 * 584 * The rest of the JPEG streams can be cropped out of this smallest bounding 585 * rectangle. 586 */ 587 int maxConfiguredJpegWidth = -1; 588 int maxConfiguredJpegHeight = -1; 589 for (Size jpegSize : configuredJpegSizes) { 590 maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ? 591 jpegSize.getWidth() : maxConfiguredJpegWidth; 592 maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ? 593 jpegSize.getHeight() : maxConfiguredJpegHeight; 594 } 595 Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight); 596 597 List<Size> supportedJpegSizes = ParameterUtils.convertSizeList( 598 params.getSupportedPictureSizes()); 599 600 /* 601 * Find the smallest supported JPEG size that can fit the smallest bounding 602 * rectangle for the configured JPEG sizes. 603 */ 604 List<Size> candidateSupportedJpegSizes = new ArrayList<>(); 605 for (Size supportedJpegSize : supportedJpegSizes) { 606 if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth && 607 supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) { 608 candidateSupportedJpegSizes.add(supportedJpegSize); 609 } 610 } 611 612 if (candidateSupportedJpegSizes.isEmpty()) { 613 throw new AssertionError( 614 "Could not find any supported JPEG sizes large enough to fit " + 615 smallestBoundJpegSize); 616 } 617 618 Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes, 619 new SizeAreaComparator()); 620 621 if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) { 622 Log.w(TAG, 623 String.format( 624 "configureOutputs - Will need to crop picture %s into " 625 + "smallest bound size %s", 626 smallestSupportedJpegSize, smallestBoundJpegSize)); 627 } 628 629 return smallestSupportedJpegSize; 630 } 631 632 return null; 633 } 634 checkAspectRatiosMatch(Size a, Size b)635 private static boolean checkAspectRatiosMatch(Size a, Size b) { 636 float aAspect = a.getWidth() / (float) a.getHeight(); 637 float bAspect = b.getWidth() / (float) b.getHeight(); 638 639 return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE; 640 } 641 642 // Calculate the highest FPS range supported getPhotoPreviewFpsRange(List<int[]> frameRates)643 private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) { 644 if (frameRates.size() == 0) { 645 Log.e(TAG, "No supported frame rates returned!"); 646 return null; 647 } 648 649 int bestMin = 0; 650 int bestMax = 0; 651 int bestIndex = 0; 652 int index = 0; 653 for (int[] rate : frameRates) { 654 int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]; 655 int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]; 656 if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) { 657 bestMin = minFps; 658 bestMax = maxFps; 659 bestIndex = index; 660 } 661 index++; 662 } 663 664 return frameRates.get(bestIndex); 665 } 666 667 private final Handler.Callback mRequestHandlerCb = new Handler.Callback() { 668 private boolean mCleanup = false; 669 private final LegacyResultMapper mMapper = new LegacyResultMapper(); 670 671 @Override 672 public boolean handleMessage(Message msg) { 673 if (mCleanup) { 674 return true; 675 } 676 677 if (DEBUG) { 678 Log.d(TAG, "Request thread handling message:" + msg.what); 679 } 680 long startTime = 0; 681 if (DEBUG) { 682 startTime = SystemClock.elapsedRealtimeNanos(); 683 } 684 switch (msg.what) { 685 case MSG_CONFIGURE_OUTPUTS: 686 ConfigureHolder config = (ConfigureHolder) msg.obj; 687 int sizes = config.surfaces != null ? config.surfaces.size() : 0; 688 Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured."); 689 690 try { 691 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT, 692 TimeUnit.MILLISECONDS); 693 if (!success) { 694 Log.e(TAG, "Timed out while queueing configure request."); 695 mCaptureCollector.failAll(); 696 } 697 } catch (InterruptedException e) { 698 Log.e(TAG, "Interrupted while waiting for requests to complete."); 699 mDeviceState.setError( 700 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 701 break; 702 } 703 704 configureOutputs(config.surfaces); 705 config.condition.open(); 706 if (DEBUG) { 707 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime; 708 Log.d(TAG, "Configure took " + totalTime + " ns"); 709 } 710 break; 711 case MSG_SUBMIT_CAPTURE_REQUEST: 712 Handler handler = RequestThreadManager.this.mRequestThread.getHandler(); 713 boolean anyRequestOutputAbandoned = false; 714 715 // Get the next burst from the request queue. 716 Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext(); 717 718 if (nextBurst == null) { 719 // If there are no further requests queued, wait for any currently executing 720 // requests to complete, then switch to idle state. 721 try { 722 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT, 723 TimeUnit.MILLISECONDS); 724 if (!success) { 725 Log.e(TAG, 726 "Timed out while waiting for prior requests to complete."); 727 mCaptureCollector.failAll(); 728 } 729 } catch (InterruptedException e) { 730 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e); 731 mDeviceState.setError( 732 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 733 break; 734 } 735 736 synchronized (mIdleLock) { 737 // Retry the the request queue. 738 nextBurst = mRequestQueue.getNext(); 739 740 // If we still have no queued requests, go idle. 741 if (nextBurst == null) { 742 mDeviceState.setIdle(); 743 break; 744 } 745 } 746 } 747 748 if (nextBurst != null) { 749 // Queue another capture if we did not get the last burst. 750 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST); 751 } 752 753 // Complete each request in the burst 754 List<RequestHolder> requests = 755 nextBurst.first.produceRequestHolders(nextBurst.second); 756 for (RequestHolder holder : requests) { 757 CaptureRequest request = holder.getRequest(); 758 759 boolean paramsChanged = false; 760 761 // Only update parameters if the request has changed 762 if (mLastRequest == null || mLastRequest.captureRequest != request) { 763 764 // The intermediate buffer is sometimes null, but we always need 765 // the Camera1 API configured preview size 766 Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize()); 767 768 LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics, 769 request, previewSize, mParams); // params are copied 770 771 772 // Parameters are mutated as a side-effect 773 LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest); 774 775 // If the parameters have changed, set them in the Camera1 API. 776 if (!mParams.same(legacyRequest.parameters)) { 777 try { 778 mCamera.setParameters(legacyRequest.parameters); 779 } catch (RuntimeException e) { 780 // If setting the parameters failed, report a request error to 781 // the camera client, and skip any further work for this request 782 Log.e(TAG, "Exception while setting camera parameters: ", e); 783 holder.failRequest(); 784 mDeviceState.setCaptureStart(holder, /*timestamp*/0, 785 CameraDeviceImpl.CameraDeviceCallbacks. 786 ERROR_CAMERA_REQUEST); 787 continue; 788 } 789 paramsChanged = true; 790 mParams = legacyRequest.parameters; 791 } 792 793 mLastRequest = legacyRequest; 794 } 795 796 try { 797 boolean success = mCaptureCollector.queueRequest(holder, 798 mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS); 799 800 if (!success) { 801 // Report a request error if we timed out while queuing this. 802 Log.e(TAG, "Timed out while queueing capture request."); 803 holder.failRequest(); 804 mDeviceState.setCaptureStart(holder, /*timestamp*/0, 805 CameraDeviceImpl.CameraDeviceCallbacks. 806 ERROR_CAMERA_REQUEST); 807 continue; 808 } 809 810 // Starting the preview needs to happen before enabling 811 // face detection or auto focus 812 if (holder.hasPreviewTargets()) { 813 doPreviewCapture(holder); 814 } 815 if (holder.hasJpegTargets()) { 816 while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT, 817 TimeUnit.MILLISECONDS)) { 818 // Fail preview requests until the queue is empty. 819 Log.e(TAG, "Timed out while waiting for preview requests to " + 820 "complete."); 821 mCaptureCollector.failNextPreview(); 822 } 823 mReceivedJpeg.close(); 824 doJpegCapturePrepare(holder); 825 } 826 827 /* 828 * Do all the actions that require a preview to have been started 829 */ 830 831 // Toggle face detection on/off 832 // - do this before AF to give AF a chance to use faces 833 mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams); 834 835 // Unconditionally process AF triggers, since they're non-idempotent 836 // - must be done after setting the most-up-to-date AF mode 837 mFocusStateMapper.processRequestTriggers(request, mParams); 838 839 if (holder.hasJpegTargets()) { 840 doJpegCapture(holder); 841 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) { 842 Log.e(TAG, "Hit timeout for jpeg callback!"); 843 mCaptureCollector.failNextJpeg(); 844 } 845 } 846 847 } catch (IOException e) { 848 Log.e(TAG, "Received device exception during capture call: ", e); 849 mDeviceState.setError( 850 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 851 break; 852 } catch (InterruptedException e) { 853 Log.e(TAG, "Interrupted during capture: ", e); 854 mDeviceState.setError( 855 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 856 break; 857 } catch (RuntimeException e) { 858 Log.e(TAG, "Received device exception during capture call: ", e); 859 mDeviceState.setError( 860 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 861 break; 862 } 863 864 if (paramsChanged) { 865 if (DEBUG) { 866 Log.d(TAG, "Params changed -- getting new Parameters from HAL."); 867 } 868 try { 869 mParams = mCamera.getParameters(); 870 } catch (RuntimeException e) { 871 Log.e(TAG, "Received device exception: ", e); 872 mDeviceState.setError( 873 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 874 break; 875 } 876 877 // Update parameters to the latest that we think the camera is using 878 mLastRequest.setParameters(mParams); 879 } 880 881 MutableLong timestampMutable = new MutableLong(/*value*/0L); 882 try { 883 boolean success = mCaptureCollector.waitForRequestCompleted(holder, 884 REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS, 885 /*out*/timestampMutable); 886 887 if (!success) { 888 Log.e(TAG, "Timed out while waiting for request to complete."); 889 mCaptureCollector.failAll(); 890 } 891 } catch (InterruptedException e) { 892 Log.e(TAG, "Interrupted waiting for request completion: ", e); 893 mDeviceState.setError( 894 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 895 break; 896 } 897 898 CameraMetadataNative result = mMapper.cachedConvertResultMetadata( 899 mLastRequest, timestampMutable.value); 900 /* 901 * Order matters: The default result mapper is state-less; the 902 * other mappers carry state and may override keys set by the default 903 * mapper with their own values. 904 */ 905 906 // Update AF state 907 mFocusStateMapper.mapResultTriggers(result); 908 // Update face-related results 909 mFaceDetectMapper.mapResultFaces(result, mLastRequest); 910 911 if (!holder.requestFailed()) { 912 mDeviceState.setCaptureResult(holder, result); 913 } 914 915 if (holder.isOutputAbandoned()) { 916 anyRequestOutputAbandoned = true; 917 } 918 } 919 920 // Stop the repeating request if any of its output surfaces is abandoned. 921 if (anyRequestOutputAbandoned && nextBurst.first.isRepeating()) { 922 long lastFrameNumber = cancelRepeating(nextBurst.first.getRequestId()); 923 if (DEBUG) { 924 Log.d(TAG, "Stopped repeating request. Last frame number is " + 925 lastFrameNumber); 926 } 927 mDeviceState.setRepeatingRequestError(lastFrameNumber); 928 } 929 930 if (DEBUG) { 931 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime; 932 Log.d(TAG, "Capture request took " + totalTime + " ns"); 933 mRequestCounter.countAndLog(); 934 } 935 break; 936 case MSG_CLEANUP: 937 mCleanup = true; 938 try { 939 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT, 940 TimeUnit.MILLISECONDS); 941 if (!success) { 942 Log.e(TAG, "Timed out while queueing cleanup request."); 943 mCaptureCollector.failAll(); 944 } 945 } catch (InterruptedException e) { 946 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e); 947 mDeviceState.setError( 948 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 949 } 950 if (mGLThreadManager != null) { 951 mGLThreadManager.quit(); 952 mGLThreadManager = null; 953 } 954 if (mCamera != null) { 955 mCamera.release(); 956 mCamera = null; 957 } 958 resetJpegSurfaceFormats(mCallbackOutputs); 959 break; 960 case RequestHandlerThread.MSG_POKE_IDLE_HANDLER: 961 // OK: Ignore message. 962 break; 963 default: 964 throw new AssertionError("Unhandled message " + msg.what + 965 " on RequestThread."); 966 } 967 return true; 968 } 969 }; 970 971 /** 972 * Create a new RequestThreadManager. 973 * 974 * @param cameraId the id of the camera to use. 975 * @param camera an open camera object. The RequestThreadManager takes ownership of this camera 976 * object, and is responsible for closing it. 977 * @param characteristics the static camera characteristics corresponding to this camera device 978 * @param deviceState a {@link CameraDeviceState} state machine. 979 */ RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics, CameraDeviceState deviceState)980 public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics, 981 CameraDeviceState deviceState) { 982 mCamera = checkNotNull(camera, "camera must not be null"); 983 mCameraId = cameraId; 984 mCharacteristics = checkNotNull(characteristics, "characteristics must not be null"); 985 String name = String.format("RequestThread-%d", cameraId); 986 TAG = name; 987 mDeviceState = checkNotNull(deviceState, "deviceState must not be null"); 988 mFocusStateMapper = new LegacyFocusStateMapper(mCamera); 989 mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics); 990 mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState); 991 mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb); 992 mCamera.setErrorCallback(mErrorCallback); 993 } 994 995 /** 996 * Start the request thread. 997 */ start()998 public void start() { 999 mRequestThread.start(); 1000 } 1001 1002 /** 1003 * Flush any pending requests. 1004 * 1005 * @return the last frame number. 1006 */ flush()1007 public long flush() { 1008 Log.i(TAG, "Flushing all pending requests."); 1009 long lastFrame = mRequestQueue.stopRepeating(); 1010 mCaptureCollector.failAll(); 1011 return lastFrame; 1012 } 1013 1014 /** 1015 * Quit the request thread, and clean up everything. 1016 */ quit()1017 public void quit() { 1018 if (!mQuit.getAndSet(true)) { // Avoid sending messages on dead thread's handler. 1019 Handler handler = mRequestThread.waitAndGetHandler(); 1020 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP)); 1021 mRequestThread.quitSafely(); 1022 try { 1023 mRequestThread.join(); 1024 } catch (InterruptedException e) { 1025 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 1026 mRequestThread.getName(), mRequestThread.getId())); 1027 } 1028 } 1029 } 1030 1031 /** 1032 * Submit the given burst of requests to be captured. 1033 * 1034 * <p>If the burst is repeating, replace the current repeating burst.</p> 1035 * 1036 * @param requests the burst of requests to add to the queue. 1037 * @param repeating true if the burst is repeating. 1038 * @return the submission info, including the new request id, and the last frame number, which 1039 * contains either the frame number of the last frame that will be returned for this request, 1040 * or the frame number of the last frame that will be returned for the current repeating 1041 * request if this burst is set to be repeating. 1042 */ submitCaptureRequests(CaptureRequest[] requests, boolean repeating)1043 public SubmitInfo submitCaptureRequests(CaptureRequest[] requests, boolean repeating) { 1044 Handler handler = mRequestThread.waitAndGetHandler(); 1045 SubmitInfo info; 1046 synchronized (mIdleLock) { 1047 info = mRequestQueue.submit(requests, repeating); 1048 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST); 1049 } 1050 return info; 1051 } 1052 1053 /** 1054 * Cancel a repeating request. 1055 * 1056 * @param requestId the id of the repeating request to cancel. 1057 * @return the last frame to be returned from the HAL for the given repeating request, or 1058 * {@code INVALID_FRAME} if none exists. 1059 */ cancelRepeating(int requestId)1060 public long cancelRepeating(int requestId) { 1061 return mRequestQueue.stopRepeating(requestId); 1062 } 1063 1064 /** 1065 * Configure with the current list of output Surfaces. 1066 * 1067 * <p> 1068 * This operation blocks until the configuration is complete. 1069 * </p> 1070 * 1071 * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p> 1072 * 1073 * @param outputs a {@link java.util.Collection} of outputs to configure. 1074 */ configure(Collection<Pair<Surface, Size>> outputs)1075 public void configure(Collection<Pair<Surface, Size>> outputs) { 1076 Handler handler = mRequestThread.waitAndGetHandler(); 1077 final ConditionVariable condition = new ConditionVariable(/*closed*/false); 1078 ConfigureHolder holder = new ConfigureHolder(condition, outputs); 1079 handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder)); 1080 condition.block(); 1081 } 1082 } 1083