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