zoukankan      html  css  js  c++  java
  • 基于java解码H264 SPS码流(哥伦布编码)研究笔记

    1、获取SPS码流

    2、编写代码

    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 基于java解码H264 SPS码流研究笔记(哥伦布编码)
     * @author fu (参考资料:码牛学院)
     * @date 2021年10月22日 10:01 上午
     */
    public class ColumbusService {
        public int nStartBit = 0; // 解析起始位置
    
        /**
         * 0阶无符号指数哥伦布解码运算,每调用一次,返回一次结果
         * @param pBuff 需要解析的16进制
         * @return 返回解析出来的十进制
         *
         * 0阶无符号指数哥伦布编码过程(例如待编码5):
         *    1、将数字以二进制写出,5的二进制为101,因为0阶指数哥伦布编码所有不用去掉低位
         *    2、将上面的二进制+1,101加1为110,留下的比特数为3,3-1=2,所有需要增加前导0的个数为2
         *    3、因为第一步没有去掉,所有这一步不进行任何操作,最终生成的比特串为00110
         *
         */
        public int ue(byte[] pBuff){
            int nZeroNum = 0;
            /*
                根据哥伦布编码原理,先统计一个段1前面0的个数
                nZeroNum 目的只要得出哥伦布编码中,一个段的内容所占位数
                    例如:00110,所占位数为3,根据0阶哥伦布编码原理前面补齐2个0,即 nZeroNum=2
    
                代码逻辑原理:
                    0x80 ==> 1000 0000
                    由左向到右 (->) 的方向进行相与(&) --> 000 00110 & 000 10000
                    如结果返回0(即 1&0 = 0),则记录0的个数 ==> nZeroNum++
                    如结果返回1(即 1&1 = 1),匹配if判断(1 != 0), 则跳出循环,得到计算结果nZeroNum
                    同时,不管结果如何,起始位(nStartBit)都多加一位,为后面持续方法调用定位
             */
            while (nStartBit < pBuff.length * 8) {
                if ((pBuff[nStartBit / 8] & (0x80 >> (nStartBit%8))) != 0){
                    break;
                }
                nZeroNum++;
                nStartBit++;
            }
            nStartBit++;
            /*
                根据统计到的nZeroNum计算出实际内容十进制数
                例如:00110 ,nZeroNum=2 --> 十进制:5
    
                代码逻辑原理:
                    先计算一个段分割1后2位(nZeroNum)十进制数,例如:00110(二进制) --> 10(二进制) --> 2(十进制)
                    初始化后nZeroNum位,通过循环nZeroNum去计算,每向右进一位(dwRet <<= 1),相当于dwRet*2
                判断准则:
                    由于起始位(nStartBit)已经在分割1后,则继续由左向到右 (->) 的方向相与(&) --> 000001 10 & 000000 10
                    如结果返回1(即 1&1 = 1),则对结果dwRet累加1
                    如结果返回0(即 1&0 = 0),则跳过,继续循环判断
             */
            return (1 << nZeroNum) -1+ u(nZeroNum,pBuff);
        }
    
        // 将二进制转十进制
        public int u(int bitIndex, byte[] pBuff){
            int dwRet = 0;
            for (int i = 0; i < bitIndex; i++) {
                dwRet <<= 1;
                if ((pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8))) != 0) {
                    dwRet += 1;
                }
                nStartBit++;
            }
            return dwRet;
        }
    
        // 十六进制转byte数组
        public byte[] hexStringToByteArray(String s) {
            int len = s.length();
            byte[] bs = new byte[len/2];
            for(int i = 0;i < len;i+=2) {
                bs[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
            }
            return bs;
        }
    
        public Map<String,Integer> SPSInfo(String data){
            byte[] spsData = hexStringToByteArray(data.replace(" ", ""));
            this.nStartBit = 4*8;
            Map<String,Integer> sps = new HashMap<>();
    
            /*
                H.264码流在网络中传输时实际是以NALU的形式进行传输的
                每个NALU由一个字节的Header和RBSP组成.
                NAL Header 的组成为:
                    forbidden_zero_bit(1bit) + nal_ref_idc(2bit) + nal_unit_type(5bit)
    
             */
            // 禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。
            sps.put("forbidden_zero_bit",u(1, spsData));
            // nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。
            sps.put("nal_ref_idc",u(2, spsData));
            /*
                帧类型:
                    7-序列参数集(sps)
                    8-图像参数集(pps)
                    5-IDR图像(I帧)
                    6-补充增强信息单元(SEI)
             */
            sps.put("nal_unit_type",u(5, spsData));
    
            if(sps.get("nal_unit_type") ==7) {
                /*
                    编码等级:
                        66-Baseline(直播)
                        77-Main(一般场景)
                        88-Extended
                        100-High (FRExt)
                        110-High 10 (FRExt)
                        122-High 4:2:2 (FRExt)
                        144-High 4:4:4 (FRExt)
                 */
                sps.put("profile_idc",u(8, spsData));
                /*
                    当constrained_set0_flag值为1的时候,就说明码流应该遵循基线profile(Baseline profile)的所有约束
                    constrained_set0_flag值为0时,说明码流不一定要遵循基线profile的所有约束。
                    当constrained_set1_flag值为1的时候,就说明码流应该遵循主profile(Main profile)的所有约束
                    当constrained_set2_flag值为1的时候,就说明码流应该遵循主profile(Extended profile)的所有约束
                    注意:当constraint_set0_flag,constraint_set1_flag或constraint_set2_flag中不只一个值为1的话,那么码流必须满足所有相应指明的profile约束。
                 */
                sps.put("constraint_set0_flag",u(1, spsData));
                sps.put("constraint_set1_flag",u(1, spsData));
                sps.put("constraint_set2_flag",u(1, spsData));
                sps.put("constraint_set3_flag",u(1, spsData));
                sps.put("reserved_zero_4bits",u(4, spsData));
    
                /*
                    标识当前码流的Level。
                    编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。
                        10 - 1    (supports only QCIF format and below with 380160 samples/sec)
                        11 - 1.1  (CIF and below. 768000 samples/sec)
                        21 - 2.1  (Supports HHR formats. Enables Interlace support. 5 068 800 samples/sec)
                        30 - 3    (Supports SD/4CIF formats. Enables Interlace support. 10368000 samples/sec)
                        31 - 3.1  (Supports 720p HD format. Enables Interlace support. 27648000 samples/sec)
                        51 - 5.1  (Supports 4096x2304 format. Frame coding only. 251658240 samples/sec)
                            ......
                 */
                sps.put("level_idc",u(8, spsData));
    
                sps.put("seq_parameter_set_id",ue(spsData));
    
                if (sps.get("profile_idc") == 100) {
                    /*
                        与亮度取样对应的色度取样
                        chroma_format_idc 的值应该在 0到 3的范围内(包括 0和 3)
                        当 chroma_format_idc不存在时,应推断其值为 1(4:2:0的色度格式)
                        0 - 单色
                        1 - 4:2:0
                        2 - 4:2:2
                        3 - 4:4:4
                     */
                    sps.put("chroma_format_idc",ue(spsData));
                    /*
                        视频位深
                            0 - High 只支持8bit
                            1   High10 才支持10bit
                     */
                    sps.put("bit_depth_luma_minus8",ue(spsData));
                    sps.put("bit_depth_chroma_minus8",ue(spsData));
    
                    sps.put("qpprime_y_zero_transform_bypass_flag",u(1,spsData));
                    sps.put("seq_scaling_matrix_present_flag",u(1,spsData));
                }
                // 最大帧率
                sps.put("log2_max_frame_num_minus4",ue(spsData));
                // 确定播放顺序和解码顺序的映射
                sps.put("pic_order_cnt_type",ue(spsData));
                if (sps.get("pic_order_cnt_type") == 0){
                    sps.put("log2_max_pic_order_cnt_lsb_minus4",ue(spsData));
                }
                // 参考帧队列可达到的最大长度
                sps.put("num_ref_frames",ue(spsData));
                sps.put("gaps_in_frame_num_value_allowed_flag",u(1,spsData));
    
                // 本元素+1 指明以宏块为单位的图像宽度
                sps.put("pic_width_in_mbs_minus1",ue(spsData));
                // 本元素+1 指明以宏块为单位的图像高度
                sps.put("pic_height_in_map_units_minus1",ue(spsData));
    
                sps.put("frame_mbs_only_flag",ue(spsData));
                // 指明B帧的直接和skip模式下的运动矢量的计算方式
                sps.put("direct_8x8_inference_flag",u(1,spsData));
                // 解码器是否要将图片裁剪后输出,如果是,则后面为裁剪的左右上下的宽度
                sps.put("frame_cropping_flag",u(1,spsData));
                sps.put("vui_parameters_present_flag",u(1,spsData));
    
            }
            return sps;
        }
    
        public void FormatPrint(Map<String,Integer> sps){
            System.out.println("[0] seq_parameter_set()");
            System.out.println("  nal_unit()");
            System.out.println("    forbidden_zero_bit =                    " + sps.get("forbidden_zero_bit"));
            System.out.println("    nal_ref_idc =                           " + sps.get("nal_ref_idc"));
            System.out.println("    nal_unit_type =                         " + sps.get("nal_unit_type"));
            System.out.println("  profile_idc =                             " + sps.get("profile_idc"));
            System.out.println("  constraint_set0_flag =                    " + sps.get("constraint_set0_flag"));
            System.out.println("  constraint_set1_flag =                    " + sps.get("constraint_set1_flag"));
            System.out.println("  constraint_set2_flag =                    " + sps.get("constraint_set2_flag"));
            System.out.println("  constraint_set3_flag =                    " + sps.get("constraint_set3_flag"));
            System.out.println("  reserved_zero_4bits =                     " + sps.get("reserved_zero_4bits"));
            System.out.println("  level_idc =                               " + sps.get("level_idc"));
            System.out.println("  seq_parameter_set_id =                    " + sps.get("seq_parameter_set_id"));
            System.out.println("  if (profile_idc == 100)");
            System.out.println("    chroma_format_idc =                     " + sps.get("chroma_format_idc"));
            System.out.println("    bit_depth_luma_minus8 =                 " + sps.get("bit_depth_luma_minus8"));
            System.out.println("    bit_depth_chroma_minus8 =               " + sps.get("bit_depth_chroma_minus8"));
            System.out.println("    qpprime_y_zero_transform_bypass_flag =  " + sps.get("qpprime_y_zero_transform_bypass_flag"));
            System.out.println("    seq_scaling_matrix_present_flag =       " + sps.get("seq_scaling_matrix_present_flag"));
            System.out.println("  log2_max_frame_num_minus4 =               " + sps.get("log2_max_frame_num_minus4"));
            System.out.println("  pic_order_cnt_type =                      " + sps.get("pic_order_cnt_type"));
            System.out.println("  if (pic_order_cnt_type == 0)");
            System.out.println("    log2_max_pic_order_cnt_lsb_minus4 =     " + sps.get("log2_max_pic_order_cnt_lsb_minus4"));
            System.out.println("  num_ref_frames =                          " + sps.get("num_ref_frames"));
            System.out.println("  gaps_in_frame_num_value_allowed_flag =    " + sps.get("gaps_in_frame_num_value_allowed_flag"));
            System.out.println("  pic_width_in_mbs_minus1 =                 " + sps.get("pic_width_in_mbs_minus1"));
            System.out.println("  pic_height_in_map_units_minus1 =          " + sps.get("pic_height_in_map_units_minus1"));
            System.out.println("  frame_mbs_only_flag =                     " + sps.get("frame_mbs_only_flag"));
            System.out.println("  direct_8x8_inference_flag =               " + sps.get("direct_8x8_inference_flag"));
            System.out.println("  frame_cropping_flag =                     " + sps.get("frame_cropping_flag"));
            System.out.println("  vui_parameters_present_flag =             " + sps.get("vui_parameters_present_flag"));
    
    
        }
    
        public static void main(String[] args) {
            ColumbusService service = new ColumbusService();
            String data = "00 00 00 01 67 64 00 1f ac d9 40 50 05 bb 01 10 00 00 03 00 10 00 00 03 03 20 f1 83 19 60";
            service.FormatPrint(service.SPSInfo(data));
    
        }
    }
    

    3、输出结果:

    [0] seq_parameter_set()
      nal_unit()
        forbidden_zero_bit =                    0
        nal_ref_idc =                           3
        nal_unit_type =                         7
      profile_idc =                             100
      constraint_set0_flag =                    0
      constraint_set1_flag =                    0
      constraint_set2_flag =                    0
      constraint_set3_flag =                    0
      reserved_zero_4bits =                     0
      level_idc =                               31
      seq_parameter_set_id =                    0
      if (profile_idc == 100)
        chroma_format_idc =                     1
        bit_depth_luma_minus8 =                 0
        bit_depth_chroma_minus8 =               0
        qpprime_y_zero_transform_bypass_flag =  0
        seq_scaling_matrix_present_flag =       0
      log2_max_frame_num_minus4 =               0
      pic_order_cnt_type =                      0
      if (pic_order_cnt_type == 0)
        log2_max_pic_order_cnt_lsb_minus4 =     2
      num_ref_frames =                          4
      gaps_in_frame_num_value_allowed_flag =    0
      pic_width_in_mbs_minus1 =                 79
      pic_height_in_map_units_minus1 =          44
      frame_mbs_only_flag =                     0
      direct_8x8_inference_flag =               1
      frame_cropping_flag =                     0
      vui_parameters_present_flag =             1
    
    
  • 相关阅读:
    kubernetes 中遇见的一些坑(持续更新)
    Docker网络解决方案-Flannel部署记录
    理解Docker :Docker 网络
    全面剖析Redis Cluster原理和应用
    python发送钉钉机器人脚本
    centos 7 部署LDAP服务
    zabbix 同步ldap帐号脚本
    zabbix TCP 连接数监控
    WebDriver基本操作入门及UI自动化练手页面
    Jmeter使用入门
  • 原文地址:https://www.cnblogs.com/liangjingfu/p/15439086.html
Copyright © 2011-2022 走看看