Android中OpenGL展示视频内容和预览摄像头数据用的纹理ID都是GLES11Ext.GL_TEXTURE_EXTERNAL_OES,所以可以直接按照上一篇的预览Camera基础之上,继承OesFilter,共用同一个GlSurfaceView,根据新生成的纹理ID创建SurfaceTexture绘画即可。
class CodecRender(val surface: CodecSurface) : GLSurfaceView.Renderer {
private val TAG: String = "CodecRender"
var textureId: Int = 0;
private var filter: OesFilter? = null
private var videoFilter: VideoFilter? = null
var surfaceTexture: SurfaceTexture? = null
private var cameraId: Int = 0;
private var listener: SurfaceTexture.OnFrameAvailableListener? = null
private var screenWidth: Int = 0
private var screenHight: Int = 0
private var videoWidth: Int = 400
private var videoHeight: Int = 400
init {
filter = OesFilter()
videoFilter = VideoFilter(surface)//将surface传入方便刷新和引用context获取本地视频数据,setOnFrameAvailableListener如果不在创建后设置会出现无法监听的问题
}
override fun onDrawFrame(gl: GL10?) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)
surfaceTexture!!.updateTexImage()
GLES20.glViewport(0, 0, screenWidth, screenHight);
if (cameraUtil!!.isOpenFinish) {
filter!!.drawFrame()
}
GLES20.glViewport(0, screenHight - videoHeight, videoWidth, videoHeight);//将视频置于左上角
//绘制视频
videoFilter!!.updateTexImage()
videoFilter!!.drawFrame()
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
screenWidth = width
screenHight = height
GLES20.glViewport(0, 0, width, height);
}
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
textureId = filter!!.createTextureId()
surfaceTexture = SurfaceTexture(textureId)
surfaceTexture!!.setOnFrameAvailableListener {
surface!!.requestRender()
}
cameraUtil!!.setTexture(surfaceTexture!!)
setCameraId(cameraId);
filter!!.setTextureId(textureId)
filter!!.create()
//绘制视频
videoFilter!!.createTextureView()
}
public fun setCameraId(id: Int) {
cameraId = id;
filter!!.setCoodData(cameraId)
cameraUtil!!.open(cameraId.toString())
}
private var cameraUtil: CameraUtil? = null
public fun release() {
surfaceTexture?.setOnFrameAvailableListener(null)
surfaceTexture?.release()
surfaceTexture = null
cameraUtil!!.close()
videoFilter?.close()
}
public fun setFrameListener(listener: SurfaceTexture.OnFrameAvailableListener) {
this.listener = listener;
}
init {
cameraUtil = CameraUtil(surface.context)
}
}
public open class OesFilter {
private val TAG: String = "OesFilter"
private var positionFloat: FloatBuffer? = null
private var coodFloat: FloatBuffer? = null
private var glHPosition: Int = 0
private var glHCoordinate: Int = 0
private var glHTexture: Int = 0
private var mHCoord: Int = 0
private var mHMatrix: Int = 0
public var coordinate: FloatArray = floatArrayOf(
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
)
private var textureId: Int = 0
private var programsId: Int = 0
private val vertexPosition = floatArrayOf(
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f
)
public open fun create() {
val frag_id = createShader(fragmentShaderCode, GLES20.GL_FRAGMENT_SHADER)
val vertex_id = createShader(vertexShaderCode, GLES20.GL_VERTEX_SHADER)
programsId = GLES20.glCreateProgram();
GLES20.glAttachShader(programsId, frag_id)
GLES20.glAttachShader(programsId, vertex_id)
GLES20.glLinkProgram(programsId)
val linkState = IntArray(1)
GLES20.glGetProgramiv(programsId, GLES20.GL_LINK_STATUS, linkState, 0)
LogUtil.e(TAG, "state=${linkState[0]} programsId=$programsId")
if (linkState[0] == 0) {
LogUtil.e(
TAG,
"Could not link program: "
)
LogUtil.e(
TAG,
GLES20.glGetProgramInfoLog(programsId)
)
GLES20.glDeleteProgram(programsId)
programsId = 0;
}
}
private fun userProgram(programsId: Int) {
GLES20.glUseProgram(programsId)
var bb: ByteBuffer = ByteBuffer.allocateDirect(vertexPosition.size * 4)
bb.order(ByteOrder.nativeOrder())
positionFloat = bb.asFloatBuffer()
positionFloat!!.put(vertexPosition)
positionFloat!!.position(0)
var cbb: ByteBuffer = ByteBuffer.allocateDirect(coordinate!!.size * 4)
cbb.order(ByteOrder.nativeOrder())
coodFloat = cbb.asFloatBuffer()
coodFloat!!.put(coordinate)
coodFloat!!.position(0)
glHPosition = GLES20.glGetAttribLocation(programsId, "vPosition")
glHCoordinate = GLES20.glGetAttribLocation(programsId, "vTextureCoordinate")
glHTexture = GLES20.glGetUniformLocation(programsId, "vTexture")
mHCoord = GLES20.glGetAttribLocation(programsId, "vCoord")
mHMatrix = GLES20.glGetUniformLocation(programsId, "vMatrix")
}
public fun createTextureId(): Int {
val texture = IntArray(1)
GLES20.glGenTextures(1, texture, 0)
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0])
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_LINEAR.toFloat()
)
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR.toFloat()
)
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE.toFloat()
)
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE.toFloat()
)
return texture[0]
}
public fun setTextureId(id: Int) {
textureId = id
}
private fun createShader(str: String, style: Int): Int {
val shader: Int = GLES20.glCreateShader(style);
GLES20.glShaderSource(shader, str)
GLES20.glCompileShader(shader)
val state = IntArray(1)
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, state, 0)
if (state[0] == 0) {
LogUtil.e(TAG, "Could not compile shader " + shader + ":");
LogUtil.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader)
throw IllegalStateException("Could not compile shader")
}
return shader;
}
private val vertexShaderCode = "attribute vec4 vPosition;" +
"attribute vec2 vCoord;" +
"varying vec2 vTextureCoordinate;" +
"uniform mat4 vMatrix;" +
"void main() {" +
" gl_Position = vMatrix*vPosition;" +
"vTextureCoordinate = vCoord;" +
"}"
private val fragmentShaderCode = "#extension GL_OES_EGL_image_external : require\r\n" +
"precision mediump float;" +
"varying vec2 vTextureCoordinate;" +
"uniform samplerExternalOES vTexture;" +
"void main() {" +
" gl_FragColor = texture2D(vTexture, vTextureCoordinate);" +
"}"
private val matrix: FloatArray = LogUtil.getOriginalMatrix()
public fun drawFrame() {
userProgram(programsId)
GLES20.glEnableVertexAttribArray(glHPosition)
GLES20.glEnableVertexAttribArray(mHCoord)
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId)
GLES20.glUniform1i(glHTexture, 0)
GLES20.glVertexAttribPointer(glHPosition, 2, GLES20.GL_FLOAT, false, 0, positionFloat)
GLES20.glVertexAttribPointer(mHCoord, 2, GLES20.GL_FLOAT, false, 0, coodFloat)
GLES20.glUniformMatrix4fv(mHMatrix, 1, false, matrix, 0)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
GLES20.glDisableVertexAttribArray(glHPosition)
GLES20.glDisableVertexAttribArray(mHCoord)
}
public fun setCoodData(cameraId: Int) {
coordinate = if (cameraId == 0) {//后置摄像头
floatArrayOf(
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
)
} else {//前置摄像头
floatArrayOf(
1.0f, 1.0f,
0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f
)
}
}
}
class VideoFilter(val surface: CodecSurface) : OesFilter() {
private var mediaPlayer: MediaPlayer? = null
private var surfaceTexture: SurfaceTexture? = null
init {
mediaPlayer = MediaPlayer()
}
fun createTextureView() {
create()
val id = createTextureId()
surfaceTexture = SurfaceTexture(id)
coordinate = floatArrayOf(
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
)
setTextureId(id)
surfaceTexture!!.setOnFrameAvailableListener {
surface.requestRender()
}
play()
}
fun updateTexImage() {
surfaceTexture!!.updateTexImage()
}
private fun play() {
try {
mediaPlayer!!.setSurface(Surface(surfaceTexture))
var fd = FileDescriptor()
mediaPlayer!!.reset()
mediaPlayer!!.isLooping = true
var afd = surface.resources.openRawResourceFd(R.raw.test);
mediaPlayer!!.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
mediaPlayer!!.setOnPreparedListener { mp ->
mp.start()
}
mediaPlayer!!.prepareAsync()
} catch (e: Exception) {
e.printStackTrace()
}
}
public fun close() {
Thread {
try {
mediaPlayer?.let {
if (it.isPlaying) {
it.pause()
it.stop()
}
it.release()
}
mediaPlayer = null
} catch (e: Exception) {
}
}.start()
}
}