Update OpenTok SDK version and refactor video call handling

ipd-changes-for-vida-plus
Haroon Amjad 5 days ago
parent c46f67569c
commit 5a0bab1d6e

@ -129,7 +129,7 @@ dependencies {
//openTok
implementation 'com.opentok.android:opentok-android-sdk:2.20.1'
implementation 'com.opentok.android:opentok-android-sdk:2.35.1'
//permissions
// implementation 'pub.devrel:easypermissions:0.4.0'
// implementation project(':easypermissions')

@ -16,7 +16,7 @@ import android.util.Log
import android.widget.Toast
import androidx.annotation.NonNull
import com.google.gson.GsonBuilder
import com.hmg.hmgDr.Service.VideoStreamFloatingWidgetService
//import com.hmg.hmgDr.Service.VideoStreamFloatingWidgetService
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
import com.hmg.hmgDr.ui.VideoCallResponseListener
@ -43,7 +43,7 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
private val LAUNCH_VIDEO: Int = 1
private var serviceIntent: Intent? = null
private var videoStreamService: VideoStreamFloatingWidgetService? = null
// private var videoStreamService: VideoStreamFloatingWidgetService? = null
private var bound = false
override fun onCreate(savedInstanceState: Bundle?) {
@ -59,46 +59,46 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
this.result = result
this.call = call
when (call.method) {
"openVideoCall" -> {
val apiKey = call.argument<String>("kApiKey")
val sessionId = call.argument<String>("kSessionId")
val token = call.argument<String>("kToken")
val appLang = call.argument<String>("appLang")
val baseUrl = call.argument<String>("baseUrl")
// Session Status model
val VC_ID = call.argument<Int>("VC_ID")
val tokenID = call.argument<String>("TokenID")
val generalId = call.argument<String>("generalId")
val doctorId = call.argument<Int>("DoctorId")
val patientName = call.argument<String>("patientName")
val isRecording = call.argument<Boolean>("isRecording")
val sessionStatusModel =
GetSessionStatusModel(
VC_ID,
tokenID,
generalId,
doctorId,
patientName,
isRecording!!
)
openVideoCall(apiKey, sessionId, token, appLang, baseUrl, sessionStatusModel)
}
"closeVideoCall" -> {
videoStreamService?.closeVideoCall()
}
else -> {
result.notImplemented()
}
}
// this.result = result
// this.call = call
//
// when (call.method) {
// "openVideoCall" -> {
// val apiKey = call.argument<String>("kApiKey")
// val sessionId = call.argument<String>("kSessionId")
// val token = call.argument<String>("kToken")
// val appLang = call.argument<String>("appLang")
// val baseUrl = call.argument<String>("baseUrl")
//
// // Session Status model
// val VC_ID = call.argument<Int>("VC_ID")
// val tokenID = call.argument<String>("TokenID")
// val generalId = call.argument<String>("generalId")
// val doctorId = call.argument<Int>("DoctorId")
// val patientName = call.argument<String>("patientName")
// val isRecording = call.argument<Boolean>("isRecording")
//
// val sessionStatusModel =
// GetSessionStatusModel(
// VC_ID,
// tokenID,
// generalId,
// doctorId,
// patientName,
// isRecording!!
// )
//
//
// openVideoCall(apiKey, sessionId, token, appLang, baseUrl, sessionStatusModel)
//
// }
// "closeVideoCall" -> {
// videoStreamService?.closeVideoCall()
// }
// else -> {
// result.notImplemented()
// }
// }
}
private fun openVideoCall(
@ -110,24 +110,24 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
sessionStatusModel: GetSessionStatusModel
) {
val arguments = Bundle()
arguments.putString("apiKey", apiKey)
arguments.putString("sessionId", sessionId)
arguments.putString("token", token)
arguments.putString("appLang", appLang)
arguments.putString("baseUrl", baseUrl)
arguments.putParcelable("sessionStatusModel", sessionStatusModel)
// start service
// serviceIntent = Intent(this@MainActivity, VideoStreamContainerService::class.java)
if (videoStreamService == null || videoStreamService?.serviceRunning == false) {
serviceIntent = Intent(this@MainActivity, VideoStreamFloatingWidgetService::class.java)
serviceIntent?.run {
putExtras(arguments)
action = VideoStreamFloatingWidgetService.ACTION_START_CALL
}
checkFloatingWidgetPermission()
}
// val arguments = Bundle()
// arguments.putString("apiKey", apiKey)
// arguments.putString("sessionId", sessionId)
// arguments.putString("token", token)
// arguments.putString("appLang", appLang)
// arguments.putString("baseUrl", baseUrl)
// arguments.putParcelable("sessionStatusModel", sessionStatusModel)
//
// // start service
//// serviceIntent = Intent(this@MainActivity, VideoStreamContainerService::class.java)
// if (videoStreamService == null || videoStreamService?.serviceRunning == false) {
// serviceIntent = Intent(this@MainActivity, VideoStreamFloatingWidgetService::class.java)
// serviceIntent?.run {
// putExtras(arguments)
// action = VideoStreamFloatingWidgetService.ACTION_START_CALL
// }
// checkFloatingWidgetPermission()
// }
}
private fun checkFloatingWidgetPermission() {
@ -212,28 +212,29 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
}
override fun minimizeVideoEvent(isMinimize: Boolean) {
if (isMinimize)
methodChannel.invokeMethod("onCallConnected", null)
else {
methodChannel.invokeMethod("onCallDisconnected", null)
unbindService()
videoStreamService?.serviceRunning = false
videoStreamService = null
}
// if (isMinimize)
// methodChannel.invokeMethod("onCallConnected", null)
// else {
// methodChannel.invokeMethod("onCallDisconnected", null)
// unbindService()
// videoStreamService?.serviceRunning = false
// videoStreamService = null
// }
}
override fun onBackPressed() {
if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
videoStreamService!!.onMinimizedClicked()
} else {
super.onBackPressed()
}
super.onBackPressed()
// if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
// videoStreamService!!.onMinimizedClicked()
// } else {
// }
}
override fun onPause() {
if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
videoStreamService!!.onMinimizedClicked()
}
// if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
// videoStreamService!!.onMinimizedClicked()
// }
super.onPause()
}
@ -248,32 +249,32 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
// }
private fun bindService() {
serviceIntent?.run {
if (videoStreamService != null && !videoStreamService!!.serviceRunning) {
startService(this)
}
bindService(this, serviceConnection, Context.BIND_AUTO_CREATE)
}
// serviceIntent?.run {
// if (videoStreamService != null && !videoStreamService!!.serviceRunning) {
// startService(this)
// }
// bindService(this, serviceConnection, Context.BIND_AUTO_CREATE)
// }
}
private fun unbindService() {
if (bound) {
videoStreamService?.videoCallResponseListener = null // unregister
unbindService(serviceConnection)
bound = false
}
// if (bound) {
// videoStreamService?.videoCallResponseListener = null // unregister
// unbindService(serviceConnection)
// bound = false
// }
}
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
// val binder: VideoStreamContainerService.VideoStreamBinder =
// service as VideoStreamContainerService.VideoStreamBinder
val binder: VideoStreamFloatingWidgetService.VideoStreamBinder =
service as VideoStreamFloatingWidgetService.VideoStreamBinder
videoStreamService = binder.service
bound = true
videoStreamService!!.videoCallResponseListener = this@MainActivity // register
videoStreamService?.serviceRunning = true
//// val binder: VideoStreamContainerService.VideoStreamBinder =
//// service as VideoStreamContainerService.VideoStreamBinder
// val binder: VideoStreamFloatingWidgetService.VideoStreamBinder =
// service as VideoStreamFloatingWidgetService.VideoStreamBinder
// videoStreamService = binder.service
// bound = true
// videoStreamService!!.videoCallResponseListener = this@MainActivity // register
// videoStreamService?.serviceRunning = true
}
override fun onServiceDisconnected(name: ComponentName?) {

@ -1,91 +1,91 @@
package com.hmg.hmgDr.Service
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import com.hmg.hmgDr.MainActivity
import com.hmg.hmgDr.ui.VideoCallResponseListener
import com.hmg.hmgDr.ui.fragment.VideoCallFragment
class VideoStreamContainerService : Service(), VideoCallResponseListener {
var videoCallResponseListener: VideoCallResponseListener? = null
var mActivity: MainActivity? = null
set(value) {
field = value
if (field != null) {
setDialogFragment()
}
}
var arguments: Bundle? = null
var serviceRunning: Boolean = false
private val serviceBinder: IBinder = VideoStreamBinder()
private var dialogFragment: VideoCallFragment? = null
override fun onBind(intent: Intent?): IBinder {
return serviceBinder
}
private fun setDialogFragment() {
mActivity?.run {
if (dialogFragment == null) {
val transaction = supportFragmentManager.beginTransaction()
dialogFragment = VideoCallFragment.newInstance(arguments!!)
dialogFragment?.let {
it.setCallListener(this@VideoStreamContainerService)
it.isCancelable = true
if (it.isAdded) {
it.dismiss()
} else {
it.show(transaction, "dialog")
}
}
} else if (!dialogFragment!!.isVisible) {
val transaction = supportFragmentManager.beginTransaction()
dialogFragment!!.show(transaction, "dialog")
} else {
// don't do anything
}
}
}
fun closeVideoCall(){
dialogFragment?.onCallClicked()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null && intent.extras != null) {
arguments = intent.extras
}
// Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
// Toast.makeText(this, "Service destroyed by user.", Toast.LENGTH_LONG).show()
}
inner class VideoStreamBinder : Binder() {
val service: VideoStreamContainerService
get() = this@VideoStreamContainerService
}
override fun onCallFinished(resultCode: Int, intent: Intent?) {
dialogFragment = null
videoCallResponseListener?.onCallFinished(resultCode, intent)
}
override fun errorHandle(message: String) {
dialogFragment = null
// Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
override fun minimizeVideoEvent(isMinimize: Boolean) {
videoCallResponseListener?.minimizeVideoEvent(isMinimize)
}
}
//package com.hmg.hmgDr.Service
//
//import android.app.Service
//import android.content.Intent
//import android.os.Binder
//import android.os.Bundle
//import android.os.IBinder
//import com.hmg.hmgDr.MainActivity
////import com.hmg.hmgDr.ui.VideoCallResponseListener
////import com.hmg.hmgDr.ui.fragment.VideoCallFragment
//
//class VideoStreamContainerService : Service(), VideoCallResponseListener {
//
//// var videoCallResponseListener: VideoCallResponseListener? = null
//// var mActivity: MainActivity? = null
//// set(value) {
//// field = value
//// if (field != null) {
//// setDialogFragment()
//// }
//// }
//// var arguments: Bundle? = null
//// var serviceRunning: Boolean = false
////
////
//// private val serviceBinder: IBinder = VideoStreamBinder()
//// private var dialogFragment: VideoCallFragment? = null
////
//// override fun onBind(intent: Intent?): IBinder {
//// return serviceBinder
//// }
////
//// private fun setDialogFragment() {
//// mActivity?.run {
//// if (dialogFragment == null) {
//// val transaction = supportFragmentManager.beginTransaction()
//// dialogFragment = VideoCallFragment.newInstance(arguments!!)
//// dialogFragment?.let {
//// it.setCallListener(this@VideoStreamContainerService)
//// it.isCancelable = true
//// if (it.isAdded) {
//// it.dismiss()
//// } else {
//// it.show(transaction, "dialog")
//// }
//// }
//// } else if (!dialogFragment!!.isVisible) {
//// val transaction = supportFragmentManager.beginTransaction()
//// dialogFragment!!.show(transaction, "dialog")
//// } else {
//// // don't do anything
//// }
//// }
//// }
////
//// fun closeVideoCall(){
//// dialogFragment?.onCallClicked()
//// }
////
//// override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//// if (intent != null && intent.extras != null) {
//// arguments = intent.extras
//// }
////// Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show()
//// return START_STICKY
//// }
////
//// override fun onDestroy() {
//// super.onDestroy()
////// Toast.makeText(this, "Service destroyed by user.", Toast.LENGTH_LONG).show()
//// }
////
//// inner class VideoStreamBinder : Binder() {
//// val service: VideoStreamContainerService
//// get() = this@VideoStreamContainerService
//// }
////
//// override fun onCallFinished(resultCode: Int, intent: Intent?) {
//// dialogFragment = null
//// videoCallResponseListener?.onCallFinished(resultCode, intent)
//// }
////
//// override fun errorHandle(message: String) {
//// dialogFragment = null
////// Toast.makeText(this, message, Toast.LENGTH_LONG).show()
//// }
////
//// override fun minimizeVideoEvent(isMinimize: Boolean) {
//// videoCallResponseListener?.minimizeVideoEvent(isMinimize)
//// }
//}

@ -1,379 +1,379 @@
package com.hmg.hmgDr.util.opentok
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
}
}
//package com.hmg.hmgDr.util.opentok
//
//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
//// }
//}

