function ExpGolomInit ( view , bitoffset ) { let bit = 0 ; let byteoffset = bitoffset >> 3 ; let skip = bitoffset & 7 ; let zeros = - 1 ; let byt = view . getUint8 ( byteoffset ) << skip ; do { bit = byt & 0x80 ; byt <<= 1 ; zeros ++ ; skip ++ ; if ( skip === 8 ) { skip = 0 ; byteoffset ++ ; byt = view . getUint8 ( byteoffset ) ; } } while ( ! bit ) ; return { zeros , skip , byt , byteoffset } ; } class Bitstream { public bitoffset = 0 ; constructor ( public view ) { } ExpGolomb ( ) { const { view } = this ; let { zeros , skip , byt , byteoffset , } = ExpGolomInit ( view , this . bitoffset ) ; let code = 1 ; while ( zeros > 0 ) { code = ( code << 1 ) | ( ( byt & 0x80 ) >>> 7 ) ; byt <<= 1 ; skip ++ ; zeros -- ; if ( skip === 8 ) { skip = 0 ; byteoffset ++ ; byt = view . getUint8 ( byteoffset ) ; } } this . bitoffset = ( byteoffset << 3 ) | skip ; return code - 1 ; } SkipExpGolomb ( ) { const { zeros , skip , byteoffset , } = ExpGolomInit ( this . view , this . bitoffset ) ; this . bitoffset = ( byteoffset << 3 ) + skip + zeros ; } SignedExpGolomb ( ) { const code = this . ExpGolomb ( ) ; return code & 1 ? ( code + 1 ) >>> 1 : - ( code >>> 1 ) ; } readBit ( ) { const skip = this . bitoffset & 7 ; const byteoffset = this . bitoffset >> 3 ; this . bitoffset ++ ; return ( ( this . view . getUint8 ( byteoffset ) >> ( 7 - skip ) ) & 1 ) as 0 | 1 ; } } function scaling_list ( stream , sizeOfScalingList ) { let lastScale = 8 ; let nextScale = 8 ; for ( let j = 0 ; j < sizeOfScalingList ; j ++ ) { if ( nextScale !== 0 ) { const deltaScale = stream . SignedExpGolomb ( ) ; nextScale = ( lastScale + deltaScale + 256 ) % 256 ; } if ( nextScale ) { lastScale = nextScale ; } } } export type SPSInfo = { profile_idc : number ; level_idc : number ; profile_compatibility : number ; frame_mbs_only_flag : 0 | 1 ; pic_width_in_mbs : number ; pic_height_in_map_units : number ; frame_cropping_flag : 0 | 1 ; frame_cropping : { left : number ; right : number ; top : number ; bottom : number ; } ; } ; export function parseSPS ( nal ) { const stream = new Bitstream ( new DataView ( nal . buffer , nal . byteOffset + 4 ) ) ; const profile_idc = nal [ 1 ] ; const profile_compatibility = nal [ 2 ] ; const level_idc = nal [ 3 ] ; stream . SkipExpGolomb ( ) ; if ( profile_idc === 100 || profile_idc === 110 || profile_idc === 122 || profile_idc === 244 || profile_idc === 44 || profile_idc === 83 || profile_idc === 86 || profile_idc === 118 || profile_idc === 128 ) { const chroma_format_idc = stream . ExpGolomb ( ) ; let limit = 8 ; if ( chroma_format_idc === 3 ) { limit = 12 ; stream . bitoffset ++ ; } stream . SkipExpGolomb ( ) ; stream . SkipExpGolomb ( ) ; stream . bitoffset ++ ; if ( stream . readBit ( ) ) { let i = 0 ; for ( ; i < 6 ; i ++ ) { if ( stream . readBit ( ) ) { scaling_list ( stream , 16 ) ; } } for ( ; i < limit ; i ++ ) { if ( stream . readBit ( ) ) { scaling_list ( stream , 64 ) ; } } } } stream . SkipExpGolomb ( ) ; const pic_order_cnt_type = stream . ExpGolomb ( ) ; if ( pic_order_cnt_type === 0 ) { stream . ExpGolomb ( ) ; } else if ( pic_order_cnt_type === 1 ) { stream . bitoffset ++ ; stream . SkipExpGolomb ( ) ; stream . SkipExpGolomb ( ) ; const num_ref_frames_in_pic_order_cnt_cycle = stream . ExpGolomb ( ) ; for ( let i = 0 ; i < num_ref_frames_in_pic_order_cnt_cycle ; i ++ ) { stream . ExpGolomb ( ) ; } } stream . SkipExpGolomb ( ) ; stream . bitoffset ++ ; const pic_width_in_mbs = stream . ExpGolomb ( ) + 1 ; const pic_height_in_map_units = stream . ExpGolomb ( ) + 1 ; const frame_mbs_only_flag = stream . readBit ( ) ; if ( ! frame_mbs_only_flag ) { stream . bitoffset ++ ; } stream . bitoffset ++ ; const frame_cropping_flag = stream . readBit ( ) ; let left_offset = 0 ; let right_offset = 0 ; let top_offset = 0 ; let bottom_offset = 0 ; if ( frame_cropping_flag ) { left_offset = stream . ExpGolomb ( ) ; right_offset = stream . ExpGolomb ( ) ; top_offset = stream . ExpGolomb ( ) ; bottom_offset = stream . ExpGolomb ( ) ; } return { profile_idc , level_idc , profile_compatibility , frame_mbs_only_flag , pic_width_in_mbs , pic_height_in_map_units , frame_cropping_flag , frame_cropping : { left : left_offset , right : right_offset , top : top_offset , bottom : bottom_offset , } , } ; }