package com.marshalchen.common.uimodule.imageprocessing.input; import java.io.PrintWriter; import java.io.StringWriter; import javax.microedition.khronos.opengles.GL10; import android.annotation.TargetApi; import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture.OnFrameAvailableListener; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.util.Log; /** * A Camera input extension of CameraPreviewInput. * This class takes advantage of the android camera preview to produce new textures for processing. <p> * Note: This class requires an API level of at least 14. To change camera parameters or get access to the * camera directly before it is used by this class, createCamera() can be override. * @author Chris Batt */ @TargetApi(value = 14) public class CameraPreviewInput extends GLTextureOutputRenderer implements OnFrameAvailableListener { private static final String UNIFORM_CAM_MATRIX = "u_Matrix"; private Camera camera; private SurfaceTexture camTex; private int matrixHandle; private float[] matrix = new float[16]; private GLSurfaceView view; /** * Creates a CameraPreviewInput which captures the camera preview with all the default camera parameters and settings. */ public CameraPreviewInput(GLSurfaceView view) { super(); this.camera = createCamera(); this.view = view; } private void bindTexture() { GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture_in); } protected Camera createCamera() { return Camera.open(); } /* (non-Javadoc) * @see com.marshalchen.common.uimodule.imageprocessing.input.GLTextureOutputRenderer#destroy() */ @Override public void destroy() { super.destroy(); if(camera != null) { camera.stopPreview(); camera.release(); camera = null; } if(camTex != null) { camTex.release(); camTex = null; } if(texture_in != 0) { int[] tex = new int[1]; tex[0] = texture_in; GLES20.glDeleteTextures(1, tex, 0); texture_in = 0; } } @Override protected void drawFrame() { camTex.updateTexImage(); super.drawFrame(); } @Override protected String getFragmentShader() { return "#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;\n" +"uniform samplerExternalOES "+UNIFORM_TEXTURE0+";\n" +"varying vec2 "+VARYING_TEXCOORD+";\n" + "void main() {\n" + " gl_FragColor = texture2D("+UNIFORM_TEXTURE0+", "+VARYING_TEXCOORD+");\n" + "}\n"; } @Override protected String getVertexShader() { return "uniform mat4 "+UNIFORM_CAM_MATRIX+";\n" + "attribute vec4 "+ATTRIBUTE_POSITION+";\n" + "attribute vec2 "+ATTRIBUTE_TEXCOORD+";\n" + "varying vec2 "+VARYING_TEXCOORD+";\n" + "void main() {\n" + " vec4 texPos = "+UNIFORM_CAM_MATRIX+" * vec4("+ATTRIBUTE_TEXCOORD+", 1, 1);\n" + " "+VARYING_TEXCOORD+" = texPos.xy;\n" + " gl_Position = "+ATTRIBUTE_POSITION+";\n" + "}\n"; } @Override protected void initShaderHandles() { super.initShaderHandles(); matrixHandle = GLES20.glGetUniformLocation(programHandle, UNIFORM_CAM_MATRIX); } @Override protected void initWithGLContext() { super.initWithGLContext(); int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); texture_in = textures[0]; camTex = new SurfaceTexture(texture_in); camTex.setOnFrameAvailableListener(this); boolean failed = true; while(failed) { try { if(camera != null) { camera.stopPreview(); camera.release(); camera = null; } camera = createCamera(); camera.setPreviewTexture(camTex); camera.startPreview(); setRenderSizeToCameraSize(); failed = false; } catch (Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); Log.e("CameraInput", sw.toString()); if(camera != null) { camera.release(); camera = null; } } } } /* (non-Javadoc) * @see android.graphics.SurfaceTexture.OnFrameAvailableListener#onFrameAvailable(android.graphics.SurfaceTexture) */ @Override public void onFrameAvailable(SurfaceTexture arg0) { markAsDirty(); view.requestRender(); } /** * Closes and releases the camera for other applications to use. * Should be called when the pause is called in the activity. */ public void onPause() { if(camera != null) { camera.stopPreview(); camera.release(); camera = null; } } /** * Re-initializes the camera and starts the preview again. * Should be called when resume is called in the activity. */ public void onResume() { reInitialize(); } @Override protected void passShaderValues() { renderVertices.position(0); GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 8, renderVertices); GLES20.glEnableVertexAttribArray(positionHandle); textureVertices[curRotation].position(0); GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureVertices[curRotation]); GLES20.glEnableVertexAttribArray(texCoordHandle); bindTexture(); GLES20.glUniform1i(textureHandle, 0); camTex.getTransformMatrix(matrix); GLES20.glUniformMatrix4fv(matrixHandle, 1, false, matrix, 0); } private void setRenderSizeToCameraSize() { Parameters params = camera.getParameters(); Size previewSize = params.getPreviewSize(); setRenderSize(previewSize.width, previewSize.height); } }