280 lines
7.3 KiB
Go
280 lines
7.3 KiB
Go
package ps
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/AlexxIT/go2rtc/pkg/h264/golomb"
|
|
)
|
|
|
|
const firstByte = 0x67
|
|
|
|
// Google to "h264 specification pdf"
|
|
// https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-201602-S!!PDF-E&type=items
|
|
|
|
type SPS struct {
|
|
Profile string
|
|
ProfileIDC uint8
|
|
ProfileIOP uint8
|
|
LevelIDC uint8
|
|
Width uint16
|
|
Height uint16
|
|
}
|
|
|
|
func NewSPS(profile string, level uint8, width uint16, height uint16) *SPS {
|
|
s := &SPS{
|
|
Profile: profile, LevelIDC: level, Width: width, Height: height,
|
|
}
|
|
s.ProfileIDC, s.ProfileIOP = DecodeProfile(profile)
|
|
return s
|
|
}
|
|
|
|
// https://www.cardinalpeak.com/blog/the-h-264-sequence-parameter-set
|
|
|
|
func (s *SPS) Marshal() []byte {
|
|
w := golomb.NewWriter()
|
|
|
|
// this is typical SPS for most H264 cameras
|
|
w.WriteByte(firstByte)
|
|
w.WriteByte(s.ProfileIDC)
|
|
w.WriteByte(s.ProfileIOP)
|
|
w.WriteByte(s.LevelIDC)
|
|
|
|
w.WriteUEGolomb(0) // seq_parameter_set_id (0)
|
|
w.WriteUEGolomb(0) // log2_max_frame_num_minus4 (depends)
|
|
w.WriteUEGolomb(0) // pic_order_cnt_type (0 or 2)
|
|
w.WriteUEGolomb(0) // log2_max_pic_order_cnt_lsb_minus4 (depends)
|
|
w.WriteUEGolomb(1) // num_ref_frames (1)
|
|
w.WriteBit(0) // gaps_in_frame_num_value_allowed_flag (0)
|
|
|
|
w.WriteUEGolomb(uint8(s.Width>>4) - 1) // pic_width_in_mbs_minus_1
|
|
w.WriteUEGolomb(uint8(s.Height>>4) - 1) // pic_height_in_map_units_minus_1
|
|
|
|
w.WriteBit(1) // frame_mbs_only_flag (1)
|
|
w.WriteBit(1) // direct_8x8_inference_flag (1)
|
|
w.WriteBit(0) // frame_cropping_flag (0 is OK)
|
|
w.WriteBit(0) // vui_prameters_present_flag (0 is OK)
|
|
w.WriteBit(1) // rbsp_stop_one_bit
|
|
|
|
return w.Bytes()
|
|
}
|
|
|
|
func (s *SPS) Unmarshal(data []byte) (err error) {
|
|
r := golomb.NewReader(data)
|
|
|
|
var b byte
|
|
var u uint
|
|
|
|
if b, err = r.ReadByte(); err != nil {
|
|
return
|
|
}
|
|
if b&0x1F != 7 {
|
|
err = errors.New("not SPS data")
|
|
return
|
|
}
|
|
|
|
if s.ProfileIDC, err = r.ReadByte(); err != nil {
|
|
return
|
|
}
|
|
if s.ProfileIOP, err = r.ReadByte(); err != nil {
|
|
return
|
|
}
|
|
if s.LevelIDC, err = r.ReadByte(); err != nil {
|
|
return
|
|
}
|
|
|
|
s.Profile = EncodeProfile(s.ProfileIDC, s.ProfileIOP)
|
|
|
|
u, err = r.ReadUEGolomb() // seq_parameter_set_id
|
|
|
|
if s.ProfileIDC == 100 || s.ProfileIDC == 110 || s.ProfileIDC == 122 ||
|
|
s.ProfileIDC == 244 || s.ProfileIDC == 44 || s.ProfileIDC == 83 ||
|
|
s.ProfileIDC == 86 || s.ProfileIDC == 118 || s.ProfileIDC == 128 ||
|
|
s.ProfileIDC == 138 || s.ProfileIDC == 139 || s.ProfileIDC == 134 ||
|
|
s.ProfileIDC == 135 {
|
|
var n byte
|
|
|
|
u, err = r.ReadUEGolomb() // chroma_format_idc
|
|
if u == 3 {
|
|
b, err = r.ReadBit() // separate_colour_plane_flag
|
|
n = 12
|
|
} else {
|
|
n = 8
|
|
}
|
|
|
|
u, err = r.ReadUEGolomb() // bit_depth_luma_minus8
|
|
u, err = r.ReadUEGolomb() // bit_depth_chroma_minus8
|
|
b, err = r.ReadBit() // qpprime_y_zero_transform_bypass_flag
|
|
|
|
b, err = r.ReadBit() // seq_scaling_matrix_present_flag
|
|
if b > 0 {
|
|
for i := byte(0); i < n; i++ {
|
|
b, err = r.ReadBit() // seq_scaling_list_present_flag[i]
|
|
if b > 0 {
|
|
panic("not implemented")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
u, err = r.ReadUEGolomb() // log2_max_frame_num_minus4
|
|
|
|
u, err = r.ReadUEGolomb() // pic_order_cnt_type
|
|
switch u {
|
|
case 0:
|
|
u, err = r.ReadUEGolomb() // log2_max_pic_order_cnt_lsb_minus4
|
|
case 1:
|
|
b, err = r.ReadBit() // delta_pic_order_always_zero_flag
|
|
_, err = r.ReadSEGolomb() // offset_for_non_ref_pic
|
|
_, err = r.ReadSEGolomb() // offset_for_top_to_bottom_field
|
|
u, err = r.ReadUEGolomb() // num_ref_frames_in_pic_order_cnt_cycle
|
|
for i := byte(0); i < b; i++ {
|
|
_, err = r.ReadSEGolomb() // offset_for_ref_frame[i]
|
|
}
|
|
}
|
|
|
|
u, err = r.ReadUEGolomb() // num_ref_frames
|
|
b, err = r.ReadBit() // gaps_in_frame_num_value_allowed_flag
|
|
|
|
u, err = r.ReadUEGolomb() // pic_width_in_mbs_minus_1
|
|
s.Width = uint16(u+1) << 4
|
|
u, err = r.ReadUEGolomb() // pic_height_in_map_units_minus_1
|
|
s.Height = uint16(u+1) << 4
|
|
|
|
b, err = r.ReadBit() // frame_mbs_only_flag
|
|
if b == 0 {
|
|
_, err = r.ReadBit()
|
|
}
|
|
|
|
b, err = r.ReadBit() // direct_8x8_inference_flag
|
|
|
|
b, err = r.ReadBit() // frame_cropping_flag
|
|
if b > 0 {
|
|
u, err = r.ReadUEGolomb() // frame_crop_left_offset
|
|
s.Width -= uint16(u) << 1
|
|
u, err = r.ReadUEGolomb() // frame_crop_right_offset
|
|
s.Width -= uint16(u) << 1
|
|
u, err = r.ReadUEGolomb() // frame_crop_top_offset
|
|
s.Height -= uint16(u) << 1
|
|
u, err = r.ReadUEGolomb() // frame_crop_bottom_offset
|
|
s.Height -= uint16(u) << 1
|
|
}
|
|
|
|
b, err = r.ReadBit() // vui_prameters_present_flag
|
|
if b > 0 {
|
|
b, err = r.ReadBit() // vui_prameters_present_flag
|
|
if b > 0 {
|
|
u, err = r.ReadBits(8) // aspect_ratio_idc
|
|
if b == 255 {
|
|
u, err = r.ReadBits(16) // sar_width
|
|
u, err = r.ReadBits(16) // sar_height
|
|
}
|
|
}
|
|
|
|
b, err = r.ReadBit() // overscan_info_present_flag
|
|
if b > 0 {
|
|
b, err = r.ReadBit() // overscan_appropriate_flag
|
|
}
|
|
|
|
b, err = r.ReadBit() // video_signal_type_present_flag
|
|
if b > 0 {
|
|
u, err = r.ReadBits(3) // video_format
|
|
b, err = r.ReadBit() // video_full_range_flag
|
|
|
|
b, err = r.ReadBit() // colour_description_present_flag
|
|
if b > 0 {
|
|
u, err = r.ReadBits(8) // colour_primaries
|
|
u, err = r.ReadBits(8) // transfer_characteristics
|
|
u, err = r.ReadBits(8) // matrix_coefficients
|
|
}
|
|
}
|
|
|
|
b, err = r.ReadBit() // chroma_loc_info_present_flag
|
|
if b > 0 {
|
|
u, err = r.ReadUEGolomb() // chroma_sample_loc_type_top_field
|
|
u, err = r.ReadUEGolomb() // chroma_sample_loc_type_bottom_field
|
|
}
|
|
|
|
b, err = r.ReadBit() // timing_info_present_flag
|
|
if b > 0 {
|
|
u, err = r.ReadBits(32) // num_units_in_tick
|
|
u, err = r.ReadBits(32) // time_scale
|
|
b, err = r.ReadBit() // fixed_frame_rate_flag
|
|
}
|
|
|
|
b, err = r.ReadBit() // nal_hrd_parameters_present_flag
|
|
if b > 0 {
|
|
//panic("not implemented")
|
|
return nil
|
|
}
|
|
|
|
b, err = r.ReadBit() // vcl_hrd_parameters_present_flag
|
|
if b > 0 {
|
|
//panic("not implemented")
|
|
return nil
|
|
}
|
|
|
|
// if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag)
|
|
// b, err = r.ReadBit() // low_delay_hrd_flag
|
|
|
|
b, err = r.ReadBit() // pic_struct_present_flag
|
|
|
|
b, err = r.ReadBit() // bitstream_restriction_flag
|
|
if b > 0 {
|
|
b, err = r.ReadBit() // motion_vectors_over_pic_boundaries_flag
|
|
u, err = r.ReadUEGolomb() // max_bytes_per_pic_denom
|
|
u, err = r.ReadUEGolomb() // max_bits_per_mb_denom
|
|
u, err = r.ReadUEGolomb() // log2_max_mv_length_horizontal
|
|
u, err = r.ReadUEGolomb() // log2_max_mv_length_vertical
|
|
u, err = r.ReadUEGolomb() // max_num_reorder_frames
|
|
u, err = r.ReadUEGolomb() // max_dec_frame_buffering
|
|
}
|
|
}
|
|
|
|
b, err = r.ReadBit() // rbsp_stop_one_bit
|
|
|
|
return
|
|
}
|
|
|
|
func EncodeProfile(idc, iop byte) string {
|
|
// https://datatracker.ietf.org/doc/html/rfc6184#page-41
|
|
switch {
|
|
// 4240xx 42C0xx 42E0xx
|
|
case idc == 0x42 && iop&0b01001111 == 0b01000000:
|
|
return "CB"
|
|
case idc == 0x4D && iop&0b10001111 == 0b10000000:
|
|
return "CB"
|
|
case idc == 0x58 && iop&0b11001111 == 0b11000000:
|
|
return "CB"
|
|
// 4200xx
|
|
case idc == 0x42 && iop&0b01001111 == 0:
|
|
return "B"
|
|
case idc == 0x58 && iop&0b11001111 == 0b10000000:
|
|
return "B"
|
|
// 4d40xx
|
|
case idc == 0x4D && iop&0b10101111 == 0:
|
|
return "M"
|
|
case idc == 0x58 && iop&0b11001111 == 0:
|
|
return "E"
|
|
case idc == 0x64 && iop == 0:
|
|
return "H"
|
|
case idc == 0x6E && iop == 0:
|
|
return "H10"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func DecodeProfile(profile string) (idc, iop byte) {
|
|
switch profile {
|
|
case "CB":
|
|
return 0x42, 0b01000000
|
|
case "B":
|
|
return 0x42, 0 // 66
|
|
case "M":
|
|
return 0x4D, 0 // 77
|
|
case "E":
|
|
return 0x58, 0 // 88
|
|
case "H":
|
|
return 0x64, 0
|
|
}
|
|
return 0, 0
|
|
}
|