GLThreadManager.java revision 4aed87a9ed31a1ea7fd1ac8fd9ae538d9ecbef7e
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 default: 117 Log.e(TAG, "Unhandled message " + msg.what + " on GLThread."); 118 break; 119 } 120 } catch (Exception e) { 121 Log.e(TAG, "Received exception on GL render thread: ", e); 122 mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); 123 } 124 return true; 125 } 126 }; 127 128 /** 129 * Create a new GL thread and renderer. 130 * 131 * @param cameraId the camera id for this thread. 132 * @param facing direction the camera is facing. 133 * @param state {@link CameraDeviceState} to use for error handling. 134 */ 135 public GLThreadManager(int cameraId, int facing, CameraDeviceState state) { 136 mTextureRenderer = new SurfaceTextureRenderer(facing); 137 TAG = String.format("CameraDeviceGLThread-%d", cameraId); 138 mGLHandlerThread = new RequestHandlerThread(TAG, mGLHandlerCb); 139 mDeviceState = state; 140 } 141 142 /** 143 * Start the thread. 144 * 145 * <p> 146 * This must be called before queueing new frames. 147 * </p> 148 */ 149 public void start() { 150 mGLHandlerThread.start(); 151 } 152 153 /** 154 * Wait until the thread has started. 155 */ 156 public void waitUntilStarted() { 157 mGLHandlerThread.waitUntilStarted(); 158 } 159 160 /** 161 * Quit the thread. 162 * 163 * <p> 164 * No further methods can be called after this. 165 * </p> 166 */ 167 public void quit() { 168 Handler handler = mGLHandlerThread.getHandler(); 169 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP)); 170 mGLHandlerThread.quitSafely(); 171 try { 172 mGLHandlerThread.join(); 173 } catch (InterruptedException e) { 174 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 175 mGLHandlerThread.getName(), mGLHandlerThread.getId())); 176 } 177 } 178 179 /** 180 * Queue a new call to draw into the surfaces specified in the next available preview 181 * request from the {@link CaptureCollector} passed to 182 * {@link #setConfigurationAndWait(java.util.Collection, CaptureCollector)}; 183 */ 184 public void queueNewFrame() { 185 Handler handler = mGLHandlerThread.getHandler(); 186 187 /** 188 * Avoid queuing more than one new frame. If we are not consuming faster than frames 189 * are produced, drop frames rather than allowing the queue to back up. 190 */ 191 if (!handler.hasMessages(MSG_NEW_FRAME)) { 192 handler.sendMessage(handler.obtainMessage(MSG_NEW_FRAME)); 193 } else { 194 Log.e(TAG, "GLThread dropping frame. Not consuming frames quickly enough!"); 195 } 196 } 197 198 /** 199 * Configure the GL renderer for the given set of output surfaces, and block until 200 * this configuration has been applied. 201 * 202 * @param surfaces a collection of {@link android.view.Surface}s to configure. 203 * @param collector a {@link CaptureCollector} to retrieve requests from. 204 */ 205 public void setConfigurationAndWait(Collection<Surface> surfaces, CaptureCollector collector) { 206 checkNotNull(collector, "collector must not be null"); 207 Handler handler = mGLHandlerThread.getHandler(); 208 209 final ConditionVariable condition = new ConditionVariable(/*closed*/false); 210 ConfigureHolder configure = new ConfigureHolder(condition, surfaces, collector); 211 212 Message m = handler.obtainMessage(MSG_NEW_CONFIGURATION, /*arg1*/0, /*arg2*/0, configure); 213 handler.sendMessage(m); 214 215 // Block until configuration applied. 216 condition.block(); 217 } 218 219 /** 220 * Get the underlying surface to produce frames from. 221 * 222 * <p> 223 * This returns the surface that is drawn into the set of surfaces passed in for each frame. 224 * This method should only be called after a call to 225 * {@link #setConfigurationAndWait(java.util.Collection)}. Calling this before the first call 226 * to {@link #setConfigurationAndWait(java.util.Collection)}, after {@link #quit()}, or 227 * concurrently to one of these calls may result in an invalid 228 * {@link android.graphics.SurfaceTexture} being returned. 229 * </p> 230 * 231 * @return an {@link android.graphics.SurfaceTexture} to draw to. 232 */ 233 public SurfaceTexture getCurrentSurfaceTexture() { 234 return mTextureRenderer.getSurfaceTexture(); 235 } 236 237 /** 238 * Ignore any subsequent calls to {@link #queueNewFrame(java.util.Collection)}. 239 */ 240 public void ignoreNewFrames() { 241 mGLHandlerThread.getHandler().sendEmptyMessage(MSG_DROP_FRAMES); 242 } 243 244 /** 245 * Wait until no messages are queued. 246 */ 247 public void waitUntilIdle() { 248 mGLHandlerThread.waitUntilIdle(); 249 } 250 251 /** 252 * Re-enable drawing new frames after a call to {@link #ignoreNewFrames()}. 253 */ 254 public void allowNewFrames() { 255 mGLHandlerThread.getHandler().sendEmptyMessage(MSG_ALLOW_FRAMES); 256 } 257} 258