在上一篇文章中提到过关于对于某些机型的安卓手机使用mediacodec时会出现绿条,需要解决绿条的方法或许有很多,同时在网上很多文章都是讲解在解码端来解决这个问题的,在解码端处理YUV数据来实现裁剪来将绿条去掉。
当然本文讲的方法是跟之前网络上不同的,本文的方法是从发送数据端的数据进行处理的,即直接处理源数据的分辨率来适配接收端的mediacodec分辨率,使其绿条部分被自动裁减掉了。核心思想如下:
(1)由于mediacodec对于某些机型而言,当解码1920*1080时,需要分辨率其实是1920*1088,因此我们如果再次发送1920*1080的数据给mediacodec进行解码时,然后再使用surface渲染时会底部数据出现绿条或者异常的情况。
(2)由于(1)的问题出现,因此可以考虑将原来1920*1080的图像转换为1920*1088的图像,但我发现如果转为这个分辨率时,我使用ffmpeg时会发现编码出来的凸显出现红线,这个应该是跟分辨率不是常规的分辨率有关。如下图所示:
图1920*1088编码后的h264图像(出现红线)
(3)考虑到(2)出现的情况,就再次尝试,将1920*1080转换为1920*1100,此时发现使用mediacodec解码出来的数据就没有了红线和绿边,但是对于底部会少了一点点图像,这种效果就有点类似于从源数据进行裁剪的效果了。如下图所示,可以看到使用了1920*1100图像时底部会有一部分缺失:
图1920*1100使用mediacodec解码后显示的图像(底部有缺失)
图1920*1080原来的图像
(1)接下来是给出采集发送端关键代码相关的部分:
H264编码器分辨率设置:
//针对1920*1080分辨率
if (in_w == 1920)
{
if (in_w % 16 != 0)
{
pCodecCtx->width = (in_w / 16 + 2) * 16;
printf("111111111pCodecCtx->width:%d\n", pCodecCtx->width);
}
else
{
pCodecCtx->width = in_w;
}
if (in_h % 16 != 0)
{
pCodecCtx->height = (in_h / 16 + 3) * 16;
printf("111111111pCodecCtx->height:%d\n", pCodecCtx->height);
}
else
{
pCodecCtx->height = in_h;
}
}
使用libyuv进行转码的关键代码:
if (pFrameTmpChangeStatus->width == 1920)
{
//进行分辨率转换
ret = libyuv::I420Scale(
(const uint8_t*)pFrameTmpChangeStatus->data[0], pFrameTmpChangeStatus->linesize[0],
(const uint8_t*)pFrameTmpChangeStatus->data[1], pFrameTmpChangeStatus->linesize[1],
(const uint8_t*)pFrameTmpChangeStatus->data[2], pFrameTmpChangeStatus->linesize[2],
1920, 1080,
(uint8_t*)pFrame->data[0], pFrame->linesize[0],
(uint8_t*)pFrame->data[1], pFrame->linesize[1],
(uint8_t*)pFrame->data[2], pFrame->linesize[2],
1920, 1100, libyuv::kFilterBox);
}
(2)关于解码端mediacodec设置的相关代码,针对1920*1080
int width = 1920;
int height = 1080;
//设置渲染类型
whlglSurfaceView.getWlRender().setRenderType(WhlRender.RENDER_MEDIACODEC);
//whlglSurfaceView.getWlRender().setRenderType(WhlRender.RENDER_YUV);
String mime = WHLVideoSupportUtil.findVideoCodecName(codecName);//寻找对应的mediacodec的名字
mediaFormat = MediaFormat.createVideoFormat(mime, width, height);
//mediaFormat = MediaFormat.createVideoFormat(mime, 1920, 900);
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, width*height);//设置最大输入解码器的数据大小
mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(csd_0));//设置sps信息
mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(csd_1));//设置pps信息
MyLog.d(mediaFormat.toString());
mediaCodec = MediaCodec.createDecoderByType(mime);
info = new MediaCodec.BufferInfo();//创建info
//mediaFormat = MediaFormat.createVideoFormat(mime, 1920, 1080);
//mediaCodec.configure(mediaFormat, null, null, 0);//获取相应的解码后的数据,不直接使用surface进行渲染
if(isFirstBindSurface != 1)
{
mediaCodec.configure(mediaFormat, surface, null, 0);//直接进行渲染
isFirstBindSurface = 1;
}
//mediaCodec.configure(mediaFormat, null, null, 0);//获取相应的解码后的数据,不直接使用surface进行渲染
mediaCodec.start();
补充说明,本文mediacodec的初始化是使用的是1920*1080的参数,同时本文目前还没调试好其他分辨率的情况,如果使用其他分辨率的话,本文提供一种方法:
(1)将其他分辨率先转换为1920*1100分辨率的
(2)送入到mediacodec进行解码,就可以消除绿边;
(3)当然考虑到效率的问题的话,可以再尝试自行调通其他分辨的参数来实现去除绿边。
最后,本文的方法是一种类似穷举的办法,虽然可行但通用性不是很强,将来如果想到更好的解决方法我也会更新分享出来,如果你们也有更好的方法希望也能留言给我,相互学习交流。