HEVCProfileMain10HDR10 和 非8bit数据位深的一些讨论。

用过Android-MediaCodec做视频编解码的同学,应该都会见过这个定义变量——HEVCProfileMain10HDR10,其定义是在MediaCodecInfo.CodecProfileLevel类下,其定义如下。

        // from OMX_VIDEO_HEVCPROFILETYPE
        public static final int HEVCProfileMain        = 0x01;
        public static final int HEVCProfileMain10      = 0x02;
        public static final int HEVCProfileMain10HDR10 = 0x1000;

        // from OMX_VIDEO_HEVCLEVELTYPE
        public static final int HEVCMainTierLevel1  = 0x1;
        public static final int HEVCHighTierLevel1  = 0x2;
        public static final int HEVCMainTierLevel2  = 0x4;
        public static final int HEVCHighTierLevel2  = 0x8;
        public static final int HEVCMainTierLevel21 = 0x10;
        public static final int HEVCHighTierLevel21 = 0x20;
        public static final int HEVCMainTierLevel3  = 0x40;
        public static final int HEVCHighTierLevel3  = 0x80;
        public static final int HEVCMainTierLevel31 = 0x100;
        public static final int HEVCHighTierLevel31 = 0x200;
        public static final int HEVCMainTierLevel4  = 0x400;
        public static final int HEVCHighTierLevel4  = 0x800;
        public static final int HEVCMainTierLevel41 = 0x1000;
        public static final int HEVCHighTierLevel41 = 0x2000;
        public static final int HEVCMainTierLevel5  = 0x4000;
        public static final int HEVCHighTierLevel5  = 0x8000;
        public static final int HEVCMainTierLevel51 = 0x10000;
        public static final int HEVCHighTierLevel51 = 0x20000;
        public static final int HEVCMainTierLevel52 = 0x40000;
        public static final int HEVCHighTierLevel52 = 0x80000;
        public static final int HEVCMainTierLevel6  = 0x100000;
        public static final int HEVCHighTierLevel6  = 0x200000;
        public static final int HEVCMainTierLevel61 = 0x400000;
        public static final int HEVCHighTierLevel61 = 0x800000;
        public static final int HEVCMainTierLevel62 = 0x1000000;
        public static final int HEVCHighTierLevel62 = 0x2000000;

一、如何理解HEVC的Profile、Level和Tier?

Profile用来规定码流中使用了哪些编码工具和算法

Level规定了给定Profile、Tier所对应的解码器处理负担和存储容量参数。主要包括采样率(帧率)、分辨率、码率的最大值、压缩率的最小值、解码图像缓存区的容量(Decoded Picture Buffer, DPB)、编码图像缓存区的容量(Coded Picture Buffer, CPB)等。

Tier规定了每个Level的码率的高低

在编解码器的兼容性方面:

1.要求支持某个Profile的解码器必须支持该Profile及低于该Profile中的所有特性(向下兼容);

2.要求支持某个Level和Tier的解码器可以解码所有等于和低于这个Level和Tier的码流;

3.支持某一个Profile的编码器,并不要求它支持该Profile的所有特性,但是编码的码流必须符合HEVC的标准,才可被支持该Profile的解码器所解码。

1.1、档次(Profile)

HEVC常用的有三个Main Profile(version 1 profile):即常规8bit像素精度的Main Profile;支持10bit像素精度的Main 10 Profile;支持静止图像的Main Still Picture Profile。

Main Profile特性如下:

1.原始图像采样格式限制为4:2:0

2.CTB的大小从16×16到64×64

3.解码图像的缓存容量限制为6幅图像

4.允许选择波前和片划分方式,但是不能同时选择。如果选择片,其尺寸至少高为64像素,宽为256像素

Main 10 Profile的特性:

主要的特点和Main Profile类似,但是不同之处在于,它能够支持10比特深度。

Main Still Profile的特性:

主要特点和Main Profile类似,也仅支持8bit深度,但区别在于它不支持帧间预测编码。

1.2、水平(Level)

水平(Level)指出了一些对解码端负载和内存占用影响较大的关键参数的约束,这些参数主要包括:采样频率(帧率)、分辨率、码率的最大值,压缩率的最小值、解码图形缓冲区(DPB)的容量、编码图像缓冲区(CPB)的容量等。

此外,水平中还约束了每帧中垂直和水平方向的Tile的最大数量,以及每秒最大的Tile数量。HEVC共设置了13个水平,如下:

1.3、等级(Tier)

对同一水平,按照最大码率和缓存容量要求的不同,HEVC设置了两档等级,定义为高等级(High Tier)和主等级(Main Tier)。

