fix circle video stream
							parent
							
								
									a08e801918
								
							
						
					
					
						commit
						c67f3d10ad
					
				@ -0,0 +1,379 @@
 | 
			
		||||
package com.hmg.hmgDr.util
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.res.Resources
 | 
			
		||||
import android.graphics.PixelFormat
 | 
			
		||||
import android.opengl.GLES20
 | 
			
		||||
import android.opengl.GLSurfaceView
 | 
			
		||||
import android.opengl.Matrix
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.opentok.android.BaseVideoRenderer
 | 
			
		||||
import java.nio.ByteBuffer
 | 
			
		||||
import java.nio.ByteOrder
 | 
			
		||||
import java.nio.FloatBuffer
 | 
			
		||||
import java.nio.ShortBuffer
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock
 | 
			
		||||
import javax.microedition.khronos.egl.EGLConfig
 | 
			
		||||
import javax.microedition.khronos.opengles.GL10
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
* https://nhancv.medium.com/android-how-to-make-a-circular-view-as-a-thumbnail-of-opentok-27992aee15c9
 | 
			
		||||
* to solve make circle video stream
 | 
			
		||||
* */
 | 
			
		||||
 | 
			
		||||
class DynamicVideoRenderer(private val mContext: Context) : BaseVideoRenderer() {
 | 
			
		||||
    private val mView: GLSurfaceView = GLSurfaceView(mContext)
 | 
			
		||||
    private val mRenderer: MyRenderer
 | 
			
		||||
 | 
			
		||||
    interface DynamicVideoRendererMetadataListener {
 | 
			
