/ocr/ocrservice/src/com/googlecode/eyesfree/ocr/intent/CameraManager.java
Java | 489 lines | 313 code | 103 blank | 73 comment | 56 complexity | 07036468606417337232a1e31cc25e4c MD5 | raw file
1/* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.googlecode.eyesfree.ocr.intent; 18 19import android.content.Context; 20import android.graphics.ImageFormat; 21import android.graphics.Point; 22import android.hardware.Camera; 23import android.hardware.Camera.Size; 24import android.os.SystemClock; 25import android.util.Log; 26import android.view.Display; 27import android.view.OrientationEventListener; 28import android.view.SurfaceHolder; 29import android.view.WindowManager; 30 31import com.googlecode.eyesfree.opticflow.FrameProducer; 32 33import java.io.IOException; 34import java.lang.ref.WeakReference; 35import java.util.LinkedList; 36import java.util.List; 37 38/** 39 * This object wraps the Camera service object and expects to be the only one 40 * talking to it. The implementation encapsulates the steps needed to take 41 * preview-sized images, which are used for both preview and decoding. Based on 42 * code from ZXing licensed under Apache License, Version 2.0. 43 * 44 * @author alanv@google.com (Alan Viverette) 45 */ 46public class CameraManager implements FrameProducer { 47 private static final String TAG = "CameraManager"; 48 49 private static final int PREVIEW_FORMAT = ImageFormat.NV21; 50 51 private static final int PICTURE_FORMAT = ImageFormat.JPEG; 52 53 private static enum CameraState { 54 NOT_READY, IDLE, FOCUSING, FOCUSED 55 } 56 57 private Camera mCamera; 58 59 private final WeakReference<Context> mContext; 60 61 private final OrientationEventListener mOrientationListener; 62 63 private Point mScreenResolution; 64 65 private boolean mInitialized; 66 67 private boolean mPreviewing; 68 69 private CameraState mState; 70 71 private boolean mSupportsFocus; 72 73 private int mPictureWidth; 74 75 private int mPictureHeight; 76 77 private int mOrientation; 78 79 private String mFlashMode; 80 81 private boolean mFlashlight; 82 83 private int mLastOrientation; 84 85 private Camera.AutoFocusCallback mAutoFocusCallback; 86 87 private Camera.PictureCallback mTakePictureCallback; 88 89 private LinkedList<FrameReceiver> mFrameRequesters; 90 91 private LinkedList<byte[]> mPreviewBuffers; 92 93 private Size mPreviewSize; 94 95 private int mPreviewFormat; 96 97 private int mPreviewBufferSize; 98 99 public CameraManager(Context context) { 100 mContext = new WeakReference<Context>(context); 101 mCamera = null; 102 mInitialized = false; 103 mPreviewing = false; 104 mLastOrientation = 0; 105 mFlashMode = Camera.Parameters.FLASH_MODE_AUTO; 106 mFlashlight = false; 107 mState = CameraState.NOT_READY; 108 109 mFrameRequesters = new LinkedList<FrameReceiver>(); 110 mPreviewBuffers = new LinkedList<byte[]>(); 111 112 // Create orientation listenter. This should be done first because it 113 // takes some time to get first orientation. 114 mOrientationListener = new OrientationEventListener(context) { 115 @Override 116 public void onOrientationChanged(int orientation) { 117 // We keep the last known orientation. So if the user 118 // first orient the camera then point the camera to 119 if (orientation != ORIENTATION_UNKNOWN) { 120 orientation += 90; 121 } 122 123 orientation = ((orientation / 90) * 90) % 360; 124 125 if (orientation != mLastOrientation) { 126 mLastOrientation = orientation; 127 } 128 } 129 }; 130 mOrientationListener.enable(); 131 } 132 133 public void openDriver(SurfaceHolder holder) throws IOException { 134 if (mCamera != null) { 135 return; 136 } 137 138 mCamera = Camera.open(); 139 mCamera.setDisplayOrientation(mOrientation); 140 mCamera.setPreviewDisplay(holder); 141 142 if (!mInitialized) { 143 mInitialized = true; 144 getScreenResolution(); 145 } 146 147 setPreviewParameters(); 148 149 Camera.Parameters params = mCamera.getParameters(); 150 151 String focusMode = params.getFocusMode(); 152 153 mSupportsFocus = !Camera.Parameters.FOCUS_MODE_FIXED.equals(focusMode); 154 155 mState = CameraState.IDLE; 156 } 157 158 public void closeDriver() { 159 if (mCamera != null) { 160 mCamera.release(); 161 mCamera = null; 162 mPreviewing = false; 163 mState = CameraState.NOT_READY; 164 } 165 166 // TODO(alanv): Need to give all requesters null frames 167 mFrameRequesters.clear(); 168 mAutoFocusCallback = null; 169 mTakePictureCallback = null; 170 } 171 172 public void startPreview() { 173 if (mCamera != null && !mPreviewing) { 174 mCamera.startPreview(); 175 mPreviewing = true; 176 } 177 } 178 179 public void stopPreview() { 180 if (mCamera != null && mPreviewing) { 181 mCamera.setPreviewCallback(null); 182 mCamera.stopPreview(); 183 mPreviewing = false; 184 } 185 } 186 187 public boolean isFocusSupported() { 188 return mSupportsFocus; 189 } 190 191 @Override 192 public int getFrameWidth() { 193 return mPreviewSize.width; 194 } 195 196 @Override 197 public int getFrameHeight() { 198 return mPreviewSize.height; 199 } 200 201 /** 202 * Requests a single preview frame from the camera. If the provided callback 203 * is <code>null</code> then pending frame requests will be canceled. 204 * 205 * @param callback 206 */ 207 @Override 208 public void requestFrame(FrameReceiver callback) { 209 if (mCamera == null) { 210 return; 211 } 212 213 if (callback == null) { 214 synchronized (mPreviewBuffers) { 215 mPreviewBuffers.clear(); 216 // mCamera.setPreviewCallback(null); 217 } 218 219 return; 220 } 221 222 synchronized (this) { 223 mFrameRequesters.addLast(callback); 224 } 225 226 byte[] buffer = null; 227 228 synchronized (mPreviewBuffers) { 229 if (mPreviewBuffers.isEmpty()) { 230 buffer = new byte[mPreviewBufferSize]; 231 } else { 232 buffer = mPreviewBuffers.remove(); 233 } 234 } 235 236 mCamera.addCallbackBuffer(buffer); 237 } 238 239 /** 240 * Returns a preview buffer to the stack of available buffers. 241 * 242 * @param buffer 243 */ 244 public void releaseData(byte[] buffer) { 245 synchronized (mPreviewBuffers) { 246 mPreviewBuffers.add(buffer); 247 } 248 } 249 250 public void requestFocus(Camera.AutoFocusCallback callback) { 251 if (mCamera == null) { 252 return; 253 } 254 255 if (callback == null) { 256 Log.e(TAG, "Focus callback was null, cancelling autofocus..."); 257 mCamera.cancelAutoFocus(); 258 } 259 260 synchronized (this) { 261 if (mState == CameraState.FOCUSED || mState == CameraState.FOCUSING) { 262 return; 263 } 264 265 mState = CameraState.FOCUSING; 266 } 267 268 mAutoFocusCallback = callback; 269 mCamera.autoFocus(autoFocusCallback); 270 } 271 272 public void requestPicture(Camera.PictureCallback callback) { 273 if (mCamera == null || !mPreviewing) { 274 return; 275 } 276 277 Camera.Parameters parameters = mCamera.getParameters(); 278 parameters.setPictureFormat(PICTURE_FORMAT); 279 parameters.setRotation(mLastOrientation); 280 281 List<Size> supported = parameters.getSupportedPictureSizes(); 282 283 if (mPictureWidth > 0 && mPictureHeight > 0) { 284 int height = -1; 285 int width = -1; 286 287 for (Size size : supported) { 288 if (size.height >= mPictureHeight && (size.height < height || height < 0)) { 289 width = size.width; 290 height = size.height; 291 } 292 } 293 294 parameters.setPictureSize(width, height); 295 } else { 296 Size size = supported.get(supported.size() - 1); 297 298 int width = size.width; 299 int height = size.height; 300 301 parameters.setPictureSize(width, height); 302 } 303 304 // Flashlight setting overrides flash mode setting 305 if (mFlashlight) { 306 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 307 } else if (mFlashMode != null) { 308 parameters.setFlashMode(mFlashMode); 309 } 310 311 mTakePictureCallback = callback; 312 313 // Must clear preview callback to take pictures 314 mCamera.setPreviewCallback(null); 315 mCamera.setParameters(parameters); 316 mCamera.takePicture(shutterCallback, rawCallback, pictureCallback); 317 } 318 319 /** 320 * Preview frames are delivered here, which we pass on to the registered 321 * handler. Make sure to clear the handler so it will only receive one 322 * message. 323 */ 324 private final Camera.PreviewCallback frameCallback = new Camera.PreviewCallback() { 325 @Override 326 public void onPreviewFrame(final byte[] data, final Camera camera) { 327 // TODO(alanv): Can't call camera.getParameters() because it causes 328 // a crash! 329 // final Camera.Parameters params = camera.getParameters(); 330 final int format = mPreviewFormat; 331 final Size size = mPreviewSize; 332 333 FrameReceiver callback; 334 335 synchronized (this) { 336 if (mFrameRequesters.isEmpty()) { 337 Log.e(TAG, "Preview callback was null, discarding results"); 338 return; 339 } else { 340 callback = mFrameRequesters.removeFirst(); 341 } 342 } 343 344 long timestamp = SystemClock.uptimeMillis(); 345 346 Frame frame = new Frame(data, size.width, size.height, format, timestamp) { 347 @Override 348 public void recycle() { 349 synchronized (mPreviewBuffers) { 350 mPreviewBuffers.add(data); 351 } 352 } 353 }; 354 355 callback.onFrameReceived(frame); 356 } 357 }; 358 359 private final Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() { 360 @Override 361 public void onShutter() { 362 // Do nothing. 363 } 364 }; 365 366 private final Camera.PictureCallback rawCallback = new Camera.PictureCallback() { 367 @Override 368 public void onPictureTaken(byte[] data, Camera camera) { 369 // Do nothing. 370 } 371 }; 372 373 private final Camera.PictureCallback pictureCallback = new Camera.PictureCallback() { 374 @Override 375 public void onPictureTaken(final byte[] data, final Camera camera) { 376 // Return jpeg data to callback. 377 mTakePictureCallback.onPictureTaken(data, camera); 378 } 379 }; 380 381 private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() { 382 @Override 383 public void onAutoFocus(final boolean success, final Camera camera) { 384 synchronized (this) { 385 mState = CameraState.FOCUSED; 386 387 // Camera.Parameters parameters = mCamera.getParameters(); 388 // parameters.setPreviewFormat(PREVIEW_FORMAT); 389 // parameters.setRotation(mLastOrientation); 390 // mCamera.setParameters(parameters); 391 392 mAutoFocusCallback.onAutoFocus(success, camera); 393 mAutoFocusCallback = null; 394 395 mState = CameraState.IDLE; 396 } 397 } 398 }; 399 400 /** 401 * Sets the camera up to take preview images which are used for both preview 402 * and decoding. We're counting on the default YUV420 semi-planar data. If 403 * that changes in the future, we'll need to specify it explicitly 404 * witrivateh setPreviewFormat(). 405 */ 406 private void setPreviewParameters() { 407 Camera.Parameters parameters = mCamera.getParameters(); 408 parameters.setPreviewFormat(PREVIEW_FORMAT); 409 410 List<Size> supported = parameters.getSupportedPreviewSizes(); 411 Size selectedSize = mCamera.new Size(0, 0); 412 413 for (Size size : supported) { 414 if (size.width * size.height > selectedSize.width * selectedSize.height 415 && size.height <= 480) { 416 selectedSize = size; 417 } 418 } 419 420 if (mFlashlight) { 421 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 422 } else { 423 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); 424 } 425 426 parameters.setPreviewSize(selectedSize.width, selectedSize.height); 427 428 int bitsPerPixel = ImageFormat.getBitsPerPixel(PREVIEW_FORMAT); 429 mPreviewBufferSize = selectedSize.width * selectedSize.height * bitsPerPixel / 8; 430 mPreviewSize = selectedSize; 431 mPreviewFormat = PREVIEW_FORMAT; 432 433 mCamera.setPreviewCallbackWithBuffer(frameCallback); 434 mCamera.setParameters(parameters); 435 } 436 437 public Size getPreviewSize() { 438 Camera.Parameters parameters = mCamera.getParameters(); 439 Size previewSize = parameters.getPreviewSize(); 440 441 return previewSize; 442 } 443 444 public void setPictureSize(int width, int height) { 445 mPictureWidth = width; 446 mPictureHeight = height; 447 } 448 449 /** 450 * Sets the camera flash mode. Uses Camera.Parameters.FLASH_ constants. 451 * 452 * @param mode 453 */ 454 public void setFlashMode(String mode) { 455 mFlashMode = mode; 456 } 457 458 /** 459 * Turns the camera light on or off. Takes effect immediately. 460 * 461 * @param light 462 */ 463 public void setFlashlight(boolean light) { 464 mFlashlight = light; 465 466 if (mCamera != null) { 467 Camera.Parameters parameters = mCamera.getParameters(); 468 469 if (mFlashlight) { 470 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 471 } else { 472 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); 473 } 474 475 mCamera.setParameters(parameters); 476 } 477 } 478 479 private Point getScreenResolution() { 480 if (mScreenResolution == null) { 481 WindowManager manager = (WindowManager) mContext.get().getSystemService( 482 Context.WINDOW_SERVICE); 483 Display display = manager.getDefaultDisplay(); 484 mScreenResolution = new Point(display.getWidth(), display.getHeight()); 485 } 486 487 return mScreenResolution; 488 } 489}