Skip to content

Most visited

Recently visited

navigation
HdrViewfinder / src / com.example.android.hdrviewfinder /

CameraOps.java

1
/*
2
 * Copyright (C) 2014 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
 
17
package com.example.android.hdrviewfinder;
18
 
19
import android.hardware.camera2.CameraAccessException;
20
import android.hardware.camera2.CameraCaptureSession;
21
import android.hardware.camera2.CameraDevice;
22
import android.hardware.camera2.CameraManager;
23
import android.hardware.camera2.CaptureRequest;
24
import android.os.ConditionVariable;
25
import android.os.Handler;
26
import android.os.HandlerThread;
27
import android.util.Log;
28
import android.view.Surface;
29
 
30
import java.util.List;
31
 
32
/**
33
 * Simple interface for operating the camera, with major camera operations
34
 * all performed on a background handler thread.
35
 */
36
public class CameraOps {
37
 
38
    private static final String TAG = "CameraOps";
39
 
40
    public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms
41
 
42
    private final CameraManager mCameraManager;
43
    private CameraDevice mCameraDevice;
44
    private CameraCaptureSession mCameraSession;
45
    private List<Surface> mSurfaces;
46
 
47
    private final ConditionVariable mCloseWaiter = new ConditionVariable();
48
 
49
    private HandlerThread mCameraThread;
50
    private Handler mCameraHandler;
51
 
52
    private final ErrorDisplayer mErrorDisplayer;
53
 
54
    private final CameraReadyListener mReadyListener;
55
    private final Handler mReadyHandler;
56
 
57
    /**
58
     * Create a new camera ops thread.
59
     *
60
     * @param errorDisplayer listener for displaying error messages
61
     * @param readyListener  listener for notifying when camera is ready for requests
62
     * @param readyHandler   the handler for calling readyListener methods on
63
     */
64
    CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer,
65
              CameraReadyListener readyListener, Handler readyHandler) {
66
        mCameraThread = new HandlerThread("CameraOpsThread");
67
        mCameraThread.start();
68
 
69
        if (manager == null || errorDisplayer == null ||
70
                readyListener == null || readyHandler == null) {
71
            throw new IllegalArgumentException("Need valid displayer, listener, handler");
72
        }
73
 
74
        mCameraManager = manager;
75
        mErrorDisplayer = errorDisplayer;
76
        mReadyListener = readyListener;
77
        mReadyHandler = readyHandler;
78
    }
79
 
80
    /**
81
     * Open the first back-facing camera listed by the camera manager.
82
     * Displays a dialog if it cannot open a camera.
83
     */
84
    public void openCamera(final String cameraId) {
85
        mCameraHandler = new Handler(mCameraThread.getLooper());
86
 
87
        mCameraHandler.post(new Runnable() {
88
            public void run() {
89
                if (mCameraDevice != null) {
90
                    throw new IllegalStateException("Camera already open");
91
                }
92
                try {
93
                    mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler);
94
                } catch (CameraAccessException e) {
95
                    String errorMessage = mErrorDisplayer.getErrorString(e);
96
                    mErrorDisplayer.showErrorDialog(errorMessage);
97
                }
98
            }
99
        });
100
    }
101
 
102
    /**
103
     * Close the camera and wait for the close callback to be called in the camera thread.
104
     * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms.
105
     */
106
    public void closeCameraAndWait() {
107
        mCloseWaiter.close();
108
        mCameraHandler.post(mCloseCameraRunnable);
109
        boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT);
110
        if (!closed) {
111
            Log.e(TAG, "Timeout closing camera");
112
        }
113
    }
114
 
115
    private Runnable mCloseCameraRunnable = new Runnable() {
116
        public void run() {
117
            if (mCameraDevice != null) {
118
                mCameraDevice.close();
119
            }
120
            mCameraDevice = null;
121
            mCameraSession = null;
122
            mSurfaces = null;
123
        }
124
    };
125
 
126
    /**
127
     * Set the output Surfaces, and finish configuration if otherwise ready.
128
     */
129
    public void setSurfaces(final List<Surface> surfaces) {
130
        mCameraHandler.post(new Runnable() {
131
            public void run() {
132
                mSurfaces = surfaces;
133
                startCameraSession();
134
            }
135
        });
136
    }
137
 
138
    /**
139
     * Get a request builder for the current camera.
140
     */
141
    public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException {
142
        CameraDevice device = mCameraDevice;
143
        if (device == null) {
144
            throw new IllegalStateException("Can't get requests when no camera is open");
145
        }
146
        return device.createCaptureRequest(template);
147
    }
