Android Camera2采集并编码为H.264

前言

本篇博文主要讲述的是基于Android原生MediaCodec通过Camera2 API进行图像数据采集并编码为H.264的实现过程,如果对此感兴趣的不妨驻足观看,也欢迎大家大家对本文中描述不当或者不正确的地方进行指正。如果对于Camera2预览还不熟悉的可以观看博主上一篇博文:Android 基于Camera2 API进行摄像机图像预览

MediaCodec简介

MediaCodec 主要是用于对音视频进行编解码。它通常与 MediaExtractor、MediaMuxer、Surface 和 AudioTrack 等组件一起使用。MediaCodec 支持硬件加速,可以利用设备的硬件资源来提高编解码的性能。

1、MediaCodec 编解码流程

MediaCodec 采用异步方式处理数据,使用一组输入输出缓冲区(ByteBuffer)。编解码流程大致如下:

  • 请求一个空的输入缓冲区,填充满数据后传递给 MediaCodec 处理。

  • MediaCodec 处理完数据后,将结果输出至一个空的输出缓冲区中。

  • 从 MediaCodec 获取输出缓冲区的数据,消耗掉里面的数据后,释放回编解码器。

具体流程可以参考下图:

在这里插入图片描述

2、MediaCodec 生命周期

在这里插入图片描述

从上图可以看出MediaCodec 的生命周期包括三种状态:Stopped、Executing、Released。

  • Stopped,分三种子状态:

    • Configured,MediaCodec实例创建后,调用configure方法后就进入了Configured状态

    • Uninitialized,MediaCodec实例被创建后,在调用configure方法前都处于该状态;

    • Error,MediaCodec遇到错误时进入该状态,通常可能是队列操作返回错误或异常导致的;

  • Executing,分三种状态

    • Flushed,在调用start()方法后MediaCodec立即进入Flushed子状态,此时MediaCodec会拥有所有的缓存。可以在Executing状态的任何时候通过调用flush()方法返回到Flushed子状态;

    • Running,一旦第一个输入缓存(input buffer)被移出队列,MediaCodec就转入Running子状态,这种状态占据了MediaCodec的大部分生命周期。通过调用stop()方法转移到Uninitialized状态;

    • End of Stream,将一个带有end-of-stream标记的输入buffer入队列时,MediaCodec将转入End-of-Stream子状态。在这种状态下,MediaCodec不再接收之后的输入buffer,但它仍然产生输出buffer直到end-of-stream标记输出

  • Released,当使用完MediaCodec后,必须调用release()方法释放其资源。调用 release()方法进入最终的Released状态;

编码实现

老规矩,依然需要对MediaCodec进行封装,在封装之前我们需要先设计下对应的接口,而根据上面了解到的MediaCodec生命周期中的几个状态我们需要对其进行记录,方便对外获取,因此接口设计如下:

interface ICodec {
    enum class State{
        IDLE,
        START,
        STOP,
        CLOSE
    }
    fun start()
    fun stop()
    fun close()
    fun getState():State
}

interface IVideoEncoder: ICodec 

这里的close对应的是MediaCodec的release接口。ICodec设计思想是考虑到后续会扩展出不止VideocEncoder还会有Decoder因此只包含了最原始的几个接口设计,针对Encoder和Decoder区别性的接口可以基于ICodec进行扩展继续添加,因为IVideoEncoder暂时没有需要额外添加的接口所以只是单纯的继承自ICodec,后面有需要再添加即可。

接着让我们再编写一个VideoCodec类并实现IVideoEncoder接口,开始我们的编码实现。

class VideoEncoder(private var params:VideoEncParams = VideoEncParams()):IVideoEncoder {
   
   
    private var state = ICodec.State.IDLE
   
    private var enccoder:MediaCodec? = null

    private var inputSurface:Surface? = null

    private var encodeThread:Thread? = null

    private var callback:EncoderCallback? = null
    
    fun setEncoderCallback(callback:EncoderCallback){
   
   
        this.callback = callback
    }
    
    interface EncoderCallback{
   
   
        fun onCallback(data:ByteArray,frameFlags:Int)
    }

VideoEncoder实现了IVideoEncoder接口并且构造时需要传入VideoEncParams,VideoEncParams我们等下再看,inputSurface是编码输入源,encodeThread用于异步进行编码,callback则是对外提供编码后数据的回调,回调并不仅仅只包含编码后的帧数据,还包含有一个Int类型的frameFlags,这个frameFlags可以理解为编码这一帧的类型,例如SPS帧或者关键帧等,现在我们回过头来看下传入的VideoEncParams:

class Vi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值