前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >播放视频时如何在视频帧上添加水印

播放视频时如何在视频帧上添加水印

作者头像
马上就说
发布2020-11-11 17:02:11
2.9K0
发布2020-11-11 17:02:11
举报
文章被收录于专栏:码上就说码上就说

之前的一篇文章中我们介绍了播放视频的时候调整音频的音量,我们能否在播放视频的时候在视频画面上加上水印?

有同学可能会说了,我直接用TextureView渲染视频画面,然后在TextureView上盖一层ImageView可以吗?

好像显示效果上没有什么问题,但是仔细分析还是不能满足要求?

  • 1.ImageView和TextureView有明显的层级关系,如果出现View层级的问题,不太好处理
  • 2.TextureView渲染视频的时候,提供了getBitmap()接口来截取视频的某一帧,如果盖上一层ImageView无法实现截图的功能
  • 3.ImageView盖在TextureView,会拦截TextureView的事件,造成播放器交互方面的问题。

上面三个问题表示TextureView上面盖一层ImageView的方式是行不通的。 既然行不通,有没有方法可以解决这个问题?

其实除了TextureView和SurfaceView渲染视频之外,GLSurfaceView也是渲染视频的一种View,GLSurfaceView和OpenGL结合,可以实现给播放中的视频添加水印的目的。

1.GLSurfaceView介绍

GLSurfaceView从Android 1.5(API level 3)开始加入,继承自SurfaceView,实现了SurfaceHolder.Callback2接口,拥有SurfaceView的全部特性,也有view所有的功能和属性,特别是处理事件的能力,它主要是在SurfaceView的基础上它加入了EGL的管理,并自带了一个GLThread绘制线程(EGLContext创建GL环境所在线程即为GL线程),绘制的工作直接通过OpenGL在绘制线程进行,不会阻塞主线程,绘制的结果输出到SurfaceView所提供的Surface上,这使得GLSurfaceView也拥有了OpenGlES所提供的图形处理能力,通过它定义的Render接口,使更改具体的Render的行为非常灵活性,只需要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。

GLSurfaceView提供了下列特性:

  • 1.提供并且管理一个独立的Surface。
  • 2.提供并且管理一个EGL display,它能让opengl把内容渲染到上述的Surface上。
  • 3.支持用户自定义渲染器(Render),通过setRenderer设置一个自定义的Renderer。
  • 4.让渲染器在独立的GLThread线程里运作,和UI线程分离。
  • 5.支持按需渲染(on-demand)和连续渲染(continuous)两种模式。
  • 6.GPU加速:GLSurfaceView的效率是SurfaceView的30倍以上,SurfaceView使用画布进行绘制,GLSurfaceView利用GPU加速提高了绘制效率
  • 7.View的绘制onDraw(Canvas canvas)使用Skia渲染引擎渲染,而GLSurfaceView的渲染器Renderer的onDrawFrame(GL10 gl)使用opengl绘制引擎进行渲染。

2.GLSurfaceView渲染视频

  • 首先定义一个CustomGLSurfaceView 继承GLSurfaceView
  • 定义一个CustomRenderer实现Renderer接口
代码语言:javascript
复制
    public interface Renderer {

        void onSurfaceCreated(GL10 gl, EGLConfig config);

        void onSurfaceChanged(GL10 gl, int width, int height);

        void onDrawFrame(GL10 gl);
    }

onSurfaceCreated 是当渲染线程启动的时候,surfacetexture被创建,这个surfacetexture设置到播放器中,之后会在这个surfacetexture上渲染数据。

onSurfaceCreated是surface大小发生变化的时候的回调,渲染的画布宽高都会写明。

onDrawFrame绘制操作,将surfacetexture设置进播放器之后,codec中的surface会不断地被填充新的视频帧,在onDrawFrame中将视频帧surfaceTexture.updateTexImage之后,开始绘制水印图片。