主等级可用于大多数场合,涵盖了13个Level水平,它要求码率较低;高等级可用于特殊要求或苛刻要求的场合,包括4和4以上的8个水平,允许码率较高,在同一水平大约高3-4倍。

也就是说不同Profile有不同的Level,相同Level有两个Tier。

回头再来看Android.MediaCodecInfo.CodecProfileLevel里面的HEVCProfileMain(main 8bit)、HEVCProfileMain10(main 10bit)、HEVCProfileMain10HDR10(main 10bit 支持HDR,怎么个支持法?暂时还没深挖。)

可以用以下简单的代码检测当前Android设备是否支持HEVCProfileMain10HDR10

public boolean testHEVCProfileMain10HDR10() {
    boolean crashed = false;
    MediaCodec codec = null;
    try {
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, 720, 1280);

        codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);

        boolean support = checkAndSetHDR10ProfileLevel(codec, format);
        crashed = !support;

        codec.configure(format, null, null, 0);
        codec.start();
    }catch (Exception e){
        crashed = true;
        YMFLog.error(this, Constant.MEDIACODE, "testHEVCProfileMain10HDR10, reason:" + e.getMessage());
    } finally {
        if (codec != null) {
            try {
                codec.stop();
                codec.release();
            } catch (Exception ex) {
                YMFLog.error(null, Constant.MEDIACODE, "release test encoder error! reason:" + ex.getMessage());
            }
        }
    }
    return !crashed;
}

public boolean checkAndSetHDR10ProfileLevel(MediaCodec codec, MediaFormat format) {
    boolean isSupportMain10HDR10 = false;
    MediaCodecInfo.CodecProfileLevel[] mProfileLevels = codec.getCodecInfo().getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_HEVC).profileLevels;
    int candidateLevel = 0;
    int candidateProfile = 0;
    for (MediaCodecInfo.CodecProfileLevel info : mProfileLevels) {
        if (info.profile <= MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10) {
            if (candidateProfile < info.profile) {
                candidateProfile = info.profile;
                candidateLevel = info.level;
            } else if (candidateProfile == info.profile && candidateLevel < info.level) {
                candidateLevel = info.level;
            }
        }
    }
    format.setInteger(MediaFormat.KEY_PROFILE, candidateProfile);
    format.setInteger(MediaFormat.KEY_LEVEL, candidateLevel);
    YMFLog.info(this, Constant.MEDIACODE, "current MediaFormat : " + format.toString());
    if (candidateProfile == MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10) {
        isSupportMain10HDR10 = true;
    }
    return isSupportMain10HDR10;
}

二、10位深(10bit)?

研究HDR的肯定知道位深这一概念,(实在不知道就到这里)除了常见的 8bit yuv 外,还有 10bit,12bit,14bit,16bit 这样的yuv 数据。网上的资料都会直接跟你说,8bit以外的数据都是以2字节的来存储有效数据,并附带这一张图给你讲述存储的结构。

但其实这样说是不全面的!上图是P16格式下存储的10位有效数据。根据标准SMPTE,位深转换关系是:源数据乘以2^(n-m),这里的n是目标位深,也就是10、12或16,m是源位深度,目标例子是8;所以如果要把8bit yuv数据转成10bit,就是乘4,即左移两位。剩余padding位补0,即如下图所示。

(8bit转10bit有效位深存储结构示意图) 

上图格式简称P10,那么P12、P14、P16原理亦如此,所以网上流传的这图是P16格式的存储结构,只是编码器利用了(n)16位深存储的结构 去 保存了(m)10位有效数据而已!还记得上面介绍的,编解码器的兼容性方面:向下兼容特性吗?这里就是利用了该特性。

链接:https://pan.baidu.com/s/1_nmoK2lhHc3wzZx0KYD7sg 
提取码:lfeu

大家如果不能理解的话,还可以利用ffmpeg加深一下印象:到这领取10bit格式的解码视频yuv文件 A_720x1280x24_I010_HLG.yuv,利用指令播放yuv文件

ffplay -f rawvideo -video_size 720x1280 -pixel_format ??? A_720x1280x24_I010_HLG.yuv

其中 ??? 可以通过 ffmpeg -pix_fmts 查询当前你的ffmpeg所支持的格式,你觉得是用yuv420p16le 或 yuv420p10le?(le是little ending小端的意思,be是大端的意思,这个yuv文件是小端)大家可以都测试测试。

猜你喜欢

转载自blog.csdn.net/a360940265a/article/details/123671174