太久没写博客了,由于工作,过年还有孩子出生搞得自己焦头烂额,现在有些时间了就搞点东西。发现浏览量突破10万了,也是挺高兴的,虽然很多东西写的不好,可也看到了自己的进步,也是前年到现在的累积。刚开始我只是学习视频解码,渲染和视频编码,慢慢的也开始搞音频了,本来没想过搞视频编辑这一块的,慢慢的做着做着就接触到了,也没想到会搞成一个系列,等完成了再好好整理一下,废话不多说开始说正题。

转gif由于文件大小问题降低了帧率所以看起来卡其实不卡

这篇文章只讲画面部分,音频部分在我以前的文章有android 使用MediaCodec和lamemp3对多段音频进行截取和拼接,以后再写一篇相结合的文章,真正实现一个有画面有声音的视频裁剪和合并。做音频拼接知道有些参数要一样才可以进行拼接,视频也一样,主要是帧速率和分辨率要一样,因为用到opengl,所以分辨率这一块可以通过计算解决,而帧速率可以进行丢帧处理,选最小的帧速率为参数,对大帧速率的视频进行丢帧。

首先裁剪视频,裁剪视频用的是我的这篇文章android 简单的视频编辑功能

主要用到这些参数,这是纯画面的,先不处理声音

已经加入了音频

    //视频路径private String videoFile;//裁剪的坐标长宽private int cropLeft;private int cropTop;private int cropWidth;private int cropHeight;//开始结束时间private long startTime;private long endTime;//帧时间private long frameTime;

得到复数的视频开始统一参数

//这里遍历数据,选择最小的参数用来初始化编码器cropWidth = decoderList.get(0).getCropWidth();cropHeight = decoderList.get(0).getCropHeight();int frameRate = decoderList.get(0).getFrameRate();frameTime = decoderList.get(0).getFrameTime();//宽高选择方案//以最小宽高来显示还是根据宽高比计算最小显示分辨率//我这选用根据宽高比计算最小显示分辨率float sh = cropWidth*1.0f/cropHeight;if(decoderList.size() != 1) {for (VideoDecoder holder : decoderList) {float vh = holder.getCropWidth()*1.0f/holder.getCropHeight();if( sh < vh){cropWidth = Math.min(cropWidth,holder.getCropWidth());cropHeight = (int) (cropWidth*vh);//宽高不能是奇数if(cropHeight%2 != 0){cropHeight = cropHeight - 1;}}//cropWidth = Math.min(cropWidth,holder.getCropWidth());//cropHeight = Math.min(cropHeight,holder.getCropHeight());//选最小帧速率,对帧素率大的进行丢帧处理frameRate = Math.min(frameRate, holder.getFrameRate());//帧时间,帧速率越小,帧时间越大frameTime = Math.max(frameTime, holder.getFrameTime());}}//防止MediaFormat取不到帧速率if(frameRate < 1){frameRate = (int) (1000/(frameTime/1000));}//编码参数int BIT_RATE = cropWidth*cropHeight*2*8;MediaFormat mediaFormat = MediaFormat.createVideoFormat(VIDEO, cropWidth, cropHeight);mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);

初始化编码器

        //创建编码器try {videoEncode = MediaCodec.createEncoderByType(VIDEO);} catch (IOException e) {e.printStackTrace();}videoEncode.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

将编码器生成的Surface传给opengl来获取opengl的显示画面

        //将编码器生成的Surface传给opengl来获取opengl的显示画面Surface surface = videoEncode.createInputSurface();videoEncode.start();//初始化opengleglUtils = new EGLUtils();eglUtils.initEGL(surface);framebuffer = new GLFramebuffer();framebuffer.initFramebuffer(cropWidth,cropHeight);surfaceTexture = framebuffer.getSurfaceTexture();surfaceTexture.setDefaultBufferSize(cropWidth,cropHeight);

解码开始前要计算显示的画面

            //计算裁剪的显示画面float f = decoder.getCropLeft()*1.0f/decoder.getVideoWidth();float t = 1.0f - decoder.getCropTop()*1.0f/decoder.getVideoHeight();float r = (decoder.getCropLeft()+decoder.getCropWidth())*1.0f/decoder.getVideoWidth();float b = 1.0f - (decoder.getCropTop()+decoder.getCropHeight())*1.0f/decoder.getVideoHeight();float[] textureVertexData = {r, b,f, b,r, t,f, t};//将要显示的画面传给openglframebuffer.setVertexDat(textureVertexData);framebuffer.setRect(decoder.getCropWidth(),decoder.getCropHeight());