		||||
        fun onMetadataReady(metadata: ByteArray?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setDynamicVideoRendererMetadataListener(metadataListener: DynamicVideoRendererMetadataListener?) {
 | 
			
		||||
        mRenderer.metadataListener = metadataListener
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun enableThumbnailCircle(enable: Boolean) {
 | 
			
		||||
        mRenderer.requestEnableThumbnailCircle = enable
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal class MyRenderer : GLSurfaceView.Renderer {
 | 
			
		||||
        var mTextureIds = IntArray(3)
 | 
			
		||||
        var mScaleMatrix = FloatArray(16)
 | 
			
		||||
        private val mVertexBuffer: FloatBuffer
 | 
			
		||||
        private val mTextureBuffer: FloatBuffer
 | 
			
		||||
        private val mDrawListBuffer: ShortBuffer
 | 
			
		||||
        var requestEnableThumbnailCircle = false
 | 
			
		||||
        var mVideoFitEnabled = true
 | 
			
		||||
        var mVideoDisabled = false
 | 
			
		||||
        private val mVertexIndex = shortArrayOf(0, 1, 2, 0, 2, 3) // order to draw
 | 
			
		||||
 | 
			
		||||
        // vertices
 | 
			
		||||
        private val vertexShaderCode = """uniform mat4 uMVPMatrix;attribute vec4 aPosition;
 | 
			
		||||
attribute vec2 aTextureCoord;
 | 
			
		||||
varying vec2 vTextureCoord;
 | 
			
		||||
void main() {
 | 
			
		||||
  gl_Position = uMVPMatrix * aPosition;
 | 
			
		||||
  vTextureCoord = aTextureCoord;
 | 
			
		||||
}
 | 
			
		||||
"""
 | 
			
		||||
        private val fragmentShaderCode = """precision mediump float;
 | 
			
		||||
uniform sampler2D Ytex;
 | 
			
		||||
uniform sampler2D Utex,Vtex;
 | 
			
		||||
uniform int enableCircle;
 | 
			
		||||
uniform vec2 radiusDp;
 | 
			
		||||
varying vec2 vTextureCoord;
 | 
			
		||||
void main(void) {
 | 
			
		||||
  float nx,ny,r,g,b,y,u,v;
 | 
			
		||||
  mediump vec4 txl,ux,vx;  nx=vTextureCoord[0];
 | 
			
		||||
  ny=vTextureCoord[1];
 | 
			
		||||
  y=texture2D(Ytex,vec2(nx,ny)).r;
 | 
			
		||||
  u=texture2D(Utex,vec2(nx,ny)).r;
 | 
			
		||||
  v=texture2D(Vtex,vec2(nx,ny)).r;
 | 
			
		||||
  y=1.1643*(y-0.0625);
 | 
			
		||||
  u=u-0.5;
 | 
			
		||||
  v=v-0.5;
 | 
			
		||||
  r=y+1.5958*v;
 | 
			
		||||
  g=y-0.39173*u-0.81290*v;
 | 
			
		||||
  b=y+2.017*u;
 | 
			
		||||
  if (enableCircle > 0) { 
 | 
			
		||||
    float radius = 0.5;
 | 
			
		||||
    vec4 color0 = vec4(0.0, 0.0, 0.0, 0.0);
 | 
			
		||||
    vec4 color1 = vec4(r, g, b, 1.0);
 | 
			
		||||
    vec2 st = (gl_FragCoord.xy/radiusDp.xy);    float dist = radius - distance(st,vec2(0.5));
 | 
			
		||||
    float t = 1.0;
 | 
			
		||||
    if (dist < 0.0) t = 0.0;
 | 
			
		||||
    gl_FragColor = mix(color0, color1, t);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    gl_FragColor = vec4(r, g, b, 1.0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
"""
 | 
			
		||||
        var mFrameLock = ReentrantLock()
 | 
			
		||||
        var mCurrentFrame: Frame? = null
 | 
			
		||||
        private var mProgram = 0
 | 
			
		||||
        private var mTextureWidth = 0
 | 
			
		||||
        private var mTextureHeight = 0
 | 
			
		||||
        private var mViewportWidth = 0
 | 
			
		||||
        private var mViewportHeight = 0
 | 
			
		||||
        override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
 | 
			
		||||
            gl.glClearColor(0f, 0f, 0f, 1f)
 | 
			
		||||
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
 | 
			
		||||
            val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
 | 
			
		||||
                    vertexShaderCode)
 | 
			
		||||
            val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
 | 
			
		||||
                    fragmentShaderCode)
 | 
			
		||||
            mProgram = GLES20.glCreateProgram() // create empty OpenGL ES
 | 
			
		||||
            // Program
 | 
			
		||||
            GLES20.glAttachShader(mProgram, vertexShader) // add the vertex
 | 
			
		||||
            // shader to program
 | 
			
		||||
            GLES20.glAttachShader(mProgram, fragmentShader) // add the fragment
 | 
			
		||||
            // shader to
 | 
			
		||||
            // program
 | 
			
		||||
            GLES20.glLinkProgram(mProgram)
 | 
			
		||||
            val positionHandle = GLES20.glGetAttribLocation(mProgram,
 | 
			
		||||
                    "aPosition")
 | 
			
		||||
            val textureHandle = GLES20.glGetAttribLocation(mProgram,
 | 
			
		||||
                    "aTextureCoord")
 | 
			
		||||
            GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
 | 
			
		||||
                    GLES20.GL_FLOAT, false, COORDS_PER_VERTEX * 4,
 | 
			
		||||
                    mVertexBuffer)
 | 
			
		||||
            GLES20.glEnableVertexAttribArray(positionHandle)
 | 
			
		||||
            GLES20.glVertexAttribPointer(textureHandle,
 | 
			
		||||
                    TEXTURECOORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
 | 
			
		||||
                    TEXTURECOORDS_PER_VERTEX * 4, mTextureBuffer)
 | 
			
		||||
            GLES20.glEnableVertexAttribArray(textureHandle)
 | 
			
		||||
            GLES20.glUseProgram(mProgram)
 | 
			
		||||
            var i = GLES20.glGetUniformLocation(mProgram, "Ytex")
 | 
			
		||||
            GLES20.glUniform1i(i, 0) /* Bind Ytex to texture unit 0 */
 | 
			
		||||
            i = GLES20.glGetUniformLocation(mProgram, "Utex")
 | 
			
		||||
            GLES20.glUniform1i(i, 1) /* Bind Utex to texture unit 1 */
 | 
			
		||||
            i = GLES20.glGetUniformLocation(mProgram, "Vtex")
 | 
			
		||||
            GLES20.glUniform1i(i, 2) /* Bind Vtex to texture unit 2 */
 | 
			
		||||
            val radiusDpLocation = GLES20.glGetUniformLocation(mProgram, "radiusDp")
 | 
			
		||||
            val radiusDp = (Resources.getSystem().displayMetrics.density * THUMBNAIL_SIZE).toInt()
 | 
			
		||||
            GLES20.glUniform2f(radiusDpLocation, radiusDp.toFloat(), radiusDp.toFloat())
 | 
			
		||||
            mTextureWidth = 0
 | 
			
		||||
            mTextureHeight = 0
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun enableThumbnailCircle(enable: Boolean) {
 | 
			
		||||
            GLES20.glUseProgram(mProgram)
 | 
			
		||||
            val enableCircleLocation = GLES20.glGetUniformLocation(mProgram, "enableCircle")
 | 
			
		||||
            GLES20.glUniform1i(enableCircleLocation, if (enable) 1 else 0)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun setupTextures(frame: Frame) {
 | 
			
		||||
            if (mTextureIds[0] != 0) {
 | 
			
		||||
                GLES20.glDeleteTextures(3, mTextureIds, 0)
 | 
			
		||||
            }
 | 
			
		||||
            GLES20.glGenTextures(3, mTextureIds, 0)
 | 
			
		||||
            val w = frame.width
 | 
			
		||||
            val h = frame.height
 | 
			
		||||
            val hw = w + 1 shr 1
 | 
			
		||||
            val hh = h + 1 shr 1
 | 
			
		||||
            initializeTexture(GLES20.GL_TEXTURE0, mTextureIds[0], w, h)
 | 
			
		||||
            initializeTexture(GLES20.GL_TEXTURE1, mTextureIds[1], hw, hh)
 | 
			
		||||
            initializeTexture(GLES20.GL_TEXTURE2, mTextureIds[2], hw, hh)
 | 
			
		||||
            mTextureWidth = frame.width
 | 
			
		||||
            mTextureHeight = frame.height
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun updateTextures(frame: Frame) {
 | 
			
		||||
            val width = frame.width
 | 
			
		||||
            val height = frame.height
 | 
			
		||||
            val half_width = width + 1 shr 1
 | 
			
		||||
            val half_height = height + 1 shr 1
 | 
			
		||||
            val y_size = width * height
 | 
			
		||||
            val uv_size = half_width * half_height
 | 
			
		||||
            val bb = frame.buffer
 | 
			
		||||
            // If we are reusing this frame, make sure we reset position and
 | 
			
		||||
            // limit
 | 
			
		||||
            bb.clear()
 | 
			
		||||
            if (bb.remaining() == y_size + uv_size * 2) {
 | 
			
		||||
                bb.position(0)
 | 
			
		||||
                GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1)
 | 
			
		||||
                GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 1)
 | 
			
		||||
                GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[0])
 | 
			
		||||
                GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width,
 | 
			
		||||
                        height, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
 | 
			
		||||
                        bb)
 | 
			
		||||
                bb.position(y_size)
 | 
			
		||||
                GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[1])
 | 
			
