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