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