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