Android录制视频带水印有在openGl中绘画再通过mediaCodec+EGLSurface读取出来,还有就是不渲染的情况下将camera的byte数组与时间戳生成的图片数组转为二进制数据混合一起,又或者录制完之后用ffmpeg后期补充水印。
此文主要讲第一个,代码下载:https://download.csdn.net/download/gan303/12509216
效果图:
主要代码:
package com.my.video.surface
import android.graphics.SurfaceTexture
import android.opengl.GLES20
import android.opengl.GLSurfaceView
import com.my.video.filter.NoFilter
import com.my.video.filter.OesFilter
import com.my.video.filter.TimeFilter
import com.my.video.util.CameraUtil
import com.my.video.util.CodecUtil
import com.my.video.util.LogUtil
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
class CodecRender(private val surface: CodecSurface) : GLSurfaceView.Renderer {
var textureId: Int = 0;
private var oesfilter: OesFilter? = null
private var noFilter: NoFilter? = null
var surfaceTexture: SurfaceTexture? = null
private var cameraId: Int = 0;
private var listener: SurfaceTexture.OnFrameAvailableListener? = null
private var screenWidth: Int = 0
private var screenHeight: Int = 0
private var codeUtil: CodecUtil? = null
private var fFrame: IntArray = IntArray(1)
private var fTexture: IntArray = IntArray(1)
private var isRelease: Boolean = false
private var timeFilter: TimeFilter? = null
init {
oesfilter = OesFilter()
noFilter = NoFilter()
}
override fun onDrawFrame(gl: GL10?) {
if (isRelease) {
return
}
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()
bindFrameTexture(fFrame[0], fTexture[0])
GLES20.glViewport(0, 0, screenWidth, screenHeight);
if (cameraUtil?.isOpenFinish == true) {
oesfilter?.drawFrame()
if (codeUtil == null) {
codeUtil = CodecUtil(
surface.context,
screenWidth,
screenHeight
)
codeUtil?.init();
}
}
timeFilter?.drawTime()//放置在这能混合到frame中一起绘画到2D
unBindFrameTexture()
if (timeFilter == null) {
timeFilter = TimeFilter(screenWidth, screenHeight)
timeFilter?.create()
}
/**绘制显示的filter */
GLES20.glViewport(0, 0, screenWidth, screenHeight)
noFilter?.setTextureId(fTexture[0])
noFilter?.drawFrame()
//timeFilter?.drawTime()放置在此处的话不会录制到视频内,但同样能显示
codeUtil?.setTextureId(fTexture[0])
codeUtil?.start()
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
screenWidth = width
screenHeight = height
//
GLES20.glViewport(0, 0, width, height)
//
GLES20.glDeleteFramebuffers(1, fFrame, 0)
GLES20.glDeleteTextures(1, fTexture, 0)
GLES20.glGenFramebuffers(1, fFrame, 0)
GLES20.glGenTextures(1, fTexture, 0)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fTexture[0])
GLES20.glTexImage2D(
GLES20.GL_TEXTURE_2D,
0,
GLES20.GL_RGBA,
screenWidth,
screenHeight,
0,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
null
)
useTexParameter()
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
}
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
textureId = oesfilter!!.createTextureId()
surfaceTexture = SurfaceTexture(textureId)
surfaceTexture?.setOnFrameAvailableListener {
surface.requestRender()
}
cameraUtil?.setTexture(surfaceTexture!!)
setCameraId(cameraId);
oesfilter?.setTextureId(textureId)
oesfilter?.create()
noFilter?.create()
}
fun setCameraId(id: Int) {
cameraId = id
oesfilter!!.setCoodData(cameraId)
cameraUtil!!.open(cameraId.toString())
}
private var cameraUtil: CameraUtil? = null
fun release() {
isRelease = true
timeFilter?.release()
surfaceTexture?.setOnFrameAvailableListener(null)
surfaceTexture?.release()
surfaceTexture = null
cameraUtil?.close()
cameraUtil = null
codeUtil?.release()
codeUtil = null
}
public fun setFrameListener(listener: SurfaceTexture.OnFrameAvailableListener) {
this.listener = listener;
}
init {
cameraUtil = CameraUtil(surface.context)
}
private fun useTexParameter() {
//设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
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()
)
//设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameterf(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE.toFloat()
)
//设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameterf(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE.toFloat()
)
}
private fun bindFrameTexture(frameBufferId: Int, textureId: Int) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId)
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D,
textureId,
0
)
}
private fun unBindFrameTexture() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
}
}
package com.my.video.filter
import android.graphics.Bitmap
import android.opengl.GLES20
import android.opengl.GLUtils
import com.my.video.util.BitmapUtil
import com.my.video.util.LogUtil
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.HashMap
class TimeFilter(val width: Int, val height: Int) : NoFilter() {
private var timeFormat: SimpleDateFormat = SimpleDateFormat("yyyy/MM/dd-HH:mm:ss")
private var bitmapUtil: BitmapUtil? = BitmapUtil()
private var textIdHashMap: HashMap = HashMap()
override fun create() {
super.create()
createFrameBuffer()
coordinate = floatArrayOf(
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
)
vertexPosition = floatArrayOf(
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f
)
}
fun drawTime() {
val time: String = timeFormat.format(Date())
bitmapUtil?.let {
var marginLeft: Int = 0
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
for (i: Int in time.indices) {
val str: String = time[i].toString()
val bitmap: Bitmap? = bitmapUtil?.getBitmapByTxt(str)
if (bitmap != null) {
GLES20.glViewport(
marginLeft,
0,
bitmap.width,
bitmap.height
)
marginLeft += bitmap.width
drawFrame(createTexture(bitmap))
}
}
}
}
private fun createTexture(mBitmap: Bitmap): Int {
if (textIdHashMap[mBitmap] == null) {
var textures = IntArray(1)
//生成纹理
GLES20.glGenTextures(1, textures, 0)
//生成纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0])
//设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
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()
)
//设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameterf(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE.toFloat()
)
//设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameterf(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE.toFloat()
)
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
textIdHashMap[mBitmap] = textures[0]
}
return textIdHashMap[mBitmap]!!
}
//创建离屏buffer
val fFrame = IntArray(1)
val fRender = IntArray(1)
val fTexture = IntArray(1)
//创建FrameBuffer
private fun createFrameBuffer(): Boolean {
GLES20.glDeleteFramebuffers(1, fFrame, 0)
GLES20.glDeleteTextures(1, fTexture, 0)
GLES20.glGenFramebuffers(1, fFrame, 0)
GLES20.glGenRenderbuffers(1, fRender, 0)
genTextures()
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fFrame[0])
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, fRender[0])
GLES20.glRenderbufferStorage(
GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width,
height
)
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, fTexture[0], 0
)
GLES20.glFramebufferRenderbuffer(
GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
GLES20.GL_RENDERBUFFER, fRender[0]
)
unBindFrame()
return false
}
//生成Textures
private fun genTextures() {
GLES20.glGenTextures(1, fTexture, 0)
for (i in 0 until 1) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fTexture[i])
GLES20.glTexImage2D(
GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height,
0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null
)
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE
)
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE
)
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR
)
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR
)
}
}
//取消绑定Texture
private fun unBindFrame() {
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0)
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
}
fun release() {
bitmapUtil?.release()
GLES20.glDeleteRenderbuffers(1, fRender, 0)
GLES20.glDeleteFramebuffers(1, fFrame, 0)
GLES20.glDeleteTextures(1, fTexture, 0)
}
}
原理就是将时间戳的绘画也加载到frame中,不然会出现无效果又或者视频没水印的问题