/ocr/ocrservice/src/com/googlecode/eyesfree/ocr/intent/CameraManager.java

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