2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.hardware.camera2.legacy;
19 import android.graphics.SurfaceTexture;
20 import android.hardware.camera2.impl.CameraDeviceImpl;
21 import android.os.ConditionVariable;
22 import android.os.Handler;
23 import android.os.Message;
24 import android.util.Log;
25 import android.util.Pair;
26 import android.util.Size;
27 import android.view.Surface;
29 import java.util.Collection;
31 import static com.android.internal.util.Preconditions.*;
34 * GLThreadManager handles the thread used for rendering into the configured output surfaces.
36 public class GLThreadManager {
37 private final String TAG;
38 private static final boolean DEBUG = false;
40 private static final int MSG_NEW_CONFIGURATION = 1;
41 private static final int MSG_NEW_FRAME = 2;
42 private static final int MSG_CLEANUP = 3;
43 private static final int MSG_DROP_FRAMES = 4;
44 private static final int MSG_ALLOW_FRAMES = 5;
46 private CaptureCollector mCaptureCollector;
48 private final CameraDeviceState mDeviceState;
50 private final SurfaceTextureRenderer mTextureRenderer;
52 private final RequestHandlerThread mGLHandlerThread;
54 private final RequestThreadManager.FpsCounter mPrevCounter =
55 new RequestThreadManager.FpsCounter("GL Preview Producer");
58 * Container object for Configure messages.
60 private static class ConfigureHolder {
61 public final ConditionVariable condition;
62 public final Collection<Pair<Surface, Size>> surfaces;
63 public final CaptureCollector collector;
65 public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
66 Size>> surfaces, CaptureCollector collector) {
67 this.condition = condition;
68 this.surfaces = surfaces;
69 this.collector = collector;
73 private final Handler.Callback mGLHandlerCb = new Handler.Callback() {
74 private boolean mCleanup = false;
75 private boolean mConfigured = false;
76 private boolean mDroppingFrames = false;
78 @SuppressWarnings("unchecked")
80 public boolean handleMessage(Message msg) {
86 case MSG_NEW_CONFIGURATION:
87 ConfigureHolder configure = (ConfigureHolder) msg.obj;
88 mTextureRenderer.cleanupEGLContext();
89 mTextureRenderer.configureSurfaces(configure.surfaces);
90 mCaptureCollector = checkNotNull(configure.collector);
91 configure.condition.open();
95 if (mDroppingFrames) {
96 Log.w(TAG, "Ignoring frame.");
100 mPrevCounter.countAndLog();
103 Log.e(TAG, "Dropping frame, EGL context not configured!");
105 mTextureRenderer.drawIntoSurfaces(mCaptureCollector);
108 mTextureRenderer.cleanupEGLContext();
112 case MSG_DROP_FRAMES:
113 mDroppingFrames = true;
115 case MSG_ALLOW_FRAMES:
116 mDroppingFrames = false;
118 case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
119 // OK: Ignore message.
122 Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
125 } catch (Exception e) {
126 Log.e(TAG, "Received exception on GL render thread: ", e);
127 mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
134 * Create a new GL thread and renderer.
136 * @param cameraId the camera id for this thread.
137 * @param facing direction the camera is facing.
138 * @param state {@link CameraDeviceState} to use for error handling.
140 public GLThreadManager(int cameraId, int facing, CameraDeviceState state) {
141 mTextureRenderer = new SurfaceTextureRenderer(facing);
142 TAG = String.format("CameraDeviceGLThread-%d", cameraId);
143 mGLHandlerThread = new RequestHandlerThread(TAG, mGLHandlerCb);
144 mDeviceState = state;
151 * This must be called before queueing new frames.
154 public void start() {
155 mGLHandlerThread.start();
159 * Wait until the thread has started.
161 public void waitUntilStarted() {
162 mGLHandlerThread.waitUntilStarted();
169 * No further methods can be called after this.
173 Handler handler = mGLHandlerThread.getHandler();
174 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
175 mGLHandlerThread.quitSafely();
177 mGLHandlerThread.join();
178 } catch (InterruptedException e) {
179 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
180 mGLHandlerThread.getName(), mGLHandlerThread.getId()));
185 * Queue a new call to draw into the surfaces specified in the next available preview
186 * request from the {@link CaptureCollector} passed to
187 * {@link #setConfigurationAndWait(java.util.Collection, CaptureCollector)};
189 public void queueNewFrame() {
190 Handler handler = mGLHandlerThread.getHandler();
193 * Avoid queuing more than one new frame. If we are not consuming faster than frames
194 * are produced, drop frames rather than allowing the queue to back up.
196 if (!handler.hasMessages(MSG_NEW_FRAME)) {
197 handler.sendMessage(handler.obtainMessage(MSG_NEW_FRAME));
199 Log.e(TAG, "GLThread dropping frame. Not consuming frames quickly enough!");
204 * Configure the GL renderer for the given set of output surfaces, and block until
205 * this configuration has been applied.
207 * @param surfaces a collection of pairs of {@link android.view.Surface}s and their
208 * corresponding sizes to configure.
209 * @param collector a {@link CaptureCollector} to retrieve requests from.
211 public void setConfigurationAndWait(Collection<Pair<Surface, Size>> surfaces,
212 CaptureCollector collector) {
213 checkNotNull(collector, "collector must not be null");
214 Handler handler = mGLHandlerThread.getHandler();
216 final ConditionVariable condition = new ConditionVariable(/*closed*/false);
217 ConfigureHolder configure = new ConfigureHolder(condition, surfaces, collector);
219 Message m = handler.obtainMessage(MSG_NEW_CONFIGURATION, /*arg1*/0, /*arg2*/0, configure);
220 handler.sendMessage(m);
222 // Block until configuration applied.
227 * Get the underlying surface to produce frames from.
230 * This returns the surface that is drawn into the set of surfaces passed in for each frame.
231 * This method should only be called after a call to
232 * {@link #setConfigurationAndWait(java.util.Collection)}. Calling this before the first call
233 * to {@link #setConfigurationAndWait(java.util.Collection)}, after {@link #quit()}, or
234 * concurrently to one of these calls may result in an invalid
235 * {@link android.graphics.SurfaceTexture} being returned.
238 * @return an {@link android.graphics.SurfaceTexture} to draw to.
240 public SurfaceTexture getCurrentSurfaceTexture() {
241 return mTextureRenderer.getSurfaceTexture();
245 * Ignore any subsequent calls to {@link #queueNewFrame(java.util.Collection)}.
247 public void ignoreNewFrames() {
248 mGLHandlerThread.getHandler().sendEmptyMessage(MSG_DROP_FRAMES);
252 * Wait until no messages are queued.
254 public void waitUntilIdle() {
255 mGLHandlerThread.waitUntilIdle();
259 * Re-enable drawing new frames after a call to {@link #ignoreNewFrames()}.
261 public void allowNewFrames() {
262 mGLHandlerThread.getHandler().sendEmptyMessage(MSG_ALLOW_FRAMES);