UVCCamera OpenGL 添加时间戳水印

视频流添加水印方式较多 本文只从渲染角度修改 

修改  CameraViewInterface 预览视图 自定义 CameraSurfaceView 将相机预览数据输出到外部纹理 通过fbo 混合水印纹理及Camera纹理 最终输出到 SurfaceView 上

打开相机输出到外部纹理

mHandlerL.startPreview(mUVCCameraViewL.getSurfaceTexture());

private final OnDeviceConnectListener mOnDeviceConnectListener = new OnDeviceConnectListener() {
		@Override
		public void onAttach(final UsbDevice device) {
			if (DEBUG) Log.v(TAG, "onAttach:" + device);
			Toast.makeText(MainActivity.this, "USB_DEVICE_ATTACHED", Toast.LENGTH_SHORT).show();
		}

		@Override
		public void onConnect(final UsbDevice device, final UsbControlBlock ctrlBlock, final boolean createNew) {
			if (DEBUG) Log.v(TAG, "onConnect:" + device);
			if (!mHandlerL.isOpened()) {
				if (DEBUG) Log.v(TAG, "mHandlerL not opend:");
				mHandlerL.open(ctrlBlock);
				mHandlerL.startPreview(mUVCCameraViewL.getSurfaceTexture());
				runOnUiThread(new Runnable() {
					@Override
					public void run() {
						mCaptureButtonL.setVisibility(View.VISIBLE);
					}
				});
			} else if (!mHandlerR.isOpened()) {
				if (DEBUG) Log.v(TAG, "mHandlerR not opend:");
				mHandlerR.open(ctrlBlock);
				mHandlerR.startPreview(mUVCCameraViewR.getSurfaceTexture());
				runOnUiThread(new Runnable() {
					@Override
					public void run() {
						mCaptureButtonR.setVisibility(View.VISIBLE);
					}
				});
			}
		}

创建外部纹理 及  SurfaceTexture 

     cameraTexture = GlUtil.createRecordCameraTextureID();
        surfaceTexture = new SurfaceTexture(cameraTexture);

RenderThread 中绑定EGL上下文 绑定显示窗口缓冲区  

   private final void init() {
                if (DEBUG) Log.v(TAG, "RenderThread#init:");
                try {
                    glRenderManager.setCameraRotate(180);
                    glRenderManager = new GlRenderManager(appContext, mTexId, mDispSurface, mPreviewSurface);
                    glRenderManager.onInputSizeChanged(640, 480);
                    glRenderManager.onDisplaySizeChanged(mViewWidth, mViewHeight);


                    // notify to caller thread that previewSurface is ready
                } catch (GlUtil.OpenGlException e) {
                    e.printStackTrace();
                }
            }
 public GlRenderManager(Context context, int texture, Surface disPlaySurface, SurfaceTexture surfaceTexture) throws GlUtil.OpenGlException {
        this.context = context;
        this.texture = texture;
        this.surfaceTexture = surfaceTexture;
        mEglCore = new EglCore(null, EglCore.FLAG_TRY_GLES3);
        setDisPlaySurface(disPlaySurface);
        mDisplaySurface.makeCurrent();
        //关闭深度测试和绘制背面
        GLES20.glDisable(GL10.GL_DEPTH_TEST);
        GLES20.glDisable(GL10.GL_CULL_FACE);
        init();
    }

/**
     * Prepares EGL display and context.
     * <p>
     *
     * @param sharedContext The context to share, or null if sharing is not desired.
     * @param flags         Configuration bit flags, e.g. FLAG_RECORDABLE.
     */
    public EglCore(EGLContext sharedContext, int flags) throws GlUtil.OpenGlException {
        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("EGL already set up");
        }

        if (sharedContext == null) {
            sharedContext = EGL14.EGL_NO_CONTEXT;
        }

        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("unable to get EGL14 display");
        }
        int[] version = new int[2];
        if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
            mEGLDisplay = null;
            throw new RuntimeException("unable to initialize EGL14");
        }

        // Try to get a GLES3 context, if requested.
        if ((flags & FLAG_TRY_GLES3) != 0) {
            //Log.d(TAG, "Trying GLES 3");
            EGLConfig config = getConfig(flags, 3);
            if (config != null) {
                int[] attrib3_list = {
                        EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
                        EGL14.EGL_NONE
                };
                EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
                        attrib3_list, 0);

                if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
                    //Log.d(TAG, "Got GLES 3 config");
                    mEGLConfig = config;
                    mEGLContext = context;
                    mGlVersion = 3;
                }
            }
        }
        if (mEGLContext == EGL14.EGL_NO_CONTEXT) {  // GLES 2 only, or GLES 3 attempt failed
            //Log.d(TAG, "Trying GLES 2");
            EGLConfig config = getConfig(flags, 2);
            if (config == null) {
                throw new RuntimeException("Unable to find a suitable EGLConfig");
            }
            int[] attrib2_list = {
                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                    EGL14.EGL_NONE
            };
            EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
                    attrib2_list, 0);
            checkEglError("eglCreateContext");
            mEGLConfig = config;
            mEGLContext = context;
            mGlVersion = 2;
        }

        // Confirm with query.
        int[] values = new int[1];
        EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
                values, 0);
        Log.d(TAG, "EGLContext created, client version " + values[0]);
    }

