Android 仿抖音之使用OpenGL实现抖音视频录制

本文介绍如何在Android上实现类似抖音的视频录制功能,利用OpenGL显示摄像头数据,并通过MediaCodec进行视频编码,同时探讨了工程结构优化和FBO在处理视频效果中的应用,最终实现实时美颜、滤镜效果并录制视频。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在之前写了仿抖音的第一步,就是使用OpenGL显示摄像头数据,今天这篇就是在之前的基础上来录制视频,并且对之前的代码的结构进行了简单的整理,然后进行了仿抖音的视频录制。

工程结构整理

在仿抖音的第一步中封装了ScreenFilter类来实现渲染屏幕的操作,我们都知道在抖音的视频录制过程中,可以添加很多的效果进行显示,比如说磨皮、美颜、大眼以及滤镜等效果,如果把这些效果都放在ScreenFilter中,就需要使用很多的if else来进行判断是否开启效果,显而易见,这样的会显得项目结构不是很美好,我们可以将每种效果都写成一个Filter,并且在ScreenFilter之前的效果,都可以不用显示到屏幕当中去,所以可以使用FBO来实现这个需求,不懂 FBO的可以翻看上一篇的博客FBO的使用

但是这里有一个问题,就是在摄像头画面经过FBO缓冲,我们再从FBO中绘制到屏幕上去,这里的ScreenFilter获取的纹理是来自于FBO中的纹理,也就是OpenGL ES中的,所以不再需要额外扩展的纹理类型了,可以直接使用sampler2D类型,也就意味着ScrennFilter,

  1. 开启效果:使用sampler2D
  2. 未开启效果:使用samplerExternalOES

那么就需要ScreenFilter使用if else去判断,很麻烦,所以我们可以不管摄像头是否开启效果都先将摄像头数据写到FBO中,这样的话,ScreenFilter的采样数据始终都可以是sampler2D了。也就是下面这种结构:

结构1.jpg

需求

长按按钮进行视频的录制,视频有5种速度的录制,极慢、慢、正常、快、以及极快,抬起手指时候停止录制,并将视频保存以MP4格式保存在sdcard中。
(抖音的视频录制在录制完成以后显示的时候都是正常速度,这里我为了看到效果,保存下来的时候是用当前选择的速度进行显示的)。

分析需求

想要录制视频,就需要对视频进行编码,摄像头采集到的视频数据一般为AVC格式的,这里我们需要将AVC格式的数据,编码成h.264的,然后再封装为MP4格式的数据。对于速度的控制,可以在写出到MP4文件格式之前,修改它的时间戳,就可以了。

实现需求
MediaCodec

MediaCodec是Android4.1.2(API 16)提供的一套编解码的API,之前试过使用FFmpeg来进行编码,效果不如这个,这个也比较简单,这次视频录制就使用它来进行编码。MediaCodec使用很简单,它存在一个输入缓冲区和一个输出缓冲区,我们把要编码的数据塞到输入缓冲区,它就可以进行编码了,然后从输出缓冲区取编码后的数据就可以了。

还有一种方式可以告知MediaCodec需要编码的数据,

 /**
     * Requests a Surface to use as the input to an encoder, in place of input buffers.  需要一个Surface作为需要编码的数据,来替代需要输入的数据
     */
    @NonNull
    public native final Surface createInputSurface();

这个接口是用来创建一个Surface的,Surface是用来干啥的呢,就是用来"画画"的,也就是说我们只要在这个Surface上画出我们需要的图像,MediaCodec就会自动帮我们编码这个Surface上面的图像数据,我们可以直接从输出缓冲区中获取到编码后的数据。之前的时候我们是使用OpenGL绘画显示到屏幕上去,我们可以同时将这个画面绘制到MediaCodec#createInputSurface() 中去,这样就可以了。

那怎么样才能绘制到MediaCodec的Surface当中去呢,我们知道录制视频是在一个线程中,显示图像(GLSurfaceView)是在另一个GLThread线程中进行的,所以这两者的EGL环境也不同,但是两者又共享上下文资源,录制现场中画面的绘制需要用到显示线程中的texture等,那么这个线程就需要我们做这些:

  1. 配置录制使用的EGL环境(可以参照GLSurfaceView怎么配置的)
  2. 将显示的图像绘制到MediaCodec中的Surface中
  3. 编码(h.264)与复用(mp4)的工作
代码实现

MediaRecorder.java
视频编码类

 public void start(float speed) throws IOException {
   
        mSpeed = speed;
        /**
         *     配置MediaCodec编码器
         */
        //类型
        MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);
        //参数配置
        //1500kbs 码率
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1500_00);
        //帧率
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
        //关键帧间隔
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 20);
        //颜色格式
        //从Surface当中获取的
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        //编码器
        mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
        //将参数配置给编码器
        mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

        //交给虚拟屏幕 通过OpenGL 将预览的纹理 会知道这一个虚拟屏幕中
        //这样MediaCodec就会自动编码mInputSurface当中的图像了
        mInputSurface = mMediaCodec.createInputSurface();
        /**
         * H264
         * 封装成MP4文件写出去
         */
        mMediaMuxer = new MediaMu
Android 仿APP下拉刷新功能,首先分析这个效果的实现思路,大致如下:   1、上拉时页面有翻页效果,可以用scrollview的pagingEnabled来实现,也就是说列表页不管你用tableview还是collectionview,只要每个cell是全屏的就可以。   2、下拉:当页面不是停留在第一个cell时,下拉就只是scrollView的滚动效果,不会触发刷新,当页面停留在第一个cell,也就是说scrollView.contentOffset.y = 0的时候,手指下拉才会触发刷新效果,并且下拉时scrollView不动,也就是没有scrollview的弹性效果,因此scrollView.bounces = NO。   3、既然下拉时scrollView不动,就不能使用代理来监听scrollView的滑动实现刷新,于是我想到了用touches的系列方法来监控手指下滑位移。   4、动画分解有五步:   (1)下拉时“推荐、附近”的那个导航条和“下拉刷新内容”的视图有渐隐渐显的效果,位置也随着手指下移,可以通过手指下滑位移计算alpha来实现   (2)下拉时,“下拉刷新内容”的视图右边那个有缺口的小圆环会随着手指滑动转圈,下滑时逆时针旋转   (3)下滑一定距离后如果不松手,又继续上滑,会执行前两步的反效果,圆环顺时针旋转,手指停在屏幕上,圆环就停止转动   (4)下滑到某个临界点,导航条和刷新视图都不再移动(此时导航条已经完全透明),所以可以通过计算起始点和当前点移动距离来计算透明度、位移、旋转角度,这些操作都在touchesMoved中实现   (5)到临界点松手后,导航条和刷新视图都回到原始位置,小圆环一直顺时针转圈,直到刷新结束,停止动画,隐藏刷新视图,显示导航条,如果没达到临界点就松手,不会触发刷新。。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值