PageRenderTime 27ms CodeModel.GetById 2ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 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}