GLThreadManager.java revision 51dcfd65a6742884e07182dd7d13b916fd4e0305
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
17package android.hardware.camera2.legacy;
18
19import android.graphics.SurfaceTexture;
20import android.hardware.camera2.impl.CameraDeviceImpl;
21import android.os.ConditionVariable;
22import android.os.Handler;
23import android.os.Message;
24import android.util.Log;
25import android.view.Surface;
26
27import java.util.Collection;
28
29import static com.android.internal.util.Preconditions.*;
30
31/**
32 * GLThreadManager handles the thread used for rendering into the configured output surfaces.
33 */
34public class GLThreadManager {
35    private final String TAG;
36    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
37
38    private static final int MSG_NEW_CONFIGURATION = 1;
39    private static final int MSG_NEW_FRAME = 2;
40    private static final int MSG_CLEANUP = 3;
41    private static final int MSG_DROP_FRAMES = 4;
42    private static final int MSG_ALLOW_FRAMES = 5;
43
44    private CaptureCollector mCaptureCollector;
45
46    private final CameraDeviceState mDeviceState;
47
48    private final SurfaceTextureRenderer mTextureRenderer;
49
50    private final RequestHandlerThread mGLHandlerThread;
51
52    private final RequestThreadManager.FpsCounter mPrevCounter =
53            new RequestThreadManager.FpsCounter("GL Preview Producer");
54
55    /**
56     * Container object for Configure messages.
57     */
58    private static class ConfigureHolder {
59        public final ConditionVariable condition;
60        public final Collection<Surface> surfaces;
61        public final CaptureCollector collector;
62
63        public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces,
64                               CaptureCollector collector) {
65            this.condition = condition;
66            this.surfaces = surfaces;
67            this.collector = collector;
68        }
69    }
70
71    private final Handler.Callback mGLHandlerCb = new Handler.Callback() {
72        private boolean mCleanup = false;
73        private boolean mConfigured = false;
74        private boolean mDroppingFrames = false;
75
76        @SuppressWarnings("unchecked")
77        @Override
78        public boolean handleMessage(Message msg) {
79            if (mCleanup) {
80                return true;
81            }
82            try {
83                switch (msg.what) {
84                    case MSG_NEW_CONFIGURATION:
85                        ConfigureHolder configure = (ConfigureHolder) msg.obj;
86                        mTextureRenderer.cleanupEGLContext();
87                        mTextureRenderer.configureSurfaces(configure.surfaces);
88                        mCaptureCollector = checkNotNull(configure.collector);
89                        configure.condition.open();
90                        mConfigured = true;
91                        break;
92                    case MSG_NEW_FRAME:
93                        if (mDroppingFrames) {
94                            Log.w(TAG, "Ignoring frame.");
95                            break;
96                        }
97                        if (DEBUG) {
98                            mPrevCounter.countAndLog();
99                        }
100                        if (!mConfigured) {
101                            Log.e(TAG, "Dropping frame, EGL context not configured!");
102                        }
103                        mTextureRenderer.drawIntoSurfaces(mCaptureCollector);
104                        break;
105                    case MSG_CLEANUP:
106                        mTextureRenderer.cleanupEGLContext();
107                        mCleanup = true;
108                        mConfigured = false;
109                        break;
110                    case MSG_DROP_FRAMES:
111                        mDroppingFrames = true;
112                        break;
113                    case MSG_ALLOW_FRAMES:
114                        mDroppingFrames = false;
115                        break;
116                    case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
117                        // OK: Ignore message.
118                        break;
119                    default:
120                        Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
121                        break;
122                }
123            } catch (Exception e) {
124                Log.e(TAG, "Received exception on GL render thread: ", e);
125                mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
126            }
127            return true;
128        }
129    };
130
131    /**
132     * Create a new GL thread and renderer.
133     *
134     * @param cameraId the camera id for this thread.
135     * @param facing direction the camera is facing.
136     * @param state {@link CameraDeviceState} to use for error handling.
137     */
138    public GLThreadManager(int cameraId, int facing, CameraDeviceState state) {
139        mTextureRenderer = new SurfaceTextureRenderer(facing);
140        TAG = String.format("CameraDeviceGLThread-%d", cameraId);
141        mGLHandlerThread = new RequestHandlerThread(TAG, mGLHandlerCb);
142        mDeviceState = state;
143    }
144
145    /**
146     * Start the thread.
147     *
148     * <p>
149     * This must be called before queueing new frames.
150     * </p>
151     */
152    public void start() {
153        mGLHandlerThread.start();
154    }
155
156    /**
157     * Wait until the thread has started.
158     */
159    public void waitUntilStarted() {
160        mGLHandlerThread.waitUntilStarted();
161    }
162
163    /**
164     * Quit the thread.
165     *
166     * <p>
167     * No further methods can be called after this.
168     * </p>
169     */
170    public void quit() {
171        Handler handler = mGLHandlerThread.getHandler();
172        handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
173        mGLHandlerThread.quitSafely();
174        try {
175            mGLHandlerThread.join();
176        } catch (InterruptedException e) {
177            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
178                    mGLHandlerThread.getName(), mGLHandlerThread.getId()));
179        }
180    }
181
182    /**
183     * Queue a new call to draw into the surfaces specified in the next available preview
184     * request from the {@link CaptureCollector} passed to
185     * {@link #setConfigurationAndWait(java.util.Collection, CaptureCollector)};
186     */
187    public void queueNewFrame() {
188        Handler handler = mGLHandlerThread.getHandler();
189
190        /**
191         * Avoid queuing more than one new frame.  If we are not consuming faster than frames
192         * are produced, drop frames rather than allowing the queue to back up.
193         */
194        if (!handler.hasMessages(MSG_NEW_FRAME)) {
195            handler.sendMessage(handler.obtainMessage(MSG_NEW_FRAME));
196        } else {
197            Log.e(TAG, "GLThread dropping frame.  Not consuming frames quickly enough!");
198        }
199    }
200
201    /**
202     * Configure the GL renderer for the given set of output surfaces, and block until
203     * this configuration has been applied.
204     *
205     * @param surfaces a collection of {@link android.view.Surface}s to configure.
206     * @param collector a {@link CaptureCollector} to retrieve requests from.
207     */
208    public void setConfigurationAndWait(Collection<Surface> surfaces, CaptureCollector collector) {
209        checkNotNull(collector, "collector must not be null");
210        Handler handler = mGLHandlerThread.getHandler();
211
212        final ConditionVariable condition = new ConditionVariable(/*closed*/false);
213        ConfigureHolder configure = new ConfigureHolder(condition, surfaces, collector);
214
215        Message m = handler.obtainMessage(MSG_NEW_CONFIGURATION, /*arg1*/0, /*arg2*/0, configure);
216        handler.sendMessage(m);
217
218        // Block until configuration applied.
219        condition.block();
220    }
221
222    /**
223     * Get the underlying surface to produce frames from.
224     *
225     * <p>
226     * This returns the surface that is drawn into the set of surfaces passed in for each frame.
227     * This method should only be called after a call to
228     * {@link #setConfigurationAndWait(java.util.Collection)}.  Calling this before the first call
229     * to {@link #setConfigurationAndWait(java.util.Collection)}, after {@link #quit()}, or
230     * concurrently to one of these calls may result in an invalid
231     * {@link android.graphics.SurfaceTexture} being returned.
232     * </p>
233     *
234     * @return an {@link android.graphics.SurfaceTexture} to draw to.
235     */
236    public SurfaceTexture getCurrentSurfaceTexture() {
237        return mTextureRenderer.getSurfaceTexture();
238    }
239
240    /**
241     * Ignore any subsequent calls to {@link #queueNewFrame(java.util.Collection)}.
242     */
243    public void ignoreNewFrames() {
244        mGLHandlerThread.getHandler().sendEmptyMessage(MSG_DROP_FRAMES);
245    }
246
247    /**
248     * Wait until no messages are queued.
249     */
250    public void waitUntilIdle() {
251        mGLHandlerThread.waitUntilIdle();
252    }
253
254    /**
255     * Re-enable drawing new frames after a call to {@link #ignoreNewFrames()}.
256     */
257    public void allowNewFrames() {
258        mGLHandlerThread.getHandler().sendEmptyMessage(MSG_ALLOW_FRAMES);
259    }
260}
261