RenderThread WHEN_DIRTY渲染

    mPreviewSurface.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
                    @Override
                    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                callDrawFrame();
                            }
                        });
                    }
                });

FBO渲染   NDK OpenGL ES 3.0 开发(五):FBO 离屏渲染_字节流动的博客-CSDN博客

1 创建FBO 关联 frameBuffferTex

    /**
     * 创建Sampler2D的Framebuffer 和 Texture
     *
     * @param frameBuffer
     * @param frameBufferTex
     * @param width
     * @param height
     */
    public static void createSampler2DFrameBuff(int[] frameBuffer, int[] frameBufferTex,
                                                int width, int height, int position) throws GlUtil.OpenGlException {
        GLES30.glGenFramebuffers(1, frameBuffer, position);
        GLES30.glGenTextures(1, frameBufferTex, position);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, frameBufferTex[position]);
        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, width, height, 0,
                GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,
                GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,
                GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,
                GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,
                GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBuffer[position]);
        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0,
                GLES30.GL_TEXTURE_2D, frameBufferTex[position], 0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
        checkGlError("createCamFrameBuff");
    }

2 drwFrame (opengl render thread)

//.....    
 mDisplaySurface.makeCurrent();
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        //刷新
        try {
            surfaceTexture.updateTexImage();
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    //....
        //绘制相机输出流 及滤镜等 FBO

        if (displayRenderGroup != null) {
            displayRenderGroup.setMirroring(mirroring);
            currentTexture = displayRenderGroup.drawFrame(currentTexture);
        }
        //后续操作 FBO切换到 currentTexture(可见窗口) 上处理
      //....
        //添加水印
        drawWaterSign();
          //送显
        mDisplaySurface.swapBuffers();

绑定FBO

GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFramebuffers[0]);

FBO处理完后切换到正常显示窗口 

GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);

水印绘制

拆分动态部分 防止闪烁 这里可以进一步优化缓存 bitmap 

 //初始化文字转图片所需的对象,避免多次生成新对象消耗过多内存
        //将字符图片与纹理绑定,返回纹理id
        for (int i = 0; i < 12; i++) {
            if (i == 10) {
                mWaterTexId[i] = TextureHelper.loadTexture(BitmapUtils.textToBitmap("-"), textureObjectIds);
            } else if (i == 11) {
                mWaterTexId[i] = TextureHelper.loadTexture(BitmapUtils.textToBitmap(":"), textureObjectIds);
            } else {
                mWaterTexId[i] = TextureHelper.loadTexture(BitmapUtils.textToBitmap(i + ""), textureObjectIds);
            }
        }

动态绘制时间纹理 

private void drawWaterSign() {
        String time = formatter.format(new Date());
        int x = waterMaskStartX;
        int y = waterMaskStartY;
        if ("".equals(time)) {
            return;
        }
        GLES20.glEnable(GLES20.GL_BLEND);
        //开启GL的混合模式,即图像叠加
        GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        //画水印
        GLES20.glViewport(x, y, waterMaskWidth, waterMaskHeight);//60
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(0, 1))]);
        GLES20.glViewport(x + 15, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(1, 2))]);
        GLES20.glViewport(x + 15 * 2, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(2, 3))]);
        GLES20.glViewport(x + 15 * 3, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(3, 4))]);
        GLES20.glViewport(x + 15 * 4, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[10]); // -
        GLES20.glViewport(x + 15 * 5, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(5, 6))]);
        GLES20.glViewport(x + 15 * 6, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(6, 7))]);
        GLES20.glViewport(x + 15 * 7, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[10]); // -
        GLES20.glViewport(x + 15 * 8, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(8, 9))]);
        GLES20.glViewport(x + 15 * 9, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(9, 10))]);
        GLES20.glViewport(x + 15 * 11, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(11, 12))]);
        GLES20.glViewport(x + 15 * 12, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(12, 13))]);
        GLES20.glViewport(x + 15 * 13, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[11]); // :
        GLES20.glViewport(x + 15 * 14, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(14, 15))]);
        GLES20.glViewport(x + 15 * 15, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(15, 16))]);
        GLES20.glViewport(x + 15 * 16, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[11]); // :
        GLES20.glViewport(x + 15 * 17, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(17, 18))]);
        GLES20.glViewport(x + 15 * 18, y, waterMaskWidth, waterMaskHeight);
        mWaterSign.drawFrame(mWaterTexId[Integer.parseInt(time.substring(18, 19))]);
        //GLES30.glDisable(GLES30.GL_BLEND);
    }

OPENGL 滤镜水印可参照GPUIMAGE库 https://link.csdn.net/?target=https%3A%2F%2Fgithub.com%2FBradLarson%2FGPUImage.git

在此基础上采集修改定制  这里也是用的他人的 渲染部分整合而来 实际项目中一般在底层 通过ffmpeg filter 实现 更加灵活 在推流端处理视频帧 

QtyearLin/UVCCamera_Water · GitHub  

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值