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