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