@ -1,357 +1,357 @@
package com.hmg.hmgDr.util.opentok
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
}
}
//package com.hmg.hmgDr.util.opentok
//
//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
//// }
//}

@ -16,11 +16,11 @@ const DOCTOR_ROTATION = 'https://doctorrota.hmg.com/';
// const BASE_URL = 'https://hmgwebservices.com/';
// const BASE_URL = 'http://10.20.200.111:1010/';
// const BASE_URL = 'https://uat.hmgwebservices.com/';
const BASE_URL = 'https://uat.hmgwebservices.com/';
// const BASE_URL = 'https://hmgwebservices.com/';
const BASE_URL = 'https://webservices.hmg.com/';
// const BASE_URL = 'https://webservices.hmg.com/';
//
// const BASE_URL = 'https://vidauat.cloudsolutions.com.sa/'; //Vida Plus URL

@ -104,7 +104,7 @@ class PatientSearchViewModel extends BaseViewModel {
String dateTo;
String dateFrom;
if (OutPatientFilterType.Previous == outPatientFilterType) {
selectedFromDate = DateTime(DateTime.now().year, DateTime.now().month - 1, DateTime.now().day);
selectedFromDate = DateTime(DateTime.now().year, DateTime.now().month , DateTime.now().day-2);
selectedToDate = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day - 1);
dateTo = AppDateUtils.convertDateToFormat(selectedToDate!, 'yyyy-MM-dd');
dateFrom = AppDateUtils.convertDateToFormat(selectedFromDate!, 'yyyy-MM-dd');

@ -26,8 +26,10 @@ class ListOfMyInpatient extends StatelessWidget {
margin: EdgeInsets.symmetric(horizontal: 16.0),
child: patientSearchViewModel.filteredMyInPatientItems.length == 0
? NoData()
: NotificationListener(
child: ListView.builder(
:
NotificationListener(
child:
ListView.builder(
itemCount:
patientSearchViewModel.filteredMyInPatientItems.length,
scrollDirection: Axis.vertical,

@ -48,6 +48,7 @@ class SMSOTP {
late ProjectViewModel projectProvider;
String displayTime = '';
bool isClosed = false;
// final TurnstileController _controller = TurnstileController();
// final TurnstileOptions _options = TurnstileOptions(
// size: TurnstileSize.normal,
@ -58,6 +59,7 @@ class SMSOTP {
// );
String? _token;
displayDialog(BuildContext context) async {
double dialogWidth = MediaQuery.of(context).size.width * 0.90;
double dialogInputWidth = (dialogWidth / 4) - (SizeConfig.isWidthLarge ? SizeConfig.getWidthMultiplier(width: dialogWidth) * 4.5 : 20);
@ -76,8 +78,8 @@ class SMSOTP {
child: Container(
color: Colors.white,
child: StatefulBuilder(builder: (context, setState) {
if (displayTime == '') {
startTimer(setState);
if (displayTime == '' && context.mounted) {
startTimer(setState, context);
}
return Center(
@ -246,7 +248,6 @@ class SMSOTP {
)),
),
),
Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
AppText(
TranslationBase.of(context).validationMessage + ' ',
@ -264,34 +265,38 @@ class SMSOTP {
fontSize: SizeConfig.getTextMultiplierBasedOnWidth(width: dialogWidth) * 3.5,
)
]),
SizedBox(height: 10,),
Column(children: [
// CloudflareTurnstile(
// siteKey: '0x4AAAAAAAkmbvEy63pcxBBt',
// baseUrl: BASE_URL,
// options: _options,
// controller: _controller,
// onTokenReceived: (token) {
// setState(() {
// print("Cloudflare token: $token");
// //todo pass this token to checkActivcationCode
// _token = token;
// checkValue();
// });
// },
// // onTokenRecived: (token) {
// // setState(() {
// // print("Cloudflare token: $token");
// // _token = token;
// // });
// // },
// onTokenExpired: () {},
// // mode: TurnstileMode.managed,
// // errorBuilder: (context, error) {
// // return Text(error.message);
// // },
// ),
],),
SizedBox(
height: 10,
),
Column(
children: [
// CloudflareTurnstile(
// siteKey: '0x4AAAAAAAkmbvEy63pcxBBt',
// baseUrl: BASE_URL,
// options: _options,
// controller: _controller,
// onTokenReceived: (token) {
// setState(() {
// print("Cloudflare token: $token");
// //todo pass this token to checkActivcationCode
// _token = token;
// checkValue();
// });
// },
// // onTokenRecived: (token) {
// // setState(() {
// // print("Cloudflare token: $token");
// // _token = token;
// // });
// // },
// onTokenExpired: () {},
// // mode: TurnstileMode.managed,
// // errorBuilder: (context, error) {
// // return Text(error.message);
// // },
// ),
],
),
],
),
),
@ -344,7 +349,8 @@ class SMSOTP {
}
checkValue() async {
if (verifyAccountForm.currentState!.validate() ) { //&& _token !=null removed cloud flare for vida plus
if (verifyAccountForm.currentState!.validate()) {
//&& _token !=null removed cloud flare for vida plus
// if (digit1.text.isNotEmpty && digit2.text.isNotEmpty && digit3.text.isNotEmpty && digit4.text.isNotEmpty) {
onSuccess(digit1.text.toString() + digit2.text.toString() + digit3.text.toString() + digit4.text.toString());
this.isClosed = true;
@ -363,8 +369,11 @@ class SMSOTP {
return minutesString + ":" + secondsString;
}
startTimer(setState) {
startTimer(setState, BuildContext context) {
this.remainingTime--;
if (context.mounted == false) {
return;
}
setState(() {
displayTime = this.getSecondsAsDigitalClock(this.remainingTime);
});
@ -372,7 +381,7 @@ class SMSOTP {
Future.delayed(Duration(seconds: 1), () {
if (this.remainingTime > 0) {
if (isClosed == false) {
startTimer(setState);
startTimer(setState,context);
}
} else {
Navigator.pop(context);

Loading…
Cancel
Save