1// Copyright 2011 Google Inc. All Rights Reserved.
2
3package com.example.android.videochatcameratest;
4
5import android.content.Context;
6import android.graphics.SurfaceTexture;
7import android.graphics.SurfaceTexture.OnFrameAvailableListener;
8import android.opengl.GLES20;
9import android.opengl.GLSurfaceView;
10import android.util.AttributeSet;
11import android.util.Log;
12
13import java.nio.ByteBuffer;
14import java.nio.ByteOrder;
15import java.nio.FloatBuffer;
16import java.util.concurrent.atomic.AtomicBoolean;
17import java.util.concurrent.atomic.AtomicInteger;
18
19import javax.microedition.khronos.egl.EGLConfig;
20import javax.microedition.khronos.opengles.GL10;
21
22class 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