mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-25 01:00:36 +08:00
Merge branch 'AlexxIT:master' into onvif-client
This commit is contained in:
27
go.mod
27
go.mod
@@ -4,20 +4,20 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/asticode/go-astits v1.13.0
|
||||
github.com/expr-lang/expr v1.16.9
|
||||
github.com/expr-lang/expr v1.17.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/miekg/dns v1.1.63
|
||||
github.com/pion/ice/v2 v2.3.37
|
||||
github.com/pion/ice/v4 v4.0.9
|
||||
github.com/pion/interceptor v0.1.37
|
||||
github.com/pion/rtcp v1.2.15
|
||||
github.com/pion/rtp v1.8.11
|
||||
github.com/pion/sdp/v3 v3.0.10
|
||||
github.com/pion/srtp/v2 v2.0.20
|
||||
github.com/pion/stun v0.6.1
|
||||
github.com/pion/webrtc/v3 v3.3.5
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/pion/rtp v1.8.13
|
||||
github.com/pion/sdp/v3 v3.0.11
|
||||
github.com/pion/srtp/v3 v3.0.4
|
||||
github.com/pion/stun/v3 v3.0.0
|
||||
github.com/pion/webrtc/v4 v4.0.14
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1
|
||||
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f
|
||||
github.com/stretchr/testify v1.10.0
|
||||
@@ -27,19 +27,18 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/asticode/go-astikit v0.52.0 // indirect
|
||||
github.com/asticode/go-astikit v0.54.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/pion/datachannel v1.5.10 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.12 // indirect
|
||||
github.com/pion/dtls/v3 v3.0.6 // indirect
|
||||
github.com/pion/logging v0.2.3 // indirect
|
||||
github.com/pion/mdns v0.0.12 // indirect
|
||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/sctp v1.8.36 // indirect
|
||||
github.com/pion/transport/v2 v2.2.10 // indirect
|
||||
github.com/pion/sctp v1.8.37 // indirect
|
||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||
github.com/pion/turn/v2 v2.1.6 // indirect
|
||||
github.com/pion/turn/v4 v4.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/wlynxg/anet v0.0.5 // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
|
||||
127
go.sum
127
go.sum
@@ -1,6 +1,6 @@
|
||||
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
||||
github.com/asticode/go-astikit v0.52.0 h1:kTl2XjgiVQhUl1H7kim7NhmTtCMwVBbPrXKqhQhbk8Y=
|
||||
github.com/asticode/go-astikit v0.52.0/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE=
|
||||
github.com/asticode/go-astikit v0.54.0 h1:uq9eurgisdkYwJU9vSWIQaPH4MH0cac82sQH00kmSNQ=
|
||||
github.com/asticode/go-astikit v0.54.0/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE=
|
||||
github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c=
|
||||
github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
@@ -8,19 +8,15 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
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/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
|
||||
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/expr-lang/expr v1.17.2 h1:o0A99O/Px+/DTjEnQiodAgOIK9PPxL8DtXhBRKC+Iso=
|
||||
github.com/expr-lang/expr v1.17.2/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
@@ -34,47 +30,36 @@ github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
|
||||
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
|
||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
|
||||
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||
github.com/pion/ice/v2 v2.3.37 h1:ObIdaNDu1rCo7hObhs34YSBcO7fjslJMZV0ux+uZWh0=
|
||||
github.com/pion/ice/v2 v2.3.37/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ=
|
||||
github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E=
|
||||
github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU=
|
||||
github.com/pion/ice/v4 v4.0.9 h1:VKgU4MwA2LUDVLq+WBkpEHTcAb8c5iCvFMECeuPOZNk=
|
||||
github.com/pion/ice/v4 v4.0.9/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
||||
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
|
||||
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
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/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
||||
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
||||
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
||||
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.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||
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.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||
github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk=
|
||||
github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4=
|
||||
github.com/pion/sctp v1.8.36 h1:owNudmnz1xmhfYje5L/FCav3V9wpPRePHle3Zi+P+M0=
|
||||
github.com/pion/sctp v1.8.36/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
||||
github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA=
|
||||
github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
|
||||
github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk=
|
||||
github.com/pion/srtp/v2 v2.0.20/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
||||
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||
github.com/pion/transport/v2 v2.2.3/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.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
|
||||
github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
|
||||
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
||||
github.com/pion/rtp v1.8.13 h1:8uSUPpjSL4OlwZI8Ygqu7+h2p9NPFB+yAZ461Xn5sNg=
|
||||
github.com/pion/rtp v1.8.13/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4=
|
||||
github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs=
|
||||
github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
||||
github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI=
|
||||
github.com/pion/sdp/v3 v3.0.11/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
|
||||
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
|
||||
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
|
||||
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
||||
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||
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/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
|
||||
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||
github.com/pion/webrtc/v3 v3.3.5 h1:ZsSzaMz/i9nblPdiAkZoP+E6Kmjw+jnyq3bEmU3EtRg=
|
||||
github.com/pion/webrtc/v3 v3.3.5/go.mod h1:liNa+E1iwyzyXqNUwvoMRNQ10x8h8FOeJKL8RkIbamE=
|
||||
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
|
||||
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
|
||||
github.com/pion/webrtc/v4 v4.0.14 h1:nyds/sFRR+HvmWoBa6wrL46sSfpArE0qR883MBW96lg=
|
||||
github.com/pion/webrtc/v4 v4.0.14/go.mod h1:R3+qTnQTS03UzwDarYecgioNf7DYgTsldxnCXB821Kk=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
|
||||
@@ -82,96 +67,38 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
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/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 h1:NVK+OqnavpyFmUiKfUMHrpvbCi2VFoWTrcpI7aDaJ2I=
|
||||
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
|
||||
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxIpoID9prlYH8nuNYKt0XvweHA=
|
||||
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8=
|
||||
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.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 h1:aeN+ghOV0b2VCmKKO3gqnDQ8mLbpABZgRR2FVYx4ouI=
|
||||
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9/go.mod h1:roo6cZ/uqpwKMuvPG0YmzI5+AmUiMWfjCBZpGXqbTxE=
|
||||
github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
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.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
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.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
10
internal/eseecloud/eseecloud.go
Normal file
10
internal/eseecloud/eseecloud.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package eseecloud
|
||||
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/internal/streams"
|
||||
"github.com/AlexxIT/go2rtc/pkg/eseecloud"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
streams.HandleFunc("eseecloud", eseecloud.Dial)
|
||||
}
|
||||
10
internal/flussonic/flussonic.go
Normal file
10
internal/flussonic/flussonic.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package flussonic
|
||||
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/internal/streams"
|
||||
"github.com/AlexxIT/go2rtc/pkg/flussonic"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
streams.HandleFunc("flussonic", flussonic.Dial)
|
||||
}
|
||||
@@ -2,12 +2,9 @@ package ivideon
|
||||
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/internal/streams"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/ivideon"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
streams.HandleFunc("ivideon", func(source string) (core.Producer, error) {
|
||||
return ivideon.Dial(source)
|
||||
})
|
||||
streams.HandleFunc("ivideon", ivideon.Dial)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
"github.com/AlexxIT/go2rtc/pkg/xnet"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
type Address struct {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
// streamsHandler supports:
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
"github.com/pion/sdp/v3"
|
||||
)
|
||||
|
||||
// https://github.com/AlexxIT/go2rtc/issues/1600
|
||||
@@ -27,7 +28,6 @@ func crealityClient(url string) (core.Producer, error) {
|
||||
|
||||
medias := []*core.Media{
|
||||
{Kind: core.KindVideo, Direction: core.DirectionRecvonly},
|
||||
{Kind: core.KindAudio, Direction: core.DirectionRecvonly},
|
||||
}
|
||||
|
||||
// TODO: return webrtc.SessionDescription
|
||||
@@ -36,6 +36,8 @@ func crealityClient(url string) (core.Producer, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Trace().Msgf("[webrtc] offer:\n%s", offer)
|
||||
|
||||
body, err := offerToB64(offer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -61,6 +63,12 @@ func crealityClient(url string) (core.Producer, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Trace().Msgf("[webrtc] answer:\n%s", answer)
|
||||
|
||||
if answer, err = fixCrealitySDP(answer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = prod.SetAnswer(answer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -108,3 +116,37 @@ func answerFromB64(r io.Reader) (string, error) {
|
||||
// string "v=0..."
|
||||
return v["sdp"], nil
|
||||
}
|
||||
|
||||
func fixCrealitySDP(value string) (string, error) {
|
||||
var sd sdp.SessionDescription
|
||||
if err := sd.UnmarshalString(value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
md := sd.MediaDescriptions[0]
|
||||
|
||||
// important to skip first codec, because second codec will be used
|
||||
skip := md.MediaName.Formats[0]
|
||||
md.MediaName.Formats = md.MediaName.Formats[1:]
|
||||
|
||||
attrs := make([]sdp.Attribute, 0, len(md.Attributes))
|
||||
for _, attr := range md.Attributes {
|
||||
switch attr.Key {
|
||||
case "fmtp", "rtpmap":
|
||||
// important to skip fmtp with x-google, because this is second fmtp for same codec
|
||||
// and pion library will fail parsing this SDP
|
||||
if strings.HasPrefix(attr.Value, skip) || strings.Contains(attr.Value, "x-google") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
attrs = append(attrs, attr)
|
||||
}
|
||||
|
||||
md.Attributes = attrs
|
||||
|
||||
b, err := sd.Marshal()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
type kinesisRequest struct {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/tcp"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
// This package handles the Milestone WebRTC session lifecycle, including authentication,
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
func openIPCClient(rawURL string, query url.Values) (core.Producer, error) {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/internal/streams"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
const MimeSDP = "application/sdp"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/internal/streams"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/internal/api/ws"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -36,3 +37,37 @@ func TestWebRTCAPIv2(t *testing.T) {
|
||||
require.Equal(t, "v=0\n...", offer.SDP)
|
||||
require.Equal(t, "stun:stun.l.google.com:19302", offer.ICEServers[0].URLs[0])
|
||||
}
|
||||
|
||||
func TestCrealitySDP(t *testing.T) {
|
||||
sdp := `v=0
|
||||
o=- 1495799811084970 1495799811084970 IN IP4 0.0.0.0
|
||||
s=-
|
||||
t=0 0
|
||||
a=msid-semantic:WMS *
|
||||
a=group:BUNDLE 0
|
||||
m=video 9 UDP/TLS/RTP/SAVPF 96 98
|
||||
a=rtcp-fb:98 nack
|
||||
a=rtcp-fb:98 nack pli
|
||||
a=fmtp:96 profile-level-id=42e01f;level-asymmetry-allowed=1
|
||||
a=fmtp:98 profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1
|
||||
a=fmtp:98 x-google-max-bitrate=6000;x-google-min-bitrate=2000;x-google-start-bitrate=4000
|
||||
a=rtpmap:96 H264/90000
|
||||
a=rtpmap:98 H264/90000
|
||||
a=ssrc:1 cname:pear
|
||||
c=IN IP4 0.0.0.0
|
||||
a=sendonly
|
||||
a=mid:0
|
||||
a=rtcp-mux
|
||||
a=ice-ufrag:7AVa
|
||||
a=ice-pwd:T+F/5y05Paw+mtG5Jrd8N3
|
||||
a=ice-options:trickle
|
||||
a=fingerprint:sha-256 A5:AB:C0:4E:29:5B:BD:3B:7D:88:24:6C:56:6B:2A:79:A3:76:99:35:57:75:AD:C8:5A:A6:34:20:88:1B:68:EF
|
||||
a=setup:passive
|
||||
a=candidate:1 1 UDP 2015363327 172.22.233.10 48929 typ host
|
||||
a=candidate:2 1 TCP 1015021823 172.22.233.10 0 typ host tcptype active
|
||||
a=candidate:3 1 TCP 1010827519 172.22.233.10 60677 typ host tcptype passive
|
||||
`
|
||||
sdp, err := fixCrealitySDP(sdp)
|
||||
require.Nil(t, err)
|
||||
require.False(t, strings.Contains(sdp, "x-google-max-bitrate"))
|
||||
}
|
||||
|
||||
4
main.go
4
main.go
@@ -9,9 +9,11 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/internal/doorbird"
|
||||
"github.com/AlexxIT/go2rtc/internal/dvrip"
|
||||
"github.com/AlexxIT/go2rtc/internal/echo"
|
||||
"github.com/AlexxIT/go2rtc/internal/eseecloud"
|
||||
"github.com/AlexxIT/go2rtc/internal/exec"
|
||||
"github.com/AlexxIT/go2rtc/internal/expr"
|
||||
"github.com/AlexxIT/go2rtc/internal/ffmpeg"
|
||||
"github.com/AlexxIT/go2rtc/internal/flussonic"
|
||||
"github.com/AlexxIT/go2rtc/internal/gopro"
|
||||
"github.com/AlexxIT/go2rtc/internal/hass"
|
||||
"github.com/AlexxIT/go2rtc/internal/hls"
|
||||
@@ -88,6 +90,8 @@ func main() {
|
||||
gopro.Init() // gopro source
|
||||
doorbird.Init() // doorbird source
|
||||
v4l2.Init() // v4l2 source
|
||||
flussonic.Init()
|
||||
eseecloud.Init()
|
||||
|
||||
// 6. Helper modules
|
||||
|
||||
|
||||
@@ -89,6 +89,12 @@ func (r *Reader) ReadBits64(n byte) (res uint64) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) ReadFloat32() float64 {
|
||||
i := r.ReadUint16()
|
||||
f := r.ReadUint16()
|
||||
return float64(i) + float64(f)/65536
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBytes(n int) (b []byte) {
|
||||
if r.bits == 0 {
|
||||
if r.pos+n > len(r.buf) {
|
||||
|
||||
@@ -97,8 +97,9 @@ func NewSender(media *Media, codec *Codec) *Sender {
|
||||
buf: buf,
|
||||
}
|
||||
s.Input = func(packet *Packet) {
|
||||
// writing to nil chan - OK, writing to closed chan - panic
|
||||
s.mu.Lock()
|
||||
if s.buf != nil {
|
||||
// unblocked write to channel
|
||||
select {
|
||||
case s.buf <- packet:
|
||||
s.Bytes += len(packet.Payload)
|
||||
@@ -106,6 +107,9 @@ func NewSender(media *Media, codec *Codec) *Sender {
|
||||
default:
|
||||
s.Drops++
|
||||
}
|
||||
} else {
|
||||
s.Drops++
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
s.Output = func(packet *Packet) {
|
||||
@@ -139,13 +143,13 @@ func (s *Sender) Start() {
|
||||
}
|
||||
s.done = make(chan struct{})
|
||||
|
||||
go func() {
|
||||
// for range on nil chan is OK
|
||||
for packet := range s.buf {
|
||||
// pass buf directly so that it's impossible for buf to be nil
|
||||
go func(buf chan *Packet) {
|
||||
for packet := range buf {
|
||||
s.Output(packet)
|
||||
}
|
||||
close(s.done)
|
||||
}()
|
||||
}(s.buf)
|
||||
}
|
||||
|
||||
func (s *Sender) Wait() {
|
||||
|
||||
180
pkg/eseecloud/eseecloud.go
Normal file
180
pkg/eseecloud/eseecloud.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package eseecloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h264/annexb"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type Producer struct {
|
||||
core.Connection
|
||||
rd *core.ReadBuffer
|
||||
|
||||
videoPT, audioPT uint8
|
||||
}
|
||||
|
||||
func Dial(rawURL string) (core.Producer, error) {
|
||||
rawURL, _ = strings.CutPrefix(rawURL, "eseecloud")
|
||||
res, err := http.Get("http" + rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prod, err := Open(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if info, ok := prod.(core.Info); ok {
|
||||
info.SetProtocol("http")
|
||||
info.SetURL(rawURL)
|
||||
}
|
||||
|
||||
return prod, nil
|
||||
}
|
||||
|
||||
func Open(r io.Reader) (core.Producer, error) {
|
||||
prod := &Producer{
|
||||
Connection: core.Connection{
|
||||
ID: core.NewID(),
|
||||
FormatName: "eseecloud",
|
||||
Transport: r,
|
||||
},
|
||||
rd: core.NewReadBuffer(r),
|
||||
}
|
||||
|
||||
if err := prod.probe(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return prod, nil
|
||||
}
|
||||
|
||||
func (p *Producer) probe() error {
|
||||
b, err := p.rd.Peek(1024)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i := bytes.Index(b, []byte("\r\n\r\n"))
|
||||
if i == -1 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
b = make([]byte, i+4)
|
||||
_, _ = p.rd.Read(b)
|
||||
|
||||
re := regexp.MustCompile(`m=(video|audio) (\d+) (\w+)/(\d+)\S*`)
|
||||
for _, item := range re.FindAllStringSubmatch(string(b), 2) {
|
||||
p.SDP += item[0] + "\n"
|
||||
|
||||
switch item[3] {
|
||||
case "H264", "H265":
|
||||
p.Medias = append(p.Medias, &core.Media{
|
||||
Kind: core.KindVideo,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{
|
||||
{
|
||||
Name: item[3],
|
||||
ClockRate: 90000,
|
||||
PayloadType: core.PayloadTypeRAW,
|
||||
},
|
||||
},
|
||||
})
|
||||
p.videoPT = byte(core.Atoi(item[2]))
|
||||
|
||||
case "G711":
|
||||
p.Medias = append(p.Medias, &core.Media{
|
||||
Kind: core.KindAudio,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{
|
||||
{
|
||||
Name: core.CodecPCMA,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
},
|
||||
})
|
||||
p.audioPT = byte(core.Atoi(item[2]))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Producer) Start() error {
|
||||
receivers := make(map[uint8]*core.Receiver)
|
||||
|
||||
for _, receiver := range p.Receivers {
|
||||
switch receiver.Codec.Kind() {
|
||||
case core.KindVideo:
|
||||
receivers[p.videoPT] = receiver
|
||||
case core.KindAudio:
|
||||
receivers[p.audioPT] = receiver
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
pkt, err := p.readPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recv := receivers[pkt.PayloadType]; recv != nil {
|
||||
switch recv.Codec.Name {
|
||||
case core.CodecH264, core.CodecH265:
|
||||
// timestamp = seconds x 1000000
|
||||
pkt = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Timestamp: uint32(uint64(pkt.Timestamp) * 90000 / 1000000),
|
||||
},
|
||||
Payload: annexb.EncodeToAVCC(pkt.Payload),
|
||||
}
|
||||
case core.CodecPCMA:
|
||||
pkt = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
SequenceNumber: pkt.SequenceNumber,
|
||||
Timestamp: uint32(uint64(pkt.Timestamp) * 8000 / 1000000),
|
||||
},
|
||||
Payload: pkt.Payload,
|
||||
}
|
||||
}
|
||||
recv.WriteRTP(pkt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Producer) readPacket() (*core.Packet, error) {
|
||||
b := make([]byte, 8)
|
||||
|
||||
if _, err := io.ReadFull(p.rd, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b[0] != '$' {
|
||||
return nil, errors.New("eseecloud: wrong start byte")
|
||||
}
|
||||
|
||||
size := binary.BigEndian.Uint32(b[4:])
|
||||
b = make([]byte, size)
|
||||
if _, err := io.ReadFull(p.rd, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkt := &core.Packet{}
|
||||
if err := pkt.Unmarshal(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.Recv += int(size)
|
||||
|
||||
return pkt, nil
|
||||
}
|
||||
176
pkg/flussonic/flussonic.go
Normal file
176
pkg/flussonic/flussonic.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package flussonic
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/aac"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h264"
|
||||
"github.com/AlexxIT/go2rtc/pkg/iso"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type Producer struct {
|
||||
core.Connection
|
||||
conn *websocket.Conn
|
||||
|
||||
videoTrackID, audioTrackID uint32
|
||||
videoTimeScale, audioTimeScale float32
|
||||
}
|
||||
|
||||
func Dial(source string) (core.Producer, error) {
|
||||
url, _ := strings.CutPrefix(source, "flussonic:")
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prod := &Producer{
|
||||
Connection: core.Connection{
|
||||
ID: core.NewID(),
|
||||
FormatName: "flussonic",
|
||||
Protocol: core.Before(url, ":"), // wss
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
URL: url,
|
||||
Transport: conn,
|
||||
},
|
||||
conn: conn,
|
||||
}
|
||||
|
||||
if err = prod.probe(); err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return prod, nil
|
||||
}
|
||||
|
||||
func (p *Producer) probe() error {
|
||||
var init struct {
|
||||
//Metadata struct {
|
||||
// Tracks []struct {
|
||||
// Width int `json:"width,omitempty"`
|
||||
// Height int `json:"height,omitempty"`
|
||||
// Fps int `json:"fps,omitempty"`
|
||||
// Content string `json:"content"`
|
||||
// TrackId string `json:"trackId"`
|
||||
// Bitrate int `json:"bitrate"`
|
||||
// } `json:"tracks"`
|
||||
//} `json:"metadata"`
|
||||
Tracks []struct {
|
||||
Content string `json:"content"`
|
||||
Id uint32 `json:"id"`
|
||||
Payload []byte `json:"payload"`
|
||||
} `json:"tracks"`
|
||||
//Type string `json:"type"`
|
||||
}
|
||||
|
||||
if err := p.conn.ReadJSON(&init); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var timeScale uint32
|
||||
|
||||
for _, track := range init.Tracks {
|
||||
atoms, _ := iso.DecodeAtoms(track.Payload)
|
||||
for _, atom := range atoms {
|
||||
switch atom := atom.(type) {
|
||||
case *iso.AtomMdhd:
|
||||
timeScale = atom.TimeScale
|
||||
case *iso.AtomVideo:
|
||||
switch atom.Name {
|
||||
case "avc1":
|
||||
codec := h264.AVCCToCodec(atom.Config)
|
||||
p.Medias = append(p.Medias, &core.Media{
|
||||
Kind: core.KindVideo,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{codec},
|
||||
})
|
||||
p.videoTrackID = track.Id
|
||||
p.videoTimeScale = float32(codec.ClockRate) / float32(timeScale)
|
||||
}
|
||||
case *iso.AtomAudio:
|
||||
switch atom.Name {
|
||||
case "mp4a":
|
||||
codec := aac.ConfigToCodec(atom.Config)
|
||||
p.Medias = append(p.Medias, &core.Media{
|
||||
Kind: core.KindAudio,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{codec},
|
||||
})
|
||||
p.audioTrackID = track.Id
|
||||
p.audioTimeScale = float32(codec.ClockRate) / float32(timeScale)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Producer) Start() error {
|
||||
if err := p.conn.WriteMessage(websocket.TextMessage, []byte("resume")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
receivers := make(map[uint32]*core.Receiver)
|
||||
timeScales := make(map[uint32]float32)
|
||||
|
||||
for _, receiver := range p.Receivers {
|
||||
switch receiver.Codec.Kind() {
|
||||
case core.KindVideo:
|
||||
receivers[p.videoTrackID] = receiver
|
||||
timeScales[p.videoTrackID] = p.videoTimeScale
|
||||
case core.KindAudio:
|
||||
receivers[p.audioTrackID] = receiver
|
||||
timeScales[p.audioTrackID] = p.audioTimeScale
|
||||
}
|
||||
}
|
||||
|
||||
ch := make(chan []byte, 10)
|
||||
defer close(ch)
|
||||
|
||||
go func() {
|
||||
for b := range ch {
|
||||
atoms, err := iso.DecodeAtoms(b)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var trackID uint32
|
||||
var decodeTime uint64
|
||||
|
||||
for _, atom := range atoms {
|
||||
switch atom := atom.(type) {
|
||||
case *iso.AtomTfhd:
|
||||
trackID = atom.TrackID
|
||||
case *iso.AtomTfdt:
|
||||
decodeTime = atom.DecodeTime
|
||||
case *iso.AtomMdat:
|
||||
b = atom.Data
|
||||
}
|
||||
}
|
||||
|
||||
if recv := receivers[trackID]; recv != nil {
|
||||
timestamp := uint32(float32(decodeTime) * timeScales[trackID])
|
||||
packet := &rtp.Packet{
|
||||
Header: rtp.Header{Timestamp: timestamp},
|
||||
Payload: b,
|
||||
}
|
||||
recv.WriteRTP(packet)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
mType, b, err := p.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if mType == websocket.BinaryMessage {
|
||||
p.Recv += len(b)
|
||||
ch <- b
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package iso
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
@@ -10,62 +11,153 @@ import (
|
||||
type Atom struct {
|
||||
Name string
|
||||
Data []byte
|
||||
|
||||
DecodeTime uint64
|
||||
|
||||
SamplesDuration []uint32
|
||||
SamplesSize []uint32
|
||||
}
|
||||
|
||||
func DecodeAtoms(b []byte) ([]*Atom, error) {
|
||||
var atoms []*Atom
|
||||
for len(b) > 8 {
|
||||
type AtomTkhd struct {
|
||||
TrackID uint32
|
||||
}
|
||||
|
||||
type AtomMdhd struct {
|
||||
TimeScale uint32
|
||||
}
|
||||
|
||||
type AtomVideo struct {
|
||||
Name string
|
||||
Config []byte
|
||||
}
|
||||
|
||||
type AtomAudio struct {
|
||||
Name string
|
||||
Channels uint16
|
||||
SampleRate uint32
|
||||
Config []byte
|
||||
}
|
||||
|
||||
type AtomMfhd struct {
|
||||
Sequence uint32
|
||||
}
|
||||
|
||||
type AtomMdat struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type AtomTfhd struct {
|
||||
TrackID uint32
|
||||
SampleDuration uint32
|
||||
SampleSize uint32
|
||||
SampleFlags uint32
|
||||
}
|
||||
type AtomTfdt struct {
|
||||
DecodeTime uint64
|
||||
}
|
||||
|
||||
type AtomTrun struct {
|
||||
DataOffset uint32
|
||||
FirstSampleFlags uint32
|
||||
SamplesDuration []uint32
|
||||
SamplesSize []uint32
|
||||
SamplesFlags []uint32
|
||||
SamplesCTS []uint32
|
||||
}
|
||||
|
||||
func DecodeAtom(b []byte) (any, error) {
|
||||
size := binary.BigEndian.Uint32(b)
|
||||
if uint32(len(b)) < size {
|
||||
if len(b) < int(size) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
name := string(b[4:8])
|
||||
data := b[8:size]
|
||||
|
||||
b = b[size:]
|
||||
|
||||
switch name {
|
||||
case Moof, MoofTraf:
|
||||
childs, err := DecodeAtoms(data)
|
||||
// useful containers
|
||||
case Moov, MoovTrak, MoovTrakMdia, MoovTrakMdiaMinf, MoovTrakMdiaMinfStbl, Moof, MoofTraf:
|
||||
return DecodeAtoms(data)
|
||||
|
||||
case MoovTrakTkhd:
|
||||
return &AtomTkhd{TrackID: binary.BigEndian.Uint32(data[1+3+4+4:])}, nil
|
||||
|
||||
case MoovTrakMdiaMdhd:
|
||||
return &AtomMdhd{TimeScale: binary.BigEndian.Uint32(data[1+3+4+4:])}, nil
|
||||
|
||||
case MoovTrakMdiaMinfStblStsd:
|
||||
// support only 1 codec entry
|
||||
if n := binary.BigEndian.Uint32(data[1+3:]); n == 1 {
|
||||
return DecodeAtom(data[1+3+4:])
|
||||
}
|
||||
|
||||
case "avc1", "hev1":
|
||||
b = data[6+2+2+2+4+4+4+2+2+4+4+4+2+32+2+2:]
|
||||
atom, err := DecodeAtom(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
atoms = append(atoms, childs...)
|
||||
|
||||
case MoofMfhd, MoofTrafTfhd:
|
||||
continue
|
||||
|
||||
case MoofTrafTfdt:
|
||||
if len(data) < 8 {
|
||||
return nil, io.EOF
|
||||
if conf, ok := atom.(*Atom); ok {
|
||||
return &AtomVideo{Name: name, Config: conf.Data}, nil
|
||||
}
|
||||
|
||||
dt := binary.BigEndian.Uint64(data[4:])
|
||||
atoms = append(atoms, &Atom{Name: name, DecodeTime: dt})
|
||||
case "mp4a":
|
||||
atom := &AtomAudio{Name: name}
|
||||
|
||||
rd := bits.NewReader(data)
|
||||
rd.ReadBytes(6 + 2 + 2 + 2 + 4) // skip
|
||||
atom.Channels = rd.ReadUint16()
|
||||
rd.ReadBytes(2 + 2 + 2) // skip
|
||||
atom.SampleRate = uint32(rd.ReadFloat32())
|
||||
|
||||
atom2, _ := DecodeAtom(rd.Left())
|
||||
if conf, ok := atom2.(*Atom); ok {
|
||||
_, b, _ = bytes.Cut(conf.Data, []byte{5, 0x80, 0x80, 0x80})
|
||||
if n := len(b); n > 0 && n > 1+int(b[0]) {
|
||||
atom.Config = b[1 : 1+b[0]]
|
||||
}
|
||||
}
|
||||
|
||||
return atom, nil
|
||||
|
||||
case MoofMfhd:
|
||||
return &AtomMfhd{Sequence: binary.BigEndian.Uint32(data[4:])}, nil
|
||||
|
||||
case MoofTrafTfhd:
|
||||
rd := bits.NewReader(data)
|
||||
_ = rd.ReadByte() // version
|
||||
flags := rd.ReadUint24()
|
||||
|
||||
atom := &AtomTfhd{
|
||||
TrackID: rd.ReadUint32(),
|
||||
}
|
||||
|
||||
if flags&TfhdDefaultSampleDuration != 0 {
|
||||
atom.SampleDuration = rd.ReadUint32()
|
||||
|
||||
}
|
||||
if flags&TfhdDefaultSampleSize != 0 {
|
||||
atom.SampleSize = rd.ReadUint32()
|
||||
}
|
||||
if flags&TfhdDefaultSampleFlags != 0 {
|
||||
atom.SampleFlags = rd.ReadUint32() // skip
|
||||
}
|
||||
|
||||
return atom, nil
|
||||
|
||||
case MoofTrafTfdt:
|
||||
return &AtomTfdt{DecodeTime: binary.BigEndian.Uint64(data[4:])}, nil
|
||||
|
||||
case MoofTrafTrun:
|
||||
rd := bits.NewReader(data)
|
||||
|
||||
_ = rd.ReadByte() // version
|
||||
flags := rd.ReadUint24()
|
||||
samples := rd.ReadUint32()
|
||||
|
||||
atom := &AtomTrun{}
|
||||
|
||||
if flags&TrunDataOffset != 0 {
|
||||
_ = rd.ReadUint32() // skip
|
||||
atom.DataOffset = rd.ReadUint32()
|
||||
}
|
||||
if flags&TrunFirstSampleFlags != 0 {
|
||||
_ = rd.ReadUint32() // skip
|
||||
atom.FirstSampleFlags = rd.ReadUint32()
|
||||
}
|
||||
|
||||
atom := &Atom{Name: name}
|
||||
|
||||
for i := uint32(0); i < samples; i++ {
|
||||
if flags&TrunSampleDuration != 0 {
|
||||
atom.SamplesDuration = append(atom.SamplesDuration, rd.ReadUint32())
|
||||
@@ -74,25 +166,37 @@ func DecodeAtoms(b []byte) ([]*Atom, error) {
|
||||
atom.SamplesSize = append(atom.SamplesSize, rd.ReadUint32())
|
||||
}
|
||||
if flags&TrunSampleFlags != 0 {
|
||||
_ = rd.ReadUint32() // skip
|
||||
atom.SamplesFlags = append(atom.SamplesFlags, rd.ReadUint32())
|
||||
}
|
||||
if flags&TrunSampleCTS != 0 {
|
||||
_ = rd.ReadUint32() // skip
|
||||
atom.SamplesCTS = append(atom.SamplesCTS, rd.ReadUint32())
|
||||
}
|
||||
}
|
||||
|
||||
if rd.EOF {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
atoms = append(atoms, atom)
|
||||
return atom, nil
|
||||
|
||||
case Mdat:
|
||||
atoms = append(atoms, &Atom{Name: name, Data: data})
|
||||
|
||||
default:
|
||||
println("iso: unsupported atom: " + name)
|
||||
return &AtomMdat{Data: data}, nil
|
||||
}
|
||||
|
||||
return &Atom{Name: name, Data: data}, nil
|
||||
}
|
||||
|
||||
func DecodeAtoms(b []byte) (atoms []any, err error) {
|
||||
for len(b) > 0 {
|
||||
atom, err := DecodeAtom(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if childs, ok := atom.([]any); ok {
|
||||
atoms = append(atoms, childs...)
|
||||
} else {
|
||||
atoms = append(atoms, atom)
|
||||
}
|
||||
|
||||
size := binary.BigEndian.Uint32(b)
|
||||
b = b[size:]
|
||||
}
|
||||
|
||||
return atoms, nil
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
package ivideon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h264"
|
||||
"github.com/AlexxIT/go2rtc/pkg/iso"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type State byte
|
||||
|
||||
const (
|
||||
StateNone State = iota
|
||||
StateConn
|
||||
StateHandle
|
||||
)
|
||||
|
||||
// Deprecated: should be rewritten to core.Connection
|
||||
type Client struct {
|
||||
core.Listener
|
||||
|
||||
ID string
|
||||
|
||||
conn *websocket.Conn
|
||||
|
||||
medias []*core.Media
|
||||
receiver *core.Receiver
|
||||
|
||||
msg *message
|
||||
t0 time.Time
|
||||
|
||||
buffer chan []byte
|
||||
state State
|
||||
mu sync.Mutex
|
||||
|
||||
recv int
|
||||
}
|
||||
|
||||
func Dial(source string) (*Client, error) {
|
||||
id := strings.Replace(source[8:], "/", ":", 1)
|
||||
client := &Client{ID: id}
|
||||
if err := client.Dial(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Client) Dial() (err error) {
|
||||
resp, err := http.Get(
|
||||
"https://openapi-alpha.ivideon.com/cameras/" + c.ID +
|
||||
"/live_stream?op=GET&access_token=public&q=2&" +
|
||||
"video_codecs=h264&format=ws-fmp4",
|
||||
)
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var v liveResponse
|
||||
if err = json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !v.Success {
|
||||
return fmt.Errorf("wrong response: %s", data)
|
||||
}
|
||||
|
||||
c.conn, _, err = websocket.DefaultDialer.Dial(v.Result.URL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.getTracks(); err != nil {
|
||||
_ = c.conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
c.state = StateConn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Handle() error {
|
||||
// add delay to the stream for smooth playing (not a best solution)
|
||||
c.t0 = time.Now().Add(time.Second)
|
||||
|
||||
c.mu.Lock()
|
||||
|
||||
if c.state == StateConn {
|
||||
c.buffer = make(chan []byte, 5)
|
||||
c.state = StateHandle
|
||||
|
||||
// processing stream in separate thread for lower delay between packets
|
||||
go c.worker(c.buffer)
|
||||
}
|
||||
|
||||
c.mu.Unlock()
|
||||
|
||||
_, data, err := c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.receiver != nil && c.receiver.ID == c.msg.Track {
|
||||
c.mu.Lock()
|
||||
if c.state == StateHandle {
|
||||
c.buffer <- data
|
||||
c.recv += len(data)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// we have one unprocessed msg after getTracks
|
||||
for {
|
||||
_, data, err = c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var msg message
|
||||
if err = json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case "stream-init":
|
||||
continue
|
||||
|
||||
case "metadata":
|
||||
continue
|
||||
|
||||
case "fragment":
|
||||
_, data, err = c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.receiver != nil && c.receiver.ID == msg.Track {
|
||||
c.mu.Lock()
|
||||
if c.state == StateHandle {
|
||||
c.buffer <- data
|
||||
c.recv += len(data)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("wrong message type: %s", data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
switch c.state {
|
||||
case StateNone:
|
||||
return nil
|
||||
case StateConn:
|
||||
case StateHandle:
|
||||
close(c.buffer)
|
||||
}
|
||||
|
||||
c.state = StateNone
|
||||
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *Client) getTracks() error {
|
||||
for {
|
||||
_, data, err := c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var msg message
|
||||
if err = json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case "metadata":
|
||||
continue
|
||||
|
||||
case "stream-init":
|
||||
s := msg.CodecString
|
||||
i := strings.IndexByte(s, '.')
|
||||
if i > 0 {
|
||||
s = s[:i]
|
||||
}
|
||||
|
||||
switch s {
|
||||
case "avc1": // avc1.4d0029
|
||||
// skip multiple identical init
|
||||
if c.receiver != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
i = bytes.Index(msg.Data, []byte("avcC")) - 4
|
||||
if i < 0 {
|
||||
return fmt.Errorf("ivideon: wrong AVC: %s", msg.Data)
|
||||
}
|
||||
|
||||
avccLen := binary.BigEndian.Uint32(msg.Data[i:])
|
||||
data = msg.Data[i+8 : i+int(avccLen)]
|
||||
|
||||
codec := h264.ConfigToCodec(data)
|
||||
|
||||
media := &core.Media{
|
||||
Kind: core.KindVideo,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{codec},
|
||||
}
|
||||
c.medias = append(c.medias, media)
|
||||
|
||||
c.receiver = core.NewReceiver(media, codec)
|
||||
c.receiver.ID = msg.TrackID
|
||||
|
||||
case "mp4a": // mp4a.40.2
|
||||
}
|
||||
|
||||
case "fragment":
|
||||
c.msg = &msg
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("wrong message type: %s", data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) worker(buffer chan []byte) {
|
||||
for data := range buffer {
|
||||
atoms, err := iso.DecodeAtoms(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var trun *iso.Atom
|
||||
var ts uint32
|
||||
|
||||
for _, atom := range atoms {
|
||||
switch atom.Name {
|
||||
case iso.MoofTrafTrun:
|
||||
trun = atom
|
||||
case iso.MoofTrafTfdt:
|
||||
ts = uint32(atom.DecodeTime)
|
||||
case iso.Mdat:
|
||||
data = atom.Data
|
||||
}
|
||||
}
|
||||
|
||||
if trun == nil || trun.SamplesDuration == nil || trun.SamplesSize == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 0; i < len(trun.SamplesDuration); i++ {
|
||||
duration := trun.SamplesDuration[i]
|
||||
size := trun.SamplesSize[i]
|
||||
|
||||
// synchronize framerate for WebRTC and MSE
|
||||
d := time.Duration(ts)*time.Millisecond - time.Since(c.t0)
|
||||
if d < 0 {
|
||||
d = time.Duration(duration) * time.Millisecond / 2
|
||||
}
|
||||
time.Sleep(d)
|
||||
|
||||
// can be SPS, PPS and IFrame in one packet
|
||||
packet := &rtp.Packet{
|
||||
// ivideon clockrate=1000, RTP clockrate=90000
|
||||
Header: rtp.Header{Timestamp: ts * 90},
|
||||
Payload: data[:size],
|
||||
}
|
||||
c.receiver.WriteRTP(packet)
|
||||
|
||||
data = data[size:]
|
||||
ts += duration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type liveResponse struct {
|
||||
Result struct {
|
||||
URL string `json:"url"`
|
||||
} `json:"result"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type message struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
CodecString string `json:"codec_string"`
|
||||
Data []byte `json:"data"`
|
||||
TrackID byte `json:"track_id"`
|
||||
|
||||
Track byte `json:"track"`
|
||||
StartTime float32 `json:"start_time"`
|
||||
Duration float32 `json:"duration"`
|
||||
IsKey bool `json:"is_key"`
|
||||
DataOffset uint32 `json:"data_offset"`
|
||||
}
|
||||
187
pkg/ivideon/ivideon.go
Normal file
187
pkg/ivideon/ivideon.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package ivideon
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/mp4"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type Producer struct {
|
||||
core.Connection
|
||||
conn *websocket.Conn
|
||||
|
||||
buf []byte
|
||||
|
||||
dem *mp4.Demuxer
|
||||
}
|
||||
|
||||
func Dial(source string) (core.Producer, error) {
|
||||
id := strings.Replace(source[8:], "/", ":", 1)
|
||||
|
||||
url, err := GetLiveStream(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prod := &Producer{
|
||||
Connection: core.Connection{
|
||||
ID: core.NewID(),
|
||||
FormatName: "ivideon",
|
||||
Protocol: core.Before(url, ":"), // wss
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
Source: source,
|
||||
URL: url,
|
||||
Transport: conn,
|
||||
},
|
||||
conn: conn,
|
||||
}
|
||||
|
||||
if err = prod.probe(); err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return prod, nil
|
||||
}
|
||||
|
||||
func GetLiveStream(id string) (string, error) {
|
||||
// &video_codecs=h264,h265&audio_codecs=aac,mp3,pcma,pcmu,none
|
||||
resp, err := http.Get(
|
||||
"https://openapi-alpha.ivideon.com/cameras/" + id +
|
||||
"/live_stream?op=GET&access_token=public&q=2&video_codecs=h264&format=ws-fmp4",
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var v struct {
|
||||
Message string `json:"message"`
|
||||
Result struct {
|
||||
URL string `json:"url"`
|
||||
} `json:"result"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
if err = json.NewDecoder(resp.Body).Decode(&v); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !v.Success {
|
||||
return "", fmt.Errorf("ivideon: can't get live_stream: " + v.Message)
|
||||
}
|
||||
|
||||
return v.Result.URL, nil
|
||||
}
|
||||
|
||||
func (p *Producer) Start() error {
|
||||
receivers := make(map[uint32]*core.Receiver)
|
||||
for _, receiver := range p.Receivers {
|
||||
trackID := p.dem.GetTrackID(receiver.Codec)
|
||||
receivers[trackID] = receiver
|
||||
}
|
||||
|
||||
ch := make(chan []byte, 10)
|
||||
defer close(ch)
|
||||
|
||||
ch <- p.buf
|
||||
|
||||
go func() {
|
||||
// add delay to the stream for smooth playing (not a best solution)
|
||||
t0 := time.Now()
|
||||
|
||||
for data := range ch {
|
||||
trackID, packets := p.dem.Demux(data)
|
||||
if receiver := receivers[trackID]; receiver != nil {
|
||||
clockRate := time.Duration(receiver.Codec.ClockRate)
|
||||
for _, packet := range packets {
|
||||
// synchronize framerate for WebRTC and MSE
|
||||
ts := time.Second * time.Duration(packet.Timestamp) / clockRate
|
||||
d := ts - time.Since(t0)
|
||||
if d < 0 {
|
||||
d = 10 * time.Millisecond
|
||||
}
|
||||
time.Sleep(d)
|
||||
|
||||
receiver.WriteRTP(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
var msg message
|
||||
if err := p.conn.ReadJSON(&msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case "stream-init", "metadata":
|
||||
continue
|
||||
|
||||
case "fragment":
|
||||
_, b, err := p.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Recv += len(b)
|
||||
ch <- b
|
||||
|
||||
default:
|
||||
return errors.New("ivideon: wrong message type: " + msg.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Producer) probe() (err error) {
|
||||
p.dem = &mp4.Demuxer{}
|
||||
|
||||
for {
|
||||
var msg message
|
||||
if err = p.conn.ReadJSON(&msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case "metadata":
|
||||
continue
|
||||
|
||||
case "stream-init":
|
||||
// it's difficult to maintain audio
|
||||
if strings.HasPrefix(msg.CodecString, "avc1") {
|
||||
medias := p.dem.Probe(msg.Data)
|
||||
p.Medias = append(p.Medias, medias...)
|
||||
}
|
||||
|
||||
case "fragment":
|
||||
_, p.buf, err = p.conn.ReadMessage()
|
||||
return
|
||||
|
||||
default:
|
||||
return errors.New("ivideon: wrong message type: " + msg.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type message struct {
|
||||
Type string `json:"type"`
|
||||
CodecString string `json:"codec_string"`
|
||||
Data []byte `json:"data"`
|
||||
//TrackID byte `json:"track_id"`
|
||||
//Track byte `json:"track"`
|
||||
//StartTime float32 `json:"start_time"`
|
||||
//Duration float32 `json:"duration"`
|
||||
//IsKey bool `json:"is_key"`
|
||||
//DataOffset uint32 `json:"data_offset"`
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package ivideon
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
)
|
||||
|
||||
func (c *Client) GetMedias() []*core.Media {
|
||||
return c.medias
|
||||
}
|
||||
|
||||
func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.Receiver, error) {
|
||||
if c.receiver != nil {
|
||||
return c.receiver, nil
|
||||
}
|
||||
return nil, core.ErrCantGetTrack
|
||||
}
|
||||
|
||||
func (c *Client) Start() error {
|
||||
err := c.Handle()
|
||||
if c.buffer == nil {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) Stop() error {
|
||||
if c.receiver != nil {
|
||||
c.receiver.Close()
|
||||
}
|
||||
return c.Close()
|
||||
}
|
||||
|
||||
func (c *Client) MarshalJSON() ([]byte, error) {
|
||||
info := &core.Connection{
|
||||
ID: core.ID(c),
|
||||
FormatName: "ivideon",
|
||||
Protocol: "ws",
|
||||
URL: c.ID,
|
||||
Medias: c.medias,
|
||||
Recv: c.recv,
|
||||
}
|
||||
if c.conn != nil {
|
||||
info.RemoteAddr = c.conn.RemoteAddr().String()
|
||||
}
|
||||
if c.receiver != nil {
|
||||
info.Receivers = []*core.Receiver{c.receiver}
|
||||
}
|
||||
return json.Marshal(info)
|
||||
}
|
||||
116
pkg/mp4/demuxer.go
Normal file
116
pkg/mp4/demuxer.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package mp4
|
||||
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/aac"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h264"
|
||||
"github.com/AlexxIT/go2rtc/pkg/iso"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type Demuxer struct {
|
||||
codecs map[uint32]*core.Codec
|
||||
timeScales map[uint32]float32
|
||||
}
|
||||
|
||||
func (d *Demuxer) Probe(init []byte) (medias []*core.Media) {
|
||||
var trackID, timeScale uint32
|
||||
|
||||
if d.codecs == nil {
|
||||
d.codecs = make(map[uint32]*core.Codec)
|
||||
d.timeScales = make(map[uint32]float32)
|
||||
}
|
||||
|
||||
atoms, _ := iso.DecodeAtoms(init)
|
||||
for _, atom := range atoms {
|
||||
var codec *core.Codec
|
||||
|
||||
switch atom := atom.(type) {
|
||||
case *iso.AtomTkhd:
|
||||
trackID = atom.TrackID
|
||||
case *iso.AtomMdhd:
|
||||
timeScale = atom.TimeScale
|
||||
case *iso.AtomVideo:
|
||||
switch atom.Name {
|
||||
case "avc1":
|
||||
codec = h264.ConfigToCodec(atom.Config)
|
||||
}
|
||||
case *iso.AtomAudio:
|
||||
switch atom.Name {
|
||||
case "mp4a":
|
||||
codec = aac.ConfigToCodec(atom.Config)
|
||||
}
|
||||
}
|
||||
|
||||
if codec != nil {
|
||||
d.codecs[trackID] = codec
|
||||
d.timeScales[trackID] = float32(codec.ClockRate) / float32(timeScale)
|
||||
|
||||
medias = append(medias, &core.Media{
|
||||
Kind: codec.Kind(),
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{codec},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Demuxer) GetTrackID(codec *core.Codec) uint32 {
|
||||
for trackID, c := range d.codecs {
|
||||
if c == codec {
|
||||
return trackID
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (d *Demuxer) Demux(data2 []byte) (trackID uint32, packets []*core.Packet) {
|
||||
atoms, err := iso.DecodeAtoms(data2)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var ts uint32
|
||||
var trun *iso.AtomTrun
|
||||
var data []byte
|
||||
|
||||
for _, atom := range atoms {
|
||||
switch atom := atom.(type) {
|
||||
case *iso.AtomTfhd:
|
||||
trackID = atom.TrackID
|
||||
case *iso.AtomTfdt:
|
||||
ts = uint32(atom.DecodeTime)
|
||||
case *iso.AtomTrun:
|
||||
trun = atom
|
||||
case *iso.AtomMdat:
|
||||
data = atom.Data
|
||||
}
|
||||
}
|
||||
|
||||
timeScale := d.timeScales[trackID]
|
||||
if timeScale == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
n := len(trun.SamplesDuration)
|
||||
packets = make([]*core.Packet, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
duration := trun.SamplesDuration[i]
|
||||
size := trun.SamplesSize[i]
|
||||
|
||||
// can be SPS, PPS and IFrame in one packet
|
||||
timestamp := uint32(float32(ts) * timeScale)
|
||||
packets[i] = &rtp.Packet{
|
||||
Header: rtp.Header{Timestamp: timestamp},
|
||||
Payload: data[:size],
|
||||
}
|
||||
|
||||
data = data[size:]
|
||||
ts += duration
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/rtsp"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
type WebRTCClient struct {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/roborock/iot"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
// Deprecated: should be rewritten to core.Connection
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/srtp/v2"
|
||||
"github.com/pion/srtp/v3"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/xnet"
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/ice/v4"
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
// ReceiveMTU = Ethernet MTU (1500) - IP Header (20) - UDP Header (8)
|
||||
|
||||
@@ -3,7 +3,7 @@ package webrtc
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/pion/sdp/v3"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
func (c *Conn) CreateOffer(medias []*core.Media) (string, error) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/ice/v4"
|
||||
"github.com/pion/sdp/v3"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/stun/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
func UnmarshalMedias(descriptions []*sdp.MediaDescription) (medias []*core.Media) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package webrtc
|
||||
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
func (c *Conn) GetTrack(media *core.Media, codec *core.Codec) (*core.Receiver, error) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package webrtc
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/pion/sdp/v3"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
func (c *Conn) SetOffer(offer string) (err error) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
type Track struct {
|
||||
|
||||
@@ -3,7 +3,7 @@ package webrtc
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/webrtc"
|
||||
"github.com/gorilla/websocket"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
func NewClient(tracker, share, pwd string, pc *pion.PeerConnection) (*webrtc.Conn, error) {
|
||||
|
||||
@@ -7,8 +7,12 @@ So we will set `go 1.20` (minimum version) inside `go.mod` file. And will use en
|
||||
`win32` and `mac_amd64` binaries. All other binaries will use latest go version.
|
||||
|
||||
```
|
||||
github.com/miekg/dns v1.1.63
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
```
|
||||
|
||||
|
||||
@@ -21,34 +21,34 @@ go build -ldflags "-s -w" -trimpath && 7z a -mx9 -sdel %FILENAME% go2rtc.exe
|
||||
@SET GOOS=linux
|
||||
@SET GOARCH=amd64
|
||||
@SET FILENAME=go2rtc_linux_amd64
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx %FILENAME%
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx --best --lzma %FILENAME%
|
||||
|
||||
@SET GOOS=linux
|
||||
@SET GOARCH=386
|
||||
@SET FILENAME=go2rtc_linux_i386
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx %FILENAME%
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx --best --lzma %FILENAME%
|
||||
|
||||
@SET GOOS=linux
|
||||
@SET GOARCH=arm64
|
||||
@SET FILENAME=go2rtc_linux_arm64
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx %FILENAME%
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx --best --lzma %FILENAME%
|
||||
|
||||
@SET GOOS=linux
|
||||
@SET GOARCH=arm
|
||||
@SET GOARM=7
|
||||
@SET FILENAME=go2rtc_linux_arm
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx %FILENAME%
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx --best --lzma %FILENAME%
|
||||
|
||||
@SET GOOS=linux
|
||||
@SET GOARCH=arm
|
||||
@SET GOARM=6
|
||||
@SET FILENAME=go2rtc_linux_armv6
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx %FILENAME%
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx --best --lzma %FILENAME%
|
||||
|
||||
@SET GOOS=linux
|
||||
@SET GOARCH=mipsle
|
||||
@SET FILENAME=go2rtc_linux_mipsel
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx %FILENAME%
|
||||
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx --best --lzma %FILENAME%
|
||||
|
||||
@SET GOTOOLCHAIN=go1.20.14
|
||||
@SET GOOS=darwin
|
||||
|
||||
Reference in New Issue
Block a user