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