基于java解码H264 SPS码流研究笔记(哥伦布编码)
2021/10/22 17:10:32
本文主要是介绍基于java解码H264 SPS码流研究笔记(哥伦布编码),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
package cn.wotv.wotvcdn.ffprobe.service; 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)); } }
输出结果:
[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
这篇关于基于java解码H264 SPS码流研究笔记(哥伦布编码)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南