LegacyCameraDevice.java revision 3e4fed203fe7c945c53c6d6bb9f160932a1d15b3
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.CameraBinderDecorator; 27import android.hardware.camera2.utils.CameraRuntimeException; 28import android.os.ConditionVariable; 29import android.os.Handler; 30import android.os.HandlerThread; 31import android.os.RemoteException; 32import android.util.Log; 33import android.view.Surface; 34 35import java.util.ArrayList; 36import java.util.List; 37import java.util.concurrent.atomic.AtomicInteger; 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 58 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true); 59 60 private final HandlerThread mResultThread = new HandlerThread("ResultThread"); 61 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread"); 62 private final Handler mCallbackHandler; 63 private final Handler mResultHandler; 64 private static final int ILLEGAL_VALUE = -1; 65 66 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { 67 if (holder == null) { 68 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, 69 ILLEGAL_VALUE, ILLEGAL_VALUE); 70 } 71 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(), 72 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber()); 73 } 74 75 /** 76 * Listener for the camera device state machine. Calls the appropriate 77 * {@link ICameraDeviceCallbacks} for each state transition. 78 */ 79 private final CameraDeviceState.CameraDeviceStateListener mStateListener = 80 new CameraDeviceState.CameraDeviceStateListener() { 81 @Override 82 public void onError(final int errorCode, RequestHolder holder) { 83 mIdle.open(); 84 final CaptureResultExtras extras = getExtrasFromRequest(holder); 85 mResultHandler.post(new Runnable() { 86 @Override 87 public void run() { 88 if (DEBUG) { 89 Log.d(TAG, "doing onError callback."); 90 } 91 try { 92 mDeviceCallbacks.onCameraError(errorCode, extras); 93 } catch (RemoteException e) { 94 throw new IllegalStateException( 95 "Received remote exception during onCameraError callback: ", e); 96 } 97 } 98 }); 99 } 100 101 @Override 102 public void onConfiguring() { 103 // Do nothing 104 if (DEBUG) { 105 Log.d(TAG, "doing onConfiguring callback."); 106 } 107 } 108 109 @Override 110 public void onIdle() { 111 mIdle.open(); 112 113 mResultHandler.post(new Runnable() { 114 @Override 115 public void run() { 116 if (DEBUG) { 117 Log.d(TAG, "doing onIdle callback."); 118 } 119 try { 120 mDeviceCallbacks.onCameraIdle(); 121 } catch (RemoteException e) { 122 throw new IllegalStateException( 123 "Received remote exception during onCameraIdle callback: ", e); 124 } 125 } 126 }); 127 } 128 129 @Override 130 public void onCaptureStarted(RequestHolder holder) { 131 final CaptureResultExtras extras = getExtrasFromRequest(holder); 132 133 final long timestamp = System.nanoTime(); 134 mResultHandler.post(new Runnable() { 135 @Override 136 public void run() { 137 if (DEBUG) { 138 Log.d(TAG, "doing onCaptureStarted callback."); 139 } 140 try { 141 // TODO: Don't fake timestamp 142 mDeviceCallbacks.onCaptureStarted(extras, timestamp); 143 } catch (RemoteException e) { 144 throw new IllegalStateException( 145 "Received remote exception during onCameraError callback: ", e); 146 } 147 } 148 }); 149 } 150 151 @Override 152 public void onCaptureResult(final CameraMetadataNative result, RequestHolder holder) { 153 final CaptureResultExtras extras = getExtrasFromRequest(holder); 154 155 mResultHandler.post(new Runnable() { 156 @Override 157 public void run() { 158 if (DEBUG) { 159 Log.d(TAG, "doing onCaptureResult callback."); 160 } 161 try { 162 // TODO: Don't fake metadata 163 mDeviceCallbacks.onResultReceived(result, extras); 164 } catch (RemoteException e) { 165 throw new IllegalStateException( 166 "Received remote exception during onCameraError callback: ", e); 167 } 168 } 169 }); 170 } 171 }; 172 173 private final RequestThreadManager mRequestThreadManager; 174 175 /** 176 * Check if a given surface uses {@link ImageFormat#YUV_420_888} format. 177 * 178 * @param s the surface to check. 179 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888}. 180 */ 181 static boolean needsConversion(Surface s) { 182 return LegacyCameraDevice.nativeDetectSurfaceType(s) == ImageFormat.YUV_420_888; 183 } 184 185 /** 186 * Create a new emulated camera device from a given Camera 1 API camera. 187 * 188 * <p> 189 * The {@link Camera} provided to this constructor must already have been successfully opened, 190 * and ownership of the provided camera is passed to this object. No further calls to the 191 * camera methods should be made following this constructor. 192 * </p> 193 * 194 * @param cameraId the id of the camera. 195 * @param camera an open {@link Camera} device. 196 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations. 197 */ 198 public LegacyCameraDevice(int cameraId, Camera camera, ICameraDeviceCallbacks callbacks) { 199 mCameraId = cameraId; 200 mDeviceCallbacks = callbacks; 201 TAG = String.format("CameraDevice-%d-LE", mCameraId); 202 203 mResultThread.start(); 204 mResultHandler = new Handler(mResultThread.getLooper()); 205 mCallbackHandlerThread.start(); 206 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper()); 207 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener); 208 mRequestThreadManager = 209 new RequestThreadManager(cameraId, camera, mDeviceState); 210 mRequestThreadManager.start(); 211 } 212 213 /** 214 * Configure the device with a set of output surfaces. 215 * 216 * @param outputs a list of surfaces to set. 217 * @return an error code for this binder operation, or {@link CameraBinderDecorator.NO_ERROR} 218 * on success. 219 */ 220 public int configureOutputs(List<Surface> outputs) { 221 int error = mDeviceState.setConfiguring(); 222 if (error == CameraBinderDecorator.NO_ERROR) { 223 mRequestThreadManager.configure(outputs); 224 error = mDeviceState.setIdle(); 225 } 226 return error; 227 } 228 229 /** 230 * Submit a burst of capture requests. 231 * 232 * @param requestList a list of capture requests to execute. 233 * @param repeating {@code true} if this burst is repeating. 234 * @param frameNumber an output argument that contains either the frame number of the last frame 235 * that will be returned for this request, or the frame number of the last 236 * frame that will be returned for the current repeating request if this 237 * burst is set to be repeating. 238 * @return the request id. 239 */ 240 public int submitRequestList(List<CaptureRequest> requestList, boolean repeating, 241 /*out*/LongParcelable frameNumber) { 242 // TODO: validate request here 243 mIdle.close(); 244 return mRequestThreadManager.submitCaptureRequests(requestList, repeating, 245 frameNumber); 246 } 247 248 /** 249 * Submit a single capture request. 250 * 251 * @param request the capture request to execute. 252 * @param repeating {@code true} if this request is repeating. 253 * @param frameNumber an output argument that contains either the frame number of the last frame 254 * that will be returned for this request, or the frame number of the last 255 * frame that will be returned for the current repeating request if this 256 * request is set to be repeating. 257 * @return the request id. 258 */ 259 public int submitRequest(CaptureRequest request, boolean repeating, 260 /*out*/LongParcelable frameNumber) { 261 ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 262 requestList.add(request); 263 return submitRequestList(requestList, repeating, frameNumber); 264 } 265 266 /** 267 * Cancel the repeating request with the given request id. 268 * 269 * @param requestId the request id of the request to cancel. 270 * @return the last frame number to be returned from the HAL for the given repeating request, or 271 * {@code INVALID_FRAME} if none exists. 272 */ 273 public long cancelRequest(int requestId) { 274 return mRequestThreadManager.cancelRepeating(requestId); 275 } 276 277 /** 278 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received. 279 */ 280 public void waitUntilIdle() { 281 mIdle.block(); 282 } 283 284 @Override 285 public void close() { 286 mRequestThreadManager.quit(); 287 mCallbackHandlerThread.quitSafely(); 288 mResultThread.quitSafely(); 289 290 try { 291 mCallbackHandlerThread.join(); 292 } catch (InterruptedException e) { 293 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 294 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId())); 295 } 296 297 try { 298 mResultThread.join(); 299 } catch (InterruptedException e) { 300 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 301 mResultThread.getName(), mResultThread.getId())); 302 } 303 304 // TODO: throw IllegalStateException in every method after close has been called 305 } 306 307 @Override 308 protected void finalize() throws Throwable { 309 try { 310 close(); 311 } catch (CameraRuntimeException e) { 312 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage()); 313 } finally { 314 super.finalize(); 315 } 316 } 317 318 protected static native int nativeDetectSurfaceType(Surface surface); 319 320 protected static native void nativeDetectSurfaceDimens(Surface surface, int[] dimens); 321 322 protected static native void nativeConfigureSurface(Surface surface, int width, int height, 323 int pixelFormat); 324 325 protected static native void nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, 326 int height, int pixelFormat); 327 328 protected static native void nativeSetSurfaceFormat(Surface surface, int pixelFormat); 329 330 protected static native void nativeSetSurfaceDimens(Surface surface, int width, int height); 331 332} 333