处理H5 流媒体时,有时需要根据SPS 获取视频宽、高,网上的大多是c/c++,这里我用js 实现类似功能。
function ceil(val)
{
return Math.ceil(val);
}
//获取buf 的前n个bit组成的值
function u(bitCount, input)
{
let ret = 0;
for( var i = 0;i< bitCount;i++)
{
ret <<= 1;
if (input.data[Math.floor(input.index / 8)] & (0x80 >> (input.index % 8)))
{
ret += 1;
}
input.index++;
}
return ret;
}
/*无符号指数哥伦布编码(UE)
*哥伦布编码的码字code_word由三部分组成:code_word = [M个0] + [1] + [Info]
*其中,Info是一个携带信息的M位数据,每个哥伦布码的长度为(2M+1)位,每个码字都可由code_num产生。
*根据码字code_word解码出code_num值的过程如下:
*1. 首先读入M位以"1"为结尾的0;
*2. 根据得到的M,读入接下来的M位Info数据;
*3. 根据这个公式得到计算结果code_num = Info – 1 + 2M
*/
function ue(input, len)
{
let zeroNum = 0;
while(input.index < len * 8)
{
if(input.data[Math.floor(input.index/8)] & (0x80 >> (input.index %8)))//遇到1则停止,统计0的个数
{
break;
}
zeroNum++;
input.index++;
}
input.index++;
let ret = 0;
//计算
for(i = 0;i < zeroNum;i++)
{
ret <<= 1;
if(input.data[Math.floor(input.index/8)] & (0x80 >> input.index %8))
{
ret += 1;
}
input.index++;
}
return (1<< zeroNum) - 1 + ret;
}
//有符号哥伦布编码
function se(input, len)
{
let ueVal = ue(input, len);
let k = ueVal;
let nValue = ceil(k/2);
if(ueVal %2 == 0)
nValue = -nValue;
return nValue;
}
function spsParser(buf)
{
let startBitIndex = 0;
buf = buf.slice(4);//去除00 00 00 01竞争码
let len = buf.length;
//输入参数
let input = {
data:buf,
index:startBitIndex
};
let forbidden_zero_bit = u(1, input);
let nal_ref_idc = u(2, input);
let nal_unit_type = u(5, input);
let chroma_format_idc;
if(nal_unit_type == 7)
{
let profile_idc = u(8, input);
let constraint_set0_flag = u(1, input);
let constraint_set1_flag = u(1, input);
let constraint_set2_flag = u(1, input);
let constraint_set3_flag = u(1, input);
let constraint_set4_flag = u(1, input);
let constraint_set5_flag = u(1, input);
let reserved_zero_2bits = u(2, input);
let level_idc = u(8, input);
let seq_parameter_set_id = ue(input, len);
if(profile_idc == 100 | profile_idc == 110 || profile_idc == 122 || profile_idc == 144 )
{
chroma_format_idc = ue(input, len);
if(chroma_format_idc == 3)
{
var residual_colour_transform_flag = u(1, input);
}
let bit_depth_luma_minus8 = ue(input, len);
let bit_depth_chroma_minus8 = ue(input, len);
let qpprime_y_zero_transform_bypass_flag = u(1, input);
let seq_scaling_matrix_present_flag = u(1, input);
let seq_scaling_list_present_flag = new Uint8Array(8);
if (seq_scaling_matrix_present_flag)
{
for (var i = 0; i < 8; i++)
{
seq_scaling_list_present_flag[i] = u(1, input);
}
}
}
let log2_max_frame_num_minus4 = ue(input, len);
let pic_order_cnt_type = ue(input, len);
if (pic_order_cnt_type == 0)
log2_max_pic_order_cnt_lsb_minus4 = ue(input, len);
else if (pic_order_cnt_type == 1)
{
let delta_pic_order_always_zero_flag = u(1, input);
let offset_for_non_ref_pic = se(input, len);
let offset_for_top_to_bottom_field = se(input, len);
let num_ref_frames_in_pic_order_cnt_cycle = ue(input, len);
let offset_for_ref_frame = new Uint8Array[num_ref_frames_in_pic_order_cnt_cycle];
for ( var i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++)
offset_for_ref_frame[i] = se(input, len);
}
let num_ref_frames = ue(input, len);
let gaps_in_frame_num_value_allowed_flag = u(1, input);
let pic_width_in_mbs_minus1 = ue(input, len);
let pic_height_in_map_units_minus1 = ue(input, len);
let width = (pic_width_in_mbs_minus1 + 1) * 16;//可能还要进行裁剪处理
let height = (pic_height_in_map_units_minus1 + 1) * 16;
let frame_mbs_only_flag = u(1, input);
if(!frame_mbs_only_flag)
{
u(1, input);
}
let direct_8x8_inference_flag = u(1, input);
let frame_cropping_flag = u(1, input);
if(frame_cropping_flag)
{
let frame_crop_left_offset = ue(input, len);
let frame_crop_right_offset = ue(input, len);
let frame_crop_top_offset = ue(input, len);
let frame_crop_bottom_offset = ue(input, len);
let crop_unit_x = 1;
let crop_unit_y = 2 - frame_mbs_only_flag;
if (chroma_format_idc == 1) { //4:2:0
crop_unit_x = 2;
crop_unit_y = 2 * (2 - frame_mbs_only_flag);
}
else if (chroma_format_idc == 2) { //4:2:2
crop_unit_x = 2;
crop_unit_y = 2 - frame_mbs_only_flag;
}
width -= crop_unit_x * (frame_crop_left_offset + frame_crop_right_offset);
height -= crop_unit_y * (frame_crop_top_offset + frame_crop_bottom_offset);
}
return {
width:width,
height:height
}
}
}
function test()
{
var spsArry = new Array(0x00, 0x00, 0x00, 0x01, 0x27, 0x64, 0x00,0x1F, 0xAC,0x1A, 0xD0, 0x2E,0x0A, 0x3C, 0xBC,0x20, 0x00, 0x00,0x03 ,0x00, 0x20, 0x00, 0x00,0x0C, 0x81, 0xE2, 0x0F,0x50);
let result = spsParser(spsArry);
}
test();
参考链接:
https://blog.csdn.net/lizhijian21/article/details/80982403