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