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