		||||
                GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
 | 
			
		||||
                        half_width, half_height, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_BYTE, bb)
 | 
			
		||||
                bb.position(y_size + uv_size)
 | 
			
		||||
                GLES20.glActiveTexture(GLES20.GL_TEXTURE2)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[2])
 | 
			
		||||
                GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
 | 
			
		||||
                        half_width, half_height, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_BYTE, bb)
 | 
			
		||||
            } else {
 | 
			
		||||
                mTextureWidth = 0
 | 
			
		||||
                mTextureHeight = 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
 | 
			
		||||
            GLES20.glViewport(0, 0, width, height)
 | 
			
		||||
            mViewportWidth = width
 | 
			
		||||
            mViewportHeight = height
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var metadataListener: DynamicVideoRendererMetadataListener? = null
 | 
			
		||||
        override fun onDrawFrame(gl: GL10) {
 | 
			
		||||
            gl.glClearColor(0f, 0f, 0f, 0f)
 | 
			
		||||
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
 | 
			
		||||
            mFrameLock.lock()
 | 
			
		||||
            if (mCurrentFrame != null && !mVideoDisabled) {
 | 
			
		||||
                GLES20.glUseProgram(mProgram)
 | 
			
		||||
                if (mTextureWidth != mCurrentFrame!!.width
 | 
			
		||||
                        || mTextureHeight != mCurrentFrame!!.height) {
 | 
			
		||||
                    setupTextures(mCurrentFrame!!)
 | 
			
		||||
                }
 | 
			
		||||
                updateTextures(mCurrentFrame!!)
 | 
			
		||||
                Matrix.setIdentityM(mScaleMatrix, 0)
 | 
			
		||||
                var scaleX = 1.0f
 | 
			
		||||
                var scaleY = 1.0f
 | 
			
		||||
                val ratio = (mCurrentFrame!!.width.toFloat()
 | 
			
		||||
                        / mCurrentFrame!!.height)
 | 
			
		||||
                val vratio = mViewportWidth.toFloat() / mViewportHeight
 | 
			
		||||
                if (mVideoFitEnabled) {
 | 
			
		||||
                    if (ratio > vratio) {
 | 
			
		||||
                        scaleY = vratio / ratio
 | 
			
		||||
                    } else {
 | 
			
		||||
                        scaleX = ratio / vratio
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (ratio < vratio) {
 | 
			
		||||
                        scaleY = vratio / ratio
 | 
			
		||||
                    } else {
 | 
			
		||||
                        scaleX = ratio / vratio
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Matrix.scaleM(mScaleMatrix, 0,
 | 
			
		||||
                        scaleX * if (mCurrentFrame!!.isMirroredX) -1.0f else 1.0f,
 | 
			
		||||
                        scaleY, 1f)
 | 
			
		||||
                metadataListener?.onMetadataReady(mCurrentFrame!!.metadata)
 | 
			
		||||
                val mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,
 | 
			
		||||
                        "uMVPMatrix")
 | 
			
		||||
                GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false,
 | 
			
		||||
                        mScaleMatrix, 0)
 | 
			
		||||
                enableThumbnailCircle(requestEnableThumbnailCircle)
 | 
			
		||||
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, mVertexIndex.size,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer)
 | 
			
		||||
            } else {
 | 
			
		||||
                //black frame when video is disabled
 | 
			
		||||
                gl.glClearColor(0f, 0f, 0f, 1f)
 | 
			
		||||
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
 | 
			
		||||
            }
 | 
			
		||||
            mFrameLock.unlock()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun displayFrame(frame: Frame?) {
 | 
			
		||||
            mFrameLock.lock()
 | 
			
		||||
            if (mCurrentFrame != null) {
 | 
			
		||||
                mCurrentFrame!!.recycle()
 | 
			
		||||
            }
 | 
			
		||||
            mCurrentFrame = frame
 | 
			
		||||
            mFrameLock.unlock()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun disableVideo(b: Boolean) {
 | 
			
		||||
            mFrameLock.lock()
 | 
			
		||||
            mVideoDisabled = b
 | 
			
		||||
            if (mVideoDisabled) {
 | 
			
		||||
                if (mCurrentFrame != null) {
 | 
			
		||||
                    mCurrentFrame!!.recycle()
 | 
			
		||||
                }
 | 
			
		||||
                mCurrentFrame = null
 | 
			
		||||
            }
 | 
			
		||||
            mFrameLock.unlock()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun enableVideoFit(enableVideoFit: Boolean) {
 | 
			
		||||
            mVideoFitEnabled = enableVideoFit
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        companion object {
 | 
			
		||||
            // number of coordinates per vertex in this array
 | 
			
		||||
            const val COORDS_PER_VERTEX = 3
 | 
			
		||||
            const val TEXTURECOORDS_PER_VERTEX = 2
 | 
			
		||||
            var mXYZCoords = floatArrayOf(
 | 
			
		||||
                    -1.0f, 1.0f, 0.0f,  // top left
 | 
			
		||||
                    -1.0f, -1.0f, 0.0f,  // bottom left
 | 
			
		||||
                    1.0f, -1.0f, 0.0f,  // bottom right
 | 
			
		||||
                    1.0f, 1.0f, 0.0f // top right
 | 
			
		||||
            )
 | 
			
		||||
            var mUVCoords = floatArrayOf(0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f)
 | 
			
		||||
            fun initializeTexture(name: Int, id: Int, width: Int, height: Int) {
 | 
			
		||||
                GLES20.glActiveTexture(name)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id)
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST.toFloat())
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE.toFloat())
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE.toFloat())
 | 
			
		||||
                GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        width, height, 0, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_BYTE, null)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fun loadShader(type: Int, shaderCode: String?): Int {
 | 
			
		||||
                val shader = GLES20.glCreateShader(type)
 | 
			
		||||
                GLES20.glShaderSource(shader, shaderCode)
 | 
			
		||||
                GLES20.glCompileShader(shader)
 | 
			
		||||
                return shader
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
            val bb = ByteBuffer.allocateDirect(mXYZCoords.size * 4)
 | 
			
		||||
            bb.order(ByteOrder.nativeOrder())
 | 
			
		||||
            mVertexBuffer = bb.asFloatBuffer()
 | 
			
		||||
            mVertexBuffer.put(mXYZCoords)
 | 
			
		||||
            mVertexBuffer.position(0)
 | 
			
		||||
            val tb = ByteBuffer.allocateDirect(mUVCoords.size * 4)
 | 
			
		||||
            tb.order(ByteOrder.nativeOrder())
 | 
			
		||||
            mTextureBuffer = tb.asFloatBuffer()
 | 
			
		||||
            mTextureBuffer.put(mUVCoords)
 | 
			
		||||
            mTextureBuffer.position(0)
 | 
			
		||||
            val dlb = ByteBuffer.allocateDirect(mVertexIndex.size * 2)
 | 
			
		||||
            dlb.order(ByteOrder.nativeOrder())
 | 
			
		||||
            mDrawListBuffer = dlb.asShortBuffer()
 | 
			
		||||
            mDrawListBuffer.put(mVertexIndex)
 | 
			
		||||
            mDrawListBuffer.position(0)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onFrame(frame: Frame) {
 | 
			
		||||
        mRenderer.displayFrame(frame)
 | 
			
		||||
        mView.requestRender()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun setStyle(key: String, value: String) {
 | 
			
		||||
        if (STYLE_VIDEO_SCALE == key) {
 | 
			
		||||
            if (STYLE_VIDEO_FIT == value) {
 | 
			
		||||
                mRenderer.enableVideoFit(true)
 | 
			
		||||
            } else if (STYLE_VIDEO_FILL == value) {
 | 
			
		||||
                mRenderer.enableVideoFit(false)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onVideoPropertiesChanged(videoEnabled: Boolean) {
 | 
			
		||||
        mRenderer.disableVideo(!videoEnabled)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getView(): View {
 | 
			
		||||
        return mView
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPause() {
 | 
			
		||||
        mView.onPause()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onResume() {
 | 
			
		||||
        mView.onResume()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private const val THUMBNAIL_SIZE = 90 //in dp
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        mView.setEGLContextClientVersion(2)
 | 
			
		||||
        mView.setEGLConfigChooser(8, 8, 8, 8, 16, 0)
 | 
			
		||||
        mView.holder.setFormat(PixelFormat.TRANSLUCENT)
 | 
			
		||||
        mRenderer = MyRenderer()
 | 
			
		||||
        mView.setRenderer(mRenderer)
 | 
			
		||||
        mView.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,357 @@
 | 
			
		||||
package com.hmg.hmgDr.util
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.res.Resources
 | 
			
		||||
import android.graphics.PixelFormat
 | 
			
		||||
import android.opengl.GLES20
 | 
			
		||||
import android.opengl.GLSurfaceView
 | 
			
		||||
import android.opengl.Matrix
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.opentok.android.BaseVideoRenderer
 | 
			
		||||
import java.nio.ByteBuffer
 | 
			
		||||
import java.nio.ByteOrder
 | 
			
		||||
import java.nio.FloatBuffer
 | 
			
		||||
import java.nio.ShortBuffer
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock
 | 
			
		||||
import javax.microedition.khronos.egl.EGLConfig
 | 
			
		||||
import javax.microedition.khronos.opengles.GL10
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ThumbnailCircleVideoRenderer(private val mContext: Context) : BaseVideoRenderer() {
 | 
			
		||||
    private val mView: GLSurfaceView = GLSurfaceView(mContext)
 | 
			
		||||
    private val mRenderer: MyRenderer
 | 
			
		||||
 | 
			
		||||
    interface ThumbnailCircleVideoRendererMetadataListener {
 | 
			
		||||
        fun onMetadataReady(metadata: ByteArray?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setThumbnailCircleVideoRendererMetadataListener(metadataListener: ThumbnailCircleVideoRendererMetadataListener?) {
 | 
			
		||||
        mRenderer.metadataListener = metadataListener
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal class MyRenderer : GLSurfaceView.Renderer {
 | 
			
		||||
        var mTextureIds = IntArray(3)
 | 
			
		||||
        var mScaleMatrix = FloatArray(16)
 | 
			
		||||
        private val mVertexBuffer: FloatBuffer
 | 
			
		||||
        private val mTextureBuffer: FloatBuffer
 | 
			
		||||
        private val mDrawListBuffer: ShortBuffer
 | 
			
		||||
        var mVideoFitEnabled = true
 | 
			
		||||
        var mVideoDisabled = false
 | 
			
		||||
        private val mVertexIndex = shortArrayOf(0, 1, 2, 0, 2, 3) // order to draw
 | 
			
		||||
 | 
			
		||||
        // vertices
 | 
			
		||||
        private val vertexShaderCode = """uniform mat4 uMVPMatrix;attribute vec4 aPosition;
 | 
			
		||||
attribute vec2 aTextureCoord;
 | 
			
		||||
varying vec2 vTextureCoord;
 | 
			
		||||
void main() {
 | 
			
		||||
  gl_Position = uMVPMatrix * aPosition;
 | 
			
		||||
  vTextureCoord = aTextureCoord;
 | 
			
		||||
}
 | 
			
		||||
"""
 | 
			
		||||
        private val fragmentShaderCode = """precision mediump float;
 | 
			
		||||
uniform sampler2D Ytex;
 | 
			
		||||
uniform sampler2D Utex,Vtex;
 | 
			
		||||
uniform vec2 radiusDp;
 | 
			
		||||
varying vec2 vTextureCoord;
 | 
			
		||||
void main(void) {
 | 
			
		||||
  float nx,ny,r,g,b,y,u,v;
 | 
			
		||||
  mediump vec4 txl,ux,vx;  nx=vTextureCoord[0];
 | 
			
		||||
  ny=vTextureCoord[1];
 | 
			
		||||
  y=texture2D(Ytex,vec2(nx,ny)).r;
 | 
			
		||||
  u=texture2D(Utex,vec2(nx,ny)).r;
 | 
			
		||||
  v=texture2D(Vtex,vec2(nx,ny)).r;
 | 
			
		||||
  y=1.1643*(y-0.0625);
 | 
			
		||||
  u=u-0.5;
 | 
			
		||||
  v=v-0.5;
 | 
			
		||||
  r=y+1.5958*v;
 | 
			
		||||
  g=y-0.39173*u-0.81290*v;
 | 
			
		||||
  b=y+2.017*u;
 | 
			
		||||
  float radius = 0.5;
 | 
			
		||||
  vec4 color0 = vec4(0.0, 0.0, 0.0, 0.0);
 | 
			
		||||
  vec4 color1 = vec4(r, g, b, 1.0);
 | 
			
		||||
  vec2 st = (gl_FragCoord.xy/radiusDp.xy);  float dist = radius - distance(st,vec2(0.5));
 | 
			
		||||
  float t = 1.0;
 | 
			
		||||
  if (dist < 0.0) t = 0.0;
 | 
			
		||||
  gl_FragColor = mix(color0, color1, t);
 | 
			
		||||
}
 | 
			
		||||
"""
 | 
			
		||||
        var mFrameLock = ReentrantLock()
 | 
			
		||||
        var mCurrentFrame: Frame? = null
 | 
			
		||||
        private var mProgram = 0
 | 
			
		||||
        private var mTextureWidth = 0
 | 
			
		||||
        private var mTextureHeight = 0
 | 
			
		||||
        private var mViewportWidth = 0
 | 
			
		||||
        private var mViewportHeight = 0
 | 
			
		||||
        override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
 | 
			
		||||
            gl.glClearColor(0f, 0f, 0f, 1f)
 | 
			
		||||
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
 | 
			
		||||
            val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
 | 
			
		||||
                    vertexShaderCode)
 | 
			
		||||
            val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
 | 
			
		||||
                    fragmentShaderCode)
 | 
			
		||||
            mProgram = GLES20.glCreateProgram() // create empty OpenGL ES
 | 
			
		||||
            // Program
 | 
			
		||||
            GLES20.glAttachShader(mProgram, vertexShader) // add the vertex
 | 
			
		||||
            // shader to program
 | 
			
		||||
            GLES20.glAttachShader(mProgram, fragmentShader) // add the fragment
 | 
			
		||||
            // shader to
 | 
			
		||||
            // program
 | 
			
		||||
            GLES20.glLinkProgram(mProgram)
 | 
			
		||||
            val positionHandle = GLES20.glGetAttribLocation(mProgram,
 | 
			
		||||
                    "aPosition")
 | 
			
		||||
            val textureHandle = GLES20.glGetAttribLocation(mProgram,
 | 
			
		||||
                    "aTextureCoord")
 | 
			
		||||
            GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
 | 
			
		||||
                    GLES20.GL_FLOAT, false, COORDS_PER_VERTEX * 4,
 | 
			
		||||
                    mVertexBuffer)
 | 
			
		||||
            GLES20.glEnableVertexAttribArray(positionHandle)
 | 
			
		||||
            GLES20.glVertexAttribPointer(textureHandle,
 | 
			
		||||
                    TEXTURECOORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
 | 
			
		||||
                    TEXTURECOORDS_PER_VERTEX * 4, mTextureBuffer)
 | 
			
		||||
            GLES20.glEnableVertexAttribArray(textureHandle)
 | 
			
		||||
            GLES20.glUseProgram(mProgram)
 | 
			
		||||
            var i = GLES20.glGetUniformLocation(mProgram, "Ytex")
 | 
			
		||||
            GLES20.glUniform1i(i, 0) /* Bind Ytex to texture unit 0 */
 | 
			
		||||
            i = GLES20.glGetUniformLocation(mProgram, "Utex")
 | 
			
		||||
            GLES20.glUniform1i(i, 1) /* Bind Utex to texture unit 1 */
 | 
			
		||||
            i = GLES20.glGetUniformLocation(mProgram, "Vtex")
 | 
			
		||||
            GLES20.glUniform1i(i, 2) /* Bind Vtex to texture unit 2 */
 | 
			
		||||
            val radiusDpLocation = GLES20.glGetUniformLocation(mProgram, "radiusDp")
 | 
			
		||||
            val radiusDp = (Resources.getSystem().displayMetrics.density * THUMBNAIL_SIZE).toInt()
 | 
			
		||||
            GLES20.glUniform2f(radiusDpLocation, radiusDp.toFloat(), radiusDp.toFloat())
 | 
			
		||||
            mTextureWidth = 0
 | 
			
		||||
            mTextureHeight = 0
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun setupTextures(frame: Frame) {
 | 
			
		||||
            if (mTextureIds[0] != 0) {
 | 
			
		||||
                GLES20.glDeleteTextures(3, mTextureIds, 0)
 | 
			
		||||
            }
 | 
			
		||||
            GLES20.glGenTextures(3, mTextureIds, 0)
 | 
			
		||||
            val w = frame.width
 | 
			
		||||
            val h = frame.height
 | 
			
		||||
            val hw = w + 1 shr 1
 | 
			
		||||
            val hh = h + 1 shr 1
 | 
			
		||||
            initializeTexture(GLES20.GL_TEXTURE0, mTextureIds[0], w, h)
 | 
			
		||||
            initializeTexture(GLES20.GL_TEXTURE1, mTextureIds[1], hw, hh)
 | 
			
		||||
            initializeTexture(GLES20.GL_TEXTURE2, mTextureIds[2], hw, hh)
 | 
			
		||||
            mTextureWidth = frame.width
 | 
			
		||||
            mTextureHeight = frame.height
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun updateTextures(frame: Frame) {
 | 
			
		||||
            val width = frame.width
 | 
			
		||||
            val height = frame.height
 | 
			
		||||
            val half_width = width + 1 shr 1
 | 
			
		||||
            val half_height = height + 1 shr 1
 | 
			
		||||
            val y_size = width * height
 | 
			
		||||
            val uv_size = half_width * half_height
 | 
			
		||||
            val bb = frame.buffer
 | 
			
		||||
            // If we are reusing this frame, make sure we reset position and
 | 
			
		||||
            // limit
 | 
			
		||||
            bb.clear()
 | 
			
		||||
            if (bb.remaining() == y_size + uv_size * 2) {
 | 
			
		||||
                bb.position(0)
 | 
			
		||||
                GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1)
 | 
			
		||||
                GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 1)
 | 
			
		||||
                GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[0])
 | 
			
		||||
                GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width,
 | 
			
		||||
                        height, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
 | 
			
		||||
                        bb)
 | 
			
		||||
                bb.position(y_size)
 | 
			
		||||
                GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[1])
 | 
			
		||||
                GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
 | 
			
		||||
                        half_width, half_height, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_BYTE, bb)
 | 
			
		||||
                bb.position(y_size + uv_size)
 | 
			
		||||
                GLES20.glActiveTexture(GLES20.GL_TEXTURE2)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[2])
 | 
			
		||||
                GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
 | 
			
		||||
                        half_width, half_height, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_BYTE, bb)
 | 
			
		||||
            } else {
 | 
			
		||||
                mTextureWidth = 0
 | 
			
		||||
                mTextureHeight = 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
 | 
			
		||||
            GLES20.glViewport(0, 0, width, height)
 | 
			
		||||
            mViewportWidth = width
 | 
			
		||||
            mViewportHeight = height
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var metadataListener: ThumbnailCircleVideoRendererMetadataListener? = null
 | 
			
		||||
        override fun onDrawFrame(gl: GL10) {
 | 
			
		||||
            gl.glClearColor(0f, 0f, 0f, 0f)
 | 
			
		||||
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
 | 
			
		||||
            mFrameLock.lock()
 | 
			
		||||
            if (mCurrentFrame != null && !mVideoDisabled) {
 | 
			
		||||
                GLES20.glUseProgram(mProgram)
 | 
			
		||||
                if (mTextureWidth != mCurrentFrame!!.width
 | 
			
		||||
                        || mTextureHeight != mCurrentFrame!!.height) {
 | 
			
		||||
                    setupTextures(mCurrentFrame!!)
 | 
			
		||||
                }
 | 
			
		||||
                updateTextures(mCurrentFrame!!)
 | 
			
		||||
                Matrix.setIdentityM(mScaleMatrix, 0)
 | 
			
		||||
                var scaleX = 1.0f
 | 
			
		||||
                var scaleY = 1.0f
 | 
			
		||||
                val ratio = (mCurrentFrame!!.width.toFloat()
 | 
			
		||||
                        / mCurrentFrame!!.height)
 | 
			
		||||
                val vratio = mViewportWidth.toFloat() / mViewportHeight
 | 
			
		||||
                if (mVideoFitEnabled) {
 | 
			
		||||
                    if (ratio > vratio) {
 | 
			
		||||
                        scaleY = vratio / ratio
 | 
			
		||||
                    } else {
 | 
			
		||||
                        scaleX = ratio / vratio
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (ratio < vratio) {
 | 
			
		||||
                        scaleY = vratio / ratio
 | 
			
		||||
                    } else {
 | 
			
		||||
                        scaleX = ratio / vratio
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Matrix.scaleM(mScaleMatrix, 0,
 | 
			
		||||
                        scaleX * if (mCurrentFrame!!.isMirroredX) -1.0f else 1.0f,
 | 
			
		||||
                        scaleY, 1f)
 | 
			
		||||
                metadataListener?.onMetadataReady(mCurrentFrame!!.metadata)
 | 
			
		||||
                val mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,
 | 
			
		||||
                        "uMVPMatrix")
 | 
			
		||||
                GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false,
 | 
			
		||||
                        mScaleMatrix, 0)
 | 
			
		||||
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, mVertexIndex.size,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer)
 | 
			
		||||
            } else {
 | 
			
		||||
                //black frame when video is disabled
 | 
			
		||||
                gl.glClearColor(0f, 0f, 0f, 1f)
 | 
			
		||||
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
 | 
			
		||||
            }
 | 
			
		||||
            mFrameLock.unlock()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun displayFrame(frame: Frame?) {
 | 
			
		||||
            mFrameLock.lock()
 | 
			
		||||
            if (mCurrentFrame != null) {
 | 
			
		||||
                mCurrentFrame!!.recycle()
 | 
			
		||||
            }
 | 
			
		||||
            mCurrentFrame = frame
 | 
			
		||||
            mFrameLock.unlock()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun disableVideo(b: Boolean) {
 | 
			
		||||
            mFrameLock.lock()
 | 
			
		||||
            mVideoDisabled = b
 | 
			
		||||
            if (mVideoDisabled) {
 | 
			
		||||
                if (mCurrentFrame != null) {
 | 
			
		||||
                    mCurrentFrame!!.recycle()
 | 
			
		||||
                }
 | 
			
		||||
                mCurrentFrame = null
 | 
			
		||||
            }
 | 
			
		||||
            mFrameLock.unlock()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun enableVideoFit(enableVideoFit: Boolean) {
 | 
			
		||||
            mVideoFitEnabled = enableVideoFit
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        companion object {
 | 
			
		||||
            // number of coordinates per vertex in this array
 | 
			
		||||
            const val COORDS_PER_VERTEX = 3
 | 
			
		||||
            const val TEXTURECOORDS_PER_VERTEX = 2
 | 
			
		||||
            var mXYZCoords = floatArrayOf(
 | 
			
		||||
                    -1.0f, 1.0f, 0.0f,  // top left
 | 
			
		||||
                    -1.0f, -1.0f, 0.0f,  // bottom left
 | 
			
		||||
                    1.0f, -1.0f, 0.0f,  // bottom right
 | 
			
		||||
                    1.0f, 1.0f, 0.0f // top right
 | 
			
		||||
            )
 | 
			
		||||
            var mUVCoords = floatArrayOf(0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f)
 | 
			
		||||
            fun initializeTexture(name: Int, id: Int, width: Int, height: Int) {
 | 
			
		||||
                GLES20.glActiveTexture(name)
 | 
			
		||||
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id)
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST.toFloat())
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE.toFloat())
 | 
			
		||||
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
 | 
			
		||||
                        GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE.toFloat())
 | 
			
		||||
                GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        width, height, 0, GLES20.GL_LUMINANCE,
 | 
			
		||||
                        GLES20.GL_UNSIGNED_BYTE, null)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fun loadShader(type: Int, shaderCode: String?): Int {
 | 
			
		||||
                val shader = GLES20.glCreateShader(type)
 | 
			
		||||
                GLES20.glShaderSource(shader, shaderCode)
 | 
			
		||||
                GLES20.glCompileShader(shader)
 | 
			
		||||
                return shader
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
            val bb = ByteBuffer.allocateDirect(mXYZCoords.size * 4)
 | 
			
		||||
            bb.order(ByteOrder.nativeOrder())
 | 
			
		||||
            mVertexBuffer = bb.asFloatBuffer()
 | 
			
		||||
            mVertexBuffer.put(mXYZCoords)
 | 
			
		||||
            mVertexBuffer.position(0)
 | 
			
		||||
            val tb = ByteBuffer.allocateDirect(mUVCoords.size * 4)
 | 
			
		||||
            tb.order(ByteOrder.nativeOrder())
 | 
			
		||||
            mTextureBuffer = tb.asFloatBuffer()
 | 
			
		||||
            mTextureBuffer.put(mUVCoords)
 | 
			
		||||
            mTextureBuffer.position(0)
 | 
			
		||||
            val dlb = ByteBuffer.allocateDirect(mVertexIndex.size * 2)
 | 
			
		||||
            dlb.order(ByteOrder.nativeOrder())
 | 
			
		||||
            mDrawListBuffer = dlb.asShortBuffer()
 | 
			
		||||
            mDrawListBuffer.put(mVertexIndex)
 | 
			
		||||
            mDrawListBuffer.position(0)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onFrame(frame: Frame) {
 | 
			
		||||
        mRenderer.displayFrame(frame)
 | 
			
		||||
        mView.requestRender()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun setStyle(key: String, value: String) {
 | 
			
		||||
        if (STYLE_VIDEO_SCALE == key) {
 | 
			
		||||
            if (STYLE_VIDEO_FIT == value) {
 | 
			
		||||
                mRenderer.enableVideoFit(true)
 | 
			
		||||
            } else if (STYLE_VIDEO_FILL == value) {
 | 
			
		||||
                mRenderer.enableVideoFit(false)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onVideoPropertiesChanged(videoEnabled: Boolean) {
 | 
			
		||||
        mRenderer.disableVideo(!videoEnabled)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getView(): View {
 | 
			
		||||
        return mView
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPause() {
 | 
			
		||||
        mView.onPause()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onResume() {
 | 
			
		||||
        mView.onResume()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private const val THUMBNAIL_SIZE = 90 //in dp
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        mView.setEGLContextClientVersion(2)
 | 
			
		||||
        mView.setEGLConfigChooser(8, 8, 8, 8, 16, 0)
 | 
			
		||||
        mView.holder.setFormat(PixelFormat.TRANSLUCENT)
 | 
			
		||||
        mRenderer = MyRenderer()
 | 
			
		||||
        mView.setRenderer(mRenderer)
 | 
			
		||||
        mView.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue