package net.ossrs.yasea; import android.content.Context; import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import android.util.AttributeSet; import android.widget.Toast; import com.seu.magicfilter.base.gpuimage.GPUImageFilter; import com.seu.magicfilter.utils.MagicFilterFactory; import com.seu.magicfilter.utils.MagicFilterType; import com.seu.magicfilter.utils.OpenGLUtils; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; /** * Created by Leo Ma on 2016/2/25. */ public class SrsCameraView extends GLSurfaceView implements GLSurfaceView.Renderer { private GPUImageFilter magicFilter; private SurfaceTexture surfaceTexture; private int mOESTextureId = OpenGLUtils.NO_TEXTURE; private int mSurfaceWidth; private int mSurfaceHeight; private int mPreviewWidth; private int mPreviewHeight; private float mInputAspectRatio; private float mOutputAspectRatio; private float[] mProjectionMatrix = new float[16]; private float[] mSurfaceMatrix = new float[16]; private float[] mTransformMatrix = new float[16]; private Camera mCamera; private ByteBuffer mGlPreviewBuffer; private int mCamId = -1; private int mPreviewRotation = 90; private Thread worker; private final Object writeLock = new Object(); private ConcurrentLinkedQueue mGLIntBufferCache = new ConcurrentLinkedQueue<>(); private PreviewCallback mPrevCb; public SrsCameraView(Context context) { this(context, null); } public SrsCameraView(Context context, AttributeSet attrs) { super(context, attrs); setEGLContextClientVersion(2); setRenderer(this); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); MagicFilterFactory.initContext(context.getApplicationContext()); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES20.glDisable(GL10.GL_DITHER); GLES20.glClearColor(0, 0, 0, 0); magicFilter = new GPUImageFilter(MagicFilterType.NONE); magicFilter.init(); magicFilter.onInputSizeChanged(mPreviewWidth, mPreviewHeight); mOESTextureId = OpenGLUtils.getExternalOESTextureID(); surfaceTexture = new SurfaceTexture(mOESTextureId); surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { requestRender(); } }); // For camera preview on activity creation if (mCamera != null) { try { mCamera.setPreviewTexture(surfaceTexture); } catch (IOException ioe) { ioe.printStackTrace(); } } } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); mSurfaceWidth = width; mSurfaceHeight = height; magicFilter.onDisplaySizeChanged(width, height); mOutputAspectRatio = width > height ? (float) width / height : (float) height / width; float aspectRatio = mOutputAspectRatio / mInputAspectRatio; if (width > height) { Matrix.orthoM(mProjectionMatrix, 0, -1.0f, 1.0f, -aspectRatio, aspectRatio, -1.0f, 1.0f); } else { Matrix.orthoM(mProjectionMatrix, 0, -aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f); } } @Override public void onDrawFrame(GL10 gl) { GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); surfaceTexture.updateTexImage(); surfaceTexture.getTransformMatrix(mSurfaceMatrix); Matrix.multiplyMM(mTransformMatrix, 0, mSurfaceMatrix, 0, mProjectionMatrix, 0); magicFilter.setTextureTransformMatrix(mTransformMatrix); magicFilter.onDrawFrame(mOESTextureId); mGLIntBufferCache.add(magicFilter.getGLFboBuffer()); synchronized (writeLock) { writeLock.notifyAll(); } } public void setPreviewCallback(PreviewCallback cb) { mPrevCb = cb; } public void setPreviewResolution(int width, int height) { mPreviewWidth = width; mPreviewHeight = height; mGlPreviewBuffer = ByteBuffer.allocate(mPreviewWidth * mPreviewHeight * 4); mInputAspectRatio = width > height ? (float) width / height : (float) height / width; } public boolean setFilter(final MagicFilterType type) { if (mCamera == null) { return false; } queueEvent(new Runnable() { @Override public void run() { if (magicFilter != null) { magicFilter.destroy(); } magicFilter = MagicFilterFactory.initFilters(type); if (magicFilter != null) { magicFilter.init(); magicFilter.onInputSizeChanged(mPreviewWidth, mPreviewHeight); magicFilter.onDisplaySizeChanged(mSurfaceWidth, mSurfaceHeight); } } }); requestRender(); return true; } private void deleteTextures() { if(mOESTextureId != OpenGLUtils.NO_TEXTURE){ queueEvent(new Runnable() { @Override public void run() { GLES20.glDeleteTextures(1, new int[]{ mOESTextureId }, 0); mOESTextureId = OpenGLUtils.NO_TEXTURE; } }); } } public void setPreviewRotation(int rotation) { mPreviewRotation = rotation; } public void setCameraId(int id) { mCamId = id; } public int getCameraId() { return mCamId; } public boolean startCamera() { if (mCamera != null) { return false; } if (mCamId > (Camera.getNumberOfCameras() - 1)) { return false; } worker = new Thread(new Runnable() { @Override public void run() { while (!Thread.interrupted()) { while (!mGLIntBufferCache.isEmpty()) { IntBuffer picture = mGLIntBufferCache.poll(); mGlPreviewBuffer.asIntBuffer().put(picture.array()); mPrevCb.onGetRgbaFrame(mGlPreviewBuffer.array(), mPreviewWidth, mPreviewHeight); } // Waiting for next frame synchronized (writeLock) { try { // isEmpty() may take some time, so we set timeout to detect next frame writeLock.wait(500); } catch (InterruptedException ie) { worker.interrupt(); } } } } }); worker.start(); if (mCamId < 0) { Camera.CameraInfo info = new Camera.CameraInfo(); int numCameras = Camera.getNumberOfCameras(); for (int i = 0; i < numCameras; i++) { Camera.getCameraInfo(i, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { mCamera = Camera.open(i); mCamId = i; break; } } } else { mCamera = Camera.open(mCamId); } if (mCamera == null) { mCamera = Camera.open(); mCamId = 0; } Camera.Parameters params = mCamera.getParameters(); Camera.Size size = mCamera.new Size(mPreviewWidth, mPreviewHeight); if (!params.getSupportedPreviewSizes().contains(size) || !params.getSupportedPictureSizes().contains(size)) { Toast.makeText(getContext(), String.format("Unsupported resolution %dx%d", size.width, size.height), Toast.LENGTH_SHORT).show(); stopCamera(); return false; } List supportedFocusModes = params.getSupportedFocusModes(); if (!supportedFocusModes.isEmpty()) { if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); } else { params.setFocusMode(supportedFocusModes.get(0)); } } /***** set parameters *****/ params.setPictureSize(mPreviewWidth, mPreviewHeight); params.setPreviewSize(mPreviewWidth, mPreviewHeight); int[] range = findClosestFpsRange(SrsEncoder.VFPS, params.getSupportedPreviewFpsRange()); params.setPreviewFpsRange(range[0], range[1]); params.setPreviewFormat(ImageFormat.NV21); params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO); params.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO); if (!params.getSupportedFocusModes().isEmpty()) { params.setFocusMode(params.getSupportedFocusModes().get(0)); } mCamera.setParameters(params); mCamera.setDisplayOrientation(mPreviewRotation); try { mCamera.setPreviewTexture(surfaceTexture); } catch (IOException e) { e.printStackTrace(); } mCamera.startPreview(); return true; } public void stopCamera() { if (worker != null) { worker.interrupt(); try { worker.join(); } catch (InterruptedException e) { e.printStackTrace(); worker.interrupt(); } mGLIntBufferCache.clear(); worker = null; } if (mCamera != null) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } } private int[] findClosestFpsRange(int expectedFps, List fpsRanges) { expectedFps *= 1000; int[] closestRange = fpsRanges.get(0); int measure = Math.abs(closestRange[0] - expectedFps) + Math.abs(closestRange[1] - expectedFps); for (int[] range : fpsRanges) { if (range[0] <= expectedFps && range[1] >= expectedFps) { int curMeasure = Math.abs(range[0] - expectedFps) + Math.abs(range[1] - expectedFps); if (curMeasure < measure) { closestRange = range; measure = curMeasure; } } } return closestRange; } public interface PreviewCallback { void onGetRgbaFrame(byte[] data, int width, int height); } }