Home | History | Annotate | Download | only in legacy
      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 
     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      */
    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      */
    154     public void start() {
    155         mGLHandlerThread.start();
    156     }
    157 
    158     /**
    159      * Wait until the thread has started.
    160      */
    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      */
    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      */
    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      */
    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      */
    240     public SurfaceTexture getCurrentSurfaceTexture() {
    241         return mTextureRenderer.getSurfaceTexture();
    242     }
    243 
    244     /**
    245      * Ignore any subsequent calls to {@link #queueNewFrame(java.util.Collection)}.
    246      */
    247     public void ignoreNewFrames() {
    248         mGLHandlerThread.getHandler().sendEmptyMessage(MSG_DROP_FRAMES);
    249     }
    250 
    251     /**
    252      * Wait until no messages are queued.
    253      */
    254     public void waitUntilIdle() {
    255         mGLHandlerThread.waitUntilIdle();
    256     }
    257 
    258     /**
    259      * Re-enable drawing new frames after a call to {@link #ignoreNewFrames()}.
    260      */
    261     public void allowNewFrames() {
    262         mGLHandlerThread.getHandler().sendEmptyMessage(MSG_ALLOW_FRAMES);
    263     }
    264 }
    265