美文网首页音视频
Android硬编转码相关问题

Android硬编转码相关问题

作者: hopewang1001 | 来源:发表于2017-09-26 22:29 被阅读0次

    Android MediaCodec转码相关问题


    最近在测试各种编码的性能的时候涉及到了android硬编,由于测试的指标有PSNR、SSIM等质量指标,中间遇到了一些坑,稍微记录一下。

    硬编的常见用法

    大多数实际工程中,硬解或者硬编都是通过Surface中转来做的,经过Surface我们可以做很多事情,比如这个Surface可以是SurfaceView的Surface,或者我们想用Opengl作一些事情,加滤镜或者编辑视频,Surface的方式都提供了方便的解决方式,这也是我们搜索MediaCodec,最常见的使用方式。bigflake(http://bigflake.com/mediacodec)列举出了很多示例代码,其中有一个google CTS代码DecodeEditEncode对于Surface的使用给出了很好的例子。

    如果我们想做一个硬解硬编的加速转码(至少相较ffmpeg软解软编还是快一些的),可以参考github项目android-transcoder,这里给出了比较完整的transcode的示例,但是真正想用起来还是需要不少改动的,比如给出的Strategy只支持几个简单的比例,Cancel接口有些问题,这里不具体说了。

    我最初测试的有一个很主要的目的是测试PSNR等指标,最初使用了ypresto的demo,但是最终得到的结果总是特别差,比如x264转码psnr能维持在40db的情况,经过Surface android硬解然后硬编基本智能再25db左右(我们最终对比PSNR是将结果mp4解码为YUV,然后直接再YUV三个channel上计算平均PSNR)。但是肉眼看起来其实硬编结果和x264甚至和原视频相差并不大,

    硬编转码PSNR很低的问题所在

    想到自己平时写shader用gpu加速来进行yuv转rgb的时候用到了yuv转RGB的转换公式,可以参考另外一篇记录color space2中YUV families的介绍,或者直接看一个综合的整理的bt601 & bt709 color matrix,比如bt601 tv range的YUV转RGB的公式为:

    bt601 tv range YUV2RGB
    bt601 tv range RGB2YUV
    看一下这里的YUV2RGB的转换公式,得到的RGB是可能有负值的,比如0, 255, 255得到的RGB就是负值,这里可能有人会说这个公式是针对tv range(也有叫studio range的,就是Y 16-235,UV 16-240),我说一下原因:
    • 1,很多情况下尤其是国内的很多厂商录制出来的视频根本不带着color range信息,即使带着,经过各种渠道的转存流转,基本什么信息也都没有了
    • 2,实际上硬解中是有KEY_COLOR_RANGE参数的,能设置两种值COLOR_RANGE_FULL和COLOR_RANGE_LIMITIED,就是对应着full range和tv range,但是更坑爹的是很多国内厂商根本没有做实现。这里表扬一下良心华为,录制出来的视频都有着完整的color range、color space、color transfer信息,华为录制出来的基本都是tv range视频,在<=480p的时候使用bt601,否则使用HDTV标准bt709,苹果录制出来的m4v文件也是比较良心的。测试了vivo x7录制出来的视频这几个信息都是空着的,难道公司的主要精力都放在了“2000W柔光自拍”上?
    • 3,即使我们使用tv range内的YUV值,比如(16,240,240)也会算出来的Green是负值。其实tv range中的很多YUV值都会算出来RGB值是负的

    回归正题,我们显示任何东西,最终都是以RGB的形式显示的,也就是说我们在Surface中的显示也是RGBA的color space,但是实际上上RGBA的值都必须是正值,负值是无法显示的,所以我们自己写shader的时候或者直接交给Surface处理的时候,需要对RGBA的值做clamp处理,裁剪到0-255范围内,也就是负值变成0,超过255的变成255。然后我们对这个clamp以后的RGB值如果再转回YUV值,就跟原始的YUV值相差比较大了。有人会觉得这样不会变色了吗?其实并不会!举个例子:

    以yuv(0,0,0)为例(也可以拿16,240,240试试)
    1. 先减128 。 (0,-128,-128)
    2. 转换为RGB并作clamp  RGB (-179.456,135.424,-226.816)->(0,135,0)
    3. 转YUV (79.245,-44.685,-56.565)
    4. 色度加128 YUV(79.245,83.315,71.435)
    5. 取整,YUV(79,83,71)  转换完毕的值
    再转回去:
    6. 先减128  YUV (79,-45,-57)
    7. 转换为RGB并作clamp  RGB (-0.914,135.178,-0.74)->(0,135,0)
    我们发现颜色是一样的,再转一下,看看YUV还会不会变化
    8. 转YUV (79.245,-44.685,-56.565)
    9. 色度加128 YUV(79.245,83.315,71.435)
    10. 得到YUV(79,83,71)  转换完毕的值
    

    上面这个例子我们发现(0,0,0) (79,83,71)实际对应的RGB是一样的,也就是人眼看起来是一样的。但是这样的处理过程对我们比较PSNR造成了很大的问题,计算出来的PSNR很低。x264进行转码却不会有这个问题,还没有自习看ff和x264转码的源码,有待确认具体是怎么个实现,猜测是没有对RGB做clamp,或者根本没有中转RGB。

    其他方式做转码

    在api 21后,android提供了Image类来做硬解的输出和硬编的输入,Image类基本类似于ffmpeg的AVFrame,里面包含帧图像的各种信息和各个plane,以及stride、width、height、crop x、crop y等信息,也就是说我们可以从Image中得到decode以后的YUV raw data,我们可以这样的到decode 出来的Image:

    int result = mDecoder.dequeueOutputBuffer(mBufferInfo, timeout);
    Image image = mDecoder.getOutputImage(result);
    

    对于decode以后直接存储为YUV raw data file可以参考VideoToFrames github code(对应的博客是android高效解码得到YUV file)

    未完待续


    References

    1. Image class & YUV_420_888
    2. YUV_420_888 convert to NV21 & I420
    3. Anroid MediaCodec stuffs
    4. VideoEncoderDecoderTest
    5. Camera and MediaCodec colorspace not match, with images, stack over flow issue
    6. color format of camera and mediacodec, google disscuss
    7. CTS samples EncodeDecodeTest
    8. How to get stride of encoder
    9. ffmpeg command: how to convert to YUV file and how to display it
    10. VideoToYUVFrames github sample code
    11. Android MediaCodec transcoder sample demo, with surface
    12. NV21 NV12 I420 YV12
    13. CTS codecUtils
    14. google CTS
    15. YUV RGB convert
    16. android mediacodec color formats
    17. CTS code: DecodeEditEncodeTest

    相关文章

      网友评论

        本文标题:Android硬编转码相关问题

        本文链接:https://www.haomeiwen.com/subject/zeneextx.html