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