install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,366 @@
|
||||
package h264
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/bits"
|
||||
)
|
||||
|
||||
// http://www.itu.int/rec/T-REC-H.264
|
||||
// https://webrtc.googlesource.com/src/+/refs/heads/main/common_video/h264/sps_parser.cc
|
||||
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type SPS struct {
|
||||
profile_idc uint8
|
||||
profile_iop uint8
|
||||
level_idc uint8
|
||||
|
||||
seq_parameter_set_id uint32
|
||||
|
||||
chroma_format_idc uint32
|
||||
separate_colour_plane_flag byte
|
||||
bit_depth_luma_minus8 uint32
|
||||
bit_depth_chroma_minus8 uint32
|
||||
qpprime_y_zero_transform_bypass_flag byte
|
||||
seq_scaling_matrix_present_flag byte
|
||||
|
||||
log2_max_frame_num_minus4 uint32
|
||||
pic_order_cnt_type uint32
|
||||
log2_max_pic_order_cnt_lsb_minus4 uint32
|
||||
delta_pic_order_always_zero_flag byte
|
||||
offset_for_non_ref_pic int32
|
||||
offset_for_top_to_bottom_field int32
|
||||
num_ref_frames_in_pic_order_cnt_cycle uint32
|
||||
num_ref_frames uint32
|
||||
gaps_in_frame_num_value_allowed_flag byte
|
||||
|
||||
pic_width_in_mbs_minus_1 uint32
|
||||
pic_height_in_map_units_minus_1 uint32
|
||||
frame_mbs_only_flag byte
|
||||
mb_adaptive_frame_field_flag byte
|
||||
direct_8x8_inference_flag byte
|
||||
|
||||
frame_cropping_flag byte
|
||||
frame_crop_left_offset uint32
|
||||
frame_crop_right_offset uint32
|
||||
frame_crop_top_offset uint32
|
||||
frame_crop_bottom_offset uint32
|
||||
|
||||
vui_parameters_present_flag byte
|
||||
aspect_ratio_info_present_flag byte
|
||||
aspect_ratio_idc byte
|
||||
sar_width uint16
|
||||
sar_height uint16
|
||||
|
||||
overscan_info_present_flag byte
|
||||
overscan_appropriate_flag byte
|
||||
|
||||
video_signal_type_present_flag byte
|
||||
video_format uint8
|
||||
video_full_range_flag byte
|
||||
|
||||
colour_description_present_flag byte
|
||||
colour_description uint32
|
||||
|
||||
chroma_loc_info_present_flag byte
|
||||
chroma_sample_loc_type_top_field uint32
|
||||
chroma_sample_loc_type_bottom_field uint32
|
||||
|
||||
timing_info_present_flag byte
|
||||
num_units_in_tick uint32
|
||||
time_scale uint32
|
||||
fixed_frame_rate_flag byte
|
||||
}
|
||||
|
||||
func (s *SPS) Width() uint16 {
|
||||
width := 16 * (s.pic_width_in_mbs_minus_1 + 1)
|
||||
crop := 2 * (s.frame_crop_left_offset + s.frame_crop_right_offset)
|
||||
return uint16(width - crop)
|
||||
}
|
||||
|
||||
func (s *SPS) Height() uint16 {
|
||||
height := 16 * (s.pic_height_in_map_units_minus_1 + 1)
|
||||
crop := 2 * (s.frame_crop_top_offset + s.frame_crop_bottom_offset)
|
||||
if s.frame_mbs_only_flag == 0 {
|
||||
height *= 2
|
||||
}
|
||||
return uint16(height - crop)
|
||||
}
|
||||
|
||||
func DecodeSPS(sps []byte) *SPS {
|
||||
// https://developer.ridgerun.com/wiki/index.php/H264_Analysis_Tools
|
||||
// ffmpeg -i file.h264 -c copy -bsf:v trace_headers -f null -
|
||||
r := bits.NewReader(sps)
|
||||
|
||||
hdr := r.ReadByte()
|
||||
if hdr&0x1F != NALUTypeSPS {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := &SPS{
|
||||
profile_idc: r.ReadByte(),
|
||||
profile_iop: r.ReadByte(),
|
||||
level_idc: r.ReadByte(),
|
||||
seq_parameter_set_id: r.ReadUEGolomb(),
|
||||
}
|
||||
|
||||
switch s.profile_idc {
|
||||
case 100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135:
|
||||
n := byte(8)
|
||||
|
||||
s.chroma_format_idc = r.ReadUEGolomb()
|
||||
if s.chroma_format_idc == 3 {
|
||||
s.separate_colour_plane_flag = r.ReadBit()
|
||||
n = 12
|
||||
}
|
||||
|
||||
s.bit_depth_luma_minus8 = r.ReadUEGolomb()
|
||||
s.bit_depth_chroma_minus8 = r.ReadUEGolomb()
|
||||
s.qpprime_y_zero_transform_bypass_flag = r.ReadBit()
|
||||
|
||||
s.seq_scaling_matrix_present_flag = r.ReadBit()
|
||||
if s.seq_scaling_matrix_present_flag != 0 {
|
||||
for i := byte(0); i < n; i++ {
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
seq_scaling_list_present_flag := r.ReadBit()
|
||||
if seq_scaling_list_present_flag != 0 {
|
||||
if i < 6 {
|
||||
s.scaling_list(r, 16)
|
||||
} else {
|
||||
s.scaling_list(r, 64)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.log2_max_frame_num_minus4 = r.ReadUEGolomb()
|
||||
|
||||
s.pic_order_cnt_type = r.ReadUEGolomb()
|
||||
switch s.pic_order_cnt_type {
|
||||
case 0:
|
||||
s.log2_max_pic_order_cnt_lsb_minus4 = r.ReadUEGolomb()
|
||||
case 1:
|
||||
s.delta_pic_order_always_zero_flag = r.ReadBit()
|
||||
s.offset_for_non_ref_pic = r.ReadSEGolomb()
|
||||
s.offset_for_top_to_bottom_field = r.ReadSEGolomb()
|
||||
|
||||
s.num_ref_frames_in_pic_order_cnt_cycle = r.ReadUEGolomb()
|
||||
for i := uint32(0); i < s.num_ref_frames_in_pic_order_cnt_cycle; i++ {
|
||||
_ = r.ReadSEGolomb() // offset_for_ref_frame[i]
|
||||
}
|
||||
}
|
||||
|
||||
s.num_ref_frames = r.ReadUEGolomb()
|
||||
s.gaps_in_frame_num_value_allowed_flag = r.ReadBit()
|
||||
|
||||
s.pic_width_in_mbs_minus_1 = r.ReadUEGolomb()
|
||||
s.pic_height_in_map_units_minus_1 = r.ReadUEGolomb()
|
||||
|
||||
s.frame_mbs_only_flag = r.ReadBit()
|
||||
if s.frame_mbs_only_flag == 0 {
|
||||
s.mb_adaptive_frame_field_flag = r.ReadBit()
|
||||
}
|
||||
|
||||
s.direct_8x8_inference_flag = r.ReadBit()
|
||||
|
||||
s.frame_cropping_flag = r.ReadBit()
|
||||
if s.frame_cropping_flag != 0 {
|
||||
s.frame_crop_left_offset = r.ReadUEGolomb()
|
||||
s.frame_crop_right_offset = r.ReadUEGolomb()
|
||||
s.frame_crop_top_offset = r.ReadUEGolomb()
|
||||
s.frame_crop_bottom_offset = r.ReadUEGolomb()
|
||||
}
|
||||
|
||||
s.vui_parameters_present_flag = r.ReadBit()
|
||||
if s.vui_parameters_present_flag != 0 {
|
||||
s.aspect_ratio_info_present_flag = r.ReadBit()
|
||||
if s.aspect_ratio_info_present_flag != 0 {
|
||||
s.aspect_ratio_idc = r.ReadByte()
|
||||
if s.aspect_ratio_idc == 255 {
|
||||
s.sar_width = r.ReadUint16()
|
||||
s.sar_height = r.ReadUint16()
|
||||
}
|
||||
}
|
||||
|
||||
s.overscan_info_present_flag = r.ReadBit()
|
||||
if s.overscan_info_present_flag != 0 {
|
||||
s.overscan_appropriate_flag = r.ReadBit()
|
||||
}
|
||||
|
||||
s.video_signal_type_present_flag = r.ReadBit()
|
||||
if s.video_signal_type_present_flag != 0 {
|
||||
s.video_format = r.ReadBits8(3)
|
||||
s.video_full_range_flag = r.ReadBit()
|
||||
|
||||
s.colour_description_present_flag = r.ReadBit()
|
||||
if s.colour_description_present_flag != 0 {
|
||||
s.colour_description = r.ReadUint24()
|
||||
}
|
||||
}
|
||||
|
||||
s.chroma_loc_info_present_flag = r.ReadBit()
|
||||
if s.chroma_loc_info_present_flag != 0 {
|
||||
s.chroma_sample_loc_type_top_field = r.ReadUEGolomb()
|
||||
s.chroma_sample_loc_type_bottom_field = r.ReadUEGolomb()
|
||||
}
|
||||
|
||||
s.timing_info_present_flag = r.ReadBit()
|
||||
if s.timing_info_present_flag != 0 {
|
||||
s.num_units_in_tick = r.ReadUint32()
|
||||
s.time_scale = r.ReadUint32()
|
||||
s.fixed_frame_rate_flag = r.ReadBit()
|
||||
}
|
||||
//...
|
||||
}
|
||||
|
||||
if r.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
func (s *SPS) scaling_list(r *bits.Reader, sizeOfScalingList int) {
|
||||
lastScale := int32(8)
|
||||
nextScale := int32(8)
|
||||
for j := 0; j < sizeOfScalingList; j++ {
|
||||
if nextScale != 0 {
|
||||
delta_scale := r.ReadSEGolomb()
|
||||
nextScale = (lastScale + delta_scale + 256) % 256
|
||||
}
|
||||
if nextScale != 0 {
|
||||
lastScale = nextScale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SPS) Profile() string {
|
||||
switch s.profile_idc {
|
||||
case 0x42:
|
||||
return "Baseline"
|
||||
case 0x4D:
|
||||
return "Main"
|
||||
case 0x58:
|
||||
return "Extended"
|
||||
case 0x64:
|
||||
return "High"
|
||||
}
|
||||
return fmt.Sprintf("0x%02X", s.profile_idc)
|
||||
}
|
||||
|
||||
func (s *SPS) PixFmt() string {
|
||||
if s.bit_depth_luma_minus8 == 0 {
|
||||
switch s.chroma_format_idc {
|
||||
case 1:
|
||||
if s.video_full_range_flag == 1 {
|
||||
return "yuvj420p"
|
||||
}
|
||||
return "yuv420p"
|
||||
case 2:
|
||||
return "yuv422p"
|
||||
case 3:
|
||||
return "yuv444p"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *SPS) String() string {
|
||||
return fmt.Sprintf(
|
||||
"%s %d.%d, %s, %dx%d",
|
||||
s.Profile(), s.level_idc/10, s.level_idc%10, s.PixFmt(), s.Width(), s.Height(),
|
||||
)
|
||||
}
|
||||
|
||||
// FixPixFmt - change yuvj420p to yuv420p in SPS
|
||||
// same as "-c:v copy -bsf:v h264_metadata=video_full_range_flag=0"
|
||||
func FixPixFmt(sps []byte) {
|
||||
r := bits.NewReader(sps)
|
||||
|
||||
_ = r.ReadByte()
|
||||
|
||||
profile := r.ReadByte()
|
||||
_ = r.ReadByte()
|
||||
_ = r.ReadByte()
|
||||
_ = r.ReadUEGolomb()
|
||||
|
||||
switch profile {
|
||||
case 100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135:
|
||||
n := byte(8)
|
||||
|
||||
if r.ReadUEGolomb() == 3 {
|
||||
_ = r.ReadBit()
|
||||
n = 12
|
||||
}
|
||||
|
||||
_ = r.ReadUEGolomb()
|
||||
_ = r.ReadUEGolomb()
|
||||
_ = r.ReadBit()
|
||||
|
||||
if r.ReadBit() != 0 {
|
||||
for i := byte(0); i < n; i++ {
|
||||
if r.ReadBit() != 0 {
|
||||
return // skip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = r.ReadUEGolomb()
|
||||
|
||||
switch r.ReadUEGolomb() {
|
||||
case 0:
|
||||
_ = r.ReadUEGolomb()
|
||||
case 1:
|
||||
_ = r.ReadBit()
|
||||
_ = r.ReadSEGolomb()
|
||||
_ = r.ReadSEGolomb()
|
||||
|
||||
n := r.ReadUEGolomb()
|
||||
for i := uint32(0); i < n; i++ {
|
||||
_ = r.ReadSEGolomb()
|
||||
}
|
||||
}
|
||||
|
||||
_ = r.ReadUEGolomb()
|
||||
_ = r.ReadBit()
|
||||
|
||||
_ = r.ReadUEGolomb()
|
||||
_ = r.ReadUEGolomb()
|
||||
|
||||
if r.ReadBit() == 0 {
|
||||
_ = r.ReadBit()
|
||||
}
|
||||
|
||||
_ = r.ReadBit()
|
||||
|
||||
if r.ReadBit() != 0 {
|
||||
_ = r.ReadUEGolomb()
|
||||
_ = r.ReadUEGolomb()
|
||||
_ = r.ReadUEGolomb()
|
||||
_ = r.ReadUEGolomb()
|
||||
}
|
||||
|
||||
if r.ReadBit() != 0 {
|
||||
if r.ReadBit() != 0 {
|
||||
if r.ReadByte() == 255 {
|
||||
_ = r.ReadUint16()
|
||||
_ = r.ReadUint16()
|
||||
}
|
||||
}
|
||||
|
||||
if r.ReadBit() != 0 {
|
||||
_ = r.ReadBit()
|
||||
}
|
||||
|
||||
if r.ReadBit() != 0 {
|
||||
_ = r.ReadBits8(3)
|
||||
if r.ReadBit() == 1 {
|
||||
pos, bit := r.Pos()
|
||||
sps[pos] &= ^byte(1 << bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user