Android 音视频录制(3)——全关键帧视频录制(视频编辑必备)

概述

在看本篇文章之前请务必先查看这面三篇文章:

第一篇:Android音视频录制概述
第二篇Android音视频录制(1)——Surface录制
第三篇Android音视频录制(2)——Buffer录制

全关键帧录制顾名思义,就是视频所有帧都是关键帧(I帧),毫无疑问,全I帧的视频肯定会比正常录制的视频要大很多,但是为什么需要全I帧录制的视频?原因就是,大部分音视频app录制完视频之后都要对视频进行编辑吧,但是如果不是全I帧录制的视频文件,编辑起来会非常困难,而全I帧视频肯定很容易编辑。比如你要做一个时光倒流的功能,或者你要对视频加特效,哪怕最简单的,视频播放拖动进度(精确seek而非关键帧seek),全I帧视频肯定比普通视频流畅很多很多。所以全关键帧录制对于录制后的视频编辑是非常重要的。

那根本原因是什么?
我举一个例子,视频的seek操作:首先要知道视频的seek分两种,一种是精确seek,一种是关键帧seek(目前绝大部分的播放器都是关键帧seek)。

假设一个视频是3秒,视频帧为10帧/秒,关键帧间隔为1秒,那么这个视频第1帧为关键帧,第11帧为关键帧,第21帧为关键帧,比如我现在需要做一个seek操作,seek到1.3秒,如果采用的是关键帧seek,它会seek到第11帧(也有可能是第21帧,这个是看播放器是根据哪个原则来seek,一般有这三种关键帧seek原则,向下取关键帧/向上取关键帧/最近关键帧),而精确seek的话,是seek到13帧,这就是关键帧seek和精确seek的区别。但涉及到视频编辑的app无一例外,精确seek才是最符合产品设计的。

所以如果我对于非全关键帧的视频我要seek到1.3秒位置,应该如何做?

首先去到最近的关键帧,第11帧,然后根据H264的编码原理,一直根据这个关键帧解码到第13帧,这个过程非常非常耗时,无论你采用多么高级的解码器,多么高深的算法,这个过程都几乎不可优化。但是如果你这个视频是全关键帧录制的就不同了,直接seek到第13帧,用不着任何的解码,所以这个过程会非常的流畅。尤其遇到这样的产品需求的时候:拖动进度条,视频画面随进度滚动。如果产品需要精确seek,而且视频又不是全关键帧,这个时候就GG了。

所以很多的视频编辑都会设计到视频帧的重编解码,采用关键帧,会使这个重编解码的过程高效很多。

说了那么多,那么我下面就说说怎样录制全关键帧视频。再次说明,要看此篇文章,请务必先查看上面说的三篇文章。

全关键帧录制——buffer录制

1,配置关键帧间隔为0
在buffer录制中,我在编码器的prepared函数中有这样的一段代码:首先配置编码器的时候需要配置关键帧间隔为0

if(!mIsAllKeyFrame) {
      mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);  //单位是 秒
   }else{
       mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);//设置为0
   }

2,输入buffer的时候请求关键帧
在编码器的input()buffer时候,需要请求关键帧,需要在dequeueInputBuffer操作之前

        if(mIsAllKeyFrame){
            requestKeyFrame();
        }

请求关键帧代码:

@TargetApi(19)
    protected void requestKeyFrame() {
        if (mIsAllKeyFrame){
            try {
                Bundle reqKeyCmd = new Bundle();
                reqKeyCmd.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
                mMediaCodec.setParameters(reqKeyCmd);
            } catch (Exception e) {
            }
        }
    }

3,输出编码器buffer的时候也要请求关键帧
请求关键帧操作需要在dequeueOutputBuffer之前

if(mIsAllKeyFrame){
            requestKeyFrame();
        }

这样子我们得到的视频就是关键帧视频了。

全关键帧视频录制——Surface录制

1,Surface录制的时候也一样,配置编码器的时候设置关键帧间隔为0

 if(!mIsAllKeyFrame) {
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
        }else{
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);//设置全关键帧
        }

2,EGL绘制前需要请求关键帧,绘制后也需要请求关键帧(这里不同于buffer录制)

 //egl 绘制
    public void render(float[] surfaceTextureMatrix, float[] mvpMatrix) {
        if(isAllKeyFrame()){
            requestKeyFrame();
        }
        mRenderer.draw(surfaceTextureMatrix, mvpMatrix);
        if(isAllKeyFrame()){
            requestKeyFrame();
        }
    }

3,同样是在输出的时候需要请求关键帧,这个操作和buffer录制一样,都需要在dequeueOutputBuffer之前操作。

@Override
    public void output(boolean isEos) {
        if(isAllKeyFrame()){
            requestKeyFrame();
        }
        super.output(isEos);
    }

至此,关键帧视频录制的已经讲完了,欢迎小伙伴们的留言!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值