MediaCodec编码

原因:由于涉及到yuv的编码故使用android自带的MediaCodec模块进行硬件编码

概况:通过获取编码设备信息创建编码对象,然后设置编码参数,通过输入输出缓冲区进行数据的传递和读取。

流程如下:

首先获取当前设备支持的编解码设备信息:首先获取codc个数,然后判断是否是编码codec.

 m_codeccount = MediaCodecList.getCodecCount();//获取codec个数

for(int i=0;i<m_codeccount;i++)
{
    MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);//获取codecinfo
    
    if(info.isEncoder())//判断当前codec是否支持编码
    {
         String[] str = info.getSupportedTypes();//获取支持的类型
         
    }
}

通过上面获取的Codec类型有如下:

Type==audio/3gpp       Name==OMX.google.amrnb.encoder
Type==audio/amr-wb     Name==OMX.google.amrwb.encoder
Type==video/hevc       Name==OMX.qcom.video.encoder.hevc
Type==video/mp4v-es    Name==OMX.qcom.video.encoder.mpeg4
Type==video/avc        Name==OMX.qcom.video.encoder.avc

根据获取的Codec类型和名字创建编码设备如下:可以看出有两种方式进行创建Codec.当前设备小米5S,如果使用ByName则会发现最后关键帧间隔设施不起作用,且最终输出的帧没有B帧.而使用ByType则可以使关键帧间隔生效和生成B帧.具体内部原因希望有了解的同学解惑。

m_codec = MediaCodec.createByCodecName(m_strName);

m_codec = MediaCodec.createEncoderByType(m_strType);

创建完成Codec后就是设置编码参数,此时涉及到有些参数没有设置最终Configure失败的问题:

vformat.setInteger(MediaFormat.KEY_FRAME_RATE,m_Fps);//帧率
vformat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,m_Gop);//关键帧间隔单位为秒
vformat.setInteger(MediaFormat.KEY_WIDTH,m_W);//宽
vformat.setInteger(MediaFormat.KEY_HEIGHT,m_H);//高
vformat.setInteger(MediaFormat.KEY_BITRATE_MODE,m_Mode);//编码模式 目前使用CBR
vformat.setInteger(MediaFormat.KEY_BIT_RATE,m_BitRate * 1000);//单位为bps
vformat.setInteger(MediaFormat.KEY_COLOR_FORMAT,m_Color);//样本类型,目前只有420p和420sp两种.
vformat.setInteger(MediaFormat.KEY_PROFILE,m_Profile);//目前使用main
vformat.setInteger(MediaFormat.KEY_LEVEL,m_Level);//目前使用2.0

m_codec.configure(vformat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);//配置编码信息

配置完成后需要使流程启动:通过介绍可以看出该方法必须在configure之后调用

m_codec.start();//成功配置完成后调用开始

获取输入和输出队列缓存区,用于输入和输出数据:通过介绍可以看出必须在start之后调用。可以简单理解为start内部完成了缓冲区的初始化和连接。

 m_InputBuffer = m_codec.getInputBuffers();//获取输入缓冲区 需要start之后调用.
 m_OutputBuffer = m_codec.getOutputBuffers();//获取输出缓冲区

接下来进行输入数据进行编码,此时涉及到两种方式一种为同步,一种为异步,异步的话需要实现MeidaCodec.Callback接口,通过内部提供的接口函数实现数据的传递和读取,而同步则只需进行同步调用即可如下:细节为时间戳设置,如果时间戳没有设置那么就会导致取数据失败的问题。

int iIndex = m_codec.dequeueInputBuffer(m_TimeDelay);//获取一个可用的输入缓冲区,当前延时时间手动设置,-1:表示INFINITE
if (iIndex >= 0) {
  ByteBuffer input = m_InputBuffer[iIndex];//获取输入队列中一个空闲的成员
  input.clear();
  input.put(data, 0, iSize);//填充数据

  long presentationtimeus =1000 * (iCount * 20);//用于设置时间戳 microseconds

   m_codec.queueInputBuffer(iIndex, 0, iSize, presentationtimeus,0);//填充一个ByteBuffer到输入队列,最后一个参数
 
  }else{
     Log.d(TAG,"dequeueInputBuffer Failed.");
     return false;
  }

数据读取如下:细节为缓存的回收,否则会导致传入数据时获取缓存阻塞.

 //同步获取编码后数据
int iIndex2 = m_codec.dequeueOutputBuffer(m_pBufferInfo, m_TimeDelay);
if (iIndex2 >= 0)
{
  ByteBuffer output = m_OutputBuffer[iIndex2];
  output.position(m_pBufferInfo.offset);//设置缓冲区起始位置
  output.limit(m_pBufferInfo.offset + m_pBufferInfo.size);//设置缓冲区有效区间

  output.get(outData,0,m_pBufferInfo.size);//将有效数据写入数组
  iLen[0] = m_pBufferInfo.size;

  Log.d(TAG,"output offset="+m_pBufferInfo.offset+"Size"+m_pBufferInfo.size);

  m_codec.releaseOutputBuffer(iIndex2,false);//回收缓存
}
  

关键帧强制输出代码:设置一次后将会一直输出I帧,如何取消?

if(iCount % 10 == 0)设置一次将会一直输出I帧
{     
 Bundle params = new Bundle();
 params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
 m_codec.setParameters(params);//app level 19  //强制I帧
 Log.d(TAG,"setParameters");
}

问题:利用MediaCodec如何设置B帧输出?强制关键帧输出?

 

总结:以上就是利用MeidaCodec实现的编码和遇到的问题,暂时没法解决,后续解决,如果有道友知道希望能够告知。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值