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