原因:由于涉及到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实现的编码和遇到的问题,暂时没法解决,后续解决,如果有道友知道希望能够告知。