创建解码器

        //创建解码器try {videoDecoder = MediaCodec.createDecoderByType(getMime());videoDecoder.configure(format, surface, null, 0);videoDecoder.start();} catch (IOException e) {e.printStackTrace();}

根据时间进行解码

            int outIndex = videoDecoder.dequeueOutputBuffer(info, 50000);if(outIndex >= 0){//根据时间进行解码if(info.presentationTimeUs >= videoHolder.getStartTime() &&info.presentationTimeUs <= videoHolder.getEndTime()){//解码一帧完成后回调if(decoderListener != null){decoderListener.onDecoder(info.presentationTimeUs,videoHolder);}}videoDecoder.releaseOutputBuffer(outIndex, true);//判断是否裁剪内部分解码完成if(info.presentationTimeUs >= videoHolder.getEndTime()){break;}}

解码出的图像进行处理

        surfaceTexture.updateTexImage();surfaceTexture.getTransformMatrix(mSTMatrix);GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);GLES20.glClearColor(0f,0f,0f,0f);GLES20.glViewport(rect.left, rect.top, rect.right, rect.bottom);GLES20.glUseProgram(programId);GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffers[0]);GLES20.glEnableVertexAttribArray(aPositionHandle);GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,12, 0);GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffers[1]);GLES20.glEnableVertexAttribArray(aTextureCoordHandle);GLES20.glVertexAttribPointer(aTextureCoordHandle, 2, GLES20.GL_FLOAT, false, 8, 0);GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);GLES20.glActiveTexture(GLES20.GL_TEXTURE0);GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);GLES20.glUniform1i(uTextureSamplerHandle,0);GLES20.glUniformMatrix4fv(uSTMMatrixHandle, 1, false, mSTMatrix, 0);GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

进行编码

