forcepush this away if i fail
This commit is contained in:
parent
02044c60b4
commit
d1f1aae0d3
4 changed files with 277 additions and 156 deletions
|
|
@ -272,11 +272,10 @@ func main() {
|
||||||
host = "127.0.0.1"
|
host = "127.0.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
rtspServer := rtsp.CreateServer(applicationContext, host, port, camera)
|
rtspServer, err := rtsp.CreateServer(applicationContext, host, port, camera)
|
||||||
defer rtspServer.Stop()
|
defer rtspServer.Close()
|
||||||
|
|
||||||
log.Printf("Created RTSP Server\n")
|
log.Printf("Created RTSP Server\n")
|
||||||
err := rtspServer.ListenAndServe()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR starting RTSP Server: %s\n", err)
|
log.Printf("ERROR starting RTSP Server: %s\n", err)
|
||||||
|
|
|
||||||
21
go.mod
21
go.mod
|
|
@ -1,8 +1,27 @@
|
||||||
module github.com/jonas-koeritz/actioncam
|
module github.com/jonas-koeritz/actioncam
|
||||||
|
|
||||||
go 1.16
|
go 1.23.0
|
||||||
|
|
||||||
|
toolchain go1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/bluenviron/gortsplib/v4 v4.16.2 // or current v4
|
||||||
github.com/icza/bitio v1.0.0
|
github.com/icza/bitio v1.0.0
|
||||||
|
github.com/pion/rtp v1.8.21 // or any recent v1
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bluenviron/mediacommon/v2 v2.4.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/pion/logging v0.2.3 // indirect
|
||||||
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
|
github.com/pion/rtcp v1.2.15 // indirect
|
||||||
|
github.com/pion/sdp/v3 v3.0.15 // indirect
|
||||||
|
github.com/pion/srtp/v3 v3.0.6 // indirect
|
||||||
|
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
golang.org/x/net v0.43.0 // indirect
|
||||||
|
golang.org/x/sys v0.35.0 // indirect
|
||||||
|
)
|
||||||
|
|
|
||||||
30
go.sum
30
go.sum
|
|
@ -23,6 +23,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
|
github.com/bluenviron/gortsplib/v4 v4.16.2 h1:10HaMsorjW13gscLp3R7Oj41ck2i1EHIUYCNWD2wpkI=
|
||||||
|
github.com/bluenviron/gortsplib/v4 v4.16.2/go.mod h1:Vm07yUMys9XKnuZJLfTT8zluAN2n9ZOtz40Xb8RKh+8=
|
||||||
|
github.com/bluenviron/mediacommon/v2 v2.4.1 h1:PsKrO/c7hDjXxiOGRUBsYtMGNb4lKWIFea6zcOchoVs=
|
||||||
|
github.com/bluenviron/mediacommon/v2 v2.4.1/go.mod h1:a6MbPmXtYda9mKibKVMZlW20GYLLrX2R7ZkUE+1pwV0=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
|
@ -32,6 +36,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
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=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
|
@ -61,6 +66,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
|
@ -125,8 +132,23 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
|
||||||
|
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
|
||||||
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
||||||
|
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
||||||
|
github.com/pion/rtp v1.8.21 h1:3yrOwmZFyUpcIosNcWRpQaU+UXIJ6yxLuJ8Bx0mw37Y=
|
||||||
|
github.com/pion/rtp v1.8.21/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
|
||||||
|
github.com/pion/sdp/v3 v3.0.15 h1:F0I1zds+K/+37ZrzdADmx2Q44OFDOPRLhPnNTaUX9hk=
|
||||||
|
github.com/pion/sdp/v3 v3.0.15/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
|
||||||
|
github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4=
|
||||||
|
github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY=
|
||||||
|
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||||
|
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
|
@ -162,6 +184,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
|
@ -208,6 +232,8 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
|
@ -229,6 +255,8 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
|
@ -283,6 +311,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
||||||
|
|
@ -1,179 +1,252 @@
|
||||||
package rtsp
|
package rtsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"context"
|
||||||
"context"
|
"fmt"
|
||||||
"crypto/md5"
|
"log"
|
||||||
"fmt"
|
"net"
|
||||||
"log"
|
"sync"
|
||||||
"net"
|
"time"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jonas-koeritz/actioncam/libipcamera"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v4"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
|
|
||||||
|
// We keep this import so the CreateServer() signature matches the original
|
||||||
|
// repo (actioncam.go calls rtsp.CreateServer(ctx, host, port, camera)).
|
||||||
|
"github.com/jonas-koeritz/actioncam/libipcamera"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server implements the RTSP protocol to serve a H.264 stream
|
// This is where your RTP relay should send H.264 packets.
|
||||||
|
// The original project uses port 5220 for the preview stream.
|
||||||
|
const defaultRTPInPort = 5220
|
||||||
|
|
||||||
|
// Server wraps the gortsplib server + the in-memory stream.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
localIP string
|
gs *gortsplib.Server
|
||||||
localPort int
|
stream *gortsplib.ServerStream
|
||||||
listener net.Listener
|
media *description.Media
|
||||||
remoteRTPPort int
|
ctx context.Context
|
||||||
remoteIP string
|
cancel context.CancelFunc
|
||||||
rtpRelay *libipcamera.RTPRelay
|
wg sync.WaitGroup
|
||||||
camera *libipcamera.Camera
|
rtpConn *net.UDPConn
|
||||||
sdp string
|
|
||||||
context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateServer creates a new Server instance
|
// handler implements the gortsplib ServerHandler* interfaces.
|
||||||
func CreateServer(ctx context.Context, localIP string, port int, camera *libipcamera.Camera) *Server {
|
type handler struct {
|
||||||
server := &Server{
|
srv *Server
|
||||||
localIP: localIP,
|
|
||||||
localPort: port,
|
|
||||||
camera: camera,
|
|
||||||
remoteRTPPort: 0,
|
|
||||||
remoteIP: "",
|
|
||||||
sdp: fmt.Sprintf("v=0\r\no=- 0 0 IN IP4 %s\r\ns=ActionCamera\r\nt=0 0\r\na=control:*\r\nm=video 0 RTP/AVP 99\r\nc=IN IP4 0.0.0.0\r\na=rtpmap:99 H264/90000\r\na=control:trackID=0", localIP),
|
|
||||||
context: ctx,
|
|
||||||
}
|
|
||||||
return server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe starts listening for connections and handles them
|
// --- RTSP callbacks (multi-client capable) ---
|
||||||
func (s *Server) ListenAndServe() error {
|
|
||||||
log.Printf("%+v\n", *s)
|
|
||||||
listener, err := net.Listen("tcp4", fmt.Sprintf("%s:%d", s.localIP, s.localPort))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.listener = listener
|
|
||||||
|
|
||||||
log.Printf("RTSP Server waiting for connections on %s:%d\n", s.localIP, s.localPort)
|
// OnDescribe: clients ask what the stream looks like (SDP).
|
||||||
|
func (h *handler) OnDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx) (*base.Response, *gortsplib.ServerStream, error) {
|
||||||
|
log.Printf("RTSP DESCRIBE from %v path=%s", ctx.Conn.NetConn().RemoteAddr(), ctx.Path)
|
||||||
|
|
||||||
for {
|
return &base.Response{
|
||||||
select {
|
StatusCode: base.StatusOK,
|
||||||
case <-s.context.Done():
|
}, h.srv.stream, nil
|
||||||
listener.Close()
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR accepting connection: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Accepted new RTSP Client %s\n", conn.RemoteAddr().String())
|
|
||||||
|
|
||||||
go s.handleClient(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleClient(conn net.Conn) error {
|
// OnSetup: client wants to SUBSCRIBE to the existing stream.
|
||||||
packet := make([]string, 0)
|
func (h *handler) OnSetup(ctx *gortsplib.ServerHandlerOnSetupCtx) (*base.Response, *gortsplib.ServerStream, error) {
|
||||||
scanner := bufio.NewScanner(conn)
|
log.Printf("RTSP SETUP from %v path=%s", ctx.Conn.NetConn().RemoteAddr(), ctx.Path)
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
return &base.Response{
|
||||||
if len(line) > 0 {
|
StatusCode: base.StatusOK,
|
||||||
packet = append(packet, line)
|
}, h.srv.stream, nil
|
||||||
} else {
|
|
||||||
s.handleRequest(packet, conn)
|
|
||||||
packet = make([]string, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleRequest(packet []string, conn net.Conn) {
|
// OnPlay: client starts receiving packets.
|
||||||
fmt.Printf("C->S:\n%s\n", packet)
|
func (h *handler) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
|
log.Printf("RTSP PLAY from %v path=%s", ctx.Conn.NetConn().RemoteAddr(), ctx.Path)
|
||||||
|
|
||||||
request := strings.Split(packet[0], " ")
|
return &base.Response{
|
||||||
if len(request) != 3 {
|
StatusCode: base.StatusOK,
|
||||||
log.Printf("Received invalid request")
|
}, nil
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
method := request[0]
|
|
||||||
headers := make(map[string]string, 0)
|
|
||||||
for _, header := range packet[1:] {
|
|
||||||
parts := strings.Split(header, ":")
|
|
||||||
if len(parts) >= 2 {
|
|
||||||
headers[parts[0]] = strings.TrimSpace(strings.Join(parts[1:], ":"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session := fmt.Sprintf("%X", md5.Sum([]byte(conn.RemoteAddr().String())))
|
|
||||||
|
|
||||||
switch method {
|
|
||||||
case "OPTIONS":
|
|
||||||
writeStatus(conn, 200, "OK")
|
|
||||||
replyCSeq(conn, headers)
|
|
||||||
conn.Write([]byte("Public: DESCRIBE, SETUP, PLAY, PAUSE, RECORD\r\n\r\n"))
|
|
||||||
case "DESCRIBE":
|
|
||||||
writeStatus(conn, 200, "OK")
|
|
||||||
replyCSeq(conn, headers)
|
|
||||||
writeHeader(conn, "Content-Type", "application/sdp")
|
|
||||||
writeHeader(conn, "Content-Length", fmt.Sprintf("%d", len(s.sdp)))
|
|
||||||
conn.Write([]byte(fmt.Sprintf("\r\n%s", s.sdp)))
|
|
||||||
case "SETUP":
|
|
||||||
transportDescription := strings.Split(headers["Transport"], ";")
|
|
||||||
rtpDescription := transportDescription[len(transportDescription)-1]
|
|
||||||
remoteRTPPort, err := strconv.ParseInt(strings.Split(strings.Split(rtpDescription, "=")[1], "-")[0], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR Parsing RTP description: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.remoteRTPPort = int(remoteRTPPort)
|
|
||||||
s.remoteIP = (conn.RemoteAddr().(*net.TCPAddr)).IP.String()
|
|
||||||
|
|
||||||
log.Printf("Preparing to Stream to %s:%d\n", s.remoteIP, s.remoteRTPPort)
|
|
||||||
|
|
||||||
writeStatus(conn, 200, "OK")
|
|
||||||
replyCSeq(conn, headers)
|
|
||||||
writeHeader(conn, "Transport", headers["Transport"]+";ssrc=0")
|
|
||||||
writeHeader(conn, "Session", session)
|
|
||||||
conn.Write([]byte("\r\n"))
|
|
||||||
|
|
||||||
case "PLAY":
|
|
||||||
s.rtpRelay = libipcamera.CreateRTPRelay(s.context, net.ParseIP(s.remoteIP), s.remoteRTPPort)
|
|
||||||
s.camera.StartPreviewStream()
|
|
||||||
|
|
||||||
writeStatus(conn, 200, "OK")
|
|
||||||
replyCSeq(conn, headers)
|
|
||||||
writeHeader(conn, "Session", session)
|
|
||||||
writeHeader(conn, "RTP-Info", "url="+request[1]+";seq=10;rtptime=10")
|
|
||||||
conn.Write([]byte("\r\n"))
|
|
||||||
case "TEARDOWN":
|
|
||||||
s.rtpRelay.Stop()
|
|
||||||
writeStatus(conn, 200, "OK")
|
|
||||||
replyCSeq(conn, headers)
|
|
||||||
conn.Write([]byte("\r\n"))
|
|
||||||
case "RECORD":
|
|
||||||
s.camera.StartRecording()
|
|
||||||
|
|
||||||
writeStatus(conn, 200, "OK")
|
|
||||||
replyCSeq(conn, headers)
|
|
||||||
writeHeader(conn, "Session", session)
|
|
||||||
conn.Write([]byte("\r\n"))
|
|
||||||
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeStatus(conn net.Conn, status int, statusWord string) {
|
// (Optional) For logging / debugging:
|
||||||
conn.Write([]byte(fmt.Sprintf("RTSP/1.0 %d %s\r\n", status, statusWord)))
|
func (h *handler) OnConnOpen(ctx *gortsplib.ServerHandlerOnConnOpenCtx) {
|
||||||
|
log.Printf("RTSP connection opened: %v", ctx.Conn.NetConn().RemoteAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
func replyCSeq(conn net.Conn, headers map[string]string) {
|
func (h *handler) OnConnClose(ctx *gortsplib.ServerHandlerOnConnCloseCtx) {
|
||||||
writeHeader(conn, "CSeq", headers["CSeq"])
|
log.Printf("RTSP connection closed: %v (err=%v)", ctx.Conn.NetConn().RemoteAddr(), ctx.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeHeader(conn net.Conn, key, value string) {
|
// --- Public API ---
|
||||||
conn.Write([]byte(fmt.Sprintf("%s: %s\r\n", key, value)))
|
|
||||||
|
// CreateServer creates and starts a RTSP server that serves *one* H.264 video
|
||||||
|
// stream, backed by a single UDP source (127.0.0.1:defaultRTPInPort).
|
||||||
|
//
|
||||||
|
// It keeps the original function signature from the repo:
|
||||||
|
// rtsp.CreateServer(applicationContext, host, port, camera)
|
||||||
|
// but the camera is NOT used directly here – you’re expected to start the
|
||||||
|
// RTP relay separately so that it forwards H.264 RTP packets into
|
||||||
|
// 127.0.0.1:defaultRTPInPort.
|
||||||
|
//
|
||||||
|
// Multiple RTSP clients are automatically supported: all of them read from
|
||||||
|
// the same gortsplib.ServerStream.
|
||||||
|
func CreateServer(parentCtx context.Context, host string, port int, _ *libipcamera.Camera) (*Server, error) {
|
||||||
|
ctx, cancel := context.WithCancel(parentCtx)
|
||||||
|
|
||||||
|
// Build an in-memory H.264 stream description suitable for gortsplib.
|
||||||
|
stream, media, err := newH264Stream()
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, fmt.Errorf("create H264 stream: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for incoming RTP packets from the RTP relay.
|
||||||
|
rtpAddr := &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("127.0.0.1"),
|
||||||
|
Port: defaultRTPInPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
rtpConn, err := net.ListenUDP("udp", rtpAddr)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, fmt.Errorf("listen udp %v: %w", rtpAddr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := &Server{
|
||||||
|
stream: stream,
|
||||||
|
media: media,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
rtpConn: rtpConn,
|
||||||
|
}
|
||||||
|
|
||||||
|
h := &handler{srv: srv}
|
||||||
|
|
||||||
|
// Configure gortsplib server.
|
||||||
|
gs := &gortsplib.Server{
|
||||||
|
Handler: h,
|
||||||
|
RTSPAddress: fmt.Sprintf("%s:%d", host, port),
|
||||||
|
// If you also want UDP transport for clients, set UDPRTPAddress/UDPRTCPAddress here.
|
||||||
|
// For most use cases, TCP (interleaved over RTSP) is fine.
|
||||||
|
}
|
||||||
|
srv.gs = gs
|
||||||
|
|
||||||
|
// Start the UDP → RTSP pump.
|
||||||
|
srv.wg.Add(1)
|
||||||
|
go srv.rtpPump()
|
||||||
|
|
||||||
|
// Start RTSP server in the background.
|
||||||
|
go func() {
|
||||||
|
log.Printf("RTSP server listening on rtsp://%s:%d/ (gortsplib)", host, port)
|
||||||
|
|
||||||
|
if err := gs.StartAndWait(); err != nil {
|
||||||
|
log.Printf("RTSP server stopped with error: %v", err)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return srv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops listening for connections
|
// Close shuts down the RTSP server and the RTP pump.
|
||||||
func (s *Server) Stop() {
|
func (s *Server) Close() {
|
||||||
s.listener.Close()
|
s.cancel()
|
||||||
|
if s.gs != nil {
|
||||||
|
s.gs.Close()
|
||||||
|
}
|
||||||
|
if s.rtpConn != nil {
|
||||||
|
_ = s.rtpConn.Close()
|
||||||
|
}
|
||||||
|
s.wg.Wait()
|
||||||
|
if s.stream != nil {
|
||||||
|
s.stream.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- internals ---
|
||||||
|
|
||||||
|
// newH264Stream builds a single-video-track ServerStream with a valid clock rate.
|
||||||
|
//
|
||||||
|
// NOTE:
|
||||||
|
// * SPS / PPS here are "generic valid" values, not tuned to your camera.
|
||||||
|
// * For best results, you can parse the real SPS/PPS from your camera.sdp and
|
||||||
|
// drop them in here.
|
||||||
|
func newH264Stream() (*gortsplib.ServerStream, *description.Media, error) {
|
||||||
|
// Generic baseline-profile SPS/PPS. They just need to be *valid* so that
|
||||||
|
// the library knows the clock rate and doesn't panic ("non-positive interval
|
||||||
|
// for NewTicker") :contentReference[oaicite:1]{index=1}
|
||||||
|
h264 := &format.H264{
|
||||||
|
PayloadTyp: 96,
|
||||||
|
SPS: []byte{
|
||||||
|
0x67, 0x42, 0xC0, 0x1F, 0x96, 0x54, 0x05, 0x01, 0xED, 0x00, 0xF0, 0x88, 0x45, 0x80,
|
||||||
|
},
|
||||||
|
PPS: []byte{
|
||||||
|
0x68, 0xCE, 0x38, 0x80,
|
||||||
|
},
|
||||||
|
PacketizationMode: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
media := &description.Media{
|
||||||
|
Type: description.MediaTypeVideo,
|
||||||
|
// You could also set media.Control if you want a specific track URL.
|
||||||
|
Formats: []format.Format{h264},
|
||||||
|
}
|
||||||
|
|
||||||
|
desc := &description.Session{
|
||||||
|
Medias: []*description.Media{media},
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := &gortsplib.ServerStream{
|
||||||
|
Desc: desc,
|
||||||
|
}
|
||||||
|
if err := stream.Initialize(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream, media, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rtpPump reads RTP packets from UDP and pushes them into the gortsplib stream.
|
||||||
|
// Every connected RTSP client gets the same packets (multi-client fan-out).
|
||||||
|
func (s *Server) rtpPump() {
|
||||||
|
defer s.wg.Done()
|
||||||
|
|
||||||
|
buf := make([]byte, 2048)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid blocking forever so we can react to shutdown.
|
||||||
|
_ = s.rtpConn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
|
||||||
|
n, _, err := s.rtpConn.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// If the context is done, exit quietly.
|
||||||
|
if s.ctx.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("RTP read error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkt rtp.Packet
|
||||||
|
if err := pkt.Unmarshal(buf[:n]); err != nil {
|
||||||
|
// Ignore malformed packets.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.stream.WritePacketRTP(s.media, &pkt); err != nil {
|
||||||
|
// This is non-fatal; clients can disconnect at any time.
|
||||||
|
log.Printf("WritePacketRTP error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue