Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2011 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 com.android.camera;
     18 
     19 import android.graphics.SurfaceTexture;
     20 import android.os.Handler;
     21 import android.util.Log;
     22 
     23 import javax.microedition.khronos.egl.EGL10;
     24 import javax.microedition.khronos.egl.EGLConfig;
     25 import javax.microedition.khronos.egl.EGLContext;
     26 import javax.microedition.khronos.egl.EGLDisplay;
     27 import javax.microedition.khronos.egl.EGLSurface;
     28 import javax.microedition.khronos.opengles.GL10;
     29 
     30 public class SurfaceTextureRenderer {
     31 
     32     public interface FrameDrawer {
     33         public void onDrawFrame(GL10 gl);
     34     }
     35 
     36     private static final String TAG = "CAM_" + SurfaceTextureRenderer.class.getSimpleName();
     37     private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
     38 
     39     private EGLConfig mEglConfig;
     40     private EGLDisplay mEglDisplay;
     41     private EGLContext mEglContext;
     42     private EGLSurface mEglSurface;
     43     private EGL10 mEgl;
     44     private GL10 mGl;
     45 
     46     private Handler mEglHandler;
     47     private FrameDrawer mFrameDrawer;
     48 
     49     private Object mRenderLock = new Object();
     50     private Runnable mRenderTask = new Runnable() {
     51         @Override
     52         public void run() {
     53             synchronized (mRenderLock) {
     54                 if (mEglDisplay != null && mEglSurface != null) {
     55                     mFrameDrawer.onDrawFrame(mGl);
     56                     mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
     57                 }
     58                 mRenderLock.notifyAll();
     59             }
     60         }
     61     };
     62 
     63     public class RenderThread extends Thread {
     64         private Boolean mRenderStopped = false;
     65 
     66         @Override
     67         public void run() {
     68             while (true) {
     69                 synchronized (mRenderStopped) {
     70                     if (mRenderStopped) return;
     71                 }
     72                 draw(true);
     73             }
     74         }
     75 
     76         public void stopRender() {
     77             synchronized (mRenderStopped) {
     78                 mRenderStopped = true;
     79             }
     80         }
     81     }
     82 
     83     public SurfaceTextureRenderer(SurfaceTexture tex,
     84             Handler handler, FrameDrawer renderer) {
     85         mEglHandler = handler;
     86         mFrameDrawer = renderer;
     87 
     88         initialize(tex);
     89     }
     90 
     91     public RenderThread createRenderThread() {
     92         return new RenderThread();
     93     }
     94 
     95     public void release() {
     96         mEglHandler.post(new Runnable() {
     97             @Override
     98             public void run() {
     99                 mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
    100                 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
    101                 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
    102                         EGL10.EGL_NO_CONTEXT);
    103                 mEgl.eglTerminate(mEglDisplay);
    104                 mEglSurface = null;
    105                 mEglContext = null;
    106                 mEglDisplay = null;
    107             }
    108         });
    109     }
    110 
    111     /**
    112      * Posts a render request to the GL thread.
    113      * @param sync      set <code>true</code> if the caller needs it to be
    114      *                  a synchronous call.
    115      */
    116     public void draw(boolean sync) {
    117         synchronized (mRenderLock) {
    118             mEglHandler.post(mRenderTask);
    119             if (sync) {
    120                 try {
    121                     mRenderLock.wait();
    122                 } catch (InterruptedException ex) {
    123                     Log.v(TAG, "RenderLock.wait() interrupted");
    124                 }
    125             }
    126         }
    127     }
    128 
    129     private void initialize(final SurfaceTexture target) {
    130         mEglHandler.post(new Runnable() {
    131             @Override
    132             public void run() {
    133                 mEgl = (EGL10) EGLContext.getEGL();
    134                 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    135                 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
    136                     throw new RuntimeException("eglGetDisplay failed");
    137                 }
    138                 int[] version = new int[2];
    139                 if (!mEgl.eglInitialize(mEglDisplay, version)) {
    140                     throw new RuntimeException("eglInitialize failed");
    141                 } else {
    142                     Log.v(TAG, "EGL version: " + version[0] + '.' + version[1]);
    143                 }
    144                 int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
    145                 mEglConfig = chooseConfig(mEgl, mEglDisplay);
    146                 mEglContext = mEgl.eglCreateContext(
    147                         mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, attribList);
    148 
    149                 if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
    150                     throw new RuntimeException("failed to createContext");
    151                 }
    152                 mEglSurface = mEgl.eglCreateWindowSurface(
    153                         mEglDisplay, mEglConfig, target, null);
    154                 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
    155                     throw new RuntimeException("failed to createWindowSurface");
    156                 }
    157 
    158                 if (!mEgl.eglMakeCurrent(
    159                         mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
    160                     throw new RuntimeException("failed to eglMakeCurrent");
    161                 }
    162 
    163                 mGl = (GL10) mEglContext.getGL();
    164             }
    165         });
    166         waitDone();
    167     }
    168 
    169     private void waitDone() {
    170         final Object lock = new Object();
    171         synchronized (lock) {
    172             mEglHandler.post(new Runnable() {
    173                 @Override
    174                 public void run() {
    175                     synchronized (lock) {
    176                         lock.notifyAll();
    177                     }
    178                 }
    179             });
    180             try {
    181                 lock.wait();
    182             } catch (InterruptedException ex) {
    183                 Log.v(TAG, "waitDone() interrupted");
    184             }
    185         }
    186     }
    187 
    188     private static void checkEglError(String prompt, EGL10 egl) {
    189         int error;
    190         while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
    191             Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
    192         }
    193     }
    194 
    195     private static final int EGL_OPENGL_ES2_BIT = 4;
    196     private static final int[] CONFIG_SPEC = new int[] {
    197             EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    198             EGL10.EGL_RED_SIZE, 8,
    199             EGL10.EGL_GREEN_SIZE, 8,
    200             EGL10.EGL_BLUE_SIZE, 8,
    201             EGL10.EGL_ALPHA_SIZE, 0,
    202             EGL10.EGL_DEPTH_SIZE, 0,
    203             EGL10.EGL_STENCIL_SIZE, 0,
    204             EGL10.EGL_NONE
    205     };
    206 
    207     private static EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
    208         int[] numConfig = new int[1];
    209         if (!egl.eglChooseConfig(display, CONFIG_SPEC, null, 0, numConfig)) {
    210             throw new IllegalArgumentException("eglChooseConfig failed");
    211         }
    212 
    213         int numConfigs = numConfig[0];
    214         if (numConfigs <= 0) {
    215             throw new IllegalArgumentException("No configs match configSpec");
    216         }
    217 
    218         EGLConfig[] configs = new EGLConfig[numConfigs];
    219         if (!egl.eglChooseConfig(
    220                 display, CONFIG_SPEC, configs, numConfigs, numConfig)) {
    221             throw new IllegalArgumentException("eglChooseConfig#2 failed");
    222         }
    223 
    224         return configs[0];
    225     }
    226 }
    227