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