CameraOps.java revision 419fb8918fcdb643dabc58437bcb0a8026a25604
1/* 2 * Copyright (C) 2013 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 com.android.testingcamera2; 18 19import android.content.Context; 20import android.graphics.ImageFormat; 21import android.hardware.camera2.CameraAccessException; 22import android.hardware.camera2.CameraDevice; 23import android.hardware.camera2.CameraManager; 24import android.hardware.camera2.CameraProperties; 25import android.hardware.camera2.CaptureRequest; 26import android.hardware.camera2.CaptureRequestKeys; 27import android.hardware.camera2.Size; 28import android.media.Image; 29import android.media.ImageReader; 30import android.os.Handler; 31import android.os.Looper; 32import android.os.Message; 33import android.util.Log; 34import android.view.Surface; 35import android.view.SurfaceHolder; 36 37import java.util.ArrayList; 38import java.util.List; 39 40/** 41 * A camera controller class that runs in its own thread, to 42 * move camera ops off the UI. Generally thread-safe. 43 */ 44public class CameraOps { 45 46 private Thread mOpsThread; 47 private Handler mOpsHandler; 48 49 private CameraManager mCameraManager; 50 private CameraDevice mCamera; 51 52 private ImageReader mCaptureReader; 53 private CameraProperties mCameraProperties = null; 54 55 private CaptureRequest mPreviewRequest; 56 // How many JPEG buffers do we want to hold on to at once 57 private static final int MAX_CONCURRENT_JPEGS = 2; 58 59 private static final int STATUS_ERROR = 0; 60 private static final int STATUS_UNINITIALIZED = 1; 61 private static final int STATUS_OK = 2; 62 private static final String TAG = "CameraOps"; 63 64 private int mStatus = STATUS_UNINITIALIZED; 65 66 private void checkOk() { 67 if (mStatus < STATUS_OK) { 68 throw new IllegalStateException(String.format("Device not OK: %d", mStatus )); 69 } 70 } 71 72 private class OpsHandler extends Handler { 73 @Override 74 public void handleMessage(Message msg) { 75 76 } 77 } 78 79 private CameraOps(Context ctx) throws ApiFailureException { 80 mCameraManager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE); 81 if (mCameraManager == null) { 82 throw new ApiFailureException("Can't connect to camera manager!"); 83 } 84 85 mOpsThread = new Thread(new Runnable() { 86 @Override 87 public void run() { 88 Looper.prepare(); 89 mOpsHandler = new OpsHandler(); 90 Looper.loop(); 91 } 92 }, "CameraOpsThread"); 93 mOpsThread.start(); 94 95 mStatus = STATUS_OK; 96 } 97 98 static public CameraOps create(Context ctx) throws ApiFailureException { 99 return new CameraOps(ctx); 100 } 101 102 public String[] getDevices() throws ApiFailureException{ 103 checkOk(); 104 try { 105 return mCameraManager.getDeviceIdList(); 106 } catch (CameraAccessException e) { 107 throw new ApiFailureException("Can't query device set", e); 108 } 109 } 110 111 public void registerCameraListener(CameraManager.CameraListener listener) 112 throws ApiFailureException { 113 checkOk(); 114 mCameraManager.registerCameraListener(listener); 115 } 116 117 public CameraProperties getCameraProperties() { 118 checkOk(); 119 if (mCameraProperties == null) { 120 throw new IllegalStateException("CameraProperties is not available"); 121 } 122 return mCameraProperties; 123 } 124 125 public void openDevice(String cameraId) 126 throws CameraAccessException, ApiFailureException { 127 checkOk(); 128 129 if (mCamera != null) { 130 throw new IllegalStateException("Already have open camera device"); 131 } 132 133 mCamera = mCameraManager.openCamera(cameraId); 134 } 135 136 public void closeDevice() 137 throws ApiFailureException { 138 checkOk(); 139 mCameraProperties = null; 140 141 if (mCamera == null) return; 142 143 try { 144 mCamera.close(); 145 } catch (Exception e) { 146 throw new ApiFailureException("can't close device!", e); 147 } 148 149 mCamera = null; 150 } 151 152 private void minimalOpenCamera() throws ApiFailureException { 153 if (mCamera == null) { 154 try { 155 String[] devices = mCameraManager.getDeviceIdList(); 156 if (devices == null || devices.length == 0) { 157 throw new ApiFailureException("no devices"); 158 } 159 mCamera = mCameraManager.openCamera(devices[0]); 160 mCameraProperties = mCamera.getProperties(); 161 } catch (CameraAccessException e) { 162 throw new ApiFailureException("open failure", e); 163 } 164 } 165 166 mStatus = STATUS_OK; 167 } 168 169 /** 170 * Set up SurfaceView dimensions for camera preview 171 */ 172 public void minimalPreviewConfig(SurfaceHolder previewHolder) throws ApiFailureException { 173 174 minimalOpenCamera(); 175 try { 176 CameraProperties properties = mCamera.getProperties(); 177 178 Size[] previewSizes = null; 179 if (properties != null) { 180 previewSizes = properties.get( 181 CameraProperties.SCALER_AVAILABLE_PROCESSED_SIZES); 182 } 183 184 if (previewSizes == null || previewSizes.length == 0) { 185 Log.w(TAG, "Unable to get preview sizes, use default one: 640x480"); 186 previewHolder.setFixedSize(640, 480); 187 } else { 188 previewHolder.setFixedSize(previewSizes[0].getWidth(), previewSizes[0].getHeight()); 189 } 190 } catch (CameraAccessException e) { 191 throw new ApiFailureException("Error setting up minimal preview", e); 192 } 193 } 194 195 196 /** 197 * Update current preview with manual control inputs. 198 */ 199 public void updatePreview(CameraControls manualCtrl) { 200 updateCaptureRequest(mPreviewRequest, manualCtrl); 201 202 try { 203 // TODO: add capture result listener 204 mCamera.setRepeatingRequest(mPreviewRequest, null); 205 } catch (CameraAccessException e) { 206 Log.e(TAG, "Update camera preview failed"); 207 } 208 } 209 210 /** 211 * Configure streams and run minimal preview 212 */ 213 public void minimalPreview(SurfaceHolder previewHolder) throws ApiFailureException { 214 215 minimalOpenCamera(); 216 try { 217 mCamera.stopRepeating(); 218 mCamera.waitUntilIdle(); 219 220 Surface previewSurface = previewHolder.getSurface(); 221 222 List<Surface> outputSurfaces = new ArrayList(1); 223 outputSurfaces.add(previewSurface); 224 225 mCamera.configureOutputs(outputSurfaces); 226 227 mPreviewRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 228 229 mPreviewRequest.addTarget(previewSurface); 230 231 mCamera.setRepeatingRequest(mPreviewRequest, null); 232 } catch (CameraAccessException e) { 233 throw new ApiFailureException("Error setting up minimal preview", e); 234 } 235 } 236 237 public void minimalJpegCapture(final CaptureListener listener, CaptureResultListener l, 238 Handler h, CameraControls cameraControl) throws ApiFailureException { 239 minimalOpenCamera(); 240 241 try { 242 mCamera.stopRepeating(); 243 mCamera.waitUntilIdle(); 244 245 CameraProperties properties = mCamera.getProperties(); 246 Size[] jpegSizes = null; 247 if (properties != null) { 248 jpegSizes = properties.get( 249 CameraProperties.SCALER_AVAILABLE_JPEG_SIZES); 250 } 251 int width = 640; 252 int height = 480; 253 254 if (jpegSizes != null && jpegSizes.length > 0) { 255 width = jpegSizes[0].getWidth(); 256 height = jpegSizes[0].getHeight(); 257 } 258 259 if (mCaptureReader == null || mCaptureReader.getWidth() != width || 260 mCaptureReader.getHeight() != height) { 261 if (mCaptureReader != null) { 262 mCaptureReader.close(); 263 } 264 mCaptureReader = new ImageReader(width, height, 265 ImageFormat.JPEG, MAX_CONCURRENT_JPEGS); 266 } 267 268 List<Surface> outputSurfaces = new ArrayList(1); 269 outputSurfaces.add(mCaptureReader.getSurface()); 270 271 mCamera.configureOutputs(outputSurfaces); 272 273 CaptureRequest captureRequest = 274 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 275 276 captureRequest.addTarget(mCaptureReader.getSurface()); 277 278 updateCaptureRequest(captureRequest, cameraControl); 279 280 ImageReader.OnImageAvailableListener readerListener = 281 new ImageReader.OnImageAvailableListener() { 282 @Override 283 public void onImageAvailable(ImageReader reader) { 284 Image i = reader.getNextImage(); 285 listener.onCaptureAvailable(i); 286 i.close(); 287 } 288 }; 289 mCaptureReader.setImageAvailableListener(readerListener, h); 290 291 mCamera.capture(captureRequest, l); 292 293 } catch (CameraAccessException e) { 294 throw new ApiFailureException("Error in minimal JPEG capture", e); 295 } 296 } 297 298 private void updateCaptureRequest(CaptureRequest request, CameraControls cameraControl) { 299 if (cameraControl != null) { 300 // Update the manual control metadata for capture request 301 // Disable 3A routines. 302 if (cameraControl.isManualControlEnabled()) { 303 Log.e(TAG, "update request: " + cameraControl.getSensitivity()); 304 request.set(CaptureRequestKeys.Control.MODE, 305 CaptureRequestKeys.Control.ModeKey.OFF); 306 request.set(CaptureRequestKeys.Sensor.SENSITIVITY, 307 cameraControl.getSensitivity()); 308 request.set(CaptureRequestKeys.Sensor.FRAME_DURATION, 309 cameraControl.getFrameDuration()); 310 request.set(CaptureRequestKeys.Sensor.EXPOSURE_TIME, 311 cameraControl.getExposure()); 312 } else { 313 request.set(CaptureRequestKeys.Control.MODE, 314 CaptureRequestKeys.Control.ModeKey.AUTO); 315 } 316 } 317 } 318 319 public interface CaptureListener { 320 void onCaptureAvailable(Image capture); 321 } 322 323 public interface CaptureResultListener extends CameraDevice.CaptureListener {} 324} 325