mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-11-01 12:03:06 +08:00
feat: add rtsp plugin
This commit is contained in:
@@ -1,21 +1,20 @@
|
|||||||
global:
|
global:
|
||||||
loglevel: debug
|
loglevel: trace
|
||||||
enableauth: true
|
enableauth: true
|
||||||
tcp:
|
tcp:
|
||||||
listenaddr: :50051
|
listenaddr: :50051
|
||||||
# publish:
|
publish:
|
||||||
|
pubaudio: false
|
||||||
|
# ringsize: 20-250
|
||||||
|
# buffertime: 10s
|
||||||
# speed: 1
|
# speed: 1
|
||||||
console:
|
console:
|
||||||
secret: de2c0bb9fd47684adc07a426e139239b
|
secret: de2c0bb9fd47684adc07a426e139239b
|
||||||
logrotate:
|
logrotate:
|
||||||
level: debug
|
level: debug
|
||||||
webrtc:
|
|
||||||
publish:
|
|
||||||
pubaudio: false
|
|
||||||
rtmp:
|
rtmp:
|
||||||
chunksize: 2048
|
chunksize: 2048
|
||||||
publish:
|
publish:
|
||||||
pubaudio: false
|
|
||||||
# idletimeout: 10s
|
# idletimeout: 10s
|
||||||
# closedelaytimeout: 4s
|
# closedelaytimeout: 4s
|
||||||
subscribe:
|
subscribe:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
_ "m7s.live/m7s/v5/plugin/hdl"
|
_ "m7s.live/m7s/v5/plugin/hdl"
|
||||||
_ "m7s.live/m7s/v5/plugin/logrotate"
|
_ "m7s.live/m7s/v5/plugin/logrotate"
|
||||||
_ "m7s.live/m7s/v5/plugin/rtmp"
|
_ "m7s.live/m7s/v5/plugin/rtmp"
|
||||||
|
_ "m7s.live/m7s/v5/plugin/rtsp"
|
||||||
_ "m7s.live/m7s/v5/plugin/webrtc"
|
_ "m7s.live/m7s/v5/plugin/webrtc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
26
go.mod
26
go.mod
@@ -5,11 +5,14 @@ go 1.22
|
|||||||
toolchain go1.22.1
|
toolchain go1.22.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/AlexxIT/go2rtc v1.9.4
|
||||||
github.com/cnotch/ipchub v1.1.0
|
github.com/cnotch/ipchub v1.1.0
|
||||||
|
github.com/emiago/sipgo v0.22.0
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
|
||||||
github.com/pion/interceptor v0.1.29
|
github.com/pion/interceptor v0.1.29
|
||||||
github.com/pion/rtcp v1.2.14
|
github.com/pion/rtcp v1.2.14
|
||||||
github.com/pion/rtp v1.8.6
|
github.com/pion/rtp v1.8.6
|
||||||
|
github.com/pion/sdp/v3 v3.0.9
|
||||||
github.com/polarsignals/frostdb v0.0.0-20240613134636-1d823f7d7299
|
github.com/polarsignals/frostdb v0.0.0-20240613134636-1d823f7d7299
|
||||||
github.com/q191201771/naza v0.30.48
|
github.com/q191201771/naza v0.30.48
|
||||||
github.com/quic-go/quic-go v0.43.1
|
github.com/quic-go/quic-go v0.43.1
|
||||||
@@ -48,12 +51,15 @@ require (
|
|||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hamba/avro/v2 v2.20.1 // indirect
|
github.com/hamba/avro/v2 v2.20.1 // indirect
|
||||||
|
github.com/icholy/digest v0.1.22 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.8 // indirect
|
github.com/klauspost/compress v1.17.8 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
@@ -65,16 +71,15 @@ require (
|
|||||||
github.com/parquet-go/parquet-go v0.22.0 // indirect
|
github.com/parquet-go/parquet-go v0.22.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
github.com/pion/datachannel v1.5.6 // indirect
|
github.com/pion/datachannel v1.5.6 // indirect
|
||||||
github.com/pion/dtls/v2 v2.2.10 // indirect
|
github.com/pion/dtls/v2 v2.2.11 // indirect
|
||||||
github.com/pion/ice/v3 v3.0.7 // indirect
|
github.com/pion/ice/v3 v3.0.7 // indirect
|
||||||
github.com/pion/logging v0.2.2 // indirect
|
github.com/pion/logging v0.2.2 // indirect
|
||||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||||
github.com/pion/randutil v0.1.0 // indirect
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
github.com/pion/sctp v1.8.16 // indirect
|
github.com/pion/sctp v1.8.16 // indirect
|
||||||
github.com/pion/sdp/v3 v3.0.9 // indirect
|
|
||||||
github.com/pion/srtp/v3 v3.0.1 // indirect
|
github.com/pion/srtp/v3 v3.0.1 // indirect
|
||||||
github.com/pion/stun/v2 v2.0.0 // indirect
|
github.com/pion/stun/v2 v2.0.0 // indirect
|
||||||
github.com/pion/transport/v2 v2.2.4 // indirect
|
github.com/pion/transport/v2 v2.2.5 // indirect
|
||||||
github.com/pion/transport/v3 v3.0.2 // indirect
|
github.com/pion/transport/v3 v3.0.2 // indirect
|
||||||
github.com/pion/turn/v3 v3.0.3 // indirect
|
github.com/pion/turn/v3 v3.0.3 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
@@ -88,6 +93,8 @@ require (
|
|||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
|
github.com/rs/zerolog v1.33.0 // indirect
|
||||||
|
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect
|
||||||
github.com/segmentio/encoding v0.3.6 // indirect
|
github.com/segmentio/encoding v0.3.6 // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/thanos-io/objstore v0.0.0-20240512204237-71ef2d0cf7c4 // indirect
|
github.com/thanos-io/objstore v0.0.0-20240512204237-71ef2d0cf7c4 // indirect
|
||||||
@@ -99,7 +106,7 @@ require (
|
|||||||
go.opentelemetry.io/otel v1.27.0 // indirect
|
go.opentelemetry.io/otel v1.27.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||||
)
|
)
|
||||||
@@ -111,17 +118,16 @@ require (
|
|||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.1
|
||||||
github.com/mcuadros/go-defaults v1.2.0
|
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||||
github.com/phsym/console-slog v0.3.1
|
github.com/phsym/console-slog v0.3.1
|
||||||
github.com/pion/webrtc/v4 v4.0.0-beta.13
|
github.com/pion/webrtc/v4 v4.0.0-beta.13
|
||||||
github.com/shirou/gopsutil/v3 v3.24.3
|
github.com/shirou/gopsutil/v3 v3.24.3
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.24.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mod v0.18.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.26.0
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
golang.org/x/tools v0.21.0 // indirect
|
golang.org/x/tools v0.22.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|||||||
62
go.sum
62
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/AlexxIT/go2rtc v1.9.4 h1:GC25fWz9S0XwZn/RV5Y4cV7UEGbtIWktYy8Aq96RROg=
|
||||||
|
github.com/AlexxIT/go2rtc v1.9.4/go.mod h1:3nYj8jnqS0O38cCxa96fbifX1RF6GxAjlzhfEl32zeY=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU=
|
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU=
|
||||||
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
|
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
|
||||||
@@ -40,6 +42,7 @@ github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9Q
|
|||||||
github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf h1:GOPo6vn/vTN+3IwZBvXX0y5doJfSC7My0cdzelyOCsQ=
|
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf h1:GOPo6vn/vTN+3IwZBvXX0y5doJfSC7My0cdzelyOCsQ=
|
||||||
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -51,6 +54,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
|||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/efficientgo/core v1.0.0-rc.2 h1:7j62qHLnrZqO3V3UA0AqOGd5d5aXV3AX6m/NZBHp78I=
|
github.com/efficientgo/core v1.0.0-rc.2 h1:7j62qHLnrZqO3V3UA0AqOGd5d5aXV3AX6m/NZBHp78I=
|
||||||
github.com/efficientgo/core v1.0.0-rc.2/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps=
|
github.com/efficientgo/core v1.0.0-rc.2/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps=
|
||||||
|
github.com/emiago/sipgo v0.22.0 h1:GaQ51m26M9QnVBVY2aDJ/mXqq/BDfZ1A+nW7XgU/4Ts=
|
||||||
|
github.com/emiago/sipgo v0.22.0/go.mod h1:a77FgPEEjJvfYWYfP3p53u+dNhWEMb/VGVS6guvBzx0=
|
||||||
github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug=
|
github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
@@ -73,6 +78,7 @@ github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
|||||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
@@ -114,6 +120,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
|
|||||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/icholy/digest v0.1.22 h1:dRIwCjtAcXch57ei+F0HSb5hmprL873+q7PoVojdMzM=
|
||||||
|
github.com/icholy/digest v0.1.22/go.mod h1:uLAeDdWKIWNFMH0wqbwchbTQOmJWhzSnL7zmqSPqEEc=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
@@ -138,11 +146,15 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
|
|||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc=
|
|
||||||
github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -152,7 +164,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
@@ -187,8 +198,9 @@ github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V
|
|||||||
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
|
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
|
||||||
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
|
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
|
||||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||||
github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA=
|
|
||||||
github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||||
|
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
|
||||||
|
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||||
github.com/pion/ice/v3 v3.0.3/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q=
|
github.com/pion/ice/v3 v3.0.3/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q=
|
||||||
github.com/pion/ice/v3 v3.0.7 h1:dfMViRKblENqzorR2cQiiRKWqQfqKZ9+nT/sREX3ra8=
|
github.com/pion/ice/v3 v3.0.7 h1:dfMViRKblENqzorR2cQiiRKWqQfqKZ9+nT/sREX3ra8=
|
||||||
github.com/pion/ice/v3 v3.0.7/go.mod h1:pBRcCoJRC0vwvFsemfRIqRLYukV4bPboGb0B4b8AhrQ=
|
github.com/pion/ice/v3 v3.0.7/go.mod h1:pBRcCoJRC0vwvFsemfRIqRLYukV4bPboGb0B4b8AhrQ=
|
||||||
@@ -226,8 +238,9 @@ github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
|
|||||||
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
|
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
|
||||||
github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI=
|
github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI=
|
||||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||||
github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo=
|
|
||||||
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||||
|
github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
|
||||||
|
github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||||
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
||||||
github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
|
github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
|
||||||
github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
|
github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
|
||||||
@@ -237,6 +250,7 @@ github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQe
|
|||||||
github.com/pion/webrtc/v4 v4.0.0-beta.13 h1:nIhz2viUhaFvKlbLDpF/XQqlsS+PhfYTxd8qcAo1pL8=
|
github.com/pion/webrtc/v4 v4.0.0-beta.13 h1:nIhz2viUhaFvKlbLDpF/XQqlsS+PhfYTxd8qcAo1pL8=
|
||||||
github.com/pion/webrtc/v4 v4.0.0-beta.13/go.mod h1:ojwmbdrsIkmRXPumQf9OFIkTJVB9AV/Z9ItMpNvsuhM=
|
github.com/pion/webrtc/v4 v4.0.0-beta.13/go.mod h1:ojwmbdrsIkmRXPumQf9OFIkTJVB9AV/Z9ItMpNvsuhM=
|
||||||
github.com/pixelbender/go-sdp v1.1.0/go.mod h1:6IBlz9+BrUHoFTea7gcp4S54khtOhjCW/nVDLhmZBAs=
|
github.com/pixelbender/go-sdp v1.1.0/go.mod h1:6IBlz9+BrUHoFTea7gcp4S54khtOhjCW/nVDLhmZBAs=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA=
|
github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA=
|
||||||
@@ -268,12 +282,17 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
|||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||||
github.com/samber/slog-formatter v1.0.0 h1:ULxHV+jNqi6aFP8xtzGHl2ejFRMl2+jI2UhCpgoXTDA=
|
github.com/samber/slog-formatter v1.0.0 h1:ULxHV+jNqi6aFP8xtzGHl2ejFRMl2+jI2UhCpgoXTDA=
|
||||||
github.com/samber/slog-formatter v1.0.0/go.mod h1:c7pRfwhCfZQNzJz+XirmTveElxXln7M0Y8Pq781uxlo=
|
github.com/samber/slog-formatter v1.0.0/go.mod h1:c7pRfwhCfZQNzJz+XirmTveElxXln7M0Y8Pq781uxlo=
|
||||||
github.com/samber/slog-multi v1.0.0 h1:snvP/P5GLQ8TQh5WSqdRaxDANW8AAA3egwEoytLsqvc=
|
github.com/samber/slog-multi v1.0.0 h1:snvP/P5GLQ8TQh5WSqdRaxDANW8AAA3egwEoytLsqvc=
|
||||||
github.com/samber/slog-multi v1.0.0/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo=
|
github.com/samber/slog-multi v1.0.0/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo=
|
||||||
|
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
|
||||||
|
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||||
github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ=
|
github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ=
|
||||||
@@ -284,6 +303,7 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt
|
|||||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A=
|
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
@@ -336,16 +356,17 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y
|
|||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
@@ -361,8 +382,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
|||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -390,6 +411,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -397,11 +419,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -422,17 +445,18 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -459,7 +483,6 @@ google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGm
|
|||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
@@ -473,3 +496,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
|
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||||
|
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
|
|||||||
@@ -55,11 +55,11 @@ type (
|
|||||||
}
|
}
|
||||||
AVRing = util.Ring[AVFrame]
|
AVRing = util.Ring[AVFrame]
|
||||||
DataFrame struct {
|
DataFrame struct {
|
||||||
sync.RWMutex `json:"-" yaml:"-"` // 读写锁
|
sync.RWMutex
|
||||||
discard bool
|
discard bool
|
||||||
Sequence uint32 // 在一个Track中的序号
|
Sequence uint32 // 在一个Track中的序号
|
||||||
WriteTime time.Time // 写入时间,可用于比较两个帧的先后
|
WriteTime time.Time // 写入时间,可用于比较两个帧的先后
|
||||||
Raw any `json:"-" yaml:"-"` // 裸格式
|
Raw any // 裸格式
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type (
|
|||||||
}
|
}
|
||||||
PCMACtx AudioCtx
|
PCMACtx AudioCtx
|
||||||
PCMUCtx AudioCtx
|
PCMUCtx AudioCtx
|
||||||
|
OPUSCtx AudioCtx
|
||||||
AACCtx struct {
|
AACCtx struct {
|
||||||
AudioCtx
|
AudioCtx
|
||||||
}
|
}
|
||||||
@@ -36,3 +37,7 @@ func (*PCMACtx) FourCC() FourCC {
|
|||||||
func (*AACCtx) FourCC() FourCC {
|
func (*AACCtx) FourCC() FourCC {
|
||||||
return FourCC_MP4A
|
return FourCC_MP4A
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*OPUSCtx) FourCC() FourCC {
|
||||||
|
return FourCC_OPUS
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,15 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/quic-go/quic-go"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,26 +33,6 @@ type Config struct {
|
|||||||
var durationType = reflect.TypeOf(time.Duration(0))
|
var durationType = reflect.TypeOf(time.Duration(0))
|
||||||
var regexpType = reflect.TypeOf(Regexp{})
|
var regexpType = reflect.TypeOf(Regexp{})
|
||||||
|
|
||||||
type Plugin interface {
|
|
||||||
// 可能的入参类型:FirstConfig 第一次初始化配置,Config 后续配置更新,SE系列(StateEvent)流状态变化事件
|
|
||||||
OnEvent(any)
|
|
||||||
}
|
|
||||||
|
|
||||||
type TCPPlugin interface {
|
|
||||||
Plugin
|
|
||||||
ServeTCP(net.Conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPPlugin interface {
|
|
||||||
Plugin
|
|
||||||
http.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
type QuicPlugin interface {
|
|
||||||
Plugin
|
|
||||||
ServeQuic(quic.Connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *Config) Range(f func(key string, value Config)) {
|
func (config *Config) Range(f func(key string, value Config)) {
|
||||||
if m, ok := config.GetValue().(map[string]Config); ok {
|
if m, ok := config.GetValue().(map[string]Config); ok {
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type QuicConfig interface {
|
type QuicConfig interface {
|
||||||
ListenQuic(context.Context, QuicPlugin) error
|
ListenQuic(context.Context, func(connection quic.Connection)) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Quic struct {
|
type Quic struct {
|
||||||
@@ -18,7 +18,7 @@ type Quic struct {
|
|||||||
KeyFile string `desc:"私钥文件"`
|
KeyFile string `desc:"私钥文件"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Quic) ListenQuic(ctx context.Context, plugin QuicPlugin) error {
|
func (q *Quic) ListenQuic(ctx context.Context, handler func(connection quic.Connection)) error {
|
||||||
listener, err := quic.ListenAddr(q.ListenAddr, q.generateTLSConfig(), &quic.Config{
|
listener, err := quic.ListenAddr(q.ListenAddr, q.generateTLSConfig(), &quic.Config{
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
})
|
})
|
||||||
@@ -31,7 +31,7 @@ func (q *Quic) ListenQuic(ctx context.Context, plugin QuicPlugin) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go plugin.ServeQuic(conn)
|
go handler(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type TCP struct {
|
|||||||
ListenNum int `desc:"同时并行监听数量,0为CPU核心数量"` //同时并行监听数量,0为CPU核心数量
|
ListenNum int `desc:"同时并行监听数量,0为CPU核心数量"` //同时并行监听数量,0为CPU核心数量
|
||||||
NoDelay bool `desc:"是否禁用Nagle算法"` //是否禁用Nagle算法
|
NoDelay bool `desc:"是否禁用Nagle算法"` //是否禁用Nagle算法
|
||||||
KeepAlive bool `desc:"是否启用KeepAlive"` //是否启用KeepAlive
|
KeepAlive bool `desc:"是否启用KeepAlive"` //是否启用KeepAlive
|
||||||
|
AutoListen bool `default:"true" desc:"是否自动监听"`
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
listenerTls net.Listener
|
listenerTls net.Listener
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,8 +161,6 @@ type Console struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
EnableAVCC bool `default:"true" desc:"启用AVCC格式,rtmp、http-flv协议使用"` //启用AVCC格式,rtmp、http-flv协议使用
|
|
||||||
EnableRTP bool `default:"true" desc:"启用RTP格式,rtsp、webrtc等协议使用"` //启用RTP格式,rtsp、webrtc等协议使用
|
|
||||||
EnableSubEvent bool `default:"true" desc:"启用订阅事件,禁用可以提高性能"` //启用订阅事件,禁用可以提高性能
|
EnableSubEvent bool `default:"true" desc:"启用订阅事件,禁用可以提高性能"` //启用订阅事件,禁用可以提高性能
|
||||||
EnableAuth bool `default:"true" desc:"启用鉴权"` //启用鉴权
|
EnableAuth bool `default:"true" desc:"启用鉴权"` //启用鉴权
|
||||||
LogLang string `default:"zh" desc:"日志语言" enum:"zh:中文,en:英文"` //日志语言
|
LogLang string `default:"zh" desc:"日志语言" enum:"zh:中文,en:英文"` //日志语言
|
||||||
@@ -172,15 +170,16 @@ type Engine struct {
|
|||||||
PulseInterval time.Duration `default:"5s" desc:"心跳事件间隔"` //心跳事件间隔
|
PulseInterval time.Duration `default:"5s" desc:"心跳事件间隔"` //心跳事件间隔
|
||||||
DisableAll bool `default:"false" desc:"禁用所有插件"` //禁用所有插件
|
DisableAll bool `default:"false" desc:"禁用所有插件"` //禁用所有插件
|
||||||
RTPReorderBufferLen int `default:"50" desc:"RTP重排序缓冲区长度"` //RTP重排序缓冲区长度
|
RTPReorderBufferLen int `default:"50" desc:"RTP重排序缓冲区长度"` //RTP重排序缓冲区长度
|
||||||
PoolSize int `desc:"内存池大小"` //内存池大小
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Common struct {
|
type Common struct {
|
||||||
|
PublicIP string
|
||||||
Publish
|
Publish
|
||||||
Subscribe
|
Subscribe
|
||||||
HTTP
|
HTTP
|
||||||
Quic
|
Quic
|
||||||
TCP
|
TCP
|
||||||
|
UDP
|
||||||
Pull
|
Pull
|
||||||
Push
|
Push
|
||||||
}
|
}
|
||||||
|
|||||||
17
pkg/config/udp.go
Normal file
17
pkg/config/udp.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UDPConfig interface {
|
||||||
|
ListenUDP(context.Context, func(conn *net.UDPConn)) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type UDP struct {
|
||||||
|
ListenAddr string `desc:"监听地址,格式为ip:port,ip 可省略默认监听所有网卡"`
|
||||||
|
CertFile string `desc:"证书文件"`
|
||||||
|
KeyFile string `desc:"私钥文件"`
|
||||||
|
AutoListen bool `default:"true" desc:"是否自动监听"`
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ const defaultBufSize = 1 << 14
|
|||||||
|
|
||||||
type BufReader struct {
|
type BufReader struct {
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
allocator *ScalableMemoryAllocator
|
Allocator *ScalableMemoryAllocator
|
||||||
buf MemoryReader
|
buf MemoryReader
|
||||||
BufLen int
|
BufLen int
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ type BufReader struct {
|
|||||||
func NewBufReaderWithBufLen(reader io.Reader, bufLen int) (r *BufReader) {
|
func NewBufReaderWithBufLen(reader io.Reader, bufLen int) (r *BufReader) {
|
||||||
r = &BufReader{
|
r = &BufReader{
|
||||||
reader: reader,
|
reader: reader,
|
||||||
allocator: NewScalableMemoryAllocator(bufLen),
|
Allocator: NewScalableMemoryAllocator(bufLen),
|
||||||
BufLen: bufLen,
|
BufLen: bufLen,
|
||||||
}
|
}
|
||||||
r.buf.Memory = &Memory{}
|
r.buf.Memory = &Memory{}
|
||||||
@@ -31,17 +31,34 @@ func NewBufReader(reader io.Reader) (r *BufReader) {
|
|||||||
func (r *BufReader) Recycle() {
|
func (r *BufReader) Recycle() {
|
||||||
r.reader = nil
|
r.reader = nil
|
||||||
r.buf = MemoryReader{}
|
r.buf = MemoryReader{}
|
||||||
r.allocator.Recycle()
|
r.Allocator.Recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BufReader) Peek(n int) (buf []byte, err error) {
|
||||||
|
defer func(snap MemoryReader) {
|
||||||
|
l := r.buf.Length + n
|
||||||
|
r.buf = snap
|
||||||
|
r.buf.Length = l
|
||||||
|
}(
|
||||||
|
r.buf)
|
||||||
|
for range n {
|
||||||
|
if b, err := r.ReadByte(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
buf = append(buf, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BufReader) eat() error {
|
func (r *BufReader) eat() error {
|
||||||
buf := r.allocator.Malloc(r.BufLen)
|
buf := r.Allocator.Malloc(r.BufLen)
|
||||||
if n, err := r.reader.Read(buf); err != nil {
|
if n, err := r.reader.Read(buf); err != nil {
|
||||||
r.allocator.Free(buf)
|
r.Allocator.Free(buf)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if n < r.BufLen {
|
if n < r.BufLen {
|
||||||
r.allocator.Free(buf[n:])
|
r.Allocator.Free(buf[n:])
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
}
|
}
|
||||||
r.buf.Buffers = append(r.buf.Buffers, buf)
|
r.buf.Buffers = append(r.buf.Buffers, buf)
|
||||||
@@ -122,7 +139,12 @@ func (r *BufReader) ReadNto(n int, to []byte) (err error) {
|
|||||||
l += ll
|
l += ll
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
func (r *BufReader) ReadString(n int) (s string, err error) {
|
||||||
|
err = r.ReadRange(n, func(buf []byte) {
|
||||||
|
s += string(buf)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
func (r *BufReader) ReadBytes(n int) (mem Memory, err error) {
|
func (r *BufReader) ReadBytes(n int) (mem Memory, err error) {
|
||||||
err = r.ReadRange(n, func(buf []byte) {
|
err = r.ReadRange(n, func(buf []byte) {
|
||||||
mem.Buffers = append(mem.Buffers, buf)
|
mem.Buffers = append(mem.Buffers, buf)
|
||||||
@@ -132,5 +154,5 @@ func (r *BufReader) ReadBytes(n int) (mem Memory, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *BufReader) recycleFront() {
|
func (r *BufReader) recycleFront() {
|
||||||
r.buf.ClipFront(r.allocator.Free)
|
r.buf.ClipFront(r.Allocator.Free)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,10 @@ type ITCPPlugin interface {
|
|||||||
OnTCPConnect(*net.TCPConn)
|
OnTCPConnect(*net.TCPConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IUDPPlugin interface {
|
||||||
|
OnUDPConnect(*net.UDPConn)
|
||||||
|
}
|
||||||
|
|
||||||
var plugins []PluginMeta
|
var plugins []PluginMeta
|
||||||
|
|
||||||
func InstallPlugin[C iPlugin](options ...any) error {
|
func InstallPlugin[C iPlugin](options ...any) error {
|
||||||
@@ -220,7 +224,7 @@ func (p *Plugin) Start() {
|
|||||||
tcphandler = p
|
tcphandler = p
|
||||||
}
|
}
|
||||||
|
|
||||||
if tcpConf.ListenAddr != "" {
|
if tcpConf.ListenAddr != "" && tcpConf.AutoListen {
|
||||||
p.Info("listen tcp", "addr", tcpConf.ListenAddr)
|
p.Info("listen tcp", "addr", tcpConf.ListenAddr)
|
||||||
go func() {
|
go func() {
|
||||||
err := tcpConf.Listen(tcphandler.OnTCPConnect)
|
err := tcpConf.Listen(tcphandler.OnTCPConnect)
|
||||||
@@ -230,7 +234,7 @@ func (p *Plugin) Start() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if tcpConf.ListenAddrTLS != "" {
|
if tcpConf.ListenAddrTLS != "" && tcpConf.AutoListen {
|
||||||
p.Info("listen tcp tls", "addr", tcpConf.ListenAddrTLS)
|
p.Info("listen tcp tls", "addr", tcpConf.ListenAddrTLS)
|
||||||
go func() {
|
go func() {
|
||||||
err := tcpConf.ListenTLS(tcphandler.OnTCPConnect)
|
err := tcpConf.ListenTLS(tcphandler.OnTCPConnect)
|
||||||
|
|||||||
33
plugin/gb28181/index.go
Normal file
33
plugin/gb28181/index.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package plugin_gb28181
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emiago/sipgo"
|
||||||
|
"github.com/emiago/sipgo/sip"
|
||||||
|
"m7s.live/m7s/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SipConfig struct {
|
||||||
|
ListenAddr []string
|
||||||
|
ListenTLSAddr []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type GB28181Plugin struct {
|
||||||
|
m7s.Plugin
|
||||||
|
Sip SipConfig
|
||||||
|
ua *sipgo.UserAgent
|
||||||
|
server *sipgo.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = m7s.InstallPlugin[GB28181Plugin]()
|
||||||
|
|
||||||
|
func (gb *GB28181Plugin) OnInit() (err error) {
|
||||||
|
gb.ua, err = sipgo.NewUA() // Build user agent
|
||||||
|
gb.server, err = sipgo.NewServer(gb.ua) // Creating server handle for ua
|
||||||
|
gb.server.OnRegister(gb.OnRegister)
|
||||||
|
gb.server.ListenAndServe(gb, "tcp", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gb *GB28181Plugin) OnRegister(req *sip.Request, tx sip.ServerTransaction) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ func (p *RTMPPlugin) OnPublish(puber *m7s.Publisher) {
|
|||||||
|
|
||||||
func (p *RTMPPlugin) OnTCPConnect(conn *net.TCPConn) {
|
func (p *RTMPPlugin) OnTCPConnect(conn *net.TCPConn) {
|
||||||
logger := p.Logger.With("remote", conn.RemoteAddr().String())
|
logger := p.Logger.With("remote", conn.RemoteAddr().String())
|
||||||
receivers := make(map[uint32]*RTMPReceiver)
|
receivers := make(map[uint32]*Receiver)
|
||||||
var err error
|
var err error
|
||||||
nc := NewNetConnection(conn, logger)
|
nc := NewNetConnection(conn, logger)
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -148,7 +148,7 @@ func (p *RTMPPlugin) OnTCPConnect(conn *net.TCPConn) {
|
|||||||
// }
|
// }
|
||||||
// s := engine.Streams.Get(nc.appName + "/" + cmd.StreamName)
|
// s := engine.Streams.Get(nc.appName + "/" + cmd.StreamName)
|
||||||
// if s != nil && s.Publisher != nil {
|
// if s != nil && s.Publisher != nil {
|
||||||
// if p, ok := s.Publisher.(*RTMPReceiver); ok {
|
// if p, ok := s.Publisher.(*Receiver); ok {
|
||||||
// // m.CommandName = "releaseStream_result"
|
// // m.CommandName = "releaseStream_result"
|
||||||
// p.Stop()
|
// p.Stop()
|
||||||
// delete(receivers, p.StreamID)
|
// delete(receivers, p.StreamID)
|
||||||
@@ -156,7 +156,7 @@ func (p *RTMPPlugin) OnTCPConnect(conn *net.TCPConn) {
|
|||||||
// }
|
// }
|
||||||
// err = nc.SendMessage(RTMP_MSG_AMF0_COMMAND, m)
|
// err = nc.SendMessage(RTMP_MSG_AMF0_COMMAND, m)
|
||||||
case *PublishMessage:
|
case *PublishMessage:
|
||||||
receiver := &RTMPReceiver{
|
receiver := &Receiver{
|
||||||
NetStream: NetStream{
|
NetStream: NetStream{
|
||||||
NetConnection: nc,
|
NetConnection: nc,
|
||||||
StreamID: cmd.StreamId,
|
StreamID: cmd.StreamId,
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NetConnection struct {
|
type NetConnection struct {
|
||||||
*slog.Logger `json:"-" yaml:"-"`
|
*slog.Logger
|
||||||
*util.BufReader `json:"-" yaml:"-"`
|
*util.BufReader
|
||||||
net.Conn `json:"-" yaml:"-"`
|
net.Conn
|
||||||
bandwidth uint32
|
bandwidth uint32
|
||||||
readSeqNum uint32 // 当前读的字节
|
readSeqNum uint32 // 当前读的字节
|
||||||
writeSeqNum uint32 // 当前写的字节
|
writeSeqNum uint32 // 当前写的字节
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ type NetStream struct {
|
|||||||
StreamID uint32
|
StreamID uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *NetStream) CreateAudioSender(c bool) *AVSender {
|
func (ns *NetStream) CreateAudioSender(c bool) *Sender {
|
||||||
var av AVSender
|
var av Sender
|
||||||
av.NetConnection = ns.NetConnection
|
av.NetConnection = ns.NetConnection
|
||||||
av.ChunkStreamID = RTMP_CSID_AUDIO
|
av.ChunkStreamID = RTMP_CSID_AUDIO
|
||||||
av.MessageTypeID = RTMP_MSG_AUDIO
|
av.MessageTypeID = RTMP_MSG_AUDIO
|
||||||
@@ -15,8 +15,8 @@ func (ns *NetStream) CreateAudioSender(c bool) *AVSender {
|
|||||||
return &av
|
return &av
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *NetStream) CreateVideoSender(c bool) *AVSender {
|
func (ns *NetStream) CreateVideoSender(c bool) *Sender {
|
||||||
var av AVSender
|
var av Sender
|
||||||
av.NetConnection = ns.NetConnection
|
av.NetConnection = ns.NetConnection
|
||||||
av.ChunkStreamID = RTMP_CSID_VIDEO
|
av.ChunkStreamID = RTMP_CSID_VIDEO
|
||||||
av.MessageTypeID = RTMP_MSG_VIDEO
|
av.MessageTypeID = RTMP_MSG_VIDEO
|
||||||
@@ -25,7 +25,7 @@ func (ns *NetStream) CreateVideoSender(c bool) *AVSender {
|
|||||||
return &av
|
return &av
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *NetStream) CreateSender(c bool) (audio *AVSender, video *AVSender) {
|
func (ns *NetStream) CreateSender(c bool) (audio *Sender, video *Sender) {
|
||||||
return ns.CreateAudioSender(c), ns.CreateVideoSender(c)
|
return ns.CreateAudioSender(c), ns.CreateVideoSender(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,22 @@ import (
|
|||||||
"m7s.live/m7s/v5"
|
"m7s.live/m7s/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AVSender struct {
|
type Sender struct {
|
||||||
*NetConnection
|
*NetConnection
|
||||||
ChunkHeader
|
ChunkHeader
|
||||||
errContinue bool
|
errContinue bool
|
||||||
lastAbs uint32
|
lastAbs uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (av *AVSender) HandleAudio(frame *RTMPAudio) (err error) {
|
func (av *Sender) HandleAudio(frame *RTMPAudio) (err error) {
|
||||||
return av.SendFrame(&frame.RTMPData)
|
return av.SendFrame(&frame.RTMPData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (av *AVSender) HandleVideo(frame *RTMPVideo) (err error) {
|
func (av *Sender) HandleVideo(frame *RTMPVideo) (err error) {
|
||||||
return av.SendFrame(&frame.RTMPData)
|
return av.SendFrame(&frame.RTMPData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (av *AVSender) SendFrame(frame *RTMPData) (err error) {
|
func (av *Sender) SendFrame(frame *RTMPData) (err error) {
|
||||||
// seq := frame.Sequence
|
// seq := frame.Sequence
|
||||||
payloadLen := frame.Size
|
payloadLen := frame.Size
|
||||||
if av.errContinue {
|
if av.errContinue {
|
||||||
@@ -67,7 +67,7 @@ func (av *AVSender) SendFrame(frame *RTMPData) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type RTMPReceiver struct {
|
type Receiver struct {
|
||||||
*m7s.Publisher
|
*m7s.Publisher
|
||||||
NetStream
|
NetStream
|
||||||
}
|
}
|
||||||
@@ -232,7 +232,7 @@ func createH26xFrame(from *AVFrame, codecID VideoCodecID) (frame IAVFrame, err e
|
|||||||
rtmpVideo.Timestamp = uint32(from.Timestamp / time.Millisecond)
|
rtmpVideo.Timestamp = uint32(from.Timestamp / time.Millisecond)
|
||||||
rtmpVideo.ScalableMemoryAllocator = from.Wraps[0].GetScalableMemoryAllocator()
|
rtmpVideo.ScalableMemoryAllocator = from.Wraps[0].GetScalableMemoryAllocator()
|
||||||
nalus := from.Raw.(Nalus)
|
nalus := from.Raw.(Nalus)
|
||||||
rtmpVideo.RecycleIndexes = make([]int, len(nalus.Nalus)) // Recycle partial data
|
rtmpVideo.RecycleIndexes = make([]int, 0, len(nalus.Nalus)) // Recycle partial data
|
||||||
head := rtmpVideo.NextN(5)
|
head := rtmpVideo.NextN(5)
|
||||||
head[0] = util.Conditoinal[byte](from.IDR, 0x10, 0x20) | byte(codecID)
|
head[0] = util.Conditoinal[byte](from.IDR, 0x10, 0x20) | byte(codecID)
|
||||||
head[1] = 1
|
head[1] = 1
|
||||||
|
|||||||
@@ -42,11 +42,17 @@ type (
|
|||||||
SequenceNumber uint16
|
SequenceNumber uint16
|
||||||
SSRC uint32
|
SSRC uint32
|
||||||
}
|
}
|
||||||
RTPG711Ctx struct {
|
RTPPCMACtx struct {
|
||||||
RTPCtx
|
RTPCtx
|
||||||
|
codec.PCMACtx
|
||||||
|
}
|
||||||
|
RTPPCMUCtx struct {
|
||||||
|
RTPCtx
|
||||||
|
codec.PCMUCtx
|
||||||
}
|
}
|
||||||
RTPOPUSCtx struct {
|
RTPOPUSCtx struct {
|
||||||
RTPCtx
|
RTPCtx
|
||||||
|
codec.OPUSCtx
|
||||||
}
|
}
|
||||||
RTPAACCtx struct {
|
RTPAACCtx struct {
|
||||||
RTPCtx
|
RTPCtx
|
||||||
@@ -100,20 +106,21 @@ type RTPAudio struct {
|
|||||||
func (r *RTPAudio) Parse(t *AVTrack) (isIDR, isSeq bool, raw any, err error) {
|
func (r *RTPAudio) Parse(t *AVTrack) (isIDR, isSeq bool, raw any, err error) {
|
||||||
switch r.MimeType {
|
switch r.MimeType {
|
||||||
case webrtc.MimeTypeOpus:
|
case webrtc.MimeTypeOpus:
|
||||||
// var ctx RTPOPUSCtx
|
var ctx RTPOPUSCtx
|
||||||
// ctx.FourCC = codec.FourCC_OPUS
|
ctx.RTPCodecParameters = *r.RTPCodecParameters
|
||||||
// ctx.RTPCodecParameters = *r.RTPCodecParameters
|
t.ICodecCtx = &ctx
|
||||||
// codecCtx = &ctx
|
|
||||||
case webrtc.MimeTypePCMA:
|
case webrtc.MimeTypePCMA:
|
||||||
// var ctx RTPG711Ctx
|
var ctx RTPPCMACtx
|
||||||
// ctx.FourCC = codec.FourCC_ALAW
|
ctx.RTPCodecParameters = *r.RTPCodecParameters
|
||||||
// ctx.RTPCodecParameters = *r.RTPCodecParameters
|
t.ICodecCtx = &ctx
|
||||||
// codecCtx = &ctx
|
|
||||||
case webrtc.MimeTypePCMU:
|
case webrtc.MimeTypePCMU:
|
||||||
// var ctx RTPG711Ctx
|
var ctx RTPPCMUCtx
|
||||||
// ctx.FourCC = codec.FourCC_ULAW
|
ctx.RTPCodecParameters = *r.RTPCodecParameters
|
||||||
// ctx.RTPCodecParameters = *r.RTPCodecParameters
|
t.ICodecCtx = &ctx
|
||||||
// codecCtx = &ctx
|
case "audio/MPEG4-GENERIC":
|
||||||
|
var ctx RTPAACCtx
|
||||||
|
ctx.RTPCodecParameters = *r.RTPCodecParameters
|
||||||
|
t.ICodecCtx = &ctx
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package rtp
|
package rtp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ var (
|
|||||||
_ IVideoCodecCtx = (*RTPH264Ctx)(nil)
|
_ IVideoCodecCtx = (*RTPH264Ctx)(nil)
|
||||||
_ IVideoCodecCtx = (*RTPH265Ctx)(nil)
|
_ IVideoCodecCtx = (*RTPH265Ctx)(nil)
|
||||||
_ IVideoCodecCtx = (*RTPAV1Ctx)(nil)
|
_ IVideoCodecCtx = (*RTPAV1Ctx)(nil)
|
||||||
|
spropReg = regexp.MustCompile(`sprop-parameter-sets=(.+),([^;]+)(;|$)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *RTPVideo) Parse(t *AVTrack) (isIDR, isSeq bool, raw any, err error) {
|
func (r *RTPVideo) Parse(t *AVTrack) (isIDR, isSeq bool, raw any, err error) {
|
||||||
@@ -49,7 +52,16 @@ func (r *RTPVideo) Parse(t *AVTrack) (isIDR, isSeq bool, raw any, err error) {
|
|||||||
ctx = t.ICodecCtx.(*RTPH264Ctx)
|
ctx = t.ICodecCtx.(*RTPH264Ctx)
|
||||||
} else {
|
} else {
|
||||||
ctx = &RTPH264Ctx{}
|
ctx = &RTPH264Ctx{}
|
||||||
|
//packetization-mode=1; sprop-parameter-sets=J2QAKaxWgHgCJ+WagICAgQ==,KO48sA==; profile-level-id=640029
|
||||||
ctx.RTPCodecParameters = *r.RTPCodecParameters
|
ctx.RTPCodecParameters = *r.RTPCodecParameters
|
||||||
|
if match := spropReg.FindStringSubmatch(ctx.SDPFmtpLine); len(match) > 2 {
|
||||||
|
if sps, err := base64.StdEncoding.DecodeString(match[1]); err == nil {
|
||||||
|
ctx.SPS = [][]byte{sps}
|
||||||
|
}
|
||||||
|
if pps, err := base64.StdEncoding.DecodeString(match[2]); err == nil {
|
||||||
|
ctx.PPS = [][]byte{pps}
|
||||||
|
}
|
||||||
|
}
|
||||||
t.ICodecCtx = ctx
|
t.ICodecCtx = ctx
|
||||||
}
|
}
|
||||||
raw, err = r.ToRaw(ctx)
|
raw, err = r.ToRaw(ctx)
|
||||||
|
|||||||
434
plugin/rtsp/index.go
Normal file
434
plugin/rtsp/index.go
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
package plugin_rtsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/tcp"
|
||||||
|
"github.com/pion/rtcp"
|
||||||
|
"github.com/pion/rtp"
|
||||||
|
"github.com/pion/webrtc/v4"
|
||||||
|
"m7s.live/m7s/v5"
|
||||||
|
"m7s.live/m7s/v5/pkg/util"
|
||||||
|
mrtp "m7s.live/m7s/v5/plugin/rtp/pkg"
|
||||||
|
. "m7s.live/m7s/v5/plugin/rtsp/pkg"
|
||||||
|
"net"
|
||||||
|
"runtime/debug"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultConfig = m7s.DefaultYaml(`tcp:
|
||||||
|
listenaddr: :554`)
|
||||||
|
|
||||||
|
var _ = m7s.InstallPlugin[RTSPPlugin](defaultConfig)
|
||||||
|
|
||||||
|
type RTSPPlugin struct {
|
||||||
|
m7s.Plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RTSPPlugin) OnTCPConnect(conn *net.TCPConn) {
|
||||||
|
logger := p.Logger.With("remote", conn.RemoteAddr().String())
|
||||||
|
var receiver *Receiver
|
||||||
|
var err error
|
||||||
|
nc := NewNetConnection(conn, logger)
|
||||||
|
defer func() {
|
||||||
|
nc.Destroy()
|
||||||
|
if p := recover(); p != nil {
|
||||||
|
err = p.(error)
|
||||||
|
logger.Error(err.Error(), "stack", string(debug.Stack()))
|
||||||
|
}
|
||||||
|
if receiver != nil {
|
||||||
|
receiver.Dispose(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var req *tcp.Request
|
||||||
|
var timeout time.Duration
|
||||||
|
var sendMode bool
|
||||||
|
mem := util.NewScalableMemoryAllocator(1 << 12)
|
||||||
|
defer mem.Recycle()
|
||||||
|
for {
|
||||||
|
req, err = nc.ReadRequest()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if nc.URL == nil {
|
||||||
|
nc.URL = req.URL
|
||||||
|
logger = logger.With("url", nc.URL.String())
|
||||||
|
nc.UserAgent = req.Header.Get("User-Agent")
|
||||||
|
logger.Info("connect", "userAgent", nc.UserAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
//if !c.auth.Validate(req) {
|
||||||
|
// res := &tcp.Response{
|
||||||
|
// Status: "401 Unauthorized",
|
||||||
|
// Header: map[string][]string{"Www-Authenticate": {`Basic realm="go2rtc"`}},
|
||||||
|
// Request: req,
|
||||||
|
// }
|
||||||
|
// if err = c.WriteResponse(res); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// continue
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Receiver: OPTIONS > DESCRIBE > SETUP... > PLAY > TEARDOWN
|
||||||
|
// Sender: OPTIONS > ANNOUNCE > SETUP... > RECORD > TEARDOWN
|
||||||
|
switch req.Method {
|
||||||
|
case MethodOptions:
|
||||||
|
res := &tcp.Response{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Public": {"OPTIONS, SETUP, TEARDOWN, DESCRIBE, PLAY, PAUSE, ANNOUNCE, RECORD"},
|
||||||
|
},
|
||||||
|
Request: req,
|
||||||
|
}
|
||||||
|
if err = nc.WriteResponse(res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case MethodAnnounce:
|
||||||
|
if req.Header.Get("Content-Type") != "application/sdp" {
|
||||||
|
err = errors.New("wrong content type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nc.SDP = string(req.Body) // for info
|
||||||
|
|
||||||
|
if nc.Medias, err = UnmarshalSDP(req.Body); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
receiver = &Receiver{
|
||||||
|
NetConnection: nc,
|
||||||
|
}
|
||||||
|
|
||||||
|
if receiver.Publisher, err = p.Publish(strings.TrimPrefix(nc.URL.Path, "/")); err != nil {
|
||||||
|
receiver = nil
|
||||||
|
err = nc.WriteResponse(&tcp.Response{
|
||||||
|
StatusCode: 500, Status: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, media := range nc.Medias {
|
||||||
|
if codec := media.Codecs[0]; codec.IsAudio() {
|
||||||
|
receiver.AudioCodecParameters = &webrtc.RTPCodecParameters{
|
||||||
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||||
|
MimeType: "audio/" + codec.Name,
|
||||||
|
ClockRate: codec.ClockRate,
|
||||||
|
Channels: codec.Channels,
|
||||||
|
SDPFmtpLine: codec.FmtpLine,
|
||||||
|
RTCPFeedback: nil,
|
||||||
|
},
|
||||||
|
PayloadType: webrtc.PayloadType(codec.PayloadType),
|
||||||
|
}
|
||||||
|
receiver.AudioChannelID = byte(i) << 1
|
||||||
|
} else if codec.IsVideo() {
|
||||||
|
receiver.VideoChannelID = byte(i) << 1
|
||||||
|
receiver.VideoCodecParameters = &webrtc.RTPCodecParameters{
|
||||||
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||||
|
MimeType: "video/" + codec.Name,
|
||||||
|
ClockRate: codec.ClockRate,
|
||||||
|
Channels: codec.Channels,
|
||||||
|
SDPFmtpLine: codec.FmtpLine,
|
||||||
|
RTCPFeedback: nil,
|
||||||
|
},
|
||||||
|
PayloadType: webrtc.PayloadType(codec.PayloadType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = time.Second * 15
|
||||||
|
|
||||||
|
res := &tcp.Response{Request: req}
|
||||||
|
if err = nc.WriteResponse(res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case MethodDescribe:
|
||||||
|
sendMode = true
|
||||||
|
timeout = time.Second * 60
|
||||||
|
|
||||||
|
//if c.Senders == nil {
|
||||||
|
// res := &tcp.Response{
|
||||||
|
// Status: "404 Not Found",
|
||||||
|
// Request: req,
|
||||||
|
// }
|
||||||
|
// return c.WriteResponse(res)
|
||||||
|
//}
|
||||||
|
|
||||||
|
res := &tcp.Response{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Content-Type": {"application/sdp"},
|
||||||
|
},
|
||||||
|
Request: req,
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert tracks to real output medias
|
||||||
|
var medias []*core.Media
|
||||||
|
|
||||||
|
//for i, track := range c.Senders {
|
||||||
|
// media := &core.Media{
|
||||||
|
// Kind: core.GetKind(track.Codec.Name),
|
||||||
|
// Direction: core.DirectionRecvonly,
|
||||||
|
// Codecs: []*core.Codec{track.Codec},
|
||||||
|
// ID: "trackID=" + strconv.Itoa(i),
|
||||||
|
// }
|
||||||
|
// medias = append(medias, media)
|
||||||
|
//}
|
||||||
|
|
||||||
|
res.Body, err = core.MarshalSDP(nc.SessionName, medias)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nc.SDP = string(res.Body) // for info
|
||||||
|
|
||||||
|
if err = nc.WriteResponse(res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case MethodSetup:
|
||||||
|
tr := req.Header.Get("Transport")
|
||||||
|
|
||||||
|
res := &tcp.Response{
|
||||||
|
Header: map[string][]string{},
|
||||||
|
Request: req,
|
||||||
|
}
|
||||||
|
|
||||||
|
const transport = "RTP/AVP/TCP;unicast;interleaved="
|
||||||
|
if strings.HasPrefix(tr, transport) {
|
||||||
|
nc.Session = core.RandString(8, 10)
|
||||||
|
|
||||||
|
if sendMode {
|
||||||
|
//if i := reqTrackID(req); i >= 0 && i < len(c.Senders) {
|
||||||
|
// // mark sender as SETUP
|
||||||
|
// c.Senders[i].Media.ID = MethodSetup
|
||||||
|
// tr = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", i*2, i*2+1)
|
||||||
|
// res.Header.Set("Transport", tr)
|
||||||
|
//} else {
|
||||||
|
// res.Status = "400 Bad Request"
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
res.Header.Set("Transport", tr[:len(transport)+3])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.Status = "461 Unsupported transport"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = nc.WriteResponse(res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case MethodRecord, MethodPlay:
|
||||||
|
if sendMode {
|
||||||
|
// stop unconfigured senders
|
||||||
|
//for _, track := range c.Senders {
|
||||||
|
// if track.Media.ID != MethodSetup {
|
||||||
|
// track.Close()
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := &tcp.Response{Request: req}
|
||||||
|
err = nc.WriteResponse(res)
|
||||||
|
audioFrame := &mrtp.RTPAudio{}
|
||||||
|
audioFrame.ScalableMemoryAllocator = mem
|
||||||
|
audioFrame.RTPCodecParameters = receiver.AudioCodecParameters
|
||||||
|
videoFrame := &mrtp.RTPVideo{}
|
||||||
|
videoFrame.ScalableMemoryAllocator = mem
|
||||||
|
videoFrame.RTPCodecParameters = receiver.VideoCodecParameters
|
||||||
|
for err == nil {
|
||||||
|
ts := time.Now()
|
||||||
|
|
||||||
|
if err = conn.SetReadDeadline(ts.Add(timeout)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var magic []byte
|
||||||
|
// we can read:
|
||||||
|
// 1. RTP interleaved: `$` + 1B channel number + 2B size
|
||||||
|
// 2. RTSP response: RTSP/1.0 200 OK
|
||||||
|
// 3. RTSP request: OPTIONS ...
|
||||||
|
magic, err = nc.Peek(4)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var channelID byte
|
||||||
|
var size int
|
||||||
|
var buf []byte
|
||||||
|
if magic[0] != '$' {
|
||||||
|
logger.Warn("not magic")
|
||||||
|
switch string(magic) {
|
||||||
|
case "RTSP":
|
||||||
|
var res *tcp.Response
|
||||||
|
if res, err = nc.ReadResponse(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Warn(string(res.Body))
|
||||||
|
// for playing backchannel only after OK response on play
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "OPTI", "TEAR", "DESC", "SETU", "PLAY", "PAUS", "RECO", "ANNO", "GET_", "SET_":
|
||||||
|
var req *tcp.Request
|
||||||
|
if req, err = nc.ReadRequest(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method == MethodOptions {
|
||||||
|
res := &tcp.Response{Request: req}
|
||||||
|
if err = nc.WriteResponse(res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
default:
|
||||||
|
logger.Error("wrong input")
|
||||||
|
//c.Fire("RTSP wrong input")
|
||||||
|
//
|
||||||
|
//for i := 0; ; i++ {
|
||||||
|
// // search next start symbol
|
||||||
|
// if _, err = c.reader.ReadBytes('$'); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if channelID, err = c.reader.ReadByte(); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO: better check maximum good channel ID
|
||||||
|
// if channelID >= 20 {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// buf4 = make([]byte, 2)
|
||||||
|
// if _, err = io.ReadFull(c.reader, buf4); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // check if size good for RTP
|
||||||
|
// size = binary.BigEndian.Uint16(buf4)
|
||||||
|
// if size <= 1500 {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 10 tries to find good packet
|
||||||
|
// if i >= 10 {
|
||||||
|
// return fmt.Errorf("RTSP wrong input")
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// hope that the odd channels are always RTCP
|
||||||
|
|
||||||
|
channelID = magic[1]
|
||||||
|
|
||||||
|
// get data size
|
||||||
|
size = int(binary.BigEndian.Uint16(magic[2:]))
|
||||||
|
// skip 4 bytes from c.reader.Peek
|
||||||
|
if err = nc.Skip(4); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf = mem.Malloc(size)
|
||||||
|
if err = nc.ReadNto(size, buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if channelID&1 == 0 {
|
||||||
|
switch channelID {
|
||||||
|
case receiver.AudioChannelID:
|
||||||
|
if !receiver.PubAudio {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
packet := &rtp.Packet{}
|
||||||
|
if err = packet.Unmarshal(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(audioFrame.Packets) == 0 || packet.Timestamp == audioFrame.Packets[0].Timestamp {
|
||||||
|
audioFrame.AddRecycleBytes(buf)
|
||||||
|
audioFrame.Packets = append(audioFrame.Packets, packet)
|
||||||
|
} else {
|
||||||
|
err = receiver.WriteAudio(audioFrame)
|
||||||
|
audioFrame = &mrtp.RTPAudio{}
|
||||||
|
audioFrame.AddRecycleBytes(buf)
|
||||||
|
audioFrame.Packets = []*rtp.Packet{packet}
|
||||||
|
audioFrame.RTPCodecParameters = receiver.VideoCodecParameters
|
||||||
|
audioFrame.ScalableMemoryAllocator = mem
|
||||||
|
}
|
||||||
|
case receiver.VideoChannelID:
|
||||||
|
if !receiver.PubVideo {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
packet := &rtp.Packet{}
|
||||||
|
if err = packet.Unmarshal(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(videoFrame.Packets) == 0 || packet.Timestamp == videoFrame.Packets[0].Timestamp {
|
||||||
|
videoFrame.AddRecycleBytes(buf)
|
||||||
|
videoFrame.Packets = append(videoFrame.Packets, packet)
|
||||||
|
} else {
|
||||||
|
// t := time.Now()
|
||||||
|
err = receiver.WriteVideo(videoFrame)
|
||||||
|
// fmt.Println("write video", time.Since(t))
|
||||||
|
videoFrame = &mrtp.RTPVideo{}
|
||||||
|
videoFrame.AddRecycleBytes(buf)
|
||||||
|
videoFrame.Packets = []*rtp.Packet{packet}
|
||||||
|
videoFrame.RTPCodecParameters = receiver.VideoCodecParameters
|
||||||
|
videoFrame.ScalableMemoryAllocator = mem
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg := &RTCP{Channel: channelID}
|
||||||
|
mem.Free(buf)
|
||||||
|
if err = msg.Header.Unmarshal(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg.Packets, err = rtcp.Unmarshal(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug("rtcp", "type", msg.Header.Type, "length", msg.Header.Length)
|
||||||
|
// TODO: rtcp msg
|
||||||
|
}
|
||||||
|
|
||||||
|
//if keepaliveDT != 0 && ts.After(keepaliveTS) {
|
||||||
|
// req := &tcp.Request{Method: MethodOptions, URL: c.URL}
|
||||||
|
// if err = c.WriteRequest(req); err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// keepaliveTS = ts.Add(keepaliveDT)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case MethodTeardown:
|
||||||
|
res := &tcp.Response{Request: req}
|
||||||
|
_ = nc.WriteResponse(res)
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
p.Warn("unsupported method", "method", req.Method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reqTrackID(req *tcp.Request) int {
|
||||||
|
var s string
|
||||||
|
if req.URL.RawQuery != "" {
|
||||||
|
s = req.URL.RawQuery
|
||||||
|
} else {
|
||||||
|
s = req.URL.Path
|
||||||
|
}
|
||||||
|
if i := strings.LastIndexByte(s, '='); i > 0 {
|
||||||
|
if i, err := strconv.Atoi(s[i+1:]); err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
190
plugin/rtsp/pkg/connection.go
Normal file
190
plugin/rtsp/pkg/connection.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
package rtsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/tcp"
|
||||||
|
"log/slog"
|
||||||
|
"m7s.live/m7s/v5/pkg/util"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Timeout = time.Second * 5
|
||||||
|
|
||||||
|
func NewNetConnection(conn net.Conn, logger *slog.Logger) *NetConnection {
|
||||||
|
defer logger.Info("new connection")
|
||||||
|
return &NetConnection{
|
||||||
|
conn: conn,
|
||||||
|
Logger: logger,
|
||||||
|
BufReader: util.NewBufReader(conn),
|
||||||
|
textReader: bufio.NewReader(conn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetConnection struct {
|
||||||
|
*slog.Logger
|
||||||
|
*util.BufReader
|
||||||
|
textReader *bufio.Reader
|
||||||
|
Backchannel bool
|
||||||
|
Media string
|
||||||
|
PacketSize uint16
|
||||||
|
SessionName string
|
||||||
|
Timeout int
|
||||||
|
Transport string // custom transport support, ex. RTSP over WebSocket
|
||||||
|
|
||||||
|
Medias []*core.Media
|
||||||
|
UserAgent string
|
||||||
|
URL *url.URL
|
||||||
|
|
||||||
|
// internal
|
||||||
|
|
||||||
|
auth *tcp.Auth
|
||||||
|
conn net.Conn
|
||||||
|
keepalive int
|
||||||
|
mode core.Mode
|
||||||
|
sequence int
|
||||||
|
Session string
|
||||||
|
sdp string
|
||||||
|
uri string
|
||||||
|
|
||||||
|
state State
|
||||||
|
stateMu sync.Mutex
|
||||||
|
SDP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NetConnection) Destroy() {
|
||||||
|
c.conn.Close()
|
||||||
|
c.BufReader.Recycle()
|
||||||
|
c.Info("destroy connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtoRTSP = "RTSP/1.0"
|
||||||
|
MethodOptions = "OPTIONS"
|
||||||
|
MethodSetup = "SETUP"
|
||||||
|
MethodTeardown = "TEARDOWN"
|
||||||
|
MethodDescribe = "DESCRIBE"
|
||||||
|
MethodPlay = "PLAY"
|
||||||
|
MethodPause = "PAUSE"
|
||||||
|
MethodAnnounce = "ANNOUNCE"
|
||||||
|
MethodRecord = "RECORD"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State byte
|
||||||
|
|
||||||
|
func (s State) String() string {
|
||||||
|
switch s {
|
||||||
|
case StateNone:
|
||||||
|
return "NONE"
|
||||||
|
case StateConn:
|
||||||
|
|
||||||
|
return "CONN"
|
||||||
|
case StateSetup:
|
||||||
|
return MethodSetup
|
||||||
|
case StatePlay:
|
||||||
|
return MethodPlay
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateNone State = iota
|
||||||
|
StateConn
|
||||||
|
StateSetup
|
||||||
|
StatePlay
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *NetConnection) WriteRequest(req *tcp.Request) error {
|
||||||
|
if req.Proto == "" {
|
||||||
|
req.Proto = ProtoRTSP
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Header == nil {
|
||||||
|
req.Header = make(map[string][]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.sequence++
|
||||||
|
// important to send case sensitive CSeq
|
||||||
|
// https://github.com/AlexxIT/go2rtc/issues/7
|
||||||
|
req.Header["CSeq"] = []string{strconv.Itoa(c.sequence)}
|
||||||
|
|
||||||
|
c.auth.Write(req)
|
||||||
|
|
||||||
|
if c.Session != "" {
|
||||||
|
req.Header.Set("Session", c.Session)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Body != nil {
|
||||||
|
val := strconv.Itoa(len(req.Body))
|
||||||
|
req.Header.Set("Content-Length", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.conn.SetWriteDeadline(time.Now().Add(Timeout)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return req.Write(c.conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NetConnection) ReadRequest() (req *tcp.Request, err error) {
|
||||||
|
if err = c.conn.SetReadDeadline(time.Now().Add(Timeout)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req, err = tcp.ReadRequest(c.textReader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Debug(req.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NetConnection) WriteResponse(res *tcp.Response) error {
|
||||||
|
if res.Proto == "" {
|
||||||
|
res.Proto = ProtoRTSP
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Status == "" {
|
||||||
|
res.Status = "200 OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Header == nil {
|
||||||
|
res.Header = make(map[string][]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Request != nil && res.Request.Header != nil {
|
||||||
|
seq := res.Request.Header.Get("CSeq")
|
||||||
|
if seq != "" {
|
||||||
|
res.Header.Set("CSeq", seq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Session != "" {
|
||||||
|
if res.Request != nil && res.Request.Method == MethodSetup {
|
||||||
|
res.Header.Set("Session", c.Session+";timeout=60")
|
||||||
|
} else {
|
||||||
|
res.Header.Set("Session", c.Session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Body != nil {
|
||||||
|
val := strconv.Itoa(len(res.Body))
|
||||||
|
res.Header.Set("Content-Length", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.conn.SetWriteDeadline(time.Now().Add(Timeout)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Debug(res.String())
|
||||||
|
return res.Write(c.conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NetConnection) ReadResponse() (*tcp.Response, error) {
|
||||||
|
if err := c.conn.SetReadDeadline(time.Now().Add(Timeout)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tcp.ReadResponse(c.textReader)
|
||||||
|
}
|
||||||
108
plugin/rtsp/pkg/sdp.go
Normal file
108
plugin/rtsp/pkg/sdp.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package rtsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pion/rtcp"
|
||||||
|
"github.com/pion/sdp/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RTCP struct {
|
||||||
|
Channel byte
|
||||||
|
Header rtcp.Header
|
||||||
|
Packets []rtcp.Packet
|
||||||
|
}
|
||||||
|
|
||||||
|
const sdpHeader = `v=0
|
||||||
|
o=- 0 0 IN IP4 0.0.0.0
|
||||||
|
s=-
|
||||||
|
t=0 0`
|
||||||
|
|
||||||
|
func UnmarshalSDP(rawSDP []byte) ([]*core.Media, error) {
|
||||||
|
sd := &sdp.SessionDescription{}
|
||||||
|
if err := sd.Unmarshal(rawSDP); err != nil {
|
||||||
|
// fix multiple `s=` https://github.com/AlexxIT/WebRTC/issues/417
|
||||||
|
re, _ := regexp.Compile("\ns=[^\n]+")
|
||||||
|
rawSDP = re.ReplaceAll(rawSDP, nil)
|
||||||
|
|
||||||
|
// fix SDP header for some cameras
|
||||||
|
if i := bytes.Index(rawSDP, []byte("\nm=")); i > 0 {
|
||||||
|
rawSDP = append([]byte(sdpHeader), rawSDP[i:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix invalid media type (errSDPInvalidValue) caused by
|
||||||
|
// some TP-LINK IP camera, e.g. TL-IPC44GW
|
||||||
|
rawSDP = bytes.ReplaceAll(rawSDP, []byte("m=application/TP-LINK "), []byte("m=application "))
|
||||||
|
// more tplink ipcams
|
||||||
|
rawSDP = bytes.ReplaceAll(rawSDP, []byte("m=application/tp-link "), []byte("m=application "))
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
rawSDP = append(rawSDP, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
sd = &sdp.SessionDescription{}
|
||||||
|
err = sd.Unmarshal(rawSDP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var medias []*core.Media
|
||||||
|
|
||||||
|
for _, md := range sd.MediaDescriptions {
|
||||||
|
media := core.UnmarshalMedia(md)
|
||||||
|
|
||||||
|
// Check buggy SDP with fmtp for H264 on another track
|
||||||
|
// https://github.com/AlexxIT/WebRTC/issues/419
|
||||||
|
for _, codec := range media.Codecs {
|
||||||
|
if codec.Name == core.CodecH264 && codec.FmtpLine == "" {
|
||||||
|
codec.FmtpLine = findFmtpLine(codec.PayloadType, sd.MediaDescriptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if media.Direction == "" {
|
||||||
|
media.Direction = core.DirectionRecvonly
|
||||||
|
}
|
||||||
|
|
||||||
|
medias = append(medias, media)
|
||||||
|
}
|
||||||
|
|
||||||
|
return medias, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findFmtpLine(payloadType uint8, descriptions []*sdp.MediaDescription) string {
|
||||||
|
s := strconv.Itoa(int(payloadType))
|
||||||
|
for _, md := range descriptions {
|
||||||
|
codec := core.UnmarshalCodec(md, s)
|
||||||
|
if codec.FmtpLine != "" {
|
||||||
|
return codec.FmtpLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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/
|
||||||
|
func urlParse(rawURL string) (*url.URL, error) {
|
||||||
|
if strings.HasPrefix(rawURL, "rtsp://rtsp://") {
|
||||||
|
rawURL = rawURL[7:]
|
||||||
|
}
|
||||||
|
|
||||||
|
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:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return u, err
|
||||||
|
}
|
||||||
15
plugin/rtsp/pkg/transceiver.go
Normal file
15
plugin/rtsp/pkg/transceiver.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package rtsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/webrtc/v4"
|
||||||
|
"m7s.live/m7s/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Receiver struct {
|
||||||
|
*m7s.Publisher
|
||||||
|
*NetConnection
|
||||||
|
AudioCodecParameters *webrtc.RTPCodecParameters
|
||||||
|
VideoCodecParameters *webrtc.RTPCodecParameters
|
||||||
|
AudioChannelID byte
|
||||||
|
VideoChannelID byte
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Version = "v5.0.0"
|
Version = "v5.0.0"
|
||||||
MergeConfigs = []string{"Publish", "Subscribe", "HTTP"}
|
MergeConfigs = []string{"Publish", "Subscribe", "HTTP", "PublicIP"}
|
||||||
ExecPath = os.Args[0]
|
ExecPath = os.Args[0]
|
||||||
ExecDir = filepath.Dir(ExecPath)
|
ExecDir = filepath.Dir(ExecPath)
|
||||||
serverIndexG atomic.Uint32
|
serverIndexG atomic.Uint32
|
||||||
@@ -377,7 +377,9 @@ func (s *Server) onUnsubscribe(subscriber *Subscriber) {
|
|||||||
|
|
||||||
func (s *Server) onUnpublish(publisher *Publisher) {
|
func (s *Server) onUnpublish(publisher *Publisher) {
|
||||||
s.Streams.Remove(publisher)
|
s.Streams.Remove(publisher)
|
||||||
|
if publisher.Subscribers.Length > 0 {
|
||||||
s.Waiting.Add(publisher)
|
s.Waiting.Add(publisher)
|
||||||
|
}
|
||||||
s.Info("unpublish", "streamPath", publisher.StreamPath, "count", s.Streams.Length, "reason", publisher.StopReason())
|
s.Info("unpublish", "streamPath", publisher.StreamPath, "count", s.Streams.Length, "reason", publisher.StopReason())
|
||||||
for subscriber := range publisher.SubscriberRange {
|
for subscriber := range publisher.SubscriberRange {
|
||||||
waitCloseTimeout := publisher.WaitCloseTimeout
|
waitCloseTimeout := publisher.WaitCloseTimeout
|
||||||
|
|||||||
18
transformer.go
Normal file
18
transformer.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package m7s
|
||||||
|
|
||||||
|
import "m7s.live/m7s/v5/pkg"
|
||||||
|
|
||||||
|
type Transformer struct {
|
||||||
|
*Publisher
|
||||||
|
*Subscriber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transformer) Transform() {
|
||||||
|
PlayBlock(t.Subscriber, func(audioFrame *pkg.AVFrame) error {
|
||||||
|
//t.Publisher.WriteAudio()
|
||||||
|
return nil
|
||||||
|
}, func(videoFrame *pkg.AVFrame) error {
|
||||||
|
//t.Publisher.WriteVideo()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user