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.
     * 

* * @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  

你可能感兴趣的:(Android之朝花夕拾,android,音视频)