Rewrite AnnexB/AVCC parsers
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ import "github.com/AlexxIT/go2rtc/pkg/h264"
|
||||
const forbiddenZeroBit = 0x80
|
||||
const nalUnitType = 0x3F
|
||||
|
||||
// DecodeStream - find and return first AU in AVC format
|
||||
// Deprecated: DecodeStream - find and return first AU in AVC format
|
||||
// useful for processing live streams with unknown separator size
|
||||
func DecodeStream(annexb []byte) ([]byte, int) {
|
||||
startPos := -1
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Package h265 - AVCC format related functions
|
||||
package h265
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
)
|
||||
|
||||
func AVCCToCodec(avcc []byte) *core.Codec {
|
||||
buf := bytes.NewBufferString("profile-id=1")
|
||||
|
||||
for {
|
||||
size := 4 + int(binary.BigEndian.Uint32(avcc))
|
||||
|
||||
switch NALUType(avcc) {
|
||||
case NALUTypeVPS:
|
||||
buf.WriteString(";sprop-vps=")
|
||||
buf.WriteString(base64.StdEncoding.EncodeToString(avcc[4:size]))
|
||||
case NALUTypeSPS:
|
||||
buf.WriteString(";sprop-sps=")
|
||||
buf.WriteString(base64.StdEncoding.EncodeToString(avcc[4:size]))
|
||||
case NALUTypePPS:
|
||||
buf.WriteString(";sprop-pps=")
|
||||
buf.WriteString(base64.StdEncoding.EncodeToString(avcc[4:size]))
|
||||
}
|
||||
|
||||
if size < len(avcc) {
|
||||
avcc = avcc[size:]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &core.Codec{
|
||||
Name: core.CodecH265,
|
||||
ClockRate: 90000,
|
||||
FmtpLine: buf.String(),
|
||||
PayloadType: core.PayloadTypeRAW,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package h265
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDecodeSPS(t *testing.T) {
|
||||
s := "QgEBAWAAAAMAAAMAAAMAAAMAmaAAoAgBaH+KrTuiS7/8AAQABbAgApMuADN/mAE="
|
||||
b, err := base64.StdEncoding.DecodeString(s)
|
||||
require.Nil(t, err)
|
||||
|
||||
sps := DecodeSPS(b)
|
||||
require.NotNil(t, sps)
|
||||
require.Equal(t, uint16(5120), sps.Width())
|
||||
require.Equal(t, uint16(1440), sps.Height())
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
package hvc
|
||||
// Package h265 - MPEG4 format related functions
|
||||
package h265
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package h265
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/bits"
|
||||
)
|
||||
|
||||
// http://www.itu.int/rec/T-REC-H.265
|
||||
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type SPS struct {
|
||||
sps_video_parameter_set_id uint8
|
||||
sps_max_sub_layers_minus1 uint8
|
||||
sps_temporal_id_nesting_flag byte
|
||||
|
||||
general_profile_space uint8
|
||||
general_tier_flag byte
|
||||
general_profile_idc uint8
|
||||
general_profile_compatibility_flags uint32
|
||||
|
||||
general_level_idc uint8
|
||||
sub_layer_profile_present_flag []byte
|
||||
sub_layer_level_present_flag []byte
|
||||
|
||||
sps_seq_parameter_set_id uint32
|
||||
chroma_format_idc uint32
|
||||
separate_colour_plane_flag byte
|
||||
|
||||
pic_width_in_luma_samples uint32
|
||||
pic_height_in_luma_samples uint32
|
||||
}
|
||||
|
||||
func (s *SPS) Width() uint16 {
|
||||
return uint16(s.pic_width_in_luma_samples)
|
||||
}
|
||||
|
||||
func (s *SPS) Height() uint16 {
|
||||
return uint16(s.pic_height_in_luma_samples)
|
||||
}
|
||||
|
||||
func DecodeSPS(nalu []byte) *SPS {
|
||||
rbsp := bytes.ReplaceAll(nalu[2:], []byte{0, 0, 3}, []byte{0, 0})
|
||||
|
||||
r := bits.NewReader(rbsp)
|
||||
s := &SPS{}
|
||||
|
||||
s.sps_video_parameter_set_id = r.ReadBits8(4)
|
||||
s.sps_max_sub_layers_minus1 = r.ReadBits8(3)
|
||||
s.sps_temporal_id_nesting_flag = r.ReadBit()
|
||||
|
||||
if !s.profile_tier_level(r) {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.sps_seq_parameter_set_id = r.ReadUEGolomb()
|
||||
s.chroma_format_idc = r.ReadUEGolomb()
|
||||
if s.chroma_format_idc == 3 {
|
||||
s.separate_colour_plane_flag = r.ReadBit()
|
||||
}
|
||||
|
||||
s.pic_width_in_luma_samples = r.ReadUEGolomb()
|
||||
s.pic_height_in_luma_samples = r.ReadUEGolomb()
|
||||
|
||||
//...
|
||||
|
||||
if r.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// profile_tier_level supports ONLY general_profile_idc == 1
|
||||
// over variants very complicated...
|
||||
//
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
func (s *SPS) profile_tier_level(r *bits.Reader) bool {
|
||||
s.general_profile_space = r.ReadBits8(2)
|
||||
s.general_tier_flag = r.ReadBit()
|
||||
s.general_profile_idc = r.ReadBits8(5)
|
||||
|
||||
s.general_profile_compatibility_flags = r.ReadBits(32)
|
||||
_ = r.ReadBits64(48) // other flags
|
||||
|
||||
if s.general_profile_idc != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
s.general_level_idc = r.ReadBits8(8)
|
||||
|
||||
s.sub_layer_profile_present_flag = make([]byte, s.sps_max_sub_layers_minus1)
|
||||
s.sub_layer_level_present_flag = make([]byte, s.sps_max_sub_layers_minus1)
|
||||
|
||||
for i := byte(0); i < s.sps_max_sub_layers_minus1; i++ {
|
||||
s.sub_layer_profile_present_flag[i] = r.ReadBit()
|
||||
s.sub_layer_level_present_flag[i] = r.ReadBit()
|
||||
}
|
||||
|
||||
if s.sps_max_sub_layers_minus1 > 0 {
|
||||
for i := s.sps_max_sub_layers_minus1; i < 8; i++ {
|
||||
_ = r.ReadBits8(2) // reserved_zero_2bits
|
||||
}
|
||||
}
|
||||
|
||||
for i := byte(0); i < s.sps_max_sub_layers_minus1; i++ {
|
||||
if s.sub_layer_profile_present_flag[i] != 0 {
|
||||
_ = r.ReadBits8(2) // sub_layer_profile_space
|
||||
_ = r.ReadBit() // sub_layer_tier_flag
|
||||
sub_layer_profile_idc := r.ReadBits8(5) // sub_layer_profile_idc
|
||||
|
||||
_ = r.ReadBits(32) // sub_layer_profile_compatibility_flag
|
||||
_ = r.ReadBits64(48) // other flags
|
||||
|
||||
if sub_layer_profile_idc != 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if s.sub_layer_level_present_flag[i] != 0 {
|
||||
_ = r.ReadBits8(8)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user