1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.camera2.legacy; 18 19 import android.graphics.ImageFormat; 20 import android.graphics.SurfaceTexture; 21 import android.hardware.Camera; 22 import android.hardware.camera2.CameraCharacteristics; 23 import android.hardware.camera2.CaptureRequest; 24 import android.hardware.camera2.impl.CameraDeviceImpl; 25 import android.hardware.camera2.impl.CaptureResultExtras; 26 import android.hardware.camera2.ICameraDeviceCallbacks; 27 import android.hardware.camera2.params.StreamConfiguration; 28 import android.hardware.camera2.params.StreamConfigurationMap; 29 import android.hardware.camera2.utils.ArrayUtils; 30 import android.hardware.camera2.utils.CameraBinderDecorator; 31 import android.hardware.camera2.utils.LongParcelable; 32 import android.hardware.camera2.impl.CameraMetadataNative; 33 import android.hardware.camera2.utils.CameraRuntimeException; 34 import android.os.ConditionVariable; 35 import android.os.Handler; 36 import android.os.HandlerThread; 37 import android.os.RemoteException; 38 import android.util.Log; 39 import android.util.Size; 40 import android.view.Surface; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Collection; 45 import java.util.List; 46 47 import static android.hardware.camera2.legacy.LegacyExceptionUtils.*; 48 import static android.hardware.camera2.utils.CameraBinderDecorator.*; 49 import static com.android.internal.util.Preconditions.*; 50 51 /** 52 * This class emulates the functionality of a Camera2 device using a the old Camera class. 53 * 54 * <p> 55 * There are two main components that are used to implement this: 56 * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}). 57 * - A message-queue based pipeline that manages an old Camera class, and executes capture and 58 * configuration requests. 59 * </p> 60 */ 61 public class LegacyCameraDevice implements AutoCloseable { 62 public static final String DEBUG_PROP = "HAL1ShimLogging"; 63 private final String TAG; 64 65 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG); 66 private final int mCameraId; 67 private final CameraCharacteristics mStaticCharacteristics; 68 private final ICameraDeviceCallbacks mDeviceCallbacks; 69 private final CameraDeviceState mDeviceState = new CameraDeviceState(); 70 private List<Surface> mConfiguredSurfaces; 71 private boolean mClosed = false; 72 73 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true); 74 75 private final HandlerThread mResultThread = new HandlerThread("ResultThread"); 76 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread"); 77 private final Handler mCallbackHandler; 78 private final Handler mResultHandler; 79 private static final int ILLEGAL_VALUE = -1; 80 81 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { 82 if (holder == null) { 83 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, 84 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE); 85 } 86 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(), 87 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(), 88 /*partialResultCount*/1); 89 } 90 91 /** 92 * Listener for the camera device state machine. Calls the appropriate 93 * {@link ICameraDeviceCallbacks} for each state transition. 94 */ 95 private final CameraDeviceState.CameraDeviceStateListener mStateListener = 96 new CameraDeviceState.CameraDeviceStateListener() { 97 @Override 98 public void onError(final int errorCode, final RequestHolder holder) { 99 if (DEBUG) { 100 Log.d(TAG, "onError called, errorCode = " + errorCode); 101 } 102 switch (errorCode) { 103 /* 104 * Only be considered idle if we hit a fatal error 105 * and no further requests can be processed. 106 */ 107 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED: 108 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE: 109 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: { 110 mIdle.open(); 111 112 if (DEBUG) { 113 Log.d(TAG, "onError - opening idle"); 114 } 115 } 116 } 117 118 final CaptureResultExtras extras = getExtrasFromRequest(holder); 119 mResultHandler.post(new Runnable() { 120 @Override 121 public void run() { 122 if (DEBUG) { 123 Log.d(TAG, "doing onError callback for request " + holder.getRequestId() + 124 ", with error code " + errorCode); 125 } 126 try { 127 mDeviceCallbacks.onDeviceError(errorCode, extras); 128 } catch (RemoteException e) { 129 throw new IllegalStateException( 130 "Received remote exception during onCameraError callback: ", e); 131 } 132 } 133 }); 134 } 135 136 @Override 137 public void onConfiguring() { 138 // Do nothing 139 if (DEBUG) { 140 Log.d(TAG, "doing onConfiguring callback."); 141 } 142 } 143 144 @Override 145 public void onIdle() { 146 if (DEBUG) { 147 Log.d(TAG, "onIdle called"); 148 } 149 150 mIdle.open(); 151 152 mResultHandler.post(new Runnable() { 153 @Override 154 public void run() { 155 if (DEBUG) { 156 Log.d(TAG, "doing onIdle callback."); 157 } 158 try { 159 mDeviceCallbacks.onDeviceIdle(); 160 } catch (RemoteException e) { 161 throw new IllegalStateException( 162 "Received remote exception during onCameraIdle callback: ", e); 163 } 164 } 165 }); 166 } 167 168 @Override 169 public void onBusy() { 170 mIdle.close(); 171 172 if (DEBUG) { 173 Log.d(TAG, "onBusy called"); 174 } 175 } 176 177 @Override 178 public void onCaptureStarted(final RequestHolder holder, final long timestamp) { 179 final CaptureResultExtras extras = getExtrasFromRequest(holder); 180 181 mResultHandler.post(new Runnable() { 182 @Override 183 public void run() { 184 if (DEBUG) { 185 Log.d(TAG, "doing onCaptureStarted callback for request " + 186 holder.getRequestId()); 187 } 188 try { 189 mDeviceCallbacks.onCaptureStarted(extras, timestamp); 190 } catch (RemoteException e) { 191 throw new IllegalStateException( 192 "Received remote exception during onCameraError callback: ", e); 193 } 194 } 195 }); 196 } 197 198 @Override 199 public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) { 200 final CaptureResultExtras extras = getExtrasFromRequest(holder); 201 202 mResultHandler.post(new Runnable() { 203 @Override 204 public void run() { 205 if (DEBUG) { 206 Log.d(TAG, "doing onCaptureResult callback for request " + 207 holder.getRequestId()); 208 } 209 try { 210 mDeviceCallbacks.onResultReceived(result, extras); 211 } catch (RemoteException e) { 212 throw new IllegalStateException( 213 "Received remote exception during onCameraError callback: ", e); 214 } 215 } 216 }); 217 } 218 }; 219 220 private final RequestThreadManager mRequestThreadManager; 221 222 /** 223 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily 224 * converted to this; YV12 and NV21 are the two currently supported formats. 225 * 226 * @param s the surface to check. 227 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible 228 * format. 229 */ 230 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException { 231 int nativeType = detectSurfaceType(s); 232 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 || 233 nativeType == ImageFormat.NV21; 234 } 235 236 /** 237 * Create a new emulated camera device from a given Camera 1 API camera. 238 * 239 * <p> 240 * The {@link Camera} provided to this constructor must already have been successfully opened, 241 * and ownership of the provided camera is passed to this object. No further calls to the 242 * camera methods should be made following this constructor. 243 * </p> 244 * 245 * @param cameraId the id of the camera. 246 * @param camera an open {@link Camera} device. 247 * @param characteristics the static camera characteristics for this camera device 248 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations. 249 */ 250 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, 251 ICameraDeviceCallbacks callbacks) { 252 mCameraId = cameraId; 253 mDeviceCallbacks = callbacks; 254 TAG = String.format("CameraDevice-%d-LE", mCameraId); 255 256 mResultThread.start(); 257 mResultHandler = new Handler(mResultThread.getLooper()); 258 mCallbackHandlerThread.start(); 259 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper()); 260 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener); 261 mStaticCharacteristics = characteristics; 262 mRequestThreadManager = 263 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState); 264 mRequestThreadManager.start(); 265 } 266 267 /** 268 * Configure the device with a set of output surfaces. 269 * 270 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p> 271 * 272 * <p>Every surface in {@code outputs} must be non-{@code null}.</p> 273 * 274 * @param outputs a list of surfaces to set. 275 * @return an error code for this binder operation, or {@link NO_ERROR} 276 * on success. 277 */ 278 public int configureOutputs(List<Surface> outputs) { 279 if (outputs != null) { 280 for (Surface output : outputs) { 281 if (output == null) { 282 Log.e(TAG, "configureOutputs - null outputs are not allowed"); 283 return BAD_VALUE; 284 } 285 StreamConfigurationMap streamConfigurations = mStaticCharacteristics. 286 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 287 288 // Validate surface size and format. 289 try { 290 Size s = getSurfaceSize(output); 291 int surfaceType = detectSurfaceType(output); 292 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType); 293 294 if (sizes == null) { 295 // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482 296 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 && 297 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) { 298 299 // YUV_420_888 is always present in LEGACY for all IMPLEMENTATION_DEFINED 300 // output sizes, and is publicly visible in the API (i.e. 301 // {@code #getOutputSizes} works here). 302 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888); 303 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) { 304 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG); 305 } 306 } 307 308 if (!ArrayUtils.contains(sizes, s)) { 309 String reason = (sizes == null) ? "format is invalid." : 310 ("size not in valid set: " + Arrays.toString(sizes)); 311 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is" 312 + " not valid, %s", s.getWidth(), s.getHeight(), surfaceType, 313 reason)); 314 return BAD_VALUE; 315 } 316 } catch (BufferQueueAbandonedException e) { 317 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e); 318 return BAD_VALUE; 319 } 320 321 } 322 } 323 324 boolean success = false; 325 if (mDeviceState.setConfiguring()) { 326 mRequestThreadManager.configure(outputs); 327 success = mDeviceState.setIdle(); 328 } 329 330 if (success) { 331 mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null; 332 } else { 333 return CameraBinderDecorator.INVALID_OPERATION; 334 } 335 return CameraBinderDecorator.NO_ERROR; 336 } 337 338 /** 339 * Submit a burst of capture requests. 340 * 341 * @param requestList a list of capture requests to execute. 342 * @param repeating {@code true} if this burst is repeating. 343 * @param frameNumber an output argument that contains either the frame number of the last frame 344 * that will be returned for this request, or the frame number of the last 345 * frame that will be returned for the current repeating request if this 346 * burst is set to be repeating. 347 * @return the request id. 348 */ 349 public int submitRequestList(List<CaptureRequest> requestList, boolean repeating, 350 /*out*/LongParcelable frameNumber) { 351 if (requestList == null || requestList.isEmpty()) { 352 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed"); 353 return BAD_VALUE; 354 } 355 356 List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() : 357 getSurfaceIds(mConfiguredSurfaces); 358 359 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 360 for (CaptureRequest request : requestList) { 361 if (request.getTargets().isEmpty()) { 362 Log.e(TAG, "submitRequestList - " 363 + "Each request must have at least one Surface target"); 364 return BAD_VALUE; 365 } 366 367 for (Surface surface : request.getTargets()) { 368 if (surface == null) { 369 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed"); 370 return BAD_VALUE; 371 } else if (mConfiguredSurfaces == null) { 372 Log.e(TAG, "submitRequestList - must configure " + 373 " device with valid surfaces before submitting requests"); 374 return INVALID_OPERATION; 375 } else if (!containsSurfaceId(surface, surfaceIds)) { 376 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured"); 377 return BAD_VALUE; 378 } 379 } 380 } 381 382 // TODO: further validation of request here 383 mIdle.close(); 384 return mRequestThreadManager.submitCaptureRequests(requestList, repeating, 385 frameNumber); 386 } 387 388 /** 389 * Submit a single capture request. 390 * 391 * @param request the capture request to execute. 392 * @param repeating {@code true} if this request is repeating. 393 * @param frameNumber an output argument that contains either the frame number of the last frame 394 * that will be returned for this request, or the frame number of the last 395 * frame that will be returned for the current repeating request if this 396 * request is set to be repeating. 397 * @return the request id. 398 */ 399 public int submitRequest(CaptureRequest request, boolean repeating, 400 /*out*/LongParcelable frameNumber) { 401 ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 402 requestList.add(request); 403 return submitRequestList(requestList, repeating, frameNumber); 404 } 405 406 /** 407 * Cancel the repeating request with the given request id. 408 * 409 * @param requestId the request id of the request to cancel. 410 * @return the last frame number to be returned from the HAL for the given repeating request, or 411 * {@code INVALID_FRAME} if none exists. 412 */ 413 public long cancelRequest(int requestId) { 414 return mRequestThreadManager.cancelRepeating(requestId); 415 } 416 417 /** 418 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received. 419 */ 420 public void waitUntilIdle() { 421 mIdle.block(); 422 } 423 424 /** 425 * Flush any pending requests. 426 * 427 * @return the last frame number. 428 */ 429 public long flush() { 430 long lastFrame = mRequestThreadManager.flush(); 431 waitUntilIdle(); 432 return lastFrame; 433 } 434 435 /** 436 * Return {@code true} if the device has been closed. 437 */ 438 public boolean isClosed() { 439 return mClosed; 440 } 441 442 @Override 443 public void close() { 444 mRequestThreadManager.quit(); 445 mCallbackHandlerThread.quitSafely(); 446 mResultThread.quitSafely(); 447 448 try { 449 mCallbackHandlerThread.join(); 450 } catch (InterruptedException e) { 451 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 452 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId())); 453 } 454 455 try { 456 mResultThread.join(); 457 } catch (InterruptedException e) { 458 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 459 mResultThread.getName(), mResultThread.getId())); 460 } 461 462 mClosed = true; 463 } 464 465 @Override 466 protected void finalize() throws Throwable { 467 try { 468 close(); 469 } catch (CameraRuntimeException e) { 470 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage()); 471 } finally { 472 super.finalize(); 473 } 474 } 475 476 /** 477 * Query the surface for its currently configured default buffer size. 478 * @param surface a non-{@code null} {@code Surface} 479 * @return the width and height of the surface 480 * 481 * @throws NullPointerException if the {@code surface} was {@code null} 482 * @throws IllegalStateException if the {@code surface} was invalid 483 */ 484 static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException { 485 checkNotNull(surface); 486 487 int[] dimens = new int[2]; 488 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens)); 489 490 return new Size(dimens[0], dimens[1]); 491 } 492 493 static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException { 494 checkNotNull(surface); 495 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface)); 496 } 497 498 static void configureSurface(Surface surface, int width, int height, 499 int pixelFormat) throws BufferQueueAbandonedException { 500 checkNotNull(surface); 501 checkArgumentPositive(width, "width must be positive."); 502 checkArgumentPositive(height, "height must be positive."); 503 504 LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height, 505 pixelFormat)); 506 } 507 508 static void produceFrame(Surface surface, byte[] pixelBuffer, int width, 509 int height, int pixelFormat) 510 throws BufferQueueAbandonedException { 511 checkNotNull(surface); 512 checkNotNull(pixelBuffer); 513 checkArgumentPositive(width, "width must be positive."); 514 checkArgumentPositive(height, "height must be positive."); 515 516 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height, 517 pixelFormat)); 518 } 519 520 static void setSurfaceFormat(Surface surface, int pixelFormat) 521 throws BufferQueueAbandonedException { 522 checkNotNull(surface); 523 524 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat)); 525 } 526 527 static void setSurfaceDimens(Surface surface, int width, int height) 528 throws BufferQueueAbandonedException { 529 checkNotNull(surface); 530 checkArgumentPositive(width, "width must be positive."); 531 checkArgumentPositive(height, "height must be positive."); 532 533 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height)); 534 } 535 536 static long getSurfaceId(Surface surface) { 537 checkNotNull(surface); 538 return nativeGetSurfaceId(surface); 539 } 540 541 static List<Long> getSurfaceIds(Collection<Surface> surfaces) { 542 if (surfaces == null) { 543 throw new NullPointerException("Null argument surfaces"); 544 } 545 List<Long> surfaceIds = new ArrayList<>(); 546 for (Surface s : surfaces) { 547 long id = getSurfaceId(s); 548 if (id == 0) { 549 throw new IllegalStateException( 550 "Configured surface had null native GraphicBufferProducer pointer!"); 551 } 552 surfaceIds.add(id); 553 } 554 return surfaceIds; 555 } 556 557 static boolean containsSurfaceId(Surface s, Collection<Long> ids) { 558 long id = getSurfaceId(s); 559 return ids.contains(id); 560 } 561 562 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation) 563 throws BufferQueueAbandonedException { 564 checkNotNull(surface); 565 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing, 566 sensorOrientation)); 567 } 568 569 static Size getTextureSize(SurfaceTexture surfaceTexture) 570 throws BufferQueueAbandonedException { 571 checkNotNull(surfaceTexture); 572 573 int[] dimens = new int[2]; 574 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture, 575 /*out*/dimens)); 576 577 return new Size(dimens[0], dimens[1]); 578 } 579 580 static void setNextTimestamp(Surface surface, long timestamp) 581 throws BufferQueueAbandonedException { 582 checkNotNull(surface); 583 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp)); 584 } 585 586 private static native int nativeDetectSurfaceType(Surface surface); 587 588 private static native int nativeDetectSurfaceDimens(Surface surface, 589 /*out*/int[/*2*/] dimens); 590 591 private static native int nativeConfigureSurface(Surface surface, int width, int height, 592 int pixelFormat); 593 594 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, 595 int height, int pixelFormat); 596 597 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat); 598 599 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height); 600 601 private static native long nativeGetSurfaceId(Surface surface); 602 603 private static native int nativeSetSurfaceOrientation(Surface surface, int facing, 604 int sensorOrientation); 605 606 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture, 607 /*out*/int[/*2*/] dimens); 608 609 private static native int nativeSetNextTimestamp(Surface surface, long timestamp); 610 611 static native int nativeGetJpegFooterSize(); 612 } 613