代码语言:javascript
复制
    public void onDrawFrame(GL10 gl) {
      if (videoProcessor == null) {
        return;
      }

      if (!initialized) {
        videoProcessor.initialize();
        initialized = true;
      }

      if (width != -1 && height != -1) {
        videoProcessor.setSurfaceSize(width, height);
        width = -1;
        height = -1;
      }

      if (frameAvailable.compareAndSet(true, false)) {
        SurfaceTexture surfaceTexture = Assertions.checkNotNull(this.surfaceTexture);
        surfaceTexture.updateTexImage();
        long lastFrameTimestampNs = surfaceTexture.getTimestamp();
        Long frameTimestampUs = sampleTimestampQueue.poll(lastFrameTimestampNs);
        if (frameTimestampUs != null) {
          this.frameTimestampUs = frameTimestampUs;
        }
      }

      videoProcessor.draw(texture, frameTimestampUs);
    }

videoProcessor.draw(texture, frameTimestampleUs)开始在视频帧基础上绘制水印。

设置水印图片的定点着色器和边缘着色器:

代码语言:javascript
复制
attribute vec4 a_position;
attribute vec3 a_texcoord;
varying vec2 v_texcoord;
void main() {
 gl_Position = a_position;
 v_texcoord = a_texcoord.xy;
}
代码语言:javascript
复制
#extension GL_OES_EGL_image_external : require
precision mediump float;
// External texture containing video decoder output.
uniform samplerExternalOES tex_sampler_0;
// Texture containing the overlap bitmap.
uniform sampler2D tex_sampler_1;
// Horizontal scaling factor for the overlap bitmap.
uniform float scaleX;
// Vertical scaling factory for the overlap bitmap.
uniform float scaleY;
varying vec2 v_texcoord;
void main() {
  vec4 videoColor = texture2D(tex_sampler_0, v_texcoord);
  vec4 overlayColor = texture2D(tex_sampler_1,
                                vec2(v_texcoord.x * scaleX,
                                     v_texcoord.y * scaleY));
  // Blend the video decoder output and the overlay bitmap.
  gl_FragColor = videoColor * (1.0 - overlayColor.a)
      + overlayColor * overlayColor.a;
}

通过Cavas将水印图片画出来。

代码语言:javascript
复制
  public void draw(int frameTexture, long frameTimestampUs) {
    // Draw to the canvas and store it in a texture.
    String text = String.format(Locale.US, "%.02f", frameTimestampUs / (float) C.MICROS_PER_SECOND);
    overlayBitmap.eraseColor(Color.TRANSPARENT);
    overlayCanvas.drawBitmap(logoBitmap, /* left= */ 32, /* top= */ 32, paint);
    overlayCanvas.drawText(text, /* x= */ 200, /* y= */ 130, paint);
    GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    GLUtils.texSubImage2D(
        GL10.GL_TEXTURE_2D, /* level= */ 0, /* xoffset= */ 0, /* yoffset= */ 0, overlayBitmap);
    GlUtil.checkGlError();

    // Run the shader program.
    GlUtil.Uniform[] uniforms = Assertions.checkNotNull(this.uniforms);
    GlUtil.Attribute[] attributes = Assertions.checkNotNull(this.attributes);
    GLES20.glUseProgram(program);
    for (GlUtil.Uniform uniform : uniforms) {
      switch (uniform.name) {
        case "tex_sampler_0":
          uniform.setSamplerTexId(frameTexture, /* unit= */ 0);
          break;
        case "tex_sampler_1":
          uniform.setSamplerTexId(textures[0], /* unit= */ 1);
          break;
        case "scaleX":
          uniform.setFloat(bitmapScaleX);
          break;
        case "scaleY":
          uniform.setFloat(bitmapScaleY);
          break;
      }
    }
    for (GlUtil.Attribute copyExternalAttribute : attributes) {
      copyExternalAttribute.bind();
    }
    for (GlUtil.Uniform copyExternalUniform : uniforms) {
      copyExternalUniform.bind();
    }
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
    GlUtil.checkGlError();
  }

下图左上角的地方有一个Android icon,就是我们绘制上去的水印。

具体代码参考开源项目:https://github.com/JeffMony/PlayerSDK

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 音视频平凡之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.GLSurfaceView介绍
  • 2.GLSurfaceView渲染视频
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档