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