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