package com.laifeng.sopcastsdk.video;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.opengl.EGL14;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import com.laifeng.sopcastsdk.camera.CameraData;
import com.laifeng.sopcastsdk.camera.CameraHolder;
import com.laifeng.sopcastsdk.entity.Watermark;
import com.laifeng.sopcastsdk.entity.WatermarkPosition;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
/**
* @Title: RenderSrfTex
* @Package com.laifeng.sopcastsdk.video
* @Description:
* @Author Jim
* @Date 16/9/14
* @Time 下午2:17
* @Version
*/
@TargetApi(18)
public class RenderSrfTex {
private final FloatBuffer mNormalVtxBuf = GlUtil.createVertexBuffer();
private final FloatBuffer mNormalTexCoordBuf = GlUtil.createTexCoordBuffer();
private int mFboTexId;
private final MyRecorder mRecorder;
private final float[] mSymmetryMtx = GlUtil.createIdentityMtx();
private final float[] mNormalMtx = GlUtil.createIdentityMtx();
private int mProgram = -1;
private int maPositionHandle = -1;
private int maTexCoordHandle = -1;
private int muSamplerHandle = -1;
private int muPosMtxHandle = -1;
private EGLDisplay mSavedEglDisplay = null;
private EGLSurface mSavedEglDrawSurface = null;
private EGLSurface mSavedEglReadSurface = null;
private EGLContext mSavedEglContext = null;
private int mVideoWidth = 0;
private int mVideoHeight = 0;
private FloatBuffer mCameraTexCoordBuffer;
private Bitmap mWatermarkImg;
private FloatBuffer mWatermarkVertexBuffer;
private int mWatermarkTextureId = -1;
public RenderSrfTex(int id, MyRecorder recorder) {
mFboTexId = id;
mRecorder = recorder;
}
public void setTextureId(int textureId) {
mFboTexId = textureId;
}
public void setWatermark(Watermark watermark) {
mWatermarkImg = watermark.markImg;
initWatermarkVertexBuffer(watermark.width, watermark.height, watermark.orientation, watermark.vMargin, watermark.hMargin);
}
private void initWatermarkVertexBuffer(int width, int height, int orientation, int vMargin, int hMargin) {
boolean isTop, isRight;
if(orientation == WatermarkPosition.WATERMARK_ORIENTATION_TOP_LEFT
|| orientation == WatermarkPosition.WATERMARK_ORIENTATION_TOP_RIGHT) {
isTop = true;
} else {
isTop = false;
}
if(orientation == WatermarkPosition.WATERMARK_ORIENTATION_TOP_RIGHT
|| orientation == WatermarkPosition.WATERMARK_ORIENTATION_BOTTOM_RIGHT) {
isRight = true;
} else {
isRight = false;
}
float leftX = (mVideoWidth/2.0f - hMargin - width)/(mVideoWidth/2.0f);
float rightX = (mVideoWidth/2.0f - hMargin)/(mVideoWidth/2.0f);
float topY = (mVideoHeight/2.0f - vMargin)/(mVideoHeight/2.0f);
float bottomY = (mVideoHeight/2.0f - vMargin - height)/(mVideoHeight/2.0f);
float temp;
if(!isRight) {
temp = leftX;
leftX = -rightX;
rightX = -temp;
}
if(!isTop) {
temp = topY;
topY = -bottomY;
bottomY = -temp;
}
final float watermarkCoords[]= {
leftX, bottomY, 0.0f,
leftX, topY, 0.0f,
rightX, bottomY, 0.0f,
rightX, topY, 0.0f
};
ByteBuffer bb = ByteBuffer.allocateDirect(watermarkCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
mWatermarkVertexBuffer = bb.asFloatBuffer();
mWatermarkVertexBuffer.put(watermarkCoords);
mWatermarkVertexBuffer.position(0);
}
public void setVideoSize(int width, int height) {
mVideoWidth = width;
mVideoHeight = height;
initCameraTexCoordBuffer();
}
private void initCameraTexCoordBuffer() {
int cameraWidth, cameraHeight;
CameraData cameraData = CameraHolder.instance().getCameraData();
int width = cameraData.cameraWidth;
int height = cameraData.cameraHeight;
if(CameraHolder.instance().isLandscape()) {
cameraWidth = Math.max(width, height);
cameraHeight = Math.min(width, height);
} else {
cameraWidth = Math.min(width, height);
cameraHeight = Math.max(width, height);
}
float hRatio = mVideoWidth / ((float)cameraWidth);
float vRatio = mVideoHeight / ((float)cameraHeight);
float ratio;
if(hRatio > vRatio) {
ratio = mVideoHeight / (cameraHeight * hRatio);
final float vtx[] = {
//UV
0f, 0.5f + ratio/2,
0f, 0.5f - ratio/2,
1f, 0.5f + ratio/2,
1f, 0.5f - ratio/2,
};
ByteBuffer bb = ByteBuffer.allocateDirect(4 * vtx.length);
bb.order(ByteOrder.nativeOrder());
mCameraTexCoordBuffer = bb.asFloatBuffer();
mCameraTexCoordBuffer.put(vtx);
mCameraTexCoordBuffer.position(0);
} else {
ratio = mVideoWidth/ (cameraWidth * vRatio);
final float vtx[] = {
//UV
0.5f - ratio/2, 1f,
0.5f - ratio/2, 0f,
0.5f + ratio/2, 1f,
0.5f + ratio/2, 0f,
};
ByteBuffer bb = ByteBuffer.allocateDirect(4 * vtx.length);
bb.order(ByteOrder.nativeOrder());
mCameraTexCoordBuffer = bb.asFloatBuffer();
mCameraTexCoordBuffer.put(vtx);
mCameraTexCoordBuffer.position(0);
}
}
public void draw() {
saveRenderState();
{
GlUtil.checkGlError("draw_S");
if (mRecorder.firstTimeSetup()) {
mRecorder.startSwapData();
mRecorder.makeCurrent();
initGL();
} else {
mRecorder.makeCurrent();
}
GLES20.glViewport(0, 0, mVideoWidth, mVideoHeight);
GLES20.glClearColor(0f, 0f, 0f, 1f);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(mProgram);
mNormalVtxBuf.position(0);
GLES20.glVertexAttribPointer(maPositionHandle,
3, GLES20.GL_FLOAT, false, 4 * 3, mNormalVtxBuf);
GLES20.glEnableVertexAttribArray(maPositionHandle);
mCameraTexCoordBuffer.position(0);
GLES20.glVertexAttribPointer(maTexCoordHandle,
2, GLES20.GL_FLOAT, false, 4 * 2, mCameraTexCoordBuffer);
GLES20.glEnableVertexAttribArray(maTexCoordHandle);
GLES20.glUniform1i(muSamplerHandle, 0);
//处理前置摄像头镜像
CameraData cameraData = CameraHolder.instance().getCameraData();
if(cameraData != null) {
int facing = cameraData.cameraFacing;
if(muPosMtxHandle>= 0) {
if(facing == CameraData.FACING_FRONT) {
GLES20.glUniformMatrix4fv(muPosMtxHandle, 1, false, mSymmetryMtx, 0);
}else {
GLES20.glUniformMatrix4fv(muPosMtxHandle, 1, false, mNormalMtx, 0);
}
}
}
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFboTexId);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
//绘制纹理
drawWatermark();
mRecorder.swapBuffers();
GlUtil.checkGlError("draw_E");
}
restoreRenderState();
}
private void drawWatermark() {
if(mWatermarkImg == null) {
return;
}
GLES20.glUniformMatrix4fv(muPosMtxHandle, 1, false, mNormalMtx, 0);
GLES20.glVertexAttribPointer(maPositionHandle,
3, GLES20.GL_FLOAT, false, 4 * 3, mWatermarkVertexBuffer);
GLES20.glEnableVertexAttribArray(maPositionHandle);
mNormalTexCoordBuf.position(0);
GLES20.glVertexAttribPointer(maTexCoordHandle,
2, GLES20.GL_FLOAT, false, 4 * 2, mNormalTexCoordBuf);
GLES20.glEnableVertexAttribArray(maTexCoordHandle);
if(mWatermarkTextureId == -1) {
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mWatermarkImg, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
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);
mWatermarkTextureId = textures[0];
}
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mWatermarkTextureId);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisable(GLES20.GL_BLEND);
}
private void initGL() {
GlUtil.checkGlError("initGL_S");
final String vertexShader =
//
"attribute vec4 position;\n" +
"attribute vec4 inputTextureCoordinate;\n" +
"varying vec2 textureCoordinate;\n" +
"uniform mat4 uPosMtx;\n" +
"void main() {\n" +
" gl_Position = uPosMtx * position;\n" +
" textureCoordinate = inputTextureCoordinate.xy;\n" +
"}\n";
final String fragmentShader =
//
"precision mediump float;\n" +
"uniform sampler2D uSampler;\n" +
"varying vec2 textureCoordinate;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(uSampler, textureCoordinate);\n" +
"}\n";
mProgram = GlUtil.createProgram(vertexShader, fragmentShader);
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "position");
maTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
muSamplerHandle = GLES20.glGetUniformLocation(mProgram, "uSampler");
muPosMtxHandle = GLES20.glGetUniformLocation(mProgram, "uPosMtx");
Matrix.scaleM(mSymmetryMtx, 0, -1, 1, 1);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_BLEND);
GlUtil.checkGlError("initGL_E");
}
private void saveRenderState() {
mSavedEglDisplay = EGL14.eglGetCurrentDisplay();
mSavedEglDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
mSavedEglReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ);
mSavedEglContext = EGL14.eglGetCurrentContext();
}
private void restoreRenderState() {
if (!EGL14.eglMakeCurrent(
mSavedEglDisplay,
mSavedEglDrawSurface,
mSavedEglReadSurface,
mSavedEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
}
}