148
 
149
    /**
150
     * Set a repeating request.
151
     */
152
    public void setRepeatingRequest(final CaptureRequest request,
153
                                    final CameraCaptureSession.CaptureCallback listener,
154
                                    final Handler handler) {
155
        mCameraHandler.post(new Runnable() {
156
            public void run() {
157
                try {
158
                    mCameraSession.setRepeatingRequest(request, listener, handler);
159
                } catch (CameraAccessException e) {
160
                    String errorMessage = mErrorDisplayer.getErrorString(e);
161
                    mErrorDisplayer.showErrorDialog(errorMessage);
162
                }
163
            }
164
        });
165
    }
166
 
167
    /**
168
     * Set a repeating request.
169
     */
170
    public void setRepeatingBurst(final List<CaptureRequest> requests,
171
                                  final CameraCaptureSession.CaptureCallback listener,
172
                                  final Handler handler) {
173
        mCameraHandler.post(new Runnable() {
174
            public void run() {
175
                try {
176
                    mCameraSession.setRepeatingBurst(requests, listener, handler);
177
                } catch (CameraAccessException e) {
178
                    String errorMessage = mErrorDisplayer.getErrorString(e);
179
                    mErrorDisplayer.showErrorDialog(errorMessage);
180
                }
181
            }
182
        });
183
    }
184
 
185
    /**
186
     * Configure the camera session.
187
     */
188
    private void startCameraSession() {
189
        // Wait until both the camera device is open and the SurfaceView is ready
190
        if (mCameraDevice == null || mSurfaces == null) return;
191
 
192
        try {
193
            mCameraDevice.createCaptureSession(
194
                    mSurfaces, mCameraSessionListener, mCameraHandler);
195
        } catch (CameraAccessException e) {
196
            String errorMessage = mErrorDisplayer.getErrorString(e);
197
            mErrorDisplayer.showErrorDialog(errorMessage);
198
            mCameraDevice.close();
199
            mCameraDevice = null;
200
        }
201
    }
202
 
203
    /**
204
     * Main listener for camera session events
205
     * Invoked on mCameraThread
206
     */
207
    private CameraCaptureSession.StateCallback mCameraSessionListener =
208
            new CameraCaptureSession.StateCallback() {
209
 
210
                @Override
211
                public void onConfigured(CameraCaptureSession session) {
212
                    mCameraSession = session;
213
                    mReadyHandler.post(new Runnable() {
214
                        public void run() {
215
                            // This can happen when the screen is turned off and turned back on.
216
                            if (null == mCameraDevice) {
217
                                return;
218
                            }
219
 
220
                            mReadyListener.onCameraReady();
221
                        }
222
                    });
223
 
224
                }
225
 
226
                @Override
227
                public void onConfigureFailed(CameraCaptureSession session) {
228
                    mErrorDisplayer.showErrorDialog("Unable to configure the capture session");
229
                    mCameraDevice.close();
230
                    mCameraDevice = null;
231
                }
232
            };
233
 
234
    /**
235
     * Main listener for camera device events.
236
     * Invoked on mCameraThread
237
     */
238
    private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() {
239
 
240
        @Override
241
        public void onOpened(CameraDevice camera) {
242
            mCameraDevice = camera;
243
            startCameraSession();
244
        }
245
 
246
        @Override
247
        public void onClosed(CameraDevice camera) {
248
            mCloseWaiter.open();
249
        }
250
 
251
        @Override
252
        public void onDisconnected(CameraDevice camera) {
253
            mErrorDisplayer.showErrorDialog("The camera device has been disconnected.");
254
            camera.close();
255
            mCameraDevice = null;
256
        }
257
 
258
        @Override
259
        public void onError(CameraDevice camera, int error) {
260
            mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error);
261
            camera.close();
262
            mCameraDevice = null;
263
        }
264
 
265
    };
266
 
267
    /**
268
     * Simple listener for main code to know the camera is ready for requests, or failed to
269
     * start.
270
     */
271
    public interface CameraReadyListener {
272
        public void onCameraReady();
273
    }
274
 
275
    /**
276
     * Simple listener for displaying error messages
277
     */
278
    public interface ErrorDisplayer {
279
        public void showErrorDialog(String errorMessage);
280
 
281
        public String getErrorString(CameraAccessException e);
282
    }
283
 
284
}
This site uses cookies to store your preferences for site-specific language and display options.