1 // Copyright 2011 Google Inc. All Rights Reserved. 2 3 package com.example.android.videochatcameratest; 4 5 import android.content.Context; 6 import android.graphics.SurfaceTexture; 7 import android.graphics.SurfaceTexture.OnFrameAvailableListener; 8 import android.opengl.GLES20; 9 import android.opengl.GLSurfaceView; 10 import android.util.AttributeSet; 11 import android.util.Log; 12 13 import java.nio.ByteBuffer; 14 import java.nio.ByteOrder; 15 import java.nio.FloatBuffer; 16 import java.util.concurrent.atomic.AtomicBoolean; 17 import java.util.concurrent.atomic.AtomicInteger; 18 19 import javax.microedition.khronos.egl.EGLConfig; 20 import javax.microedition.khronos.opengles.GL10; 21 22 class SurfaceTextureView extends GLSurfaceView { 23 static final private String TAG = "VideoChatTest"; 24 25 private int mTextureName; 26 private SurfaceTexture mSurfaceTexture; 27 public int getTextureName() { 28 return mTextureName; 29 } 30 public SurfaceTexture getSurfaceTexture() { 31 return mSurfaceTexture; 32 } 33 34 public static int loadShader(int shaderType, String source) { 35 int shader = GLES20.glCreateShader(shaderType); 36 if (shader != 0) { 37 GLES20.glShaderSource(shader, source); 38 GLES20.glCompileShader(shader); 39 int[] compiled = new int[1]; 40 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 41 if (compiled[0] == 0) { 42 Log.e(TAG, "Could not compile shader " + shaderType + ":"); 43 Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); 44 GLES20.glDeleteShader(shader); 45 shader = 0; 46 } 47 } 48 return shader; 49 } 50 51 public static void checkGlError(String op) { 52 int error; 53 while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 54 Log.e(TAG, op + ": glError " + error); 55 throw new RuntimeException(op + ": glError " + error); 56 } 57 } 58 59 public static int createProgram(String vertexSource, String fragmentSource) { 60 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 61 if (vertexShader == 0) { 62 return 0; 63 } 64 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 65 if (pixelShader == 0) { 66 return 0; 67 } 68 69 int program = GLES20.glCreateProgram(); 70 if (program != 0) { 71 GLES20.glAttachShader(program, vertexShader); 72 checkGlError("glAttachShader"); 73 GLES20.glAttachShader(program, pixelShader); 74 checkGlError("glAttachShader"); 75 GLES20.glLinkProgram(program); 76 int[] linkStatus = new int[1]; 77 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 78 if (linkStatus[0] != GLES20.GL_TRUE) { 79 Log.e(TAG, "Could not link program: "); 80 Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 81 GLES20.glDeleteProgram(program); 82 program = 0; 83 } 84 } 85 return program; 86 } 87 88 AtomicInteger mReportedFrameCount = new AtomicInteger(); 89 AtomicBoolean mCameraEnabled = new AtomicBoolean(); 90 AtomicInteger mCameraFrameCount = new AtomicInteger(); 91 92 /** 93 * @param context 94 */ 95 public SurfaceTextureView(Context context) { 96 super(context); 97 init(); 98 } 99 100 public SurfaceTextureView(Context context, AttributeSet attrs) { 101 super(context, attrs); 102 init(); 103 } 104 105 private void init() { 106 setEGLContextClientVersion(2); 107 setRenderer(new Renderer()); 108 } 109 110 public void setCameraEnabled(boolean enabled) { 111 mCameraEnabled.set(enabled); 112 } 113 114 public void resetFrameCounter() { 115 mReportedFrameCount.set(0); 116 } 117 118 public int getFrameCounter() { 119 return mReportedFrameCount.get(); 120 } 121 122 class Renderer implements GLSurfaceView.Renderer { 123 private final static String VERTEX_SHADER = 124 "attribute vec4 vPosition;\n" + 125 "attribute vec2 a_texCoord;\n" + 126 "varying vec2 v_texCoord;\n" + 127 "uniform mat4 u_xform;\n" + 128 "void main() {\n" + 129 " gl_Position = vPosition;\n" + 130 " v_texCoord = vec2(u_xform * vec4(a_texCoord, 1.0, 1.0));\n" + 131 "}\n"; 132 133 private final static String FRAGMENT_SHADER = 134 "#extension GL_OES_EGL_image_external : require\n" + 135 "precision mediump float;\n" + 136 "uniform samplerExternalOES s_texture;\n" + 137 "varying vec2 v_texCoord;\n" + 138 "void main() {\n" + 139 " gl_FragColor = texture2D(s_texture, v_texCoord);\n" + 140 "}\n"; 141 142 private final float[] TEXTURE_VERTICES = 143 { 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f }; 144 145 private final float[] QUAD_VERTICES = 146 { 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f }; 147 148 private final static int FLOAT_SIZE_BYTES = 4; 149 150 private final FloatBuffer mTextureVertices; 151 private final FloatBuffer mQuadVertices; 152 153 154 private int mGLProgram; 155 private int mTexHandle; 156 private int mTexCoordHandle; 157 private int mTriangleVerticesHandle; 158 private int mTransformHandle; 159 private int mViewWidth; 160 private int mViewHeight; 161 private float[] mTransformMatrix; 162 private int mLastCameraFrameCount; 163 public Renderer() { 164 mTextureVertices = ByteBuffer.allocateDirect(TEXTURE_VERTICES.length * 165 FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer(); 166 mTextureVertices.put(TEXTURE_VERTICES).position(0); 167 mQuadVertices = ByteBuffer.allocateDirect(QUAD_VERTICES.length * 168 FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer(); 169 mQuadVertices.put(QUAD_VERTICES).position(0); 170 mTransformMatrix = new float[16]; 171 mLastCameraFrameCount = mCameraFrameCount.get(); 172 } 173 174 @Override 175 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 176 mGLProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER); 177 178 mTexHandle = GLES20.glGetUniformLocation(mGLProgram, "s_texture"); 179 mTexCoordHandle = GLES20.glGetAttribLocation(mGLProgram, "a_texCoord"); 180 mTriangleVerticesHandle = GLES20.glGetAttribLocation(mGLProgram, "vPosition"); 181 mTransformHandle = GLES20.glGetUniformLocation(mGLProgram, "u_xform"); 182 int[] textures = new int[1]; 183 GLES20.glGenTextures(1, textures, 0); 184 mTextureName = textures[0]; 185 GLES20.glUseProgram(mGLProgram); 186 GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, 187 false, 0, mTextureVertices); 188 GLES20.glVertexAttribPointer(mTriangleVerticesHandle, 2, GLES20.GL_FLOAT, 189 false, 0, mQuadVertices); 190 checkGlError("initialization"); 191 mSurfaceTexture = new SurfaceTexture(mTextureName); 192 mSurfaceTexture.setOnFrameAvailableListener(new OnFrameAvailableListener() { 193 @Override 194 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 195 mCameraFrameCount.incrementAndGet(); 196 } 197 }); 198 } 199 200 /* (non-Javadoc) 201 * @see android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int) 202 */ 203 @Override 204 public void onSurfaceChanged(GL10 gl, int width, int height) { 205 mViewWidth = width; 206 mViewHeight = height; 207 } 208 209 private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; 210 @Override 211 public void onDrawFrame(GL10 gl) { 212 GLES20.glUseProgram(mGLProgram); 213 GLES20.glViewport(0, 0, mViewWidth, mViewHeight); 214 checkGlError("glViewport"); 215 216 if (mCameraEnabled.get()) { 217 int cameraFrameCount = mCameraFrameCount.get(); 218 if (mLastCameraFrameCount != cameraFrameCount) { 219 mReportedFrameCount.incrementAndGet(); 220 mSurfaceTexture.updateTexImage(); 221 mSurfaceTexture.getTransformMatrix(mTransformMatrix); 222 GLES20.glUniformMatrix4fv(mTransformHandle, 1, false, mTransformMatrix, 0); 223 checkGlError("glUniformMatrix4fv"); 224 mLastCameraFrameCount = cameraFrameCount; 225 } 226 GLES20.glDisable(GLES20.GL_BLEND); 227 checkGlError("setup"); 228 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 229 checkGlError("setup"); 230 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName); 231 checkGlError("setup"); 232 GLES20.glUniform1i(mTexHandle, 0); 233 checkGlError("setup"); 234 GLES20.glEnableVertexAttribArray(mTexCoordHandle); 235 checkGlError("setup"); 236 GLES20.glEnableVertexAttribArray(mTriangleVerticesHandle); 237 checkGlError("setup"); 238 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4); 239 checkGlError("glDrawArrays"); 240 } else { 241 GLES20.glClearColor(0,0,0,0); 242 } 243 } 244 245 } 246 } 247