AndroidCameraAgentImpl.java revision 4f425ba476d62b4be7078f2084af37cf306b31c6
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.ex.camera2.portability;
18
19import android.annotation.TargetApi;
20import android.graphics.SurfaceTexture;
21import android.hardware.Camera;
22import android.hardware.Camera.AutoFocusCallback;
23import android.hardware.Camera.AutoFocusMoveCallback;
24import android.hardware.Camera.ErrorCallback;
25import android.hardware.Camera.FaceDetectionListener;
26import android.hardware.Camera.OnZoomChangeListener;
27import android.hardware.Camera.Parameters;
28import android.hardware.Camera.PictureCallback;
29import android.hardware.Camera.PreviewCallback;
30import android.hardware.Camera.ShutterCallback;
31import android.os.Build;
32import android.os.Handler;
33import android.os.HandlerThread;
34import android.os.Looper;
35import android.os.Message;
36import android.view.SurfaceHolder;
37
38import com.android.ex.camera2.portability.debug.Log;
39
40import java.io.IOException;
41
42/**
43 * A class to implement {@link CameraAgent} of the Android camera framework.
44 */
45class AndroidCameraAgentImpl implements CameraAgent {
46    private static final Log.Tag TAG = new Log.Tag("AndroidCamMgrImpl");
47
48    private Parameters mParameters;
49    private boolean mParametersIsDirty;
50    private AndroidCameraCapabilities mCapabilities;
51
52    private final CameraHandler mCameraHandler;
53    private final HandlerThread mCameraHandlerThread;
54    private final CameraStateHolder mCameraState;
55    private final DispatchThread mDispatchThread;
56
57    private Handler mCameraExceptionCallbackHandler;
58    private CameraExceptionCallback mCameraExceptionCallback =
59        new CameraExceptionCallback() {
60            @Override
61            public void onCameraException(RuntimeException e) {
62                throw e;
63            }
64        };
65
66    AndroidCameraAgentImpl() {
67        mCameraHandlerThread = new HandlerThread("Camera Handler Thread");
68        mCameraHandlerThread.start();
69        mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper());
70        mCameraExceptionCallbackHandler = mCameraHandler;
71        mCameraState = new CameraStateHolder();
72        mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
73        mDispatchThread.start();
74    }
75
76    @Override
77    public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
78            Handler handler) {
79        synchronized (mCameraExceptionCallback) {
80            mCameraExceptionCallback = callback;
81            mCameraExceptionCallbackHandler = handler;
82        }
83    }
84
85    @Override
86    public void recycle() {
87        closeCamera(null, true);
88        mDispatchThread.end();
89    }
90
91    @Override
92    public CameraDeviceInfo getCameraDeviceInfo() {
93        return AndroidCameraDeviceInfo.create();
94    }
95
96    private static class AndroidCameraDeviceInfo implements CameraDeviceInfo {
97        private final Camera.CameraInfo[] mCameraInfos;
98        private final int mNumberOfCameras;
99        private final int mFirstBackCameraId;
100        private final int mFirstFrontCameraId;
101
102        private AndroidCameraDeviceInfo(Camera.CameraInfo[] info, int numberOfCameras,
103                int firstBackCameraId, int firstFrontCameraId) {
104
105            mCameraInfos = info;
106            mNumberOfCameras = numberOfCameras;
107            mFirstBackCameraId = firstBackCameraId;
108            mFirstFrontCameraId = firstFrontCameraId;
109        }
110
111        public static AndroidCameraDeviceInfo create() {
112            int numberOfCameras;
113            Camera.CameraInfo[] cameraInfos;
114            try {
115                numberOfCameras = Camera.getNumberOfCameras();
116                cameraInfos = new Camera.CameraInfo[numberOfCameras];
117                for (int i = 0; i < numberOfCameras; i++) {
118                    cameraInfos[i] = new Camera.CameraInfo();
119                    Camera.getCameraInfo(i, cameraInfos[i]);
120                }
121            } catch (RuntimeException ex) {
122                return null;
123            }
124
125            int firstFront = NO_DEVICE;
126            int firstBack = NO_DEVICE;
127            // Get the first (smallest) back and first front camera id.
128            for (int i = numberOfCameras - 1; i >= 0; i--) {
129                if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
130                    firstBack = i;
131                } else {
132                    if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
133                        firstFront = i;
134                    }
135                }
136            }
137
138            return new AndroidCameraDeviceInfo(cameraInfos, numberOfCameras, firstBack, firstFront);
139        }
140
141        @Override
142        public Camera.CameraInfo[] getCameraInfos() {
143            return mCameraInfos;
144        }
145
146        @Override
147        public int getNumberOfCameras() {
148            return mNumberOfCameras;
149        }
150
151        @Override
152        public int getFirstBackCameraId() {
153            return mFirstBackCameraId;
154        }
155
156        @Override
157        public int getFirstFrontCameraId() {
158            return mFirstFrontCameraId;
159        }
160    }
161
162    /**
163     * The handler on which the actual camera operations happen.
164     */
165    private class CameraHandler extends HistoryHandler {
166
167        // Used to retain a copy of Parameters for setting parameters.
168        private Parameters mParamsToSet;
169        private Camera mCamera;
170        private int mCameraId;
171
172        private class CaptureCallbacks {
173            public final ShutterCallback mShutter;
174            public final PictureCallback mRaw;
175            public final PictureCallback mPostView;
176            public final PictureCallback mJpeg;
177
178            CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView,
179                    PictureCallback jpeg) {
180                mShutter = shutter;
181                mRaw = raw;
182                mPostView = postView;
183                mJpeg = jpeg;
184            }
185        }
186
187        CameraHandler(Looper looper) {
188            super(looper);
189        }
190
191        private void startFaceDetection() {
192            mCamera.startFaceDetection();
193        }
194
195        private void stopFaceDetection() {
196            mCamera.stopFaceDetection();
197        }
198
199        private void setFaceDetectionListener(FaceDetectionListener listener) {
200            mCamera.setFaceDetectionListener(listener);
201        }
202
203        private void setPreviewTexture(Object surfaceTexture) {
204            try {
205                mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
206            } catch (IOException e) {
207                Log.e(TAG, "Could not set preview texture", e);
208            }
209        }
210
211        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
212        private void enableShutterSound(boolean enable) {
213            mCamera.enableShutterSound(enable);
214        }
215
216        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
217        private void setAutoFocusMoveCallback(
218                android.hardware.Camera camera, Object cb) {
219            try {
220                camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
221            } catch (RuntimeException ex) {
222                Log.w(TAG, ex.getMessage());
223            }
224        }
225
226        private void capture(final CaptureCallbacks cb) {
227            try {
228                mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg);
229            } catch (RuntimeException e) {
230                // TODO: output camera state and focus state for debugging.
231                Log.e(TAG, "take picture failed.");
232                throw e;
233            }
234        }
235
236        public void requestTakePicture(
237                final ShutterCallback shutter,
238                final PictureCallback raw,
239                final PictureCallback postView,
240                final PictureCallback jpeg) {
241            final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg);
242            obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget();
243        }
244
245        /**
246         * This method does not deal with the API level check.  Everyone should
247         * check first for supported operations before sending message to this handler.
248         */
249        @Override
250        public void handleMessage(final Message msg) {
251            super.handleMessage(msg);
252            try {
253                switch (msg.what) {
254                    case CameraActions.OPEN_CAMERA: {
255                        final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
256                        final int cameraId = msg.arg1;
257                        if (mCameraState.getState() != CameraStateHolder.CAMERA_UNOPENED) {
258                            openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId));
259                            break;
260                        }
261
262                        mCamera = android.hardware.Camera.open(cameraId);
263                        if (mCamera != null) {
264                            mCameraId = cameraId;
265                            mParametersIsDirty = true;
266
267                            // Get a instance of Camera.Parameters for later use.
268                            mParamsToSet = mCamera.getParameters();
269                            mCapabilities = new AndroidCameraCapabilities(mParamsToSet);
270
271                            mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
272                            if (openCallback != null) {
273                                openCallback.onCameraOpened(
274                                        new AndroidCameraProxyImpl(cameraId, mCamera,
275                                                mCapabilities));
276                            }
277                        } else {
278                            if (openCallback != null) {
279                                openCallback.onDeviceOpenFailure(cameraId, generateHistoryString(cameraId));
280                            }
281                        }
282                        break;
283                    }
284
285                    case CameraActions.RELEASE: {
286                        if (mCamera != null) {
287                            mCamera.release();
288                            mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED);
289                            mCamera = null;
290                        } else {
291                            Log.w(TAG, "Releasing camera without any camera opened.");
292                        }
293                        break;
294                    }
295
296                    case CameraActions.RECONNECT: {
297                        final CameraOpenCallbackForward cbForward =
298                                (CameraOpenCallbackForward) msg.obj;
299                        final int cameraId = msg.arg1;
300                        try {
301                            mCamera.reconnect();
302                        } catch (IOException ex) {
303                            if (cbForward != null) {
304                                cbForward.onReconnectionFailure(AndroidCameraAgentImpl.this,
305                                        generateHistoryString(mCameraId));
306                            }
307                            break;
308                        }
309
310                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
311                        if (cbForward != null) {
312                            cbForward.onCameraOpened(
313                                    new AndroidCameraProxyImpl(cameraId, mCamera, mCapabilities));
314                        }
315                        break;
316                    }
317
318                    case CameraActions.UNLOCK: {
319                        mCamera.unlock();
320                        mCameraState.setState(CameraStateHolder.CAMERA_UNLOCKED);
321                        break;
322                    }
323
324                    case CameraActions.LOCK: {
325                        mCamera.lock();
326                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
327                        break;
328                    }
329
330                    case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: {
331                        setPreviewTexture(msg.obj);
332                        break;
333                    }
334
335                    case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: {
336                        try {
337                            mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
338                        } catch (IOException e) {
339                            throw new RuntimeException(e);
340                        }
341                        break;
342                    }
343
344                    case CameraActions.START_PREVIEW_ASYNC: {
345                        final CameraStartPreviewCallbackForward cbForward =
346                            (CameraStartPreviewCallbackForward) msg.obj;
347                        mCamera.startPreview();
348                        if (cbForward != null) {
349                            cbForward.onPreviewStarted();
350                        }
351                        break;
352                    }
353
354                    case CameraActions.STOP_PREVIEW: {
355                        mCamera.stopPreview();
356                        break;
357                    }
358
359                    case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: {
360                        mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj);
361                        break;
362                    }
363
364                    case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: {
365                        mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj);
366                        break;
367                    }
368
369                    case CameraActions.ADD_CALLBACK_BUFFER: {
370                        mCamera.addCallbackBuffer((byte[]) msg.obj);
371                        break;
372                    }
373
374                    case CameraActions.AUTO_FOCUS: {
375                        mCameraState.setState(CameraStateHolder.CAMERA_FOCUSING);
376                        mCamera.autoFocus((AutoFocusCallback) msg.obj);
377                        break;
378                    }
379
380                    case CameraActions.CANCEL_AUTO_FOCUS: {
381                        mCamera.cancelAutoFocus();
382                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
383                        break;
384                    }
385
386                    case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: {
387                        setAutoFocusMoveCallback(mCamera, msg.obj);
388                        break;
389                    }
390
391                    case CameraActions.SET_DISPLAY_ORIENTATION: {
392                        mCamera.setDisplayOrientation(msg.arg1);
393                        break;
394                    }
395
396                    case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
397                        mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj);
398                        break;
399                    }
400
401                    case CameraActions.SET_FACE_DETECTION_LISTENER: {
402                        setFaceDetectionListener((FaceDetectionListener) msg.obj);
403                        break;
404                    }
405
406                    case CameraActions.START_FACE_DETECTION: {
407                        startFaceDetection();
408                        break;
409                    }
410
411                    case CameraActions.STOP_FACE_DETECTION: {
412                        stopFaceDetection();
413                        break;
414                    }
415
416                    case CameraActions.SET_ERROR_CALLBACK: {
417                        mCamera.setErrorCallback((ErrorCallback) msg.obj);
418                        break;
419                    }
420
421                    case CameraActions.APPLY_SETTINGS: {
422                        mParametersIsDirty = true;
423                        CameraSettings settings = (CameraSettings) msg.obj;
424                        applyToParameters(settings);
425                        mCamera.setParameters(mParamsToSet);
426                        break;
427                    }
428
429                    case CameraActions.SET_PARAMETERS: {
430                        mParametersIsDirty = true;
431                        mParamsToSet.unflatten((String) msg.obj);
432                        mCamera.setParameters(mParamsToSet);
433                        break;
434                    }
435
436                    case CameraActions.GET_PARAMETERS: {
437                        if (mParametersIsDirty) {
438                            mParameters = mCamera.getParameters();
439                            mParametersIsDirty = false;
440                        }
441                        break;
442                    }
443
444                    case CameraActions.SET_PREVIEW_CALLBACK: {
445                        mCamera.setPreviewCallback((PreviewCallback) msg.obj);
446                        break;
447                    }
448
449                    case CameraActions.ENABLE_SHUTTER_SOUND: {
450                        enableShutterSound((msg.arg1 == 1) ? true : false);
451                        break;
452                    }
453
454                    case CameraActions.REFRESH_PARAMETERS: {
455                        mParametersIsDirty = true;
456                        break;
457                    }
458
459                    case CameraActions.CAPTURE_PHOTO: {
460                        mCameraState.setState(CameraStateHolder.CAMERA_CAPTURING);
461                        capture((CaptureCallbacks) msg.obj);
462                        break;
463                    }
464
465                    default: {
466                        throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
467                    }
468                }
469            } catch (final RuntimeException e) {
470                if (msg.what != CameraActions.RELEASE && mCamera != null) {
471                    try {
472                        mCamera.release();
473                        mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED);
474                    } catch (Exception ex) {
475                        Log.e(TAG, "Fail to release the camera.");
476                    }
477                    mCamera = null;
478                } else {
479                    if (mCamera == null) {
480                        if (msg.what == CameraActions.OPEN_CAMERA) {
481                            final int cameraId = msg.arg1;
482                            if (msg.obj != null) {
483                                ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
484                                        msg.arg1, generateHistoryString(cameraId));
485                            }
486                        } else {
487                            Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null.");
488                        }
489                        return;
490                    }
491                }
492                synchronized (mCameraExceptionCallback) {
493                    mCameraExceptionCallbackHandler.post(new Runnable() {
494                        @Override
495                        public void run() {
496                                mCameraExceptionCallback.onCameraException(e);
497                            }
498                        });
499                }
500            }
501        }
502
503        private void applyToParameters(final CameraSettings settings) {
504            final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier();
505            Size photoSize = settings.getCurrentPhotoSize();
506            mParamsToSet.setPictureSize(photoSize.width(), photoSize.height());
507            Size previewSize = settings.getCurrentPreviewSize();
508            mParamsToSet.setPreviewSize(previewSize.width(), previewSize.height());
509            if (settings.getPreviewFrameRate() == -1) {
510                mParamsToSet.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
511                        settings.getPreviewFpsRangeMax());
512            } else {
513                mParamsToSet.setPreviewFrameRate(settings.getPreviewFrameRate());
514            }
515            mParamsToSet.setJpegQuality(settings.getPhotoJpegCompressionQuality());
516            if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
517                // Should use settings.getCurrentZoomRatio() instead here.
518                mParamsToSet.setZoom(settings.getCurrentZoomIndex());
519            }
520            mParamsToSet.setRotation((int) settings.getCurrentPhotoRotationDegrees());
521            mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex());
522            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
523                mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked());
524            }
525            mParamsToSet.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
526            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) {
527                mParamsToSet.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
528            }
529            if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) {
530                if (settings.getFocusAreas().size() != 0) {
531                    mParamsToSet.setFocusAreas(settings.getFocusAreas());
532                }
533            }
534            if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) {
535                if (settings.getMeteringAreas().size() != 0) {
536                    mParamsToSet.setMeteringAreas(settings.getMeteringAreas());
537                }
538            }
539            if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
540                mParamsToSet.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
541            }
542            if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
543                if (settings.getCurrentSceneMode() != null) {
544                    mParamsToSet
545                            .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode()));
546                }
547            }
548
549            CameraSettings.GpsData gpsData = settings.getGpsData();
550            if (gpsData == null) {
551                mParamsToSet.removeGpsData();
552            } else {
553                mParamsToSet.setGpsTimestamp(gpsData.timeStamp);
554                if (gpsData.processingMethod != null) {
555                    // It's a hack since we always use GPS time stamp but does
556                    // not use other fields sometimes. Setting processing
557                    // method to null means the other fields should not be used.
558                    mParamsToSet.setGpsAltitude(gpsData.altitude);
559                    mParamsToSet.setGpsLatitude(gpsData.latitude);
560                    mParamsToSet.setGpsLongitude(gpsData.longitude);
561                    mParamsToSet.setGpsProcessingMethod(gpsData.processingMethod);
562                }
563            }
564
565        }
566    }
567
568    @Override
569    public void openCamera(final Handler handler, final int cameraId,
570                           final CameraOpenCallback callback) {
571        mDispatchThread.runJob(new Runnable() {
572            @Override
573            public void run() {
574                mCameraHandler.obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0,
575                        CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget();
576            }
577        });
578    }
579
580    @Override
581    public void closeCamera(CameraProxy camera, boolean synced) {
582        if (synced) {
583            final WaitDoneBundle bundle = new WaitDoneBundle();
584
585            mDispatchThread.runJobSync(new Runnable() {
586                @Override
587                public void run() {
588                    mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget();
589                    mCameraHandler.post(bundle.mUnlockRunnable);
590                }
591            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release");
592        } else {
593            mDispatchThread.runJob(new Runnable() {
594                @Override
595                public void run() {
596                    mCameraHandler.removeCallbacksAndMessages(null);
597                    mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget();
598                }
599            });
600        }
601    }
602
603    /**
604     * A class which implements {@link CameraAgent.CameraProxy} and
605     * camera handler thread.
606     */
607    private class AndroidCameraProxyImpl implements CameraAgent.CameraProxy {
608        private final int mCameraId;
609        /* TODO: remove this Camera instance. */
610        private final Camera mCamera;
611        private final AndroidCameraCapabilities mCapabilities;
612
613        private AndroidCameraProxyImpl(int cameraId, Camera camera,
614                AndroidCameraCapabilities capabilities) {
615            mCamera = camera;
616            mCameraId = cameraId;
617            mCapabilities = capabilities;
618        }
619
620        @Override
621        public android.hardware.Camera getCamera() {
622            return mCamera;
623        }
624
625        @Override
626        public int getCameraId() {
627            return mCameraId;
628        }
629
630        @Override
631        public CameraCapabilities getCapabilities() {
632            return new AndroidCameraCapabilities(mCapabilities);
633        }
634
635        @Override
636        public void reconnect(final Handler handler, final CameraOpenCallback cb) {
637            mDispatchThread.runJob(new Runnable() {
638                @Override
639                public void run() {
640                    mCameraHandler.obtainMessage(CameraActions.RECONNECT, mCameraId, 0,
641                            CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget();
642                }
643            });
644        }
645
646        @Override
647        public void unlock() {
648            final WaitDoneBundle bundle = new WaitDoneBundle();
649            mDispatchThread.runJobSync(new Runnable() {
650                @Override
651                public void run() {
652                    mCameraHandler.sendEmptyMessage(CameraActions.UNLOCK);
653                    mCameraHandler.post(bundle.mUnlockRunnable);
654                }
655            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock");
656        }
657
658        @Override
659        public void lock() {
660            mDispatchThread.runJob(new Runnable() {
661                @Override
662                public void run() {
663                    mCameraHandler.sendEmptyMessage(CameraActions.LOCK);
664                }
665            });
666        }
667
668        @Override
669        public void setPreviewTexture(final SurfaceTexture surfaceTexture) {
670            mDispatchThread.runJob(new Runnable() {
671                @Override
672                public void run() {
673                    mCameraHandler
674                            .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
675                            .sendToTarget();
676                }
677            });
678        }
679
680        @Override
681        public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) {
682            final WaitDoneBundle bundle = new WaitDoneBundle();
683            mDispatchThread.runJobSync(new Runnable() {
684                @Override
685                public void run() {
686                    mCameraHandler
687                            .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
688                            .sendToTarget();
689                    mCameraHandler.post(bundle.mUnlockRunnable);
690                }
691            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture");
692        }
693
694        @Override
695        public void setPreviewDisplay(final SurfaceHolder surfaceHolder) {
696            mDispatchThread.runJob(new Runnable() {
697                @Override
698                public void run() {
699                    mCameraHandler
700                            .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder)
701                            .sendToTarget();
702                }
703            });
704        }
705
706        @Override
707        public void startPreview() {
708            mDispatchThread.runJob(new Runnable() {
709                @Override
710                public void run() {
711                    mCameraHandler
712                            .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget();
713                }
714            });
715        }
716
717        @Override
718        public void startPreviewWithCallback(final Handler handler,
719                final CameraStartPreviewCallback cb) {
720            mDispatchThread.runJob(new Runnable() {
721                @Override
722                public void run() {
723                    mCameraHandler.obtainMessage(CameraActions.START_PREVIEW_ASYNC,
724                        CameraStartPreviewCallbackForward.getNewInstance(handler, cb)).sendToTarget();
725                }
726            });
727        }
728
729        @Override
730        public void stopPreview() {
731            final WaitDoneBundle bundle = new WaitDoneBundle();
732            mDispatchThread.runJobSync(new Runnable() {
733                @Override
734                public void run() {
735                    mCameraHandler.sendEmptyMessage(CameraActions.STOP_PREVIEW);
736                    mCameraHandler.post(bundle.mUnlockRunnable);
737                }
738            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview");
739        }
740
741        @Override
742        public void setPreviewDataCallback(
743                final Handler handler, final CameraPreviewDataCallback cb) {
744            mDispatchThread.runJob(new Runnable() {
745                @Override
746                public void run() {
747                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK,
748                            PreviewCallbackForward.getNewInstance(
749                                    handler, AndroidCameraProxyImpl.this, cb))
750                            .sendToTarget();
751                }
752            });
753        }
754        @Override
755        public void setOneShotPreviewCallback(final Handler handler,
756                final CameraPreviewDataCallback cb) {
757            mDispatchThread.runJob(new Runnable() {
758                @Override
759                public void run() {
760                    mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK,
761                            PreviewCallbackForward
762                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
763                            .sendToTarget();
764                }
765            });
766        }
767
768        @Override
769        public void setPreviewDataCallbackWithBuffer(
770                final Handler handler, final CameraPreviewDataCallback cb) {
771            mDispatchThread.runJob(new Runnable() {
772                @Override
773                public void run() {
774                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER,
775                            PreviewCallbackForward
776                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
777                            .sendToTarget();
778                }
779            });
780        }
781
782        @Override
783        public void addCallbackBuffer(final byte[] callbackBuffer) {
784            mDispatchThread.runJob(new Runnable() {
785                @Override
786                public void run() {
787                    mCameraHandler.obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer)
788                            .sendToTarget();
789                }
790            });
791        }
792
793        @Override
794        public void autoFocus(final Handler handler, final CameraAFCallback cb) {
795            final AutoFocusCallback afCallback = new AutoFocusCallback() {
796                @Override
797                public void onAutoFocus(final boolean b, Camera camera) {
798                    if (mCameraState.getState() != CameraStateHolder.CAMERA_FOCUSING) {
799                        Log.w(TAG, "onAutoFocus callback returning when not focusing");
800                    } else {
801                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
802                    }
803                    handler.post(new Runnable() {
804                        @Override
805                        public void run() {
806                            cb.onAutoFocus(b, AndroidCameraProxyImpl.this);
807                        }
808                    });
809                }
810            };
811            mDispatchThread.runJob(new Runnable() {
812                @Override
813                public void run() {
814                    mCameraState.waitForStates(CameraStateHolder.CAMERA_IDLE);
815                    mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback)
816                            .sendToTarget();
817                }
818            });
819        }
820
821        @Override
822        public void cancelAutoFocus() {
823            mDispatchThread.runJob(new Runnable() {
824                @Override
825                public void run() {
826                    mCameraHandler.removeMessages(CameraActions.AUTO_FOCUS);
827                    mCameraHandler.sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS);
828                }
829            });
830        }
831
832        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
833        @Override
834        public void setAutoFocusMoveCallback(
835                final Handler handler, final CameraAFMoveCallback cb) {
836            mDispatchThread.runJob(new Runnable() {
837                @Override
838                public void run() {
839                    mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
840                            AFMoveCallbackForward.getNewInstance(
841                                    handler, AndroidCameraProxyImpl.this, cb))
842                            .sendToTarget();
843                }
844            });
845        }
846
847        @Override
848        public void takePicture(
849                final Handler handler, final CameraShutterCallback shutter,
850                final CameraPictureCallback raw, final CameraPictureCallback post,
851                final CameraPictureCallback jpeg) {
852            final PictureCallback jpegCallback = new PictureCallback() {
853                @Override
854                public void onPictureTaken(final byte[] data, Camera camera) {
855                    if (mCameraState.getState() != CameraStateHolder.CAMERA_CAPTURING) {
856                        Log.w(TAG, "picture callback returning when not capturing");
857                    } else {
858                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
859                    }
860                    handler.post(new Runnable() {
861                        @Override
862                        public void run() {
863                            jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this);
864                        }
865                    });
866                }
867            };
868
869            mDispatchThread.runJob(new Runnable() {
870                @Override
871                public void run() {
872                    mCameraState.waitForStates(
873                            CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED);
874                    mCameraHandler.requestTakePicture(ShutterCallbackForward
875                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
876                            PictureCallbackForward
877                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
878                            PictureCallbackForward
879                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
880                            jpegCallback
881                    );
882                }
883            });
884        }
885
886        @Override
887        public void setDisplayOrientation(final int degrees) {
888            mDispatchThread.runJob(new Runnable() {
889                @Override
890                public void run() {
891                    mCameraHandler.obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 0)
892                            .sendToTarget();
893                }
894            });
895        }
896
897        @Override
898        public void setZoomChangeListener(final OnZoomChangeListener listener) {
899            mDispatchThread.runJob(new Runnable() {
900                @Override
901                public void run() {
902                    mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
903                            .sendToTarget();
904                }
905            });
906        }
907
908        @Override
909        public void setFaceDetectionCallback(final Handler handler,
910                final CameraFaceDetectionCallback cb) {
911            mDispatchThread.runJob(new Runnable() {
912                @Override
913                public void run() {
914                    mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
915                            FaceDetectionCallbackForward
916                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
917                            .sendToTarget();
918                }
919            });
920        }
921
922        @Override
923        public void startFaceDetection() {
924            mDispatchThread.runJob(new Runnable() {
925                @Override
926                public void run() {
927                    mCameraHandler.sendEmptyMessage(CameraActions.START_FACE_DETECTION);
928                }
929            });
930        }
931
932        @Override
933        public void stopFaceDetection() {
934            mDispatchThread.runJob(new Runnable() {
935                @Override
936                public void run() {
937                    mCameraHandler.sendEmptyMessage(CameraActions.STOP_FACE_DETECTION);
938                }
939            });
940        }
941
942        @Override
943        public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) {
944            mDispatchThread.runJob(new Runnable() {
945                @Override
946                public void run() {
947                    mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK,
948                            ErrorCallbackForward.getNewInstance(
949                                    handler, AndroidCameraProxyImpl.this, cb))
950                            .sendToTarget();
951                }
952            });
953        }
954
955        @Override
956        public void setParameters(final Parameters params) {
957            if (params == null) {
958                Log.v(TAG, "null parameters in setParameters()");
959                return;
960            }
961            final String flattenedParameters = params.flatten();
962            mDispatchThread.runJob(new Runnable() {
963                @Override
964                public void run() {
965                    mCameraState.waitForStates(
966                            CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED);
967                    mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
968                            .sendToTarget();
969                }
970            });
971        }
972
973        @Override
974        public Parameters getParameters() {
975            final WaitDoneBundle bundle = new WaitDoneBundle();
976            mDispatchThread.runJobSync(new Runnable() {
977                @Override
978                public void run() {
979                    mCameraHandler.sendEmptyMessage(CameraActions.GET_PARAMETERS);
980                    mCameraHandler.post(bundle.mUnlockRunnable);
981                }
982            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
983            return mParameters;
984        }
985
986        @Override
987        public CameraSettings getSettings() {
988            return new AndroidCameraSettings(mCapabilities, getParameters());
989        }
990
991        @Override
992        public boolean applySettings(final CameraSettings settings) {
993            if (settings == null) {
994                Log.v(TAG, "null parameters in applySettings()");
995                return false;
996            }
997            if (!mCapabilities.supports(settings)) {
998                return false;
999            }
1000
1001            final CameraSettings copyOfSettings = new CameraSettings(settings);
1002            mDispatchThread.runJob(new Runnable() {
1003                @Override
1004                public void run() {
1005                    mCameraState.waitForStates(
1006                            CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED);
1007                    mCameraHandler.obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings)
1008                            .sendToTarget();
1009                }
1010            });
1011            return true;
1012        }
1013
1014        @Override
1015        public void refreshSettings() {
1016            mDispatchThread.runJob(new Runnable() {
1017                @Override
1018                public void run() {
1019                    mCameraHandler.sendEmptyMessage(CameraActions.REFRESH_PARAMETERS);
1020                }
1021            });
1022        }
1023
1024        @Override
1025        public void enableShutterSound(final boolean enable) {
1026            mDispatchThread.runJob(new Runnable() {
1027                @Override
1028                public void run() {
1029                    mCameraHandler
1030                            .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0)
1031                            .sendToTarget();
1032                }
1033            });
1034        }
1035    }
1036
1037    private static class WaitDoneBundle {
1038        public Runnable mUnlockRunnable;
1039        private final Object mWaitLock;
1040
1041        WaitDoneBundle() {
1042            mWaitLock = new Object();
1043            mUnlockRunnable = new Runnable() {
1044                @Override
1045                public void run() {
1046                    synchronized (mWaitLock) {
1047                        mWaitLock.notifyAll();
1048                    }
1049                }
1050            };
1051        }
1052    }
1053
1054    /**
1055     * A helper class to forward AutoFocusCallback to another thread.
1056     */
1057    private static class AFCallbackForward implements AutoFocusCallback {
1058        private final Handler mHandler;
1059        private final CameraProxy mCamera;
1060        private final CameraAFCallback mCallback;
1061
1062        /**
1063         * Returns a new instance of {@link AFCallbackForward}.
1064         *
1065         * @param handler The handler in which the callback will be invoked in.
1066         * @param camera  The {@link CameraProxy} which the callback is from.
1067         * @param cb      The callback to be invoked.
1068         * @return        The instance of the {@link AFCallbackForward},
1069         *                or null if any parameter is null.
1070         */
1071        public static AFCallbackForward getNewInstance(
1072                Handler handler, CameraProxy camera, CameraAFCallback cb) {
1073            if (handler == null || camera == null || cb == null) {
1074                return null;
1075            }
1076            return new AFCallbackForward(handler, camera, cb);
1077        }
1078
1079        private AFCallbackForward(
1080                Handler h, CameraProxy camera, CameraAFCallback cb) {
1081            mHandler = h;
1082            mCamera = camera;
1083            mCallback = cb;
1084        }
1085
1086        @Override
1087        public void onAutoFocus(final boolean b, Camera camera) {
1088            mHandler.post(new Runnable() {
1089                @Override
1090                public void run() {
1091                    mCallback.onAutoFocus(b, mCamera);
1092                }
1093            });
1094        }
1095    }
1096
1097    /**
1098     * A helper class to forward ErrorCallback to another thread.
1099     */
1100    private static class ErrorCallbackForward implements Camera.ErrorCallback {
1101        private final Handler mHandler;
1102        private final CameraProxy mCamera;
1103        private final CameraErrorCallback mCallback;
1104
1105        /**
1106         * Returns a new instance of {@link AFCallbackForward}.
1107         *
1108         * @param handler The handler in which the callback will be invoked in.
1109         * @param camera  The {@link CameraProxy} which the callback is from.
1110         * @param cb      The callback to be invoked.
1111         * @return        The instance of the {@link AFCallbackForward},
1112         *                or null if any parameter is null.
1113         */
1114        public static ErrorCallbackForward getNewInstance(
1115                Handler handler, CameraProxy camera, CameraErrorCallback cb) {
1116            if (handler == null || camera == null || cb == null) {
1117                return null;
1118            }
1119            return new ErrorCallbackForward(handler, camera, cb);
1120        }
1121
1122        private ErrorCallbackForward(
1123                Handler h, CameraProxy camera, CameraErrorCallback cb) {
1124            mHandler = h;
1125            mCamera = camera;
1126            mCallback = cb;
1127        }
1128
1129        @Override
1130        public void onError(final int error, Camera camera) {
1131            mHandler.post(new Runnable() {
1132                @Override
1133                public void run() {
1134                    mCallback.onError(error, mCamera);
1135                }
1136            });
1137        }
1138    }
1139
1140    /** A helper class to forward AutoFocusMoveCallback to another thread. */
1141    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1142    private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
1143        private final Handler mHandler;
1144        private final CameraAFMoveCallback mCallback;
1145        private final CameraProxy mCamera;
1146
1147        /**
1148         * Returns a new instance of {@link AFMoveCallbackForward}.
1149         *
1150         * @param handler The handler in which the callback will be invoked in.
1151         * @param camera  The {@link CameraProxy} which the callback is from.
1152         * @param cb      The callback to be invoked.
1153         * @return        The instance of the {@link AFMoveCallbackForward},
1154         *                or null if any parameter is null.
1155         */
1156        public static AFMoveCallbackForward getNewInstance(
1157                Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
1158            if (handler == null || camera == null || cb == null) {
1159                return null;
1160            }
1161            return new AFMoveCallbackForward(handler, camera, cb);
1162        }
1163
1164        private AFMoveCallbackForward(
1165                Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
1166            mHandler = h;
1167            mCamera = camera;
1168            mCallback = cb;
1169        }
1170
1171        @Override
1172        public void onAutoFocusMoving(
1173                final boolean moving, android.hardware.Camera camera) {
1174            mHandler.post(new Runnable() {
1175                @Override
1176                public void run() {
1177                    mCallback.onAutoFocusMoving(moving, mCamera);
1178                }
1179            });
1180        }
1181    }
1182
1183    /**
1184     * A helper class to forward ShutterCallback to to another thread.
1185     */
1186    private static class ShutterCallbackForward implements ShutterCallback {
1187        private final Handler mHandler;
1188        private final CameraShutterCallback mCallback;
1189        private final CameraProxy mCamera;
1190
1191        /**
1192         * Returns a new instance of {@link ShutterCallbackForward}.
1193         *
1194         * @param handler The handler in which the callback will be invoked in.
1195         * @param camera  The {@link CameraProxy} which the callback is from.
1196         * @param cb      The callback to be invoked.
1197         * @return        The instance of the {@link ShutterCallbackForward},
1198         *                or null if any parameter is null.
1199         */
1200        public static ShutterCallbackForward getNewInstance(
1201                Handler handler, CameraProxy camera, CameraShutterCallback cb) {
1202            if (handler == null || camera == null || cb == null) {
1203                return null;
1204            }
1205            return new ShutterCallbackForward(handler, camera, cb);
1206        }
1207
1208        private ShutterCallbackForward(
1209                Handler h, CameraProxy camera, CameraShutterCallback cb) {
1210            mHandler = h;
1211            mCamera = camera;
1212            mCallback = cb;
1213        }
1214
1215        @Override
1216        public void onShutter() {
1217            mHandler.post(new Runnable() {
1218                @Override
1219                public void run() {
1220                    mCallback.onShutter(mCamera);
1221                }
1222            });
1223        }
1224    }
1225
1226    /**
1227     * A helper class to forward PictureCallback to another thread.
1228     */
1229    private static class PictureCallbackForward implements PictureCallback {
1230        private final Handler mHandler;
1231        private final CameraPictureCallback mCallback;
1232        private final CameraProxy mCamera;
1233
1234        /**
1235         * Returns a new instance of {@link PictureCallbackForward}.
1236         *
1237         * @param handler The handler in which the callback will be invoked in.
1238         * @param camera  The {@link CameraProxy} which the callback is from.
1239         * @param cb      The callback to be invoked.
1240         * @return        The instance of the {@link PictureCallbackForward},
1241         *                or null if any parameters is null.
1242         */
1243        public static PictureCallbackForward getNewInstance(
1244                Handler handler, CameraProxy camera, CameraPictureCallback cb) {
1245            if (handler == null || camera == null || cb == null) {
1246                return null;
1247            }
1248            return new PictureCallbackForward(handler, camera, cb);
1249        }
1250
1251        private PictureCallbackForward(
1252                Handler h, CameraProxy camera, CameraPictureCallback cb) {
1253            mHandler = h;
1254            mCamera = camera;
1255            mCallback = cb;
1256        }
1257
1258        @Override
1259        public void onPictureTaken(
1260                final byte[] data, android.hardware.Camera camera) {
1261            mHandler.post(new Runnable() {
1262                @Override
1263                public void run() {
1264                    mCallback.onPictureTaken(data, mCamera);
1265                }
1266            });
1267        }
1268    }
1269
1270    /**
1271     * A helper class to forward PreviewCallback to another thread.
1272     */
1273    private static class PreviewCallbackForward implements PreviewCallback {
1274        private final Handler mHandler;
1275        private final CameraPreviewDataCallback mCallback;
1276        private final CameraProxy mCamera;
1277
1278        /**
1279         * Returns a new instance of {@link PreviewCallbackForward}.
1280         *
1281         * @param handler The handler in which the callback will be invoked in.
1282         * @param camera  The {@link CameraProxy} which the callback is from.
1283         * @param cb      The callback to be invoked.
1284         * @return        The instance of the {@link PreviewCallbackForward},
1285         *                or null if any parameters is null.
1286         */
1287        public static PreviewCallbackForward getNewInstance(
1288                Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
1289            if (handler == null || camera == null || cb == null) {
1290                return null;
1291            }
1292            return new PreviewCallbackForward(handler, camera, cb);
1293        }
1294
1295        private PreviewCallbackForward(
1296                Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
1297            mHandler = h;
1298            mCamera = camera;
1299            mCallback = cb;
1300        }
1301
1302        @Override
1303        public void onPreviewFrame(
1304                final byte[] data, android.hardware.Camera camera) {
1305            mHandler.post(new Runnable() {
1306                @Override
1307                public void run() {
1308                    mCallback.onPreviewFrame(data, mCamera);
1309                }
1310            });
1311        }
1312    }
1313
1314    private static class FaceDetectionCallbackForward implements FaceDetectionListener {
1315        private final Handler mHandler;
1316        private final CameraFaceDetectionCallback mCallback;
1317        private final CameraProxy mCamera;
1318
1319        /**
1320         * Returns a new instance of {@link FaceDetectionCallbackForward}.
1321         *
1322         * @param handler The handler in which the callback will be invoked in.
1323         * @param camera  The {@link CameraProxy} which the callback is from.
1324         * @param cb      The callback to be invoked.
1325         * @return        The instance of the {@link FaceDetectionCallbackForward},
1326         *                or null if any parameter is null.
1327         */
1328        public static FaceDetectionCallbackForward getNewInstance(
1329                Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
1330            if (handler == null || camera == null || cb == null) {
1331                return null;
1332            }
1333            return new FaceDetectionCallbackForward(handler, camera, cb);
1334        }
1335
1336        private FaceDetectionCallbackForward(
1337                Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
1338            mHandler = h;
1339            mCamera = camera;
1340            mCallback = cb;
1341        }
1342
1343        @Override
1344        public void onFaceDetection(
1345                final Camera.Face[] faces, Camera camera) {
1346            mHandler.post(new Runnable() {
1347                @Override
1348                public void run() {
1349                    mCallback.onFaceDetection(faces, mCamera);
1350                }
1351            });
1352        }
1353    }
1354}
1355