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