ByteBuffer encodedData = videoEncode.getOutputBuffer(inputIndex);if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {info.size = 0;}if (info.size != 0) {encodedData.position(info.offset);encodedData.limit(info.offset + info.size);info.presentationTimeUs = timeUs;//填充数据mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info);Log.d("==============","timeUs = "+timeUs);timeUs = timeUs+frameTime;//结合presentationTimeUs - videoHolder.getStartTime() < startFrameTime//对帧速率大的进行丢帧startFrameTime = startFrameTime + frameTime;}videoEncode.releaseOutputBuffer(inputIndex, false);

详细的可以看我的demo

VideoMerge

现在没加进度条,处理的进度就看log打印,等整理好以后加上音频和进度条,成为一个可以看的demo

2019/3/27更新

因为音频要转码成MP3,再转成视频所以效率不太好,可是也找不到什么好方法将采样率,比特率和声道变一样的方法

在整合音频时代码做了大改动,画面核心代码没变只是放到其他类和函数内了,所以就不改博客了,可以下载demo,里面我也做了注释

Android使用MediaCodec和OpenGL对多段视频画面进行裁剪和拼接相关推荐

  1. 剪辑技巧!如何利用背景图片给多段视频画面四周添加黑色边框

    除了图片,其实视频四周也是可以通过添加边框来给人不一样的视觉享受,下面小编就用黑色图片为例,教大家如何为多段视频画四周添加边框. 材料准备: 一台WIN系统 的电脑 视频剪辑高手 多段视频素材,一张黑 ...

  2. 多段视频画面四周同时插入光晕效果并导出的详细步骤

    在视频画面中插入光晕效果,是在很多视频素材中经常被用到的效果.那么这么效果是怎么制作出来的呢?下面一起来试试. 预览视频效果 先看一下用视频剪辑高手快速给多段视频同时插入光晕效果 双击视频进去查看,该 ...

  3. 如何在保证画面完整的基础上,快速缩小整段视频画面

    随着制作视频的小伙伴越来越多,而在剪辑过程中,就会遇到这样或者那样的问题,比如怎么在保证画面完整的基础上,缩小每段视频的画面呢?下面随小编一起来试试新技巧. 方法1:横屏改竖屏 在视频剪辑高手中,可以 ...

  4. 教你同时将每段视频画面倒放而不改变声音的方法

    最近有很多朋友在问,该如何剪辑视频,比如在倒放视频的同时不改变声音的呢?今天小编给大家分享一个新的技巧,下面一起来学习一下. 需要哪些工具? 视频剪辑高手 多段视频素材 怎么快速剪辑? 运行视频剪辑高 ...

  5. 如何将竖屏改横屏,并裁切多段视频画面

    在剪辑过程中,该如何将多段竖屏视频转为横屏,并裁切画面呢?今天小编给分享一个新的技巧,下面一起来试试. 材料准备: 一台Win系统电脑 视频剪辑高手 多段视频素材 步骤演示: 运行视频剪辑高手,在剪辑 ...

  6. Android使用MediaCodec硬解码播放H264格式视频文件

    前些时间,通过各种搜索加请教了好几个同行的朋友,在他们的指点下实现: RTSP+H264实时视频播放播放及把实时视频流保存到手机SD卡中,再对保存的H264格式文件进行播放等基本功能.非常感谢这些朋友 ...

  7. Android使用MediaCodec解码H264视频解码器

    前些日子有写了一篇博客[Android使用MediaCodec硬解码播放H264格式视频文件](http://blog.csdn.net/true100/article/details/5399293 ...

  8. QT利用opengl 进行视频裁剪、拼接,4宫格,9宫格

    一.概述 1.1 前言 在上一篇文章我们讲了Y420P视频数据如何裁剪.拼接.旋转等,但是缺点也很明显,一是工作量大,代码量较大.二是容错率低,因为涉及到大量的浮点型计算,导致在数据拷贝的时候存在误差 ...

  9. 教你自由裁切每段视频的画面,横竖屏都支持

    很多人在拍摄视频的过程中都是比较随意的,没有考虑到视频画面比例问题,导致很多视频在上传或者制作时遇到困难,那么该如何快速裁切多段视频画面,剪辑成横屏.或者竖屏呢?下面随小编一起来试试. 当视频是竖屏时 ...

最新文章

  1. 高级转录组分析和R数据可视化专题研讨会(2019.12)
  2. 相关子查询中exists后select 加数字的理解
  3. Codeforces Round #535 (Div. 3) [codeforces div3 难度测评]
  4. 一秒等于多少毫秒_使用kibana对电影一秒钟影评数据测试,详述配图演示如何使用收藏了...
  5. 在 Shell 脚本中执行语法检查调试模式
  6. 项目实战-linux下安装activeMQ
  7. Redis五种数据结构详解
  8. android中layout、drawable及styles的xml文件加载探索
  9. 从零开始玩转 logback、完整配置详解
  10. horizon服务主要模块_openstack七大模块概述
  11. 发光二极管pcb封装图画法_【AD封装】PH2.0座子插件贴片(带3D)
  12. c语言冒泡排序字母排序,排序与查找之冒泡排序篇(C语言实现)
  13. ajax 微信code获取_ajax实现微信网页授权登录
  14. Dreamweaver中出现 以下翻译器没有被装载,由于错误:xxxx.htm:有不正确的设置信息 问题的解决方案(8,cs3,cs4似乎都会出现改问题)...
  15. GitHub网页版开始教程
  16. Arcgis地籍图河流注记字体批量修改
  17. oracle ipad函数(从左边填充)
  18. linux mint 图标主题_Ubuntu/Linux Mint 用上仿 Win7/Win8 主题
  19. 怎样才能提取图片中的文字
  20. 旅游企业该怎么进行网络品牌推广呢?如何宣传和规划旅游商品?

热门文章

  1. boundschecher2
  2. 计算机没有开启还原的功能,win10系统无法开启系统还原功能的详细教程
  3. php fwrite 图片,PHP fwrite( )用法及代码示例
  4. 【Python】max()中key的使用
  5. IDEA 安装快捷键提示工具:Key promoter X
  6. Java String intern()方法
  7. 数据分类分析--聚类
  8. MemSQL性能测试结果
  9. Java发邮件(详解+源代码)
  10. (P9)awk:什么是awk,awk简单用法,awk脚本语法,awk执行过程