Merge branch 'master' of https://github.com/AlexxIT/go2rtc into secrets-file

This commit is contained in:
seydx
2025-09-26 15:15:59 +02:00
19 changed files with 249 additions and 79 deletions
+14
View File
@@ -118,3 +118,17 @@ func TestName(t *testing.T) {
// stage3
_ = prod2.Stop()
}
func TestStripUserinfo(t *testing.T) {
s := `streams:
test:
- ffmpeg:rtsp://username:password@10.1.2.3:554/stream1
- ffmpeg:rtsp://10.1.2.3:554/stream1@#video=copy
`
s = StripUserinfo(s)
require.Equal(t, `streams:
test:
- ffmpeg:rtsp://***@10.1.2.3:554/stream1
- ffmpeg:rtsp://10.1.2.3:554/stream1@#video=copy
`, s)
}
+12
View File
@@ -2,6 +2,7 @@ package core
import (
"crypto/rand"
"regexp"
"runtime"
"strconv"
"strings"
@@ -80,6 +81,17 @@ func Caller() string {
return file + ":" + strconv.Itoa(line)
}
const (
unreserved = `A-Za-z0-9-._~`
subdelims = `!$&'()*+,;=`
userinfo = unreserved + subdelims + `%:`
)
func StripUserinfo(s string) string {
sanitizer := regexp.MustCompile(`://[` + userinfo + `]+@`)
return sanitizer.ReplaceAllString(s, `://***@`)
}
func NewSecret(name string, defaultValues interface{}) (*app.Secret, error) {
return app.NewSecret(name, defaultValues)
}
+5
View File
@@ -16,6 +16,11 @@ func RepairAVCC(codec *core.Codec, handler core.HandlerFunc) core.HandlerFunc {
ps := JoinNALU(sps, pps)
return func(packet *rtp.Packet) {
// this can happen for FLV from FFmpeg
if NALUType(packet.Payload) == NALUTypeSEI {
size := int(binary.BigEndian.Uint32(packet.Payload)) + 4
packet.Payload = packet.Payload[size:]
}
if NALUType(packet.Payload) == NALUTypeIFrame {
packet.Payload = Join(ps, packet.Payload)
}
+13 -13
View File
@@ -12,7 +12,7 @@ func NewAccessory(manuf, model, name, serial, firmware string) *hap.Accessory {
hap.ServiceAccessoryInformation(manuf, model, name, serial, firmware),
ServiceCameraRTPStreamManagement(),
//hap.ServiceHAPProtocolInformation(),
//ServiceMicrophone(),
ServiceMicrophone(),
},
}
acc.InitIID()
@@ -30,17 +30,17 @@ func ServiceMicrophone() *hap.Service {
Perms: hap.EVPRPW,
//Descr: "Mute",
},
{
Type: "119",
Format: hap.FormatUInt8,
Value: 100,
Perms: hap.EVPRPW,
//Descr: "Volume",
//Unit: hap.UnitPercentage,
//MinValue: 0,
//MaxValue: 100,
//MinStep: 1,
},
//{
// Type: "119",
// Format: hap.FormatUInt8,
// Value: 100,
// Perms: hap.EVPRPW,
// //Descr: "Volume",
// //Unit: hap.UnitPercentage,
// //MinValue: 0,
// //MaxValue: 100,
// //MinStep: 1,
//},
},
}
}
@@ -62,7 +62,7 @@ func ServiceCameraRTPStreamManagement() *hap.Service {
VideoAttrs: []VideoAttrs{
{Width: 1920, Height: 1080, Framerate: 30},
{Width: 1280, Height: 720, Framerate: 30}, // important for iPhones
{Width: 320, Height: 240, Framerate: 15}, // apple watch
{Width: 320, Height: 240, Framerate: 15}, // apple watch
},
},
},
+6
View File
@@ -71,11 +71,17 @@ type JSONCharacter struct {
Event any `json:"ev,omitempty"`
}
// 4.2.1.2 Invalid Setup Codes
const insecurePINs = "00000000 11111111 22222222 33333333 44444444 55555555 66666666 77777777 88888888 99999999 12345678 87654321"
func SanitizePin(pin string) (string, error) {
s := strings.ReplaceAll(pin, "-", "")
if len(s) != 8 {
return "", errors.New("hap: wrong PIN format: " + pin)
}
if strings.Contains(insecurePINs, s) {
return "", errors.New("hap: insecure PIN: " + pin)
}
// 123-45-678
return s[:3] + "-" + s[3:5] + "-" + s[5:], nil
}
+86 -45
View File
@@ -46,6 +46,8 @@ func Marshal(v any) ([]byte, error) {
}
switch kind {
case reflect.Slice:
return appendSlice(nil, value)
case reflect.Struct:
return appendStruct(nil, value)
}
@@ -53,6 +55,23 @@ func Marshal(v any) ([]byte, error) {
return nil, errors.New("tlv8: not implemented: " + kind.String())
}
// separator the most confusing meaning in the documentation.
// It can have a value of 0x00 or 0xFF or even 0x05.
const separator = 0xFF
func appendSlice(b []byte, value reflect.Value) ([]byte, error) {
for i := 0; i < value.Len(); i++ {
if i > 0 {
b = append(b, separator, 0)
}
var err error
if b, err = appendStruct(b, value.Index(i)); err != nil {
return nil, err
}
}
return b, nil
}
func appendStruct(b []byte, value reflect.Value) ([]byte, error) {
valueType := value.Type()
@@ -121,7 +140,7 @@ func appendValue(b []byte, tag byte, value reflect.Value) ([]byte, error) {
case reflect.Slice:
for i := 0; i < value.Len(); i++ {
if i > 0 {
b = append(b, 0, 0)
b = append(b, separator, 0)
}
if b, err = appendValue(b, tag, value.Index(i)); err != nil {
return nil, err
@@ -179,64 +198,86 @@ func Unmarshal(data []byte, v any) error {
kind = value.Kind()
}
if kind != reflect.Struct {
return errors.New("tlv8: not implemented: " + kind.String())
switch kind {
case reflect.Slice:
return unmarshalSlice(data, value)
case reflect.Struct:
return unmarshalStruct(data, value)
}
return unmarshalStruct(data, value)
return errors.New("tlv8: not implemented: " + kind.String())
}
func unmarshalStruct(b []byte, value reflect.Value) error {
var waitSlice bool
// unmarshalTLV can return two types of errors:
// - critical and then the value of []byte will be nil
// - not critical and then []byte will contain the value
func unmarshalTLV(b []byte, value reflect.Value) ([]byte, error) {
if len(b) < 2 {
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
}
for len(b) >= 2 {
t := b[0]
l := int(b[1])
t := b[0]
l := int(b[1])
// array item divider
if t == 0 && l == 0 {
b = b[2:]
waitSlice = true
continue
// array item divider (t == 0x00 || t == 0xFF)
if l == 0 {
return b[2:], errors.New("tlv8: zero item")
}
var v []byte
for {
if len(b) < 2+l {
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
}
var v []byte
v = append(v, b[2:2+l]...)
b = b[2+l:]
for {
if len(b) < 2+l {
return errors.New("tlv8: wrong size: " + value.Type().Name())
// if size == 255 and same tag - continue read big payload
if l < 255 || len(b) < 2 || b[0] != t {
break
}
l = int(b[1])
}
tag := strconv.Itoa(int(t))
valueField, ok := getStructField(value, tag)
if !ok {
return b, fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
}
if err := unmarshalValue(v, valueField); err != nil {
return nil, err
}
return b, nil
}
func unmarshalSlice(b []byte, value reflect.Value) error {
valueIndex := value.Index(growSlice(value))
for len(b) > 0 {
var err error
if b, err = unmarshalTLV(b, valueIndex); err != nil {
if b != nil {
valueIndex = value.Index(growSlice(value))
continue
}
v = append(v, b[2:2+l]...)
b = b[2+l:]
// if size == 255 and same tag - continue read big payload
if l < 255 || len(b) < 2 || b[0] != t {
break
}
l = int(b[1])
}
tag := strconv.Itoa(int(t))
valueField, ok := getStructField(value, tag)
if !ok {
return fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
}
if waitSlice {
if valueField.Kind() != reflect.Slice {
return fmt.Errorf("tlv8: should be slice T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
}
waitSlice = false
}
if err := unmarshalValue(v, valueField); err != nil {
return err
}
}
return nil
}
func unmarshalStruct(b []byte, value reflect.Value) error {
for len(b) > 0 {
var err error
if b, err = unmarshalTLV(b, value); b == nil && err != nil {
return err
}
}
return nil
}
+47
View File
@@ -2,6 +2,7 @@ package tlv8
import (
"encoding/hex"
"strings"
"testing"
"github.com/stretchr/testify/require"
@@ -107,3 +108,49 @@ func TestInterface(t *testing.T) {
require.Equal(t, src, dst)
}
func TestSlice1(t *testing.T) {
var v struct {
VideoAttrs []struct {
Width uint16 `tlv8:"1"`
Height uint16 `tlv8:"2"`
Framerate uint8 `tlv8:"3"`
} `tlv8:"3"`
}
s := `030b010280070202380403011e ff00 030b010200050202d00203011e`
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
require.NoError(t, err)
err = Unmarshal(b1, &v)
require.NoError(t, err)
require.Len(t, v.VideoAttrs, 2)
b2, err := Marshal(v)
require.NoError(t, err)
require.Equal(t, b1, b2)
}
func TestSlice2(t *testing.T) {
var v []struct {
Width uint16 `tlv8:"1"`
Height uint16 `tlv8:"2"`
Framerate uint8 `tlv8:"3"`
}
s := `010280070202380403011e ff00 010200050202d00203011e`
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
require.NoError(t, err)
err = Unmarshal(b1, &v)
require.NoError(t, err)
require.Len(t, v, 2)
b2, err := Marshal(v)
require.NoError(t, err)
require.Equal(t, b1, b2)
}
+23 -4
View File
@@ -116,20 +116,39 @@ func findFmtpLine(payloadType uint8, descriptions []*sdp.MediaDescription) strin
// urlParse fix bugs:
// 1. Content-Base: rtsp://::ffff:192.168.1.123/onvif/profile.1/
// 2. Content-Base: rtsp://rtsp://turret2-cam.lan:554/stream1/
// 3. Content-Base: 192.168.253.220:1935/
func urlParse(rawURL string) (*url.URL, error) {
// fix https://github.com/AlexxIT/go2rtc/issues/830
if strings.HasPrefix(rawURL, "rtsp://rtsp://") {
rawURL = rawURL[7:]
}
// fix https://github.com/AlexxIT/go2rtc/issues/1852
if !strings.Contains(rawURL, "://") {
rawURL = "rtsp://" + rawURL
}
u, err := url.Parse(rawURL)
if err != nil && strings.HasSuffix(err.Error(), "after host") {
if i1 := strings.Index(rawURL, "://"); i1 > 0 {
if i2 := strings.IndexByte(rawURL[i1+3:], '/'); i2 > 0 {
return urlParse(rawURL[:i1+3+i2] + ":" + rawURL[i1+3+i2:])
}
if i := indexN(rawURL, '/', 3); i > 0 {
return urlParse(rawURL[:i] + ":" + rawURL[i:])
}
}
return u, err
}
func indexN(s string, c byte, n int) int {
var offset int
for {
i := strings.IndexByte(s[offset:], c)
if i < 0 {
break
}
if n--; n == 0 {
return offset + i
}
offset += i + 1
}
return -1
}
+8 -2
View File
@@ -11,14 +11,20 @@ func TestURLParse(t *testing.T) {
// https://github.com/AlexxIT/WebRTC/issues/395
base := "rtsp://::ffff:192.168.1.123/onvif/profile.1/"
u, err := urlParse(base)
assert.Empty(t, err)
assert.NoError(t, err)
assert.Equal(t, "::ffff:192.168.1.123:", u.Host)
// https://github.com/AlexxIT/go2rtc/issues/208
base = "rtsp://rtsp://turret2-cam.lan:554/stream1/"
u, err = urlParse(base)
assert.Empty(t, err)
assert.NoError(t, err)
assert.Equal(t, "turret2-cam.lan:554", u.Host)
// https://github.com/AlexxIT/go2rtc/issues/1852
base = "192.168.253.220:1935/"
u, err = urlParse(base)
assert.NoError(t, err)
assert.Equal(t, "192.168.253.220:1935", u.Host)
}
func TestBugSDP1(t *testing.T) {
+10 -3
View File
@@ -125,13 +125,20 @@ func NewServerAPI(network, address string, filters *Filters) (*webrtc.API, error
networks = append(networks, ice.NetworkType(ntype))
}
udpMux, _ = ice.NewMultiUDPMuxFromPort(
var err error
if udpMux, err = ice.NewMultiUDPMuxFromPort(
port,
ice.UDPMuxFromPortWithInterfaceFilter(interfaceFilter),
ice.UDPMuxFromPortWithIPFilter(ipFilter),
ice.UDPMuxFromPortWithNetworks(networks...),
)
} else if ln, err := net.ListenPacket("udp", address); err == nil {
); err != nil {
return nil, err
}
} else {
ln, err := net.ListenPacket("udp", address)
if err != nil {
return nil, err
}
udpMux = ice.NewUDPMuxDefault(ice.UDPMuxParams{UDPConn: ln})
}
s.SetICEUDPMux(udpMux)