Android视频编辑器(二)预览、录制视频加上水印和美白磨皮效果
前言
Android + JNI +Opengl 开发自己的美图秀秀(我们项目关于美白和滤镜部分,基本上都是参考这个作者的开源项目,非常值得学习一下)
给视频加水印
public class WaterMarkFilter extends NoFilter{}
然后在构造函数中创建一个NoFilter
mFilter=new NoFilter(mRes){@Overrideprotected void onClear() {}};
@Overrideprotected void onCreate() {super.onCreate();mFilter.create();createTexture();}
@Overrideprotected void onSizeChanged(int width, int height) {this.width=width;this.height=height;mFilter.setSize(width,height);}
@Overridepublic void draw() {super.draw();GLES20.glViewport(x,y,w == 0 ? mBitmap.getWidth():w,h==0?mBitmap.getHeight():h);GLES20.glDisable(GLES20.GL_DEPTH_TEST);GLES20.glEnable(GLES20.GL_BLEND);GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_DST_ALPHA);mFilter.draw();GLES20.glDisable(GLES20.GL_BLEND);GLES20.glViewport(0,0,width,height);}
public void setWaterMark(Bitmap bitmap){if(this.mBitmap!=null){this.mBitmap.recycle();}this.mBitmap=bitmap;}
public void setPosition(int x,int y,int width,int height){this.x=x;this.y=y;this.w=width;this.h=height;}
然后这个给将原始画面和水印图片进行混合的代码就完成了
private void createTexture() {if(mBitmap!=null){//生成纹理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);//设置放大过滤为使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);//设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);//设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);//对画面进行矩阵旋转MatrixUtils.flip(mFilter.getMatrix(),false,true);mFilter.setTextureId(textures[0]);}}
其中非常关键的一行就是,GLUtils.textImage2D(GLES20.GL_TEXTURE_2D,0,mBitmap,0) 。
GroupFilter类
private Queue<AFilter> mFilterQueue;private List<AFilter> mFilters;
在构造函数中进行初始化
public GroupFilter(Resources res) {super(res);mFilters=new ArrayList<>();mFilterQueue=new ConcurrentLinkedQueue<>();}
提供添加Filter的方法
public void addFilter(final AFilter filter){mFilterQueue.add(filter);}
然后写一个updateFilter,用于将Filter,从Queue中取出,加入到List中
private void updateFilter(){AFilter f;while ((f=mFilterQueue.poll())!=null){f.create();f.setSize(width,height);mFilters.add(f);size++;}}
然后创建离屏的buffer以及输入和输出的Texture
private int fTextureSize = 2;private int[] fFrame = new int[1];private int[] fRender = new int[1];private int[] fTexture = new int[fTextureSize];
//创建FrameBufferprivate boolean createFrameBuffer() {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]);
// int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
// if(status==GLES20.GL_FRAMEBUFFER_COMPLETE){
// return true;
// }unBindFrame();return false;}//生成Texturesprivate void genTextures() {GLES20.glGenTextures(fTextureSize, fTexture, 0);for (int i = 0; i < fTextureSize; i++) {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);}}
然后就是draw的代码,主要是从List中,取出Filter,并且进行绘制
public void draw(){updateFilter();textureIndex=0;GLES20.glViewport(0,0,width,height);for (AFilter filter:mFilters){GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fFrame[0]);GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,GLES20.GL_TEXTURE_2D, fTexture[textureIndex%2], 0);GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,GLES20.GL_RENDERBUFFER, fRender[0]);if(textureIndex==0){filter.setTextureId(getTextureId());}else{filter.setTextureId(fTexture[(textureIndex-1)%2]);}filter.draw();unBindFrame();textureIndex++;}}
GroupFilter的核心代码就是上面这些,然后我们需要将水印的Filter 在我们上文中的CameraDrawer中进行创建和添加,然后在合适的位置进行绘制。
在CameraDrawer代码中,基于上文中的代码,创建一个GroupFilter的实例,
/**绘制水印的filter组*/private final GroupFilter mBeFilter;
在构造函数中进行水印的Filter的创建和添加
mBeFilter = new GroupFilter(resources);WaterMarkFilter waterMarkFilter = new WaterMarkFilter(resources);waterMarkFilter.setWaterMark(BitmapFactory.decodeResource(resources,R.mipmap.watermark));waterMarkFilter.setPosition(30,50,0,0);mBeFilter.addFilter(waterMarkFilter);
然后在onSurfaceCreated方法中进行mBeFilter的初始化
mBeFilter.create();
在onSurfaceChanged方法中,设置GroupFilter的size
drawFilter.setSize(mPreviewWidth,mPreviewHeight);
然后我们需要对核心的onDrawFrame方法进行改造,我们现在的绘制流程是这样的,首先使用drawFilter将摄像头的画面,绘制到离屏的fFrame和fTexture中
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fFrame[0]);GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,GLES20.GL_TEXTURE_2D, fTexture[0], 0);GLES20.glViewport(0,0,mPreviewWidth,mPreviewHeight);drawFilter.draw();//解绑GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
下一步,我们需要调用GroupFilter,将绘制的画面输入,然后和水印进行混合,然后再输出
mBeFilter.setTextureId(fTexture[0]);mBeFilter.draw();
下一步就是,从GroupFilter取出绘制完成的texture,我们需要将这个texture分别给到两个第一,第一个就是屏幕上显示,第二个就是给到Encoder,进行后台的视频编码。
/**绘制显示的filter*/GLES20.glViewport(0,0,width,height);showFilter.setTextureId(mBeFilter.getOutputTexture());showFilter.draw();/**将加过水印的texture,给到encoder进行后台的编码*/if (videoEncoder != null && recordingEnabled && recordingStatus == RECORDING_ON){videoEncoder.setTextureId(mBeFilter.getOutputTexture());videoEncoder.frameAvailable(mSurfaceTextrue);}
通过上面的流程,我们就完成了视频加水印在屏幕上显示以及编码到视频中。
预览和录制加上美白效果
public MagicBeautyFilter(){super(NO_FILTER_VERTEX_SHADER ,OpenGlUtils.readShaderFromRawResource(R.raw.beauty));}
这里的R.raw.beauty文件,就是美白算法的实现shader。
public GPUImageFilter(final String vertexShader, final String fragmentShader) {mRunOnDraw = new LinkedList<>();mVertexShader = vertexShader;mFragmentShader = fragmentShader;mGLCubeBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.CUBE.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mGLCubeBuffer.put(TextureRotationUtil.CUBE).position(0);mGLTextureBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.TEXTURE_NO_ROTATION.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mGLTextureBuffer.put(TextureRotationUtil.getRotation(Rotation.NORMAL, false, true)).position(0);}
包括了shader的设置以及一些buffer的初始化
protected void onInit() {super.onInit();mSingleStepOffsetLocation = GLES20.glGetUniformLocation(getProgram(), "singleStepOffset");mParamsLocation = GLES20.glGetUniformLocation(getProgram(), "params");setBeautyLevel(3);//beauty Level}
然后就是对外提供设置美白的等级的方法
public void setBeautyLevel(int level){mLevel=level;switch (level) {case 1:setFloat(mParamsLocation, 1.0f);break;case 2:setFloat(mParamsLocation, 0.8f);break;case 3:setFloat(mParamsLocation,0.6f);break;case 4:setFloat(mParamsLocation, 0.4f);break;case 5:setFloat(mParamsLocation,0.33f);break;default:break;}}
而这个setFloat方法,就是修改shader文件里面的params的值 setFloat的代码主要如下
protected void setFloat(final int location, final float floatValue) {runOnDraw(new Runnable() {@Overridepublic void run() {GLES20.glUniform1f(location, floatValue);}});}
到这里美白的filter就算写完了。接下来我们就要在CamreaDrawer类中添加上美白filter的使用了。
mBeautyFilter = new MagicBeautyFilter();
然后在onSurfaceCreated中进行初始化
mBeautyFilter.init();
在onSurfaceChanged中进行size的设置
mBeautyFilter.onDisplaySizeChanged(mPreviewWidth,mPreviewHeight);mBeautyFilter.onInputSizeChanged(mPreviewWidth,mPreviewHeight);
然后在onDrawFrame方法中,如果当前是需要美白的话,就对数据进行美白绘制
if (mBeautyFilter != null && mBeautyFilter.getBeautyLevel() != 0){EasyGlUtils.bindFrameTexture(fFrame[0],fTexture[0]);GLES20.glViewport(0,0,mPreviewWidth,mPreviewHeight);mBeautyFilter.onDrawFrame(mBeFilter.getOutputTexture());EasyGlUtils.unBindFrameBuffer();mProcessFilter.setTextureId(fTexture[0]);}else {mProcessFilter.setTextureId(mBeFilter.getOutputTexture());}
基本过程就是,如果是要进行美白的话,就将纹理传递到美白的filter中,而美白的filter的onDrawFrame函数主要做了什么?这就涉及到了以后我们会说的给视频加滤镜之类的操作,我们在后面的文章中进行详细的解释。这里暂时就不深入了。需要知道的是,经过上述代码的操作,就成功的给视频加上了美白效果。
然后在CameraDrawer类中,对上层提供修改美白效果等级的接口
/**提供修改美白等级的接口*/public void changeBeautyLevel(int level){mBeautyFilter.setBeautyLevel(level);}public int getBeautyLevel(){return mBeautyFilter.getBeautyLevel();}
到这里,我们的美白fitler以及将其在核心的绘制类中使用,已经全部写完了,现在要做的就是,给项目上层ui,加上一些控制美白效果的代码。就不仔细说了 。
结语
Android视频编辑器(二)预览、录制视频加上水印和美白磨皮效果相关推荐
- Android 使用CameraX实现预览/拍照/录制视频/图片分析/对焦/缩放/切换摄像头等操作
1. CameraX架构 看官方文档 CameraX架构 有如下这一段话 使用CameraX,借助名为"用例"的抽象概念与设备的相机进行交互. 预览 : 接受用于显示预览的Surf ...
- Android OpenGL使用GLSurfaceView预览视频
Android OpenGL使用GLSurfaceView预览视频 第一章 相关知识介绍 在介绍具体的功能之前,先对一些主要的类和方法进行一些介绍,这样可以更好的理解整个程序 1.1 GLSurfac ...
- android 与后台实时视频,Android实时监控项目第四篇:后台线程发送预览帧视频数据...
还记得上篇提到的setPreviewCallback(Camera.PreviewCallback cb)函数吗?我们在开始预览帧视频之前,调用的它,这里要注意其内部的Camera.PreviewCa ...
- 直播视频跨浏览器预览方案(ffmpeg+VideoJS+H5)
直播视频跨浏览器预览方案(ffmpeg+VideoJS+H5)附源码 1.概述 2.前言 3.思路 4.详解 4.1.ffmpeg 4.1.1.配置方法 4.1.2.验证方法 4.1.3.启动方法 4 ...
- 视频软件Vegas预览画面很卡怎么解决?
做视频的小伙伴都知道,剪视频的时候最烦躁的就是卡顿,不能编辑,不能预览.最近很多同学就反映在使用Vegas的时候,预览窗口播放非常卡顿,有时候根本预览不了,这该如何解决呢? 方法一: 点击左侧轨道头中 ...
- Atitit.web预览播放视频的总结
Atitit.web预览播放视频的总结 1. 浏览器类型的兼容性(chrome,ff,ie) 1 2. 操作系统的兼容性 1 3. 视频格式的内部视频格式跟播放器插件的兼容性.. 2 4. 指定播放器 ...
- windows 电脑图片/视频不展示预览图
文章目录 windows 电脑图片/视频不展示预览图 描述 可能原因及解决办法 1. Windows的缩略图预览功能被禁用 2. 缩略图预览功能卡住 3. 重新安装Windows Media Play ...
- vue 视频长传与预览
vue <el-col :span="11"><el-form-itemlabel="快学视频"prop="videoUrl&quo ...
- H5 Vue 视频 video 支持预览图 poster
最近项目上要求文章中插入视频,并且视频需要支持预览图,给用户更好的视觉效果.本来以为加上poster就够了,但是没想到 ios 微信内置浏览器会有如下这种效果,很影响视觉体验. 优化步骤: 1.上传视 ...
最新文章
- linxu passwd 给linux用户设置密码 命令
- poi 合并单元格_POI数据获取脚本分享
- Python进阶11-标准库介绍02
- 产品经理面试中那些不忍直视的奇葩题目,面试官你真是够了!
- 文件操作-小文件复制
- noip2011day1题解
- 联想MWC大秀另一面AI实力,BAT为此转型以求
- Atitit 时间的展示格式与存储格式 目录 1.1. 赛事时间的格式起源	1 1.1.1. 六十[编辑]	1 1.2. 1h 12m 23s 模式 (可读性最好	2 1.3. 日常模式 1:45:
- Atitit 并发技术的选项 attilax总结 艾龙 著 1. 三大并发模型	1 2. 从可读性考虑 优先使用 并行工作者 多线程模式,不要使用异步流水线模式	2 2.1. 多线程模式方便全局
- 中华石杉Java面试突击第一季笔记一(消息队列)
- 文件打不开只读或服务器未响应,Mac的Word经常未响应怎么办
- OSG智能指针---Referenced类
- 【机器学习】机器学习模型迭代方法(Python)
- JPA映射数据库mysql表名,字段名大小写转化,下划线分割.
- 深层揭露百度缘何被黑
- 利用大数据挖掘创新市场监管新方式
- 计算机组成原理 程序计数器和寄存器的长度
- 8个快速提高用户忠诚度的套路,运营人必备
- 乐理基础知识-5.和弦
- 利用开源软件架设中小型私有云存储系统【简要选型】
热门文章
- 记一次被虐的很惨的面试
- CDH集群中HDFS单点故障解决方案:HA模式(High Availability)
- 0基础学MySQL数据库—从小白到大牛(20)大小写规范、sql_mode的合理设置
- 不一样的“中国速度”,数据可视化交通运输大屏,带你见证中国高铁
- 迎国庆,2021新款苹果 iPad,包邮送一台!
- 2022年煤矿探放水题库及模拟考试
- php c端,蛋白测序(N端,C端测序)
- NeurIPS 2020 | 基于协同集成与分发的协同显著性目标检测网络
- 一键安装微信已完成,编号10,欢迎品尝
- 360查出 HEUR/Malware.QVMxx.Gen 病毒含义