mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-09-27 11:22:26 +08:00
split components
This commit is contained in:
6
go.mod
6
go.mod
@@ -5,8 +5,10 @@ go 1.19
|
|||||||
require (
|
require (
|
||||||
github.com/asticode/go-astisrt v0.3.0
|
github.com/asticode/go-astisrt v0.3.0
|
||||||
github.com/asticode/go-astits v1.11.0
|
github.com/asticode/go-astits v1.11.0
|
||||||
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/pion/webrtc/v3 v3.1.47
|
github.com/pion/webrtc/v3 v3.1.47
|
||||||
github.com/szatmary/gocaption v0.0.0-20220607192049-fdd59655f0c3
|
github.com/szatmary/gocaption v0.0.0-20220607192049-fdd59655f0c3
|
||||||
|
go.uber.org/fx v1.20.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -28,6 +30,10 @@ require (
|
|||||||
github.com/pion/transport v0.13.1 // indirect
|
github.com/pion/transport v0.13.1 // indirect
|
||||||
github.com/pion/turn/v2 v2.0.8 // indirect
|
github.com/pion/turn/v2 v2.0.8 // indirect
|
||||||
github.com/pion/udp v0.1.1 // indirect
|
github.com/pion/udp v0.1.1 // indirect
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
go.uber.org/dig v1.17.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
go.uber.org/zap v1.23.0 // indirect
|
||||||
golang.org/x/crypto v0.2.0 // indirect
|
golang.org/x/crypto v0.2.0 // indirect
|
||||||
golang.org/x/net v0.2.0 // indirect
|
golang.org/x/net v0.2.0 // indirect
|
||||||
golang.org/x/sys v0.2.0 // indirect
|
golang.org/x/sys v0.2.0 // indirect
|
||||||
|
24
go.sum
24
go.sum
@@ -1,13 +1,11 @@
|
|||||||
github.com/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflxkRsZA=
|
|
||||||
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
||||||
github.com/asticode/go-astikit v0.36.0 h1:WHSY88YT76D/XRbdp0lMLwfjyUGw8dygnbKKtbGNIG8=
|
github.com/asticode/go-astikit v0.36.0 h1:WHSY88YT76D/XRbdp0lMLwfjyUGw8dygnbKKtbGNIG8=
|
||||||
github.com/asticode/go-astikit v0.36.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
github.com/asticode/go-astikit v0.36.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
||||||
github.com/asticode/go-astisrt v0.2.0 h1:pfaSfW2BfW2O2NFtzlktzIf5gHc4Pue1q1scTuj7uhc=
|
|
||||||
github.com/asticode/go-astisrt v0.2.0/go.mod h1:tP5Dx+MXyaICUeF0gz4nwyav3RDI609e0en3QQkrxKE=
|
|
||||||
github.com/asticode/go-astisrt v0.3.0 h1:LpvqOc17qfMr2suLZPzMs9wYLozxXYu/PE9CA1tH88c=
|
github.com/asticode/go-astisrt v0.3.0 h1:LpvqOc17qfMr2suLZPzMs9wYLozxXYu/PE9CA1tH88c=
|
||||||
github.com/asticode/go-astisrt v0.3.0/go.mod h1:tP5Dx+MXyaICUeF0gz4nwyav3RDI609e0en3QQkrxKE=
|
github.com/asticode/go-astisrt v0.3.0/go.mod h1:tP5Dx+MXyaICUeF0gz4nwyav3RDI609e0en3QQkrxKE=
|
||||||
github.com/asticode/go-astits v1.11.0 h1:GTHUXht0ZXAJXsVbsLIcyfHr1Bchi4QQwMARw2ZWAng=
|
github.com/asticode/go-astits v1.11.0 h1:GTHUXht0ZXAJXsVbsLIcyfHr1Bchi4QQwMARw2ZWAng=
|
||||||
github.com/asticode/go-astits v1.11.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
|
github.com/asticode/go-astits v1.11.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
|
||||||
|
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
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 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=
|
||||||
@@ -30,6 +28,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||||
|
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
@@ -48,7 +48,6 @@ github.com/pion/dtls/v2 v2.1.5 h1:jlh2vtIyUBShchoTDqpCCqiYCyRFJ/lvf/gQ8TALs+c=
|
|||||||
github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY=
|
github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY=
|
||||||
github.com/pion/ice/v2 v2.2.11 h1:wiAy7TSrVZ4KdyjC0CcNTkwltz9ywetbe4wbHLKUbIg=
|
github.com/pion/ice/v2 v2.2.11 h1:wiAy7TSrVZ4KdyjC0CcNTkwltz9ywetbe4wbHLKUbIg=
|
||||||
github.com/pion/ice/v2 v2.2.11/go.mod h1:NqUDUao6SjSs1+4jrqpexDmFlptlVhGxQjcymXLaVvE=
|
github.com/pion/ice/v2 v2.2.11/go.mod h1:NqUDUao6SjSs1+4jrqpexDmFlptlVhGxQjcymXLaVvE=
|
||||||
github.com/pion/interceptor v0.1.11 h1:00U6OlqxA3FFB50HSg25J/8cWi7P6FbSzw4eFn24Bvs=
|
|
||||||
github.com/pion/interceptor v0.1.11/go.mod h1:tbtKjZY14awXd7Bq0mmWvgtHB5MDaRN7HV3OZ/uy7s8=
|
github.com/pion/interceptor v0.1.11/go.mod h1:tbtKjZY14awXd7Bq0mmWvgtHB5MDaRN7HV3OZ/uy7s8=
|
||||||
github.com/pion/interceptor v0.1.12 h1:CslaNriCFUItiXS5o+hh5lpL0t0ytQkFnUcbbCs2Zq8=
|
github.com/pion/interceptor v0.1.12 h1:CslaNriCFUItiXS5o+hh5lpL0t0ytQkFnUcbbCs2Zq8=
|
||||||
github.com/pion/interceptor v0.1.12/go.mod h1:bDtgAD9dRkBZpWHGKaoKb42FhDHTG2rX8Ii9LRALLVA=
|
github.com/pion/interceptor v0.1.12/go.mod h1:bDtgAD9dRkBZpWHGKaoKb42FhDHTG2rX8Ii9LRALLVA=
|
||||||
@@ -64,7 +63,6 @@ github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL
|
|||||||
github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA=
|
github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA=
|
||||||
github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||||
github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
|
github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
|
||||||
github.com/pion/sctp v1.8.2 h1:yBBCIrUMJ4yFICL3RIvR4eh/H2BTTvlligmSTy+3kiA=
|
|
||||||
github.com/pion/sctp v1.8.2/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
|
github.com/pion/sctp v1.8.2/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
|
||||||
github.com/pion/sctp v1.8.3 h1:LWcciN2ptLkw9Ugp/Ks2E76fiWy7yk3Wm79D6oFbFNo=
|
github.com/pion/sctp v1.8.3 h1:LWcciN2ptLkw9Ugp/Ks2E76fiWy7yk3Wm79D6oFbFNo=
|
||||||
github.com/pion/sctp v1.8.3/go.mod h1:OHbDjdk7kg+L+7TJim9q/qGVefdEJohuA2SZyihccgI=
|
github.com/pion/sctp v1.8.3/go.mod h1:OHbDjdk7kg+L+7TJim9q/qGVefdEJohuA2SZyihccgI=
|
||||||
@@ -85,11 +83,13 @@ github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o=
|
|||||||
github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
|
github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
|
||||||
github.com/pion/webrtc/v3 v3.1.47 h1:2dFEKRI1rzFvehXDq43hK9OGGyTGJSusUi3j6QKHC5s=
|
github.com/pion/webrtc/v3 v3.1.47 h1:2dFEKRI1rzFvehXDq43hK9OGGyTGJSusUi3j6QKHC5s=
|
||||||
github.com/pion/webrtc/v3 v3.1.47/go.mod h1:8U39MYZCLVV4sIBn01htASVNkWQN2zDa/rx5xisEXWs=
|
github.com/pion/webrtc/v3 v3.1.47/go.mod h1:8U39MYZCLVV4sIBn01htASVNkWQN2zDa/rx5xisEXWs=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
|
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@@ -99,11 +99,21 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
|
|||||||
github.com/szatmary/gocaption v0.0.0-20220607192049-fdd59655f0c3 h1:j8SVIV6YZreqjOPGjxM48tB4XgS8oUZdgy0cyN7YrBg=
|
github.com/szatmary/gocaption v0.0.0-20220607192049-fdd59655f0c3 h1:j8SVIV6YZreqjOPGjxM48tB4XgS8oUZdgy0cyN7YrBg=
|
||||||
github.com/szatmary/gocaption v0.0.0-20220607192049-fdd59655f0c3/go.mod h1:l9r7RYKHGLuHbXpKJhJgASvi8xT+Uqxnz9B26uVU73c=
|
github.com/szatmary/gocaption v0.0.0-20220607192049-fdd59655f0c3/go.mod h1:l9r7RYKHGLuHbXpKJhJgASvi8xT+Uqxnz9B26uVU73c=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI=
|
||||||
|
go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU=
|
||||||
|
go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk=
|
||||||
|
go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg=
|
||||||
|
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||||
|
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
||||||
|
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 h1:x8vtB3zMecnlqZIwJNUUpwYKYSqCz5jXbiyv0ZJJZeI=
|
|
||||||
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
||||||
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
@@ -121,7 +131,6 @@ golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4=
|
|
||||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
@@ -145,7 +154,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc=
|
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
49
internal/controller/srt/controller.go
Normal file
49
internal/controller/srt/controller.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package srt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
astisrt "github.com/asticode/go-astisrt/pkg"
|
||||||
|
"github.com/flavioribeiro/donut/internal/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SRTController struct {
|
||||||
|
c *entity.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSRTController(c *entity.Config) *SRTController {
|
||||||
|
return &SRTController{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SRTController) Connect(offer *entity.ParamsOffer) error {
|
||||||
|
if err := offer.Valid(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// conn, err := c.srtConnect(offer)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SRTController) srtConnect(offer *entity.ParamsOffer) (*astisrt.Connection, error) {
|
||||||
|
srtConnection, err := astisrt.Dial(astisrt.DialOptions{
|
||||||
|
ConnectionOptions: []astisrt.ConnectionOption{
|
||||||
|
astisrt.WithLatency(c.c.SRTConnectionLatencyMS),
|
||||||
|
astisrt.WithStreamid(offer.SRTStreamID),
|
||||||
|
},
|
||||||
|
OnDisconnect: func(c *astisrt.Connection, err error) {
|
||||||
|
log.Fatal("Disconnected from SRT")
|
||||||
|
},
|
||||||
|
Host: offer.SRTHost,
|
||||||
|
Port: offer.SRTPort,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return srtConnection, nil
|
||||||
|
}
|
@@ -1,7 +1,6 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pion/webrtc/v3"
|
"github.com/pion/webrtc/v3"
|
||||||
@@ -18,19 +17,19 @@ type ParamsOffer struct {
|
|||||||
|
|
||||||
func (p *ParamsOffer) Valid() error {
|
func (p *ParamsOffer) Valid() error {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return errors.New("ParamsOffer must not be nil")
|
return ErrMissingParamsOffer
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.SRTHost == "" {
|
if p.SRTHost == "" {
|
||||||
return errors.New("SRTHost must not be nil")
|
return ErrMissingSRTHost
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.SRTPort == 0 {
|
if p.SRTPort == 0 {
|
||||||
return errors.New("SRTPort must be valid")
|
return ErrMissingSRTPort
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.SRTStreamID == "" {
|
if p.SRTStreamID == "" {
|
||||||
return errors.New("SRTStreamID must not be empty")
|
return ErrMissingSRTStreamID
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -53,3 +52,18 @@ type Message struct {
|
|||||||
Type MessageType
|
Type MessageType
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
HTTPPort int32 `required:"true" default:"8080"`
|
||||||
|
HTTPHost string `required:"true" default:"0.0.0.0"`
|
||||||
|
|
||||||
|
TCPICEPort int `required:"true" default:"8081"`
|
||||||
|
UDPICEPort int `required:"true" default:"8081"`
|
||||||
|
ICEReadBufferSize int `required:"true" default:"8"`
|
||||||
|
ICEExternalIPsDNAT []string `required:"true" default:"127.0.0.1"`
|
||||||
|
EnableICEMux bool `require:"true" default:"false"`
|
||||||
|
StunServers []string `required:"true" default:"stun:stun4.l.google.com:19302"`
|
||||||
|
|
||||||
|
SRTConnectionLatencyMS int32 `required:"true" default:"300"`
|
||||||
|
SRTReadBufferSizeBytes int `required:"true" default:"1316"`
|
||||||
|
}
|
||||||
|
10
internal/entity/errors.go
Normal file
10
internal/entity/errors.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var ErrHTTPGetOnly = errors.New("you must use http GET verb")
|
||||||
|
var ErrHTTPPostOnly = errors.New("you must use http POST verb")
|
||||||
|
var ErrMissingParamsOffer = errors.New("ParamsOffer must not be nil")
|
||||||
|
var ErrMissingSRTHost = errors.New("SRTHost must not be nil")
|
||||||
|
var ErrMissingSRTPort = errors.New("SRTPort must be valid")
|
||||||
|
var ErrMissingSRTStreamID = errors.New("SRTStreamID must not be empty")
|
@@ -1,7 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
func MediaInfo(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
}
|
|
41
internal/web/http.go
Normal file
41
internal/web/http.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/flavioribeiro/donut/internal/entity"
|
||||||
|
"go.uber.org/fx"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHTTPServer(
|
||||||
|
c *entity.Config,
|
||||||
|
mux *http.ServeMux,
|
||||||
|
log *zap.Logger,
|
||||||
|
lc fx.Lifecycle,
|
||||||
|
) *http.Server {
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort),
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
lc.Append(fx.Hook{
|
||||||
|
OnStart: func(ctx context.Context) error {
|
||||||
|
ln, err := net.Listen("tcp", srv.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Sugar().Infow(fmt.Sprintf("Starting HTTP server. Open http://%s to access the demo", srv.Addr),
|
||||||
|
"addr", srv.Addr,
|
||||||
|
)
|
||||||
|
go srv.Serve(ln)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
OnStop: func(ctx context.Context) error {
|
||||||
|
return srv.Shutdown(ctx)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return srv
|
||||||
|
}
|
50
internal/web/ice.go
Normal file
50
internal/web/ice.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/flavioribeiro/donut/internal/entity"
|
||||||
|
"github.com/pion/webrtc/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTCPICEServer(c *entity.Config) (*net.TCPListener, error) {
|
||||||
|
tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{
|
||||||
|
IP: net.IP{0, 0, 0, 0},
|
||||||
|
Port: c.TCPICEPort,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tcpListener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPICEServer(c *entity.Config) (*net.UDPConn, error) {
|
||||||
|
|
||||||
|
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{
|
||||||
|
IP: net.IP{0, 0, 0, 0},
|
||||||
|
Port: c.UDPICEPort,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return udpListener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebRTCSettingsEngine(c *entity.Config, tcpListener *net.TCPListener, udpListener *net.UDPConn) *webrtc.SettingEngine {
|
||||||
|
settingEngine := &webrtc.SettingEngine{}
|
||||||
|
|
||||||
|
settingEngine.SetNAT1To1IPs(c.ICEExternalIPsDNAT, webrtc.ICECandidateTypeHost)
|
||||||
|
settingEngine.SetICETCPMux(webrtc.NewICETCPMux(nil, tcpListener, c.ICEReadBufferSize))
|
||||||
|
settingEngine.SetICEUDPMux(webrtc.NewICEUDPMux(nil, udpListener))
|
||||||
|
|
||||||
|
return settingEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebRTCMediaEngine() (*webrtc.MediaEngine, error) {
|
||||||
|
mediaEngine := &webrtc.MediaEngine{}
|
||||||
|
if err := mediaEngine.RegisterDefaultCodecs(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mediaEngine, nil
|
||||||
|
}
|
20
internal/web/index.go
Normal file
20
internal/web/index.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed index.html
|
||||||
|
var indexHTML string
|
||||||
|
|
||||||
|
type IndexHandler struct{}
|
||||||
|
|
||||||
|
func NewIndexHandler() *IndexHandler {
|
||||||
|
return &IndexHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *IndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(indexHTML))
|
||||||
|
}
|
49
internal/web/media.go
Normal file
49
internal/web/media.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
astisrt "github.com/asticode/go-astisrt/pkg"
|
||||||
|
"github.com/flavioribeiro/donut/internal/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaHandler struct{}
|
||||||
|
|
||||||
|
func NewMediaHandler() *MediaHandler {
|
||||||
|
return &MediaHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MediaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
SetCORS(w, r)
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
ErrorToHTTP(w, entity.ErrHTTPGetOnly)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
offer := entity.ParamsOffer{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&offer); err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Connecting to SRT ", offer)
|
||||||
|
_, err := astisrt.Dial(astisrt.DialOptions{
|
||||||
|
ConnectionOptions: []astisrt.ConnectionOption{
|
||||||
|
astisrt.WithLatency(300),
|
||||||
|
astisrt.WithStreamid(offer.SRTStreamID),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Callback when the connection is disconnected
|
||||||
|
OnDisconnect: func(c *astisrt.Connection, err error) { log.Fatal("Disconnected from SRT") },
|
||||||
|
|
||||||
|
Host: offer.SRTHost,
|
||||||
|
Port: offer.SRTPort,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Connected to SRT")
|
||||||
|
}
|
16
internal/web/router.go
Normal file
16
internal/web/router.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func NewServeMux(
|
||||||
|
index *IndexHandler,
|
||||||
|
signaling *SignalingHandler,
|
||||||
|
) *http.ServeMux {
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.Handle("/", index)
|
||||||
|
mux.Handle("/doSignaling", signaling)
|
||||||
|
|
||||||
|
return mux
|
||||||
|
}
|
233
internal/web/signaling.go
Normal file
233
internal/web/signaling.go
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
astisrt "github.com/asticode/go-astisrt/pkg"
|
||||||
|
"github.com/asticode/go-astits"
|
||||||
|
"github.com/flavioribeiro/donut/eia608"
|
||||||
|
"github.com/flavioribeiro/donut/internal/entity"
|
||||||
|
"github.com/pion/webrtc/v3"
|
||||||
|
"github.com/pion/webrtc/v3/pkg/media"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignalingHandler struct {
|
||||||
|
c *entity.Config
|
||||||
|
l *zap.Logger
|
||||||
|
webrtcSetting *webrtc.SettingEngine
|
||||||
|
mediaEngine *webrtc.MediaEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignalingHandler(c *entity.Config, log *zap.Logger, webrtcSetting *webrtc.SettingEngine, mediaEngine *webrtc.MediaEngine) *SignalingHandler {
|
||||||
|
return &SignalingHandler{
|
||||||
|
c: c,
|
||||||
|
l: log,
|
||||||
|
webrtcSetting: webrtcSetting,
|
||||||
|
mediaEngine: mediaEngine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
SetCORS(w, r)
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
ErrorToHTTP(w, entity.ErrHTTPPostOnly)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peerConnectionConfiguration := webrtc.Configuration{}
|
||||||
|
if !h.c.EnableICEMux {
|
||||||
|
peerConnectionConfiguration.ICEServers = []webrtc.ICEServer{
|
||||||
|
{
|
||||||
|
URLs: h.c.StunServers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api := webrtc.NewAPI(
|
||||||
|
webrtc.WithSettingEngine(*h.webrtcSetting),
|
||||||
|
webrtc.WithMediaEngine(h.mediaEngine),
|
||||||
|
)
|
||||||
|
|
||||||
|
peerConnection, err := api.NewPeerConnection(peerConnectionConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
offer := entity.ParamsOffer{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&offer); err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a video track
|
||||||
|
videoTrack, err := webrtc.NewTrackLocalStaticSample(
|
||||||
|
webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264},
|
||||||
|
"video", offer.SRTStreamID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := peerConnection.AddTrack(videoTrack); err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create data channel for metadata transmission
|
||||||
|
metadataSender, err := peerConnection.CreateDataChannel("metadata", nil)
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the handler for ICE connection state
|
||||||
|
// This will notify you when the peer has connected/disconnected
|
||||||
|
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||||
|
h.l.Sugar().Infow("ICE Connection State has changed",
|
||||||
|
"status", connectionState.String(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
err = offer.Valid()
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = peerConnection.SetRemoteDescription(offer.Offer); err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.l.Sugar().Infow("Gathering WebRTC Candidates")
|
||||||
|
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
|
||||||
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
} else if err = peerConnection.SetLocalDescription(answer); err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
<-gatherComplete
|
||||||
|
|
||||||
|
h.l.Sugar().Infow("Gathering WebRTC Candidates Complete")
|
||||||
|
|
||||||
|
response, err := json.Marshal(*peerConnection.LocalDescription())
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.l.Sugar().Infow("Connecting to SRT ",
|
||||||
|
"offer", offer,
|
||||||
|
)
|
||||||
|
srtConnection, err := astisrt.Dial(astisrt.DialOptions{
|
||||||
|
ConnectionOptions: []astisrt.ConnectionOption{
|
||||||
|
astisrt.WithLatency(h.c.SRTConnectionLatencyMS),
|
||||||
|
astisrt.WithStreamid(offer.SRTStreamID),
|
||||||
|
},
|
||||||
|
|
||||||
|
OnDisconnect: func(c *astisrt.Connection, err error) {
|
||||||
|
h.l.Sugar().Fatalw("Disconnected from SRT",
|
||||||
|
"error", err,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
Host: offer.SRTHost,
|
||||||
|
Port: offer.SRTPort,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.l.Sugar().Infow("Connected to SRT")
|
||||||
|
|
||||||
|
go srtToWebRTC(srtConnection, videoTrack, metadataSender)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if _, err := w.Write(response); err != nil {
|
||||||
|
ErrorToHTTP(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func srtToWebRTC(srtConnection *astisrt.Connection, videoTrack *webrtc.TrackLocalStaticSample, metadataTrack *webrtc.DataChannel) {
|
||||||
|
r, w := io.Pipe()
|
||||||
|
defer r.Close()
|
||||||
|
defer w.Close()
|
||||||
|
defer srtConnection.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer srtConnection.Close()
|
||||||
|
inboundMpegTsPacket := make([]byte, 1316) // SRT Read Size
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := srtConnection.Read(inboundMpegTsPacket)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := w.Write(inboundMpegTsPacket[:n]); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dmx := astits.NewDemuxer(context.Background(), r)
|
||||||
|
eia608Reader := eia608.NewEIA608Reader()
|
||||||
|
h264PID := uint16(0)
|
||||||
|
for {
|
||||||
|
d, err := dmx.NextData()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.PMT != nil {
|
||||||
|
for _, es := range d.PMT.ElementaryStreams {
|
||||||
|
msg, _ := json.Marshal(entity.Message{
|
||||||
|
Type: entity.MessageTypeMetadata,
|
||||||
|
Message: es.StreamType.String(),
|
||||||
|
})
|
||||||
|
metadataTrack.SendText(string(msg))
|
||||||
|
if es.StreamType == astits.StreamTypeH264Video {
|
||||||
|
h264PID = es.ElementaryPID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range d.PMT.ProgramDescriptors {
|
||||||
|
if d.MaximumBitrate != nil {
|
||||||
|
bitrateInMbitsPerSecond := float32(d.MaximumBitrate.Bitrate) / float32(125000)
|
||||||
|
msg, _ := json.Marshal(entity.Message{
|
||||||
|
Type: entity.MessageTypeMetadata,
|
||||||
|
Message: fmt.Sprintf("Bitrate %.2fMbps", bitrateInMbitsPerSecond),
|
||||||
|
})
|
||||||
|
metadataTrack.SendText(string(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.PID == h264PID && d.PES != nil {
|
||||||
|
if err = videoTrack.WriteSample(media.Sample{Data: d.PES.Data, Duration: time.Second / 30}); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
captions, err := eia608Reader.Parse(d.PES)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if captions != "" {
|
||||||
|
captionsMsg, err := eia608.BuildCaptionsMessage(d.PES.Header.OptionalHeader.PTS, captions)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
metadataTrack.SendText(captionsMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,15 +1,12 @@
|
|||||||
package main
|
package handlers
|
||||||
|
|
||||||
import (
|
import "net/http"
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func errorToHTTP(w http.ResponseWriter, err error) {
|
func ErrorToHTTP(w http.ResponseWriter, err error) {
|
||||||
w.WriteHeader(500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setCors(w http.ResponseWriter, r *http.Request) {
|
func SetCORS(w http.ResponseWriter, r *http.Request) {
|
||||||
if origin := r.Header.Get("Origin"); origin != "" {
|
if origin := r.Header.Get("Origin"); origin != "" {
|
||||||
allowedHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization,X-CSRF-Token"
|
allowedHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization,X-CSRF-Token"
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
264
main.go
264
main.go
@@ -4,259 +4,49 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
_ "embed"
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/flavioribeiro/donut/eia608"
|
|
||||||
"github.com/flavioribeiro/donut/internal/entity"
|
"github.com/flavioribeiro/donut/internal/entity"
|
||||||
|
handlers "github.com/flavioribeiro/donut/internal/web"
|
||||||
|
|
||||||
astisrt "github.com/asticode/go-astisrt/pkg"
|
"github.com/kelseyhightower/envconfig"
|
||||||
"github.com/asticode/go-astits"
|
"go.uber.org/fx"
|
||||||
"github.com/pion/webrtc/v3"
|
"go.uber.org/zap"
|
||||||
"github.com/pion/webrtc/v3/pkg/media"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed index.html
|
|
||||||
indexHTML string
|
|
||||||
|
|
||||||
api *webrtc.API //nolint
|
|
||||||
|
|
||||||
enableICEMux = false
|
|
||||||
)
|
|
||||||
|
|
||||||
func srtToWebRTC(srtConnection *astisrt.Connection, videoTrack *webrtc.TrackLocalStaticSample, metadataTrack *webrtc.DataChannel) {
|
|
||||||
r, w := io.Pipe()
|
|
||||||
defer r.Close()
|
|
||||||
defer w.Close()
|
|
||||||
defer srtConnection.Close()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer srtConnection.Close()
|
|
||||||
inboundMpegTsPacket := make([]byte, 1316) // SRT Read Size
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err := srtConnection.Read(inboundMpegTsPacket)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := w.Write(inboundMpegTsPacket[:n]); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
dmx := astits.NewDemuxer(context.Background(), r)
|
|
||||||
eia608Reader := eia608.NewEIA608Reader()
|
|
||||||
h264PID := uint16(0)
|
|
||||||
for {
|
|
||||||
d, err := dmx.NextData()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.PMT != nil {
|
|
||||||
for _, es := range d.PMT.ElementaryStreams {
|
|
||||||
msg, _ := json.Marshal(entity.Message{
|
|
||||||
Type: entity.MessageTypeMetadata,
|
|
||||||
Message: es.StreamType.String(),
|
|
||||||
})
|
|
||||||
metadataTrack.SendText(string(msg))
|
|
||||||
if es.StreamType == astits.StreamTypeH264Video {
|
|
||||||
h264PID = es.ElementaryPID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range d.PMT.ProgramDescriptors {
|
|
||||||
if d.MaximumBitrate != nil {
|
|
||||||
bitrateInMbitsPerSecond := float32(d.MaximumBitrate.Bitrate) / float32(125000)
|
|
||||||
msg, _ := json.Marshal(entity.Message{
|
|
||||||
Type: entity.MessageTypeMetadata,
|
|
||||||
Message: fmt.Sprintf("Bitrate %.2fMbps", bitrateInMbitsPerSecond),
|
|
||||||
})
|
|
||||||
metadataTrack.SendText(string(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.PID == h264PID && d.PES != nil {
|
|
||||||
if err = videoTrack.WriteSample(media.Sample{Data: d.PES.Data, Duration: time.Second / 30}); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
captions, err := eia608Reader.Parse(d.PES)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if captions != "" {
|
|
||||||
captionsMsg, err := eia608.BuildCaptionsMessage(d.PES.Header.OptionalHeader.PTS, captions)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
metadataTrack.SendText(captionsMsg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func doSignaling(w http.ResponseWriter, r *http.Request) {
|
|
||||||
setCors(w, r)
|
|
||||||
if r.Method != http.MethodPost {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
peerConnectionConfiguration := webrtc.Configuration{}
|
|
||||||
if !enableICEMux {
|
|
||||||
peerConnectionConfiguration.ICEServers = []webrtc.ICEServer{
|
|
||||||
{
|
|
||||||
URLs: []string{
|
|
||||||
"stun:stun4.l.google.com:19302",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
peerConnection, err := api.NewPeerConnection(peerConnectionConfiguration)
|
|
||||||
if err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
offer := entity.ParamsOffer{}
|
|
||||||
|
|
||||||
if err = json.NewDecoder(r.Body).Decode(&offer); err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a video track
|
|
||||||
videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264}, "video", offer.SRTStreamID)
|
|
||||||
if err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := peerConnection.AddTrack(videoTrack); err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create data channel for metadata transmission
|
|
||||||
metadataSender, err := peerConnection.CreateDataChannel("metadata", nil)
|
|
||||||
if err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
|
||||||
// This will notify you when the peer has connected/disconnected
|
|
||||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
|
||||||
log.Printf("ICE Connection State has changed: %s\n", connectionState.String())
|
|
||||||
})
|
|
||||||
|
|
||||||
err = offer.Valid()
|
|
||||||
if err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = peerConnection.SetRemoteDescription(offer.Offer); err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Gathering WebRTC Candidates")
|
|
||||||
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
|
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
|
||||||
if err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
} else if err = peerConnection.SetLocalDescription(answer); err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
<-gatherComplete
|
|
||||||
log.Println("Gathering WebRTC Candidates Complete")
|
|
||||||
|
|
||||||
response, err := json.Marshal(*peerConnection.LocalDescription())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Connecting to SRT ", offer)
|
|
||||||
srtConnection, err := astisrt.Dial(astisrt.DialOptions{
|
|
||||||
ConnectionOptions: []astisrt.ConnectionOption{
|
|
||||||
astisrt.WithLatency(300),
|
|
||||||
astisrt.WithStreamid(offer.SRTStreamID),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Callback when the connection is disconnected
|
|
||||||
OnDisconnect: func(c *astisrt.Connection, err error) { log.Fatal("Disconnected from SRT") },
|
|
||||||
|
|
||||||
Host: offer.SRTHost,
|
|
||||||
Port: offer.SRTPort,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println("Connected to SRT")
|
|
||||||
|
|
||||||
go srtToWebRTC(srtConnection, videoTrack, metadataSender)
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
if _, err := w.Write(response); err != nil {
|
|
||||||
errorToHTTP(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
enableICEMux := false
|
||||||
flag.BoolVar(&enableICEMux, "enable-ice-mux", false, "Enable ICE Mux on :8081")
|
flag.BoolVar(&enableICEMux, "enable-ice-mux", false, "Enable ICE Mux on :8081")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
mediaEngine := &webrtc.MediaEngine{}
|
var c entity.Config
|
||||||
settingEngine := webrtc.SettingEngine{}
|
err := envconfig.Process("donut", &c)
|
||||||
if err := mediaEngine.RegisterDefaultCodecs(); err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
c.EnableICEMux = enableICEMux
|
||||||
|
|
||||||
if enableICEMux {
|
fx.New(
|
||||||
tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{
|
fx.Provide(func() *entity.Config {
|
||||||
IP: net.IP{0, 0, 0, 0},
|
return &c
|
||||||
Port: 8081,
|
}),
|
||||||
})
|
fx.Provide(handlers.NewHTTPServer),
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{
|
fx.Provide(handlers.NewSignalingHandler),
|
||||||
IP: net.IP{0, 0, 0, 0},
|
fx.Provide(handlers.NewIndexHandler),
|
||||||
Port: 8081,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settingEngine.SetNAT1To1IPs([]string{"127.0.0.1"}, webrtc.ICECandidateTypeHost)
|
fx.Provide(handlers.NewServeMux),
|
||||||
settingEngine.SetICETCPMux(webrtc.NewICETCPMux(nil, tcpListener, 8))
|
|
||||||
settingEngine.SetICEUDPMux(webrtc.NewICEUDPMux(nil, udpListener))
|
|
||||||
}
|
|
||||||
api = webrtc.NewAPI(webrtc.WithSettingEngine(settingEngine), webrtc.WithMediaEngine(mediaEngine))
|
|
||||||
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
fx.Provide(handlers.NewTCPICEServer),
|
||||||
w.WriteHeader(200)
|
fx.Provide(handlers.NewUDPICEServer),
|
||||||
w.Write([]byte(indexHTML))
|
fx.Provide(handlers.NewWebRTCSettingsEngine),
|
||||||
})
|
fx.Provide(handlers.NewWebRTCMediaEngine),
|
||||||
http.HandleFunc("/doSignaling", doSignaling)
|
|
||||||
|
|
||||||
log.Println("Open http://localhost:8080 to access this demo")
|
fx.Provide(zap.NewProduction),
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
|
||||||
|
// just to enforce the lifecycle by using NewHTTPServer
|
||||||
|
fx.Invoke(func(*http.Server) {}),
|
||||||
|
).Run()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user