mirror of https://github.com/opencv/opencv.git
refactored; added Camera2, notify callbacks, front/back maxCamera sizes; disable new stuff if target API < 21
parent
8e088d38a5
commit
15db8243ef
5 changed files with 880 additions and 309 deletions
@ -0,0 +1,302 @@ |
||||
package org.opencv.android; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.concurrent.Semaphore; |
||||
import java.util.concurrent.TimeUnit; |
||||
import android.annotation.TargetApi; |
||||
import android.content.Context; |
||||
import android.graphics.SurfaceTexture; |
||||
import android.hardware.camera2.CameraAccessException; |
||||
import android.hardware.camera2.CameraCaptureSession; |
||||
import android.hardware.camera2.CameraCharacteristics; |
||||
import android.hardware.camera2.CameraDevice; |
||||
import android.hardware.camera2.CameraManager; |
||||
import android.hardware.camera2.CaptureRequest; |
||||
import android.hardware.camera2.params.StreamConfigurationMap; |
||||
import android.os.Handler; |
||||
import android.os.HandlerThread; |
||||
import android.util.Log; |
||||
import android.util.Size; |
||||
import android.view.Surface; |
||||
|
||||
@TargetApi(21) |
||||
public class Camera2Renderer extends CameraGLRendererBase { |
||||
|
||||
protected final String LOGTAG = "Camera2Renderer"; |
||||
private CameraDevice mCameraDevice; |
||||
private CameraCaptureSession mCaptureSession; |
||||
private CaptureRequest.Builder mPreviewRequestBuilder; |
||||
private String mCameraID; |
||||
private Size mPreviewSize = new Size(-1, -1); |
||||
|
||||
private HandlerThread mBackgroundThread; |
||||
private Handler mBackgroundHandler; |
||||
private Semaphore mCameraOpenCloseLock = new Semaphore(1); |
||||
|
||||
Camera2Renderer(CameraGLSurfaceView view) { |
||||
super(view); |
||||
} |
||||
|
||||
@Override |
||||
protected void doStart() { |
||||
Log.d(LOGTAG, "doStart"); |
||||
startBackgroundThread(); |
||||
super.doStart(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected void doStop() { |
||||
Log.d(LOGTAG, "doStop"); |
||||
super.doStop(); |
||||
stopBackgroundThread(); |
||||
} |
||||
|
||||
boolean cacPreviewSize(final int width, final int height) { |
||||
Log.i(LOGTAG, "cacPreviewSize: "+width+"x"+height); |
||||
if(mCameraID == null) { |
||||
Log.e(LOGTAG, "Camera isn't initialized!"); |
||||
return false; |
||||
} |
||||
CameraManager manager = (CameraManager) mView.getContext() |
||||
.getSystemService(Context.CAMERA_SERVICE); |
||||
try { |
||||
CameraCharacteristics characteristics = manager |
||||
.getCameraCharacteristics(mCameraID); |
||||
StreamConfigurationMap map = characteristics |
||||
.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); |
||||
int bestWidth = 0, bestHeight = 0; |
||||
float aspect = (float)width / height; |
||||
for (Size psize : map.getOutputSizes(SurfaceTexture.class)) { |
||||
int w = psize.getWidth(), h = psize.getHeight(); |
||||
Log.d(LOGTAG, "trying size: "+w+"x"+h); |
||||
if ( width >= w && height >= h && |
||||
bestWidth <= w && bestHeight <= h && |
||||
Math.abs(aspect - (float)w/h) < 0.2 ) { |
||||
bestWidth = w; |
||||
bestHeight = h; |
||||
} |
||||
} |
||||
Log.i(LOGTAG, "best size: "+bestWidth+"x"+bestHeight); |
||||
if( bestWidth == 0 || bestHeight == 0 || |
||||
mPreviewSize.getWidth() == bestWidth && |
||||
mPreviewSize.getHeight() == bestHeight ) |
||||
return false; |
||||
else { |
||||
mPreviewSize = new Size(bestWidth, bestHeight); |
||||
return true; |
||||
} |
||||
} catch (CameraAccessException e) { |
||||
Log.e(LOGTAG, "cacPreviewSize - Camera Access Exception"); |
||||
} catch (IllegalArgumentException e) { |
||||
Log.e(LOGTAG, "cacPreviewSize - Illegal Argument Exception"); |
||||
} catch (SecurityException e) { |
||||
Log.e(LOGTAG, "cacPreviewSize - Security Exception"); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
protected void openCamera(int id) { |
||||
Log.i(LOGTAG, "openCamera"); |
||||
CameraManager manager = (CameraManager) mView.getContext().getSystemService(Context.CAMERA_SERVICE); |
||||
try { |
||||
String camList[] = manager.getCameraIdList(); |
||||
if(camList.length == 0) { |
||||
Log.e(LOGTAG, "Error: camera isn't detected."); |
||||
return; |
||||
} |
||||
if(id == CameraBridgeViewBase.CAMERA_ID_ANY) { |
||||
mCameraID = camList[0]; |
||||
} else { |
||||
for (String cameraID : camList) { |
||||
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraID); |
||||
if( id == CameraBridgeViewBase.CAMERA_ID_BACK && |
||||
characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK || |
||||
id == CameraBridgeViewBase.CAMERA_ID_FRONT && |
||||
characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) { |
||||
mCameraID = cameraID; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
if(mCameraID != null) { |
||||
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { |
||||
throw new RuntimeException( |
||||
"Time out waiting to lock camera opening."); |
||||
} |
||||
Log.i(LOGTAG, "Opening camera: " + mCameraID); |
||||
manager.openCamera(mCameraID, mStateCallback, mBackgroundHandler); |
||||
} |
||||
} catch (CameraAccessException e) { |
||||
Log.e(LOGTAG, "OpenCamera - Camera Access Exception"); |
||||
} catch (IllegalArgumentException e) { |
||||
Log.e(LOGTAG, "OpenCamera - Illegal Argument Exception"); |
||||
} catch (SecurityException e) { |
||||
Log.e(LOGTAG, "OpenCamera - Security Exception"); |
||||
} catch (InterruptedException e) { |
||||
Log.e(LOGTAG, "OpenCamera - Interrupted Exception"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void closeCamera() { |
||||
Log.i(LOGTAG, "closeCamera"); |
||||
try { |
||||
mCameraOpenCloseLock.acquire(); |
||||
if (null != mCaptureSession) { |
||||
mCaptureSession.close(); |
||||
mCaptureSession = null; |
||||
} |
||||
if (null != mCameraDevice) { |
||||
mCameraDevice.close(); |
||||
mCameraDevice = null; |
||||
} |
||||
} catch (InterruptedException e) { |
||||
throw new RuntimeException("Interrupted while trying to lock camera closing.", e); |
||||
} finally { |
||||
mCameraOpenCloseLock.release(); |
||||
} |
||||
} |
||||
|
||||
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { |
||||
|
||||
@Override |
||||
public void onOpened(CameraDevice cameraDevice) { |
||||
mCameraDevice = cameraDevice; |
||||
mCameraOpenCloseLock.release(); |
||||
createCameraPreviewSession(); |
||||
} |
||||
|
||||
@Override |
||||
public void onDisconnected(CameraDevice cameraDevice) { |
||||
cameraDevice.close(); |
||||
mCameraDevice = null; |
||||
mCameraOpenCloseLock.release(); |
||||
} |
||||
|
||||
@Override |
||||
public void onError(CameraDevice cameraDevice, int error) { |
||||
cameraDevice.close(); |
||||
mCameraDevice = null; |
||||
mCameraOpenCloseLock.release(); |
||||
} |
||||
|
||||
}; |
||||
|
||||
private void createCameraPreviewSession() { |
||||
int w=mPreviewSize.getWidth(), h=mPreviewSize.getHeight(); |
||||
Log.i(LOGTAG, "createCameraPreviewSession("+w+"x"+h+")"); |
||||
if(w<0 || h<0) |
||||
return; |
||||
try { |
||||
mCameraOpenCloseLock.acquire(); |
||||
if (null == mCameraDevice) { |
||||
mCameraOpenCloseLock.release(); |
||||
Log.e(LOGTAG, "createCameraPreviewSession: camera isn't opened"); |
||||
return; |
||||
} |
||||
if (null != mCaptureSession) { |
||||
mCameraOpenCloseLock.release(); |
||||
Log.e(LOGTAG, "createCameraPreviewSession: mCaptureSession is already started"); |
||||
return; |
||||
} |
||||
if(null == mSTexture) { |
||||
mCameraOpenCloseLock.release(); |
||||
Log.e(LOGTAG, "createCameraPreviewSession: preview SurfaceTexture is null"); |
||||
return; |
||||
} |
||||
mSTexture.setDefaultBufferSize(w, h); |
||||
|
||||
Surface surface = new Surface(mSTexture); |
||||
|
||||
mPreviewRequestBuilder = mCameraDevice |
||||
.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); |
||||
mPreviewRequestBuilder.addTarget(surface); |
||||
|
||||
mCameraDevice.createCaptureSession(Arrays.asList(surface), |
||||
new CameraCaptureSession.StateCallback() { |
||||
@Override |
||||
public void onConfigured( CameraCaptureSession cameraCaptureSession) { |
||||
mCaptureSession = cameraCaptureSession; |
||||
try { |
||||
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); |
||||
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); |
||||
|
||||
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler); |
||||
Log.i(LOGTAG, "CameraPreviewSession has been started"); |
||||
} catch (CameraAccessException e) { |
||||
Log.e(LOGTAG, "createCaptureSession failed"); |
||||
} |
||||
mCameraOpenCloseLock.release(); |
||||
} |
||||
|
||||
@Override |
||||
public void onConfigureFailed( |
||||
CameraCaptureSession cameraCaptureSession) { |
||||
Log.e(LOGTAG, "createCameraPreviewSession failed"); |
||||
mCameraOpenCloseLock.release(); |
||||
} |
||||
}, mBackgroundHandler); |
||||
} catch (CameraAccessException e) { |
||||
Log.e(LOGTAG, "createCameraPreviewSession"); |
||||
} catch (InterruptedException e) { |
||||
throw new RuntimeException( |
||||
"Interrupted while createCameraPreviewSession", e); |
||||
} |
||||
finally { |
||||
//mCameraOpenCloseLock.release();
|
||||
} |
||||
} |
||||
|
||||
private void startBackgroundThread() { |
||||
Log.i(LOGTAG, "startBackgroundThread"); |
||||
stopBackgroundThread(); |
||||
mBackgroundThread = new HandlerThread("CameraBackground"); |
||||
mBackgroundThread.start(); |
||||
mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); |
||||
} |
||||
|
||||
private void stopBackgroundThread() { |
||||
Log.i(LOGTAG, "stopBackgroundThread"); |
||||
if(mBackgroundThread == null) |
||||
return; |
||||
mBackgroundThread.quitSafely(); |
||||
try { |
||||
mBackgroundThread.join(); |
||||
mBackgroundThread = null; |
||||
mBackgroundHandler = null; |
||||
} catch (InterruptedException e) { |
||||
Log.e(LOGTAG, "stopBackgroundThread"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void setCameraPreviewSize(int width, int height) { |
||||
Log.i(LOGTAG, "setCameraPreviewSize("+width+"x"+height+")"); |
||||
if(mMaxCameraWidth > 0 && mMaxCameraWidth < width) width = mMaxCameraWidth; |
||||
if(mMaxCameraHeight > 0 && mMaxCameraHeight < height) height = mMaxCameraHeight; |
||||
try { |
||||
mCameraOpenCloseLock.acquire(); |
||||
|
||||
boolean needReconfig = cacPreviewSize(width, height); |
||||
mCameraWidth = mPreviewSize.getWidth(); |
||||
mCameraHeight = mPreviewSize.getHeight(); |
||||
|
||||
if( !needReconfig ) { |
||||
mCameraOpenCloseLock.release(); |
||||
return; |
||||
} |
||||
if (null != mCaptureSession) { |
||||
Log.d(LOGTAG, "closing existing previewSession"); |
||||
mCaptureSession.close(); |
||||
mCaptureSession = null; |
||||
} |
||||
mCameraOpenCloseLock.release(); |
||||
createCameraPreviewSession(); |
||||
} catch (InterruptedException e) { |
||||
mCameraOpenCloseLock.release(); |
||||
throw new RuntimeException("Interrupted while setCameraPreviewSize.", e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,424 @@ |
||||
package org.opencv.android; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.nio.ByteOrder; |
||||
import java.nio.FloatBuffer; |
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig; |
||||
import javax.microedition.khronos.opengles.GL10; |
||||
|
||||
import org.opencv.android.CameraGLSurfaceView.CameraTextureListener; |
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.graphics.SurfaceTexture; |
||||
import android.opengl.GLES11Ext; |
||||
import android.opengl.GLES20; |
||||
import android.opengl.GLSurfaceView; |
||||
import android.util.Log; |
||||
import android.view.View; |
||||
|
||||
@TargetApi(15) |
||||
public abstract class CameraGLRendererBase implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener { |
||||
|
||||
protected final String LOGTAG = "CameraGLRendererBase"; |
||||
|
||||
// shaders
|
||||
private final String vss = "" |
||||
+ "attribute vec2 vPosition;\n" |
||||
+ "attribute vec2 vTexCoord;\n" + "varying vec2 texCoord;\n" |
||||
+ "void main() {\n" + " texCoord = vTexCoord;\n" |
||||
+ " gl_Position = vec4 ( vPosition.x, vPosition.y, 0.0, 1.0 );\n" |
||||
+ "}"; |
||||
|
||||
private final String fssOES = "" |
||||
+ "#extension GL_OES_EGL_image_external : require\n" |
||||
+ "precision mediump float;\n" |
||||
+ "uniform samplerExternalOES sTexture;\n" |
||||
+ "varying vec2 texCoord;\n" |
||||
+ "void main() {\n" |
||||
+ " gl_FragColor = texture2D(sTexture,texCoord);\n" + "}"; |
||||
|
||||
private final String fss2D = "" |
||||
+ "precision mediump float;\n" |
||||
+ "uniform sampler2D sTexture;\n" |
||||
+ "varying vec2 texCoord;\n" |
||||
+ "void main() {\n" |
||||
+ " gl_FragColor = texture2D(sTexture,texCoord);\n" + "}"; |
||||
|
||||
// coord-s
|
||||
private final float vertices[] = { |
||||
-1, -1, |
||||
-1, 1, |
||||
1, -1, |
||||
1, 1 }; |
||||
private final float texCoordOES[] = { |
||||
0, 1, |
||||
0, 0, |
||||
1, 1, |
||||
1, 0 }; |
||||
private final float texCoord2D[] = { |
||||
0, 0, |
||||
0, 1, |
||||
1, 0, |
||||
1, 1 }; |
||||
|
||||
private int[] texCamera = {0}, texFBO = {0}, texDraw = {0}; |
||||
private int[] FBO = {0}; |
||||
private int progOES = -1, prog2D = -1; |
||||
private int vPosOES, vTCOES, vPos2D, vTC2D; |
||||
|
||||
private FloatBuffer vert, texOES, tex2D; |
||||
|
||||
protected int mCameraWidth = -1, mCameraHeight = -1; |
||||
protected int mFBOWidth = -1, mFBOHeight = -1; |
||||
protected int mMaxCameraWidth = -1, mMaxCameraHeight = -1; |
||||
protected int mCameraIndex = CameraBridgeViewBase.CAMERA_ID_ANY; |
||||
|
||||
protected SurfaceTexture mSTexture; |
||||
|
||||
protected boolean mHaveSurface = false; |
||||
protected boolean mHaveFBO = false; |
||||
protected boolean mUpdateST = false; |
||||
protected boolean mEnabled = true; |
||||
protected boolean mIsStarted = false; |
||||
|
||||
protected CameraGLSurfaceView mView; |
||||
|
||||
protected abstract void openCamera(int id); |
||||
protected abstract void closeCamera(); |
||||
protected abstract void setCameraPreviewSize(int width, int height); // updates mCameraWidth & mCameraHeight
|
||||
|
||||
public CameraGLRendererBase(CameraGLSurfaceView view) { |
||||
mView = view; |
||||
int bytes = vertices.length * Float.SIZE / Byte.SIZE; |
||||
vert = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer(); |
||||
texOES = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer(); |
||||
tex2D = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer(); |
||||
vert.put(vertices).position(0); |
||||
texOES.put(texCoordOES).position(0); |
||||
tex2D.put(texCoord2D).position(0); |
||||
} |
||||
|
||||
@Override |
||||
public synchronized void onFrameAvailable(SurfaceTexture surfaceTexture) { |
||||
//Log.i(LOGTAG, "onFrameAvailable");
|
||||
mUpdateST = true; |
||||
mView.requestRender(); |
||||
} |
||||
|
||||
@Override |
||||
public void onDrawFrame(GL10 gl) { |
||||
//Log.i(LOGTAG, "onDrawFrame start");
|
||||
|
||||
if (!mHaveFBO) |
||||
return; |
||||
|
||||
synchronized(this) { |
||||
if (mUpdateST) { |
||||
mSTexture.updateTexImage(); |
||||
mUpdateST = false; |
||||
} |
||||
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
||||
|
||||
CameraTextureListener texListener = mView.getCameraTextureListener(); |
||||
if(texListener != null) { |
||||
//Log.d(LOGTAG, "haveUserCallback");
|
||||
// texCamera(OES) -> texFBO
|
||||
drawTex(texCamera[0], true, FBO[0]); |
||||
|
||||
// call user code (texFBO -> texDraw)
|
||||
boolean modified = texListener.onCameraTexture(texFBO[0], texDraw[0], mCameraWidth, mCameraHeight); |
||||
|
||||
if(modified) { |
||||
// texDraw -> screen
|
||||
drawTex(texDraw[0], false, 0); |
||||
} else { |
||||
// texFBO -> screen
|
||||
drawTex(texFBO[0], false, 0); |
||||
} |
||||
} else { |
||||
Log.d(LOGTAG, "texCamera(OES) -> screen"); |
||||
// texCamera(OES) -> screen
|
||||
drawTex(texCamera[0], true, 0); |
||||
} |
||||
//Log.i(LOGTAG, "onDrawFrame end");
|
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onSurfaceChanged(GL10 gl, int surfaceWidth, int surfaceHeight) { |
||||
Log.i(LOGTAG, "onSurfaceChanged("+surfaceWidth+"x"+surfaceHeight+")"); |
||||
mHaveSurface = true; |
||||
updateState(); |
||||
setPreviewSize(surfaceWidth, surfaceHeight); |
||||
} |
||||
|
||||
@Override |
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) { |
||||
Log.i(LOGTAG, "onSurfaceCreated"); |
||||
initShaders(); |
||||
} |
||||
|
||||
private void initShaders() { |
||||
String strGLVersion = GLES20.glGetString(GLES20.GL_VERSION); |
||||
if (strGLVersion != null) |
||||
Log.i(LOGTAG, "OpenGL ES version: " + strGLVersion); |
||||
|
||||
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); |
||||
|
||||
progOES = loadShader(vss, fssOES); |
||||
vPosOES = GLES20.glGetAttribLocation(progOES, "vPosition"); |
||||
vTCOES = GLES20.glGetAttribLocation(progOES, "vTexCoord"); |
||||
GLES20.glEnableVertexAttribArray(vPosOES); |
||||
GLES20.glEnableVertexAttribArray(vTCOES); |
||||
|
||||
prog2D = loadShader(vss, fss2D); |
||||
vPos2D = GLES20.glGetAttribLocation(prog2D, "vPosition"); |
||||
vTC2D = GLES20.glGetAttribLocation(prog2D, "vTexCoord"); |
||||
GLES20.glEnableVertexAttribArray(vPos2D); |
||||
GLES20.glEnableVertexAttribArray(vTC2D); |
||||
} |
||||
|
||||
private void initSurfaceTexture() { |
||||
Log.d(LOGTAG, "initSurfaceTexture"); |
||||
deleteSurfaceTexture(); |
||||
initTexOES(texCamera); |
||||
mSTexture = new SurfaceTexture(texCamera[0]); |
||||
mSTexture.setOnFrameAvailableListener(this); |
||||
} |
||||
|
||||
private void deleteSurfaceTexture() { |
||||
Log.d(LOGTAG, "deleteSurfaceTexture"); |
||||
if(mSTexture != null) { |
||||
mSTexture.release(); |
||||
mSTexture = null; |
||||
deleteTex(texCamera); |
||||
} |
||||
} |
||||
|
||||
private void initTexOES(int[] tex) { |
||||
if(tex.length == 1) { |
||||
GLES20.glGenTextures(1, tex, 0); |
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]); |
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); |
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); |
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); |
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); |
||||
} |
||||
} |
||||
|
||||
private static void deleteTex(int[] tex) { |
||||
if(tex.length == 1) { |
||||
GLES20.glDeleteTextures(1, tex, 0); |
||||
} |
||||
} |
||||
|
||||
private static int loadShader(String vss, String fss) { |
||||
Log.d("CameraGLRendererBase", "loadShader"); |
||||
int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); |
||||
GLES20.glShaderSource(vshader, vss); |
||||
GLES20.glCompileShader(vshader); |
||||
int[] compiled = new int[1]; |
||||
GLES20.glGetShaderiv(vshader, GLES20.GL_COMPILE_STATUS, compiled, 0); |
||||
if (compiled[0] == 0) { |
||||
Log.e("CameraGLRendererBase", "Could not compile vertex shader: "+GLES20.glGetShaderInfoLog(vshader)); |
||||
GLES20.glDeleteShader(vshader); |
||||
vshader = 0; |
||||
return 0; |
||||
} |
||||
|
||||
int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); |
||||
GLES20.glShaderSource(fshader, fss); |
||||
GLES20.glCompileShader(fshader); |
||||
GLES20.glGetShaderiv(fshader, GLES20.GL_COMPILE_STATUS, compiled, 0); |
||||
if (compiled[0] == 0) { |
||||
Log.e("CameraGLRendererBase", "Could not compile fragment shader:"+GLES20.glGetShaderInfoLog(fshader)); |
||||
GLES20.glDeleteShader(vshader); |
||||
GLES20.glDeleteShader(fshader); |
||||
fshader = 0; |
||||
return 0; |
||||
} |
||||
|
||||
int program = GLES20.glCreateProgram(); |
||||
GLES20.glAttachShader(program, vshader); |
||||
GLES20.glAttachShader(program, fshader); |
||||
GLES20.glLinkProgram(program); |
||||
Log.d("CameraGLRendererBase", "shaders were compiled OK"); |
||||
GLES20.glDeleteShader(vshader); |
||||
GLES20.glDeleteShader(fshader); |
||||
|
||||
return program; |
||||
} |
||||
|
||||
private void deleteFBO() |
||||
{ |
||||
Log.d(LOGTAG, "deleteFBO("+mFBOWidth+"x"+mFBOHeight+")"); |
||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); |
||||
GLES20.glDeleteFramebuffers(1, FBO, 0); |
||||
|
||||
deleteTex(texFBO); |
||||
deleteTex(texDraw); |
||||
mFBOWidth = mFBOHeight = 0; |
||||
} |
||||
|
||||
private void initFBO(int width, int height) |
||||
{ |
||||
Log.d(LOGTAG, "initFBO("+width+"x"+height+")"); |
||||
|
||||
deleteFBO(); |
||||
|
||||
GLES20.glGenTextures(1, texDraw, 0); |
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texDraw[0]); |
||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); |
||||
|
||||
GLES20.glGenTextures(1, texFBO, 0); |
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texFBO[0]); |
||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); |
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); |
||||
|
||||
//int hFBO;
|
||||
GLES20.glGenFramebuffers(1, FBO, 0); |
||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, FBO[0]); |
||||
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texFBO[0], 0); |
||||
Log.d(LOGTAG, "initFBO error status: " + GLES20.glGetError()); |
||||
|
||||
int FBOstatus = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); |
||||
if (FBOstatus != GLES20.GL_FRAMEBUFFER_COMPLETE) |
||||
Log.e(LOGTAG, "initFBO failed, status: " + FBOstatus); |
||||
|
||||
mFBOWidth = width; |
||||
mFBOHeight = height; |
||||
} |
||||
|
||||
// draw texture to FBO or to screen if fbo == 0
|
||||
private void drawTex(int tex, boolean isOES, int fbo) |
||||
{ |
||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo); |
||||
|
||||
if(fbo == 0) |
||||
GLES20.glViewport(0, 0, mView.getWidth(), mView.getHeight()); |
||||
else |
||||
GLES20.glViewport(0, 0, mFBOWidth, mFBOHeight); |
||||
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
||||
|
||||
if(isOES) { |
||||
GLES20.glUseProgram(progOES); |
||||
GLES20.glVertexAttribPointer(vPosOES, 2, GLES20.GL_FLOAT, false, 4*2, vert); |
||||
GLES20.glVertexAttribPointer(vTCOES, 2, GLES20.GL_FLOAT, false, 4*2, texOES); |
||||
} else { |
||||
GLES20.glUseProgram(prog2D); |
||||
GLES20.glVertexAttribPointer(vPos2D, 2, GLES20.GL_FLOAT, false, 4*2, vert); |
||||
GLES20.glVertexAttribPointer(vTC2D, 2, GLES20.GL_FLOAT, false, 4*2, tex2D); |
||||
} |
||||
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
||||
|
||||
if(isOES) { |
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex); |
||||
GLES20.glUniform1i(GLES20.glGetUniformLocation(progOES, "sTexture"), 0); |
||||
} else { |
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex); |
||||
GLES20.glUniform1i(GLES20.glGetUniformLocation(prog2D, "sTexture"), 0); |
||||
} |
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); |
||||
GLES20.glFlush(); |
||||
} |
||||
|
||||
public synchronized void enableView() { |
||||
Log.d(LOGTAG, "enableView"); |
||||
mEnabled = true; |
||||
updateState(); |
||||
} |
||||
|
||||
public synchronized void disableView() { |
||||
Log.d(LOGTAG, "disableView"); |
||||
mEnabled = false; |
||||
updateState(); |
||||
} |
||||
|
||||
protected void updateState() { |
||||
Log.d(LOGTAG, "updateState"); |
||||
Log.d(LOGTAG, "mEnabled="+mEnabled+", mHaveSurface="+mHaveSurface); |
||||
boolean willStart = mEnabled && mHaveSurface && mView.getVisibility() == View.VISIBLE; |
||||
if (willStart != mIsStarted) { |
||||
if(willStart) doStart(); |
||||
else doStop(); |
||||
} else { |
||||
Log.d(LOGTAG, "keeping State unchanged"); |
||||
} |
||||
Log.d(LOGTAG, "updateState end"); |
||||
} |
||||
|
||||
protected synchronized void doStart() { |
||||
Log.d(LOGTAG, "doStart"); |
||||
initSurfaceTexture(); |
||||
openCamera(mCameraIndex); |
||||
mIsStarted = true; |
||||
if(mCameraWidth>0 && mCameraHeight>0) |
||||
setPreviewSize(mCameraWidth, mCameraHeight); // start preview and call listener.onCameraViewStarted()
|
||||
} |
||||
|
||||
|
||||
protected void doStop() { |
||||
Log.d(LOGTAG, "doStop"); |
||||
synchronized(this) { |
||||
mUpdateST = false; |
||||
mIsStarted = false; |
||||
mHaveFBO = false; |
||||
closeCamera(); |
||||
deleteSurfaceTexture(); |
||||
} |
||||
CameraTextureListener listener = mView.getCameraTextureListener(); |
||||
if(listener != null) listener.onCameraViewStopped(); |
||||
|
||||
} |
||||
|
||||
protected void setPreviewSize(int width, int height) { |
||||
synchronized(this) { |
||||
mHaveFBO = false; |
||||
mCameraWidth = width; |
||||
mCameraHeight = height; |
||||
setCameraPreviewSize(width, height); // can change mCameraWidth & mCameraHeight
|
||||
initFBO(mCameraWidth, mCameraHeight); |
||||
mHaveFBO = true; |
||||
} |
||||
|
||||
CameraTextureListener listener = mView.getCameraTextureListener(); |
||||
if(listener != null) listener.onCameraViewStarted(mCameraWidth, mCameraHeight); |
||||
} |
||||
|
||||
public void setCameraIndex(int cameraIndex) { |
||||
disableView(); |
||||
mCameraIndex = cameraIndex; |
||||
enableView(); |
||||
} |
||||
|
||||
public void setMaxCameraPreviewSize(int maxWidth, int maxHeight) { |
||||
disableView(); |
||||
mMaxCameraWidth = maxWidth; |
||||
mMaxCameraHeight = maxHeight; |
||||
enableView(); |
||||
} |
||||
|
||||
public void onResume() { |
||||
Log.i(LOGTAG, "onResume"); |
||||
} |
||||
|
||||
public void onPause() { |
||||
Log.i(LOGTAG, "onPause"); |
||||
mHaveSurface = false; |
||||
updateState(); |
||||
mCameraWidth = mCameraHeight = -1; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue