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