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