/* * Copyright (C) 2015 iChano incorporation's Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.zhongyun.viewer.video; import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import com.ichano.rvs.viewer.Media; import com.zhongyun.viewer.utils.CommUtil; import android.app.Activity; import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.YuvImage; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView.Renderer; import android.os.Handler; import android.os.Message; import android.util.Log; public class MyRenderer implements Renderer{ private static final String TAG = "MyRenderer"; private byte[] y,u,v; int connectCount; Handler handler; private int[] yTextureNames; private int[] uTextureNames; private int[] vTextureNames; private FloatBuffer mVertices; private ShortBuffer mIndices; private int mProgramObject; private int mPositionLoc; private int mTexCoordLoc; private int yTexture; private int uTexture; private int vTexture; private final float[] mVerticesData = { -1.f, 1.f, 0.0f, // Position 0 0.0f, 0.0f, // TexCoord 0 -1.f, -1.f, 0.0f, // Position 1 0.0f, 1.0f, // TexCoord 1 1.f, -1.f, 0.0f, // Position 2 1.0f, 1.0f, // TexCoord 2 1.f, 1.f, 0.0f, // Position 3 1.0f, 0.0f // TexCoord 3 }; private short[] mIndicesData = { 0, 1, 2, 0, 2, 3 }; private ByteBuffer yBuffer = null; private ByteBuffer uBuffer = null; private ByteBuffer vBuffer = null; private IntBuffer frameBuffer; private IntBuffer renderBuffer; private IntBuffer parameterBufferWidth; private IntBuffer parameterBufferHeigth; public int display_w, display_h, play_pos_x, play_pos_y,gl_w,gl_h,pixelsWidth,yuv_w,yuv_h; String FRAG_SHADER = "varying lowp vec2 tc;\n" + "uniform sampler2D SamplerY;\n" + "uniform sampler2D SamplerU;\n" + "uniform sampler2D SamplerV;\n" + "void main(void)\n" + "{\n" + "mediump vec3 yuv;\n" + "lowp vec3 rgb;\n" + "yuv.x = texture2D(SamplerY, tc).r;\n" + "yuv.y = texture2D(SamplerU, tc).r - 0.5;\n" + "yuv.z = texture2D(SamplerV, tc).r - 0.5;\n" + "rgb = mat3( 1, 1, 1,\n" + "0, -0.39465, 2.03211,\n" + "1.13983, -0.58060, 0) * yuv;\n" + "gl_FragColor = vec4(rgb, 1);\n" + "}\n"; String VERTEX_SHADER = "attribute vec4 vPosition;\n" + "attribute vec2 a_texCoord;\n" + "varying vec2 tc;\n" + "void main()\n" + "{\n" + "gl_Position = vPosition;\n" + "tc = a_texCoord;\n" + "}\n"; private GLSurfaceView glSurfaceView; // ////////////////////new avs related////////////////////// // private boolean isNewAvs; private long mediaStreamId; private long decoderId; private Media media; private AtomicInteger timestamp = new AtomicInteger(0);// 解码录制视频时的时间戳,毫秒 public boolean isShowVideo = true,isCloseStream = false; public MyRenderer(Context context, long handle, Media media, Handler handler) { this.mediaStreamId = handle; this.media = media; this.handler = handler; mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); mVertices.put(mVerticesData).position(0); mIndices = ByteBuffer.allocateDirect(mIndicesData.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer(); mIndices.put(mIndicesData).position(0); if (CommUtil.getPixelsWidth((Activity) context) > CommUtil.getPixelsHeight((Activity) context)) { pixelsWidth = CommUtil.getPixelsHeight((Activity) context); } else { pixelsWidth = CommUtil.getPixelsWidth((Activity) context); } initYUV(); } private void initYUV() { try{ y = new byte[1280 * 720]; u = new byte[640 * 360]; v = new byte[640 * 360]; Arrays.fill(y, 0, 1280 * 720, (byte) 0); Arrays.fill(u, 0, 640 * 360, (byte) 128); Arrays.fill(v, 0, 640 * 360, (byte) 128); }catch(OutOfMemoryError e){ Log.e("renderer", "OutOfMemoryError"); handler.sendEmptyMessage(1005); } } public void setStreamDecoder(long decoderId, int width, int height) { Log.d("media", "decoderId:" + decoderId + ",width:" + width + ",height:" + height); this.decoderId = decoderId; yuv_w = width; yuv_h = height; } /*public int getCurTime() { return timestamp.get(); }*/ public void setCurTime(int time){ //timestamp.set(time); Message message = Message.obtain(); message.what = 1099; message.obj = time; handler.sendMessage(message); } public void setGlSurfaceView(GLSurfaceView glSurfaceView) { this.glSurfaceView = glSurfaceView; } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glActiveTexture(GLES20.GL_ACTIVE_TEXTURE); try { gl_w = width; gl_h = height; if (yuv_w == 0) { GLES20.glViewport(0, 0, width, height); } else { float w_h = (float) width / height; if (yuv_h * w_h > yuv_w) { display_w = height * yuv_w / yuv_h; display_h = height; play_pos_x = (width - display_w) / 2; play_pos_y = 0; } else { display_w = width; display_h = width * yuv_h / yuv_w; play_pos_x = 0; play_pos_y = (gl_h - display_h) / 2; } GLES20.glViewport(play_pos_x, play_pos_y, display_w, display_h); if (pixelsWidth == display_h) { GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); } else { GLES20.glClearColor(0.957f, 0.937f, 0.921f, 0.0f); } if (glSurfaceView != null) { glSurfaceView.getLayoutParams().width = display_w; glSurfaceView.getLayoutParams().height = display_h; } handler.sendEmptyMessage(8000); } } catch (Exception e) { e.printStackTrace(); } } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { frameBuffer = IntBuffer.allocate(1); renderBuffer = IntBuffer.allocate(1); GLES20.glEnable(GLES20.GL_TEXTURE_2D); GLES20.glGenFramebuffers(1, frameBuffer); GLES20.glGenRenderbuffers(1, renderBuffer); GLES20.glActiveTexture(GLES20.GL_ACTIVE_TEXTURE); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer.get(0)); GLES20.glClear(0); GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBuffer.get(0)); GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, 1280, 720); parameterBufferHeigth = IntBuffer.allocate(1); parameterBufferWidth = IntBuffer.allocate(1); GLES20.glGetRenderbufferParameteriv(GLES20.GL_RENDERBUFFER, GLES20.GL_RENDERBUFFER_WIDTH, parameterBufferWidth); GLES20.glGetRenderbufferParameteriv(GLES20.GL_RENDERBUFFER, GLES20.GL_RENDERBUFFER_HEIGHT, parameterBufferHeigth); GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_RENDERBUFFER, renderBuffer.get(0)); if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { System.out.println("gl frame buffer status != frame buffer complete"); } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glClear(0); mProgramObject = loadProgram(VERTEX_SHADER, FRAG_SHADER); mPositionLoc = GLES20.glGetAttribLocation(mProgramObject, "vPosition"); mTexCoordLoc = GLES20.glGetAttribLocation(mProgramObject, "a_texCoord"); GLES20.glEnable(GLES20.GL_TEXTURE_2D); yTexture = GLES20.glGetUniformLocation(mProgramObject, "SamplerY"); yTextureNames = new int[1]; GLES20.glGenTextures(1, yTextureNames, 0); GLES20.glEnable(GLES20.GL_TEXTURE_2D); uTexture = GLES20.glGetUniformLocation(mProgramObject, "SamplerU"); uTextureNames = new int[1]; GLES20.glGenTextures(1, uTextureNames, 0); GLES20.glEnable(GLES20.GL_TEXTURE_2D); vTexture = GLES20.glGetUniformLocation(mProgramObject, "SamplerV"); vTextureNames = new int[1]; GLES20.glGenTextures(1, vTextureNames, 0); GLES20.glClearColor(0.957f, 0.937f, 0.921f, 0.0f); } boolean isFirst = true; int count = 0; int[] getWH = new int[2]; @Override public void onDrawFrame(GL10 gl) { if (isShowVideo) { if(!isCloseStream){ if (decoderId == 0||yuv_w==0||yuv_h==0) return; int tmp = 0; if(null != y && null != media){ tmp = media.getVideoDecodedData(mediaStreamId, decoderId, y, u, v,getWH); } if (tmp > 0) { setCurTime(tmp); } if (getWH[0] != 0 && isFirst) { yuv_w = getWH[0]; yuv_h = getWH[1]; onSurfaceChanged(gl, gl_w, gl_h); isFirst = false; handler.sendEmptyMessage(8001); } if (!isFirst) { try{ yBuffer = ByteBuffer.wrap(y); uBuffer = ByteBuffer.wrap(u); vBuffer = ByteBuffer.wrap(v); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(mProgramObject); mVertices.position(0); GLES20.glVertexAttribPointer(mPositionLoc, 3, GLES20.GL_FLOAT, false, 5 * 4, mVertices); mVertices.position(3); GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 5 * 4, mVertices); GLES20.glEnableVertexAttribArray(mPositionLoc); GLES20.glEnableVertexAttribArray(mTexCoordLoc); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yTextureNames[0]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, yuv_w, yuv_h, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yBuffer); 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.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glUniform1i(yTexture, 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, uTextureNames[0]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, yuv_w / 2, yuv_h / 2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, uBuffer); 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.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glUniform1i(uTexture, 1); GLES20.glActiveTexture(GLES20.GL_TEXTURE2); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, vTextureNames[0]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, yuv_w / 2, yuv_h / 2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, vBuffer); 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.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glUniform1i(vTexture, 2); GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, mIndices); GLES20.glUseProgram(0); }catch(Exception e){} } } } } private static String readTextFileFromRawResource(final Context context, final int resourceId) { final InputStream inputStream = context.getResources().openRawResource(resourceId); final InputStreamReader inputStreamReader = new InputStreamReader(inputStream); final BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String nextLine; final StringBuilder body = new StringBuilder(); try { while ((nextLine = bufferedReader.readLine()) != null) { body.append(nextLine); body.append('\n'); } } catch (IOException e) { return null; } return body.toString(); } public static int loadShader(int type, String shaderSrc) { int shader; int[] compiled = new int[1]; // Create the shader object shader = GLES20.glCreateShader(type); if (shader == 0) { return 0; } // Load the shader source GLES20.glShaderSource(shader, shaderSrc); // Compile the shader GLES20.glCompileShader(shader); // Check the compile status GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { GLES20.glDeleteShader(shader); return 0; } return shader; } public static int loadProgram(String vertShaderSrc, String fragShaderSrc) { int vertexShader; int fragmentShader; int programObject; int[] linked = new int[1]; vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertShaderSrc); if (vertexShader == 0) { return 0; } fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragShaderSrc); if (fragmentShader == 0) { GLES20.glDeleteShader(vertexShader); return 0; } // Create the program object programObject = GLES20.glCreateProgram(); if (programObject == 0) { return 0; } GLES20.glAttachShader(programObject, vertexShader); GLES20.glAttachShader(programObject, fragmentShader); GLES20.glLinkProgram(programObject); GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linked, 0); if (linked[0] == 0) { GLES20.glDeleteProgram(programObject); return 0; } // Free up no longer needed shader resources GLES20.glDeleteShader(vertexShader); GLES20.glDeleteShader(fragmentShader); return programObject; } public void rawByteArray2RGBABitmap2(FileOutputStream b) { int yuvi = yuv_w * yuv_h; int uvi = 0; byte[] yuv = new byte[yuv_w * yuv_h * 3 / 2]; System.arraycopy(y, 0, yuv, 0, yuvi); for (int i = 0; i < yuv_h / 2; i++) { for (int j = 0; j < yuv_w / 2; j++) { yuv[yuvi++] = v[uvi]; yuv[yuvi++] = u[uvi++]; } } YuvImage yuvImage = new YuvImage(yuv, ImageFormat.NV21, yuv_w, yuv_h, null); Rect rect = new Rect(0, 0, yuv_w, yuv_h); yuvImage.compressToJpeg(rect, 100, b); } public void clear(){ mIndicesData = null; yBuffer = null; uBuffer = null; vBuffer = null; y = null; u = null; v = null; Log.d("media", "clear"); System.gc(); } }