LegacyCameraDevice.java revision 91b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0
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.view.Surface; 33 34import java.util.ArrayList; 35import java.util.List; 36 37import static android.hardware.camera2.utils.CameraBinderDecorator.*; 38 39/** 40 * This class emulates the functionality of a Camera2 device using a the old Camera class. 41 * 42 * <p> 43 * There are two main components that are used to implement this: 44 * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}). 45 * - A message-queue based pipeline that manages an old Camera class, and executes capture and 46 * configuration requests. 47 * </p> 48 */ 49public class LegacyCameraDevice implements AutoCloseable { 50 public static final String DEBUG_PROP = "HAL1ShimLogging"; 51 private final String TAG; 52 53 private static final boolean DEBUG = false; 54 private final int mCameraId; 55 private final ICameraDeviceCallbacks mDeviceCallbacks; 56 private final CameraDeviceState mDeviceState = new CameraDeviceState(); 57 private List<Surface> mConfiguredSurfaces; 58 59 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true); 60 61 private final HandlerThread mResultThread = new HandlerThread("ResultThread"); 62 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread"); 63 private final Handler mCallbackHandler; 64 private final Handler mResultHandler; 65 private static final int ILLEGAL_VALUE = -1; 66 67 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { 68 if (holder == null) { 69 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, 70 ILLEGAL_VALUE, ILLEGAL_VALUE); 71 } 72 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(), 73 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber()); 74 } 75 76 /** 77 * Listener for the camera device state machine. Calls the appropriate 78 * {@link ICameraDeviceCallbacks} for each state transition. 79 */ 80 private final CameraDeviceState.CameraDeviceStateListener mStateListener = 81 new CameraDeviceState.CameraDeviceStateListener() { 82 @Override 83 public void onError(final int errorCode, RequestHolder holder) { 84 mIdle.open(); 85 final CaptureResultExtras extras = getExtrasFromRequest(holder); 86 mResultHandler.post(new Runnable() { 87 @Override 88 public void run() { 89 if (DEBUG) { 90 Log.d(TAG, "doing onError callback."); 91 } 92 try { 93 mDeviceCallbacks.onCameraError(errorCode, extras); 94 } catch (RemoteException e) { 95 throw new IllegalStateException( 96 "Received remote exception during onCameraError callback: ", e); 97 } 98 } 99 }); 100 } 101 102 @Override 103 public void onConfiguring() { 104 // Do nothing 105 if (DEBUG) { 106 Log.d(TAG, "doing onConfiguring callback."); 107 } 108 } 109 110 @Override 111 public void onIdle() { 112 mIdle.open(); 113 114 mResultHandler.post(new Runnable() { 115 @Override 116 public void run() { 117 if (DEBUG) { 118 Log.d(TAG, "doing onIdle callback."); 119 } 120 try { 121 mDeviceCallbacks.onCameraIdle(); 122 } catch (RemoteException e) { 123 throw new IllegalStateException( 124 "Received remote exception during onCameraIdle callback: ", e); 125 } 126 } 127 }); 128 } 129 130 @Override 131 public void onCaptureStarted(RequestHolder holder) { 132 final CaptureResultExtras extras = getExtrasFromRequest(holder); 133 134 final long timestamp = System.nanoTime(); 135 mResultHandler.post(new Runnable() { 136 @Override 137 public void run() { 138 if (DEBUG) { 139 Log.d(TAG, "doing onCaptureStarted callback."); 140 } 141 try { 142 // TODO: Don't fake timestamp 143 mDeviceCallbacks.onCaptureStarted(extras, timestamp); 144 } catch (RemoteException e) { 145 throw new IllegalStateException( 146 "Received remote exception during onCameraError callback: ", e); 147 } 148 } 149 }); 150 } 151 152 @Override 153 public void onCaptureResult(final CameraMetadataNative result, RequestHolder holder) { 154 final CaptureResultExtras extras = getExtrasFromRequest(holder); 155 156 mResultHandler.post(new Runnable() { 157 @Override 158 public void run() { 159 if (DEBUG) { 160 Log.d(TAG, "doing onCaptureResult callback."); 161 } 162 try { 163 // TODO: Don't fake metadata 164 mDeviceCallbacks.onResultReceived(result, extras); 165 } catch (RemoteException e) { 166 throw new IllegalStateException( 167 "Received remote exception during onCameraError callback: ", e); 168 } 169 } 170 }); 171 } 172 }; 173 174 private final RequestThreadManager mRequestThreadManager; 175 176 /** 177 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily 178 * converted to this; YV12 and NV21 are the two currently supported formats. 179 * 180 * @param s the surface to check. 181 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible 182 * format. 183 */ 184 static boolean needsConversion(Surface s) { 185 int nativeType = LegacyCameraDevice.nativeDetectSurfaceType(s); 186 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 || 187 nativeType == ImageFormat.NV21; 188 } 189 190 /** 191 * Create a new emulated camera device from a given Camera 1 API camera. 192 * 193 * <p> 194 * The {@link Camera} provided to this constructor must already have been successfully opened, 195 * and ownership of the provided camera is passed to this object. No further calls to the 196 * camera methods should be made following this constructor. 197 * </p> 198 * 199 * @param cameraId the id of the camera. 200 * @param camera an open {@link Camera} device. 201 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations. 202 */ 203 public LegacyCameraDevice(int cameraId, Camera camera, ICameraDeviceCallbacks callbacks) { 204 mCameraId = cameraId; 205 mDeviceCallbacks = callbacks; 206 TAG = String.format("CameraDevice-%d-LE", mCameraId); 207 208 mResultThread.start(); 209 mResultHandler = new Handler(mResultThread.getLooper()); 210 mCallbackHandlerThread.start(); 211 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper()); 212 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener); 213 mRequestThreadManager = 214 new RequestThreadManager(cameraId, camera, mDeviceState); 215 mRequestThreadManager.start(); 216 } 217 218 /** 219 * Configure the device with a set of output surfaces. 220 * 221 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p> 222 * 223 * <p>Every surface in {@code outputs} must be non-{@code null}.</p> 224 * 225 * @param outputs a list of surfaces to set. 226 * @return an error code for this binder operation, or {@link NO_ERROR} 227 * on success. 228 */ 229 public int configureOutputs(List<Surface> outputs) { 230 if (outputs != null) { 231 for (Surface output : outputs) { 232 if (output == null) { 233 Log.e(TAG, "configureOutputs - null outputs are not allowed"); 234 return BAD_VALUE; 235 } 236 } 237 } 238 239 int error = mDeviceState.setConfiguring(); 240 if (error == NO_ERROR) { 241 mRequestThreadManager.configure(outputs); 242 error = mDeviceState.setIdle(); 243 } 244 245 // TODO: May also want to check the surfaces more deeply (e.g. state, formats, sizes..) 246 if (error == NO_ERROR) { 247 mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null; 248 } 249 250 return error; 251 } 252 253 /** 254 * Submit a burst of capture requests. 255 * 256 * @param requestList a list of capture requests to execute. 257 * @param repeating {@code true} if this burst is repeating. 258 * @param frameNumber an output argument that contains either the frame number of the last frame 259 * that will be returned for this request, or the frame number of the last 260 * frame that will be returned for the current repeating request if this 261 * burst is set to be repeating. 262 * @return the request id. 263 */ 264 public int submitRequestList(List<CaptureRequest> requestList, boolean repeating, 265 /*out*/LongParcelable frameNumber) { 266 if (requestList == null || requestList.isEmpty()) { 267 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed"); 268 return BAD_VALUE; 269 } 270 271 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 272 for (CaptureRequest request : requestList) { 273 if (request.getTargets().isEmpty()) { 274 Log.e(TAG, "submitRequestList - " 275 + "Each request must have at least one Surface target"); 276 return BAD_VALUE; 277 } 278 279 for (Surface surface : request.getTargets()) { 280 if (surface == null) { 281 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed"); 282 return BAD_VALUE; 283 } else if (mConfiguredSurfaces == null) { 284 Log.e(TAG, "submitRequestList - must configure " + 285 " device with valid surfaces before submitting requests"); 286 return INVALID_OPERATION; 287 } else if (!mConfiguredSurfaces.contains(surface)) { 288 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured"); 289 return BAD_VALUE; 290 } 291 } 292 } 293 294 // TODO: further validation of request here 295 mIdle.close(); 296 return mRequestThreadManager.submitCaptureRequests(requestList, repeating, 297 frameNumber); 298 } 299 300 /** 301 * Submit a single capture request. 302 * 303 * @param request the capture request to execute. 304 * @param repeating {@code true} if this request is repeating. 305 * @param frameNumber an output argument that contains either the frame number of the last frame 306 * that will be returned for this request, or the frame number of the last 307 * frame that will be returned for the current repeating request if this 308 * request is set to be repeating. 309 * @return the request id. 310 */ 311 public int submitRequest(CaptureRequest request, boolean repeating, 312 /*out*/LongParcelable frameNumber) { 313 ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 314 requestList.add(request); 315 return submitRequestList(requestList, repeating, frameNumber); 316 } 317 318 /** 319 * Cancel the repeating request with the given request id. 320 * 321 * @param requestId the request id of the request to cancel. 322 * @return the last frame number to be returned from the HAL for the given repeating request, or 323 * {@code INVALID_FRAME} if none exists. 324 */ 325 public long cancelRequest(int requestId) { 326 return mRequestThreadManager.cancelRepeating(requestId); 327 } 328 329 /** 330 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received. 331 */ 332 public void waitUntilIdle() { 333 mIdle.block(); 334 } 335 336 @Override 337 public void close() { 338 mRequestThreadManager.quit(); 339 mCallbackHandlerThread.quitSafely(); 340 mResultThread.quitSafely(); 341 342 try { 343 mCallbackHandlerThread.join(); 344 } catch (InterruptedException e) { 345 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 346 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId())); 347 } 348 349 try { 350 mResultThread.join(); 351 } catch (InterruptedException e) { 352 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 353 mResultThread.getName(), mResultThread.getId())); 354 } 355 356 // TODO: throw IllegalStateException in every method after close has been called 357 } 358 359 @Override 360 protected void finalize() throws Throwable { 361 try { 362 close(); 363 } catch (CameraRuntimeException e) { 364 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage()); 365 } finally { 366 super.finalize(); 367 } 368 } 369 370 protected static native int nativeDetectSurfaceType(Surface surface); 371 372 protected static native void nativeDetectSurfaceDimens(Surface surface, int[] dimens); 373 374 protected static native void nativeConfigureSurface(Surface surface, int width, int height, 375 int pixelFormat); 376 377 protected static native void nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, 378 int height, int pixelFormat); 379 380 protected static native void nativeSetSurfaceFormat(Surface surface, int pixelFormat); 381 382 protected static native void nativeSetSurfaceDimens(Surface surface, int width, int height); 383 384} 385