Live video on the AT Protocol

media: proof-of-concept embedded rtmp server

+231 -50
+20 -16
go.mod
··· 17 17 github.com/NYTimes/gziphandler v1.1.1 18 18 github.com/ThalesGroup/crypto11 v0.0.0-00010101000000-000000000000 19 19 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d 20 + github.com/bluenviron/gortmplib v0.1.2 21 + github.com/bluenviron/gortsplib/v5 v5.2.1 20 22 github.com/bluesky-social/indigo v0.0.0-20251206005924-d49b45419635 21 23 github.com/cenkalti/backoff v2.2.1+incompatible 22 24 github.com/cenkalti/backoff/v5 v5.0.2 ··· 48 50 github.com/patrickmn/go-cache v2.1.0+incompatible 49 51 github.com/peterbourgon/ff/v3 v3.4.0 50 52 github.com/pion/interceptor v0.1.37 51 - github.com/pion/rtcp v1.2.15 53 + github.com/pion/rtcp v1.2.16 52 54 github.com/pion/webrtc/v4 v4.0.11 53 55 github.com/piprate/json-gold v0.5.0 54 56 github.com/prometheus/client_golang v1.23.0 ··· 59 61 github.com/starttoaster/prometheus-exporter-scraper v0.0.1 60 62 github.com/streamplace/atproto-oauth-golang v0.0.0-20250619231223-a9c04fb888ac 61 63 github.com/streamplace/oatproxy v0.0.0-20251207230012-236e9bd6dae6 62 - github.com/stretchr/testify v1.10.0 64 + github.com/stretchr/testify v1.11.1 63 65 github.com/tdewolff/canvas v0.0.0-20250728095813-50d4cb1eee71 64 66 github.com/whyrusleeping/cbor-gen v0.3.1 65 67 github.com/whyrusleeping/go-did v0.0.0-20230824162731-404d1707d5d6 ··· 69 71 go.opentelemetry.io/otel/sdk v1.36.0 70 72 go.uber.org/goleak v1.3.0 71 73 golang.org/x/image v0.30.0 72 - golang.org/x/net v0.43.0 73 - golang.org/x/sync v0.16.0 74 - golang.org/x/term v0.34.0 74 + golang.org/x/net v0.47.0 75 + golang.org/x/sync v0.18.0 76 + golang.org/x/term v0.37.0 75 77 golang.org/x/time v0.12.0 76 - golang.org/x/tools v0.36.0 78 + golang.org/x/tools v0.38.0 77 79 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da 78 80 google.golang.org/api v0.228.0 79 81 gorm.io/datatypes v1.2.4 ··· 86 88 github.com/jinzhu/inflection v1.0.0 // indirect 87 89 github.com/jinzhu/now v1.1.5 // indirect 88 90 github.com/mattn/go-isatty v0.0.20 89 - golang.org/x/sys v0.35.0 // indirect 91 + golang.org/x/sys v0.38.0 // indirect 90 92 gorm.io/gorm v1.26.1 91 93 ) 92 94 ··· 137 139 github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b // indirect 138 140 github.com/StackExchange/wmi v1.2.1 // indirect 139 141 github.com/VictoriaMetrics/fastcache v1.13.0 // indirect 142 + github.com/abema/go-mp4 v1.4.1 // indirect 140 143 github.com/agnivade/levenshtein v1.2.0 // indirect 141 144 github.com/alecthomas/chroma/v2 v2.17.2 // indirect 142 145 github.com/alecthomas/go-check-sumtype v0.3.1 // indirect ··· 149 152 github.com/ashanbrown/forbidigo v1.6.0 // indirect 150 153 github.com/ashanbrown/makezero v1.2.0 // indirect 151 154 github.com/asticode/go-astikit v0.30.0 // indirect 152 - github.com/asticode/go-astits v1.13.0 // indirect 155 + github.com/asticode/go-astits v1.14.0 // indirect 153 156 github.com/aws/aws-sdk-go v1.44.273 // indirect 154 157 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 155 158 github.com/benoitkugler/textlayout v0.3.1 // indirect ··· 159 162 github.com/bkielbasa/cyclop v1.2.3 // indirect 160 163 github.com/blizzy78/varnamelen v0.8.0 // indirect 161 164 github.com/bluenviron/gortsplib/v4 v4.12.3 // indirect 162 - github.com/bluenviron/mediacommon/v2 v2.4.0 // indirect 165 + github.com/bluenviron/mediacommon/v2 v2.5.2 // indirect 163 166 github.com/bombsimon/wsl/v4 v4.7.0 // indirect 164 167 github.com/breml/bidichk v0.3.3 // indirect 165 168 github.com/breml/errchkjson v0.4.1 // indirect ··· 398 401 github.com/pion/logging v0.2.4 // indirect 399 402 github.com/pion/mdns/v2 v2.0.7 // indirect 400 403 github.com/pion/randutil v0.1.0 // indirect 401 - github.com/pion/rtp v1.8.21 // indirect 404 + github.com/pion/rtp v1.8.26 // indirect 402 405 github.com/pion/sctp v1.8.37 // indirect 403 - github.com/pion/sdp/v3 v3.0.10 // indirect 404 - github.com/pion/srtp/v3 v3.0.4 // indirect 406 + github.com/pion/sdp/v3 v3.0.16 // indirect 407 + github.com/pion/srtp/v3 v3.0.9 // indirect 405 408 github.com/pion/stun/v3 v3.0.0 // indirect 406 - github.com/pion/transport/v3 v3.0.7 // indirect 409 + github.com/pion/transport/v3 v3.1.1 // indirect 407 410 github.com/pion/turn/v4 v4.0.0 // indirect 408 411 github.com/pjbgf/sha1cd v0.3.0 // indirect 409 412 github.com/pkg/errors v0.9.1 // indirect ··· 507 510 go.uber.org/automaxprocs v1.6.0 // indirect 508 511 go.uber.org/multierr v1.11.0 // indirect 509 512 go.uber.org/zap v1.27.0 // indirect 510 - golang.org/x/crypto v0.41.0 // indirect 513 + golang.org/x/crypto v0.44.0 // indirect 511 514 golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect 512 515 golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect 513 - golang.org/x/mod v0.27.0 // indirect 516 + golang.org/x/mod v0.29.0 // indirect 514 517 golang.org/x/oauth2 v0.30.0 // indirect 515 - golang.org/x/text v0.28.0 // indirect 518 + golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect 519 + golang.org/x/text v0.31.0 // indirect 516 520 gonum.org/v1/plot v0.16.0 // indirect 517 521 google.golang.org/appengine/v2 v2.0.2 // indirect 518 522 google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
+47 -34
go.sum
··· 130 130 github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= 131 131 github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= 132 132 github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= 133 + github.com/abema/go-mp4 v1.4.1 h1:YoS4VRqd+pAmddRPLFf8vMk74kuGl6ULSjzhsIqwr6M= 134 + github.com/abema/go-mp4 v1.4.1/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws= 133 135 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= 134 136 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= 135 137 github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= ··· 182 184 github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= 183 185 github.com/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflxkRsZA= 184 186 github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= 185 - github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c= 186 - github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= 187 + github.com/asticode/go-astits v1.14.0 h1:zkgnZzipx2XX5mWycqsSBeEyDH58+i4HtyF4j2ROb00= 188 + github.com/asticode/go-astits v1.14.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= 187 189 github.com/aws/aws-sdk-go v1.44.273 h1:CX8O0gK+cGrgUyv7bgJ6QQP9mQg7u5mweHdNzULH47c= 188 190 github.com/aws/aws-sdk-go v1.44.273/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 189 191 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= ··· 209 211 github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= 210 212 github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= 211 213 github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= 214 + github.com/bluenviron/gortmplib v0.1.2 h1:bJ+5pUxKnHjEoKPbNs7lDLyYy5R9Z1lISJ1n54DsjWE= 215 + github.com/bluenviron/gortmplib v0.1.2/go.mod h1:oVOWgfs7wsZNoKYLttyqLar7a71ZTohO1JOVXkNxtHg= 212 216 github.com/bluenviron/gortsplib/v4 v4.12.3 h1:3EzbyGb5+MIOJQYiWytRegFEP4EW5paiyTrscQj63WE= 213 217 github.com/bluenviron/gortsplib/v4 v4.12.3/go.mod h1:SkZPdaMNr+IvHt2PKRjUXxZN6FDutmSZn4eT0GmF0sk= 214 - github.com/bluenviron/mediacommon/v2 v2.4.0 h1:Ss1T7AMxTrICJ+a/N5urS/1lp1ZpsF+3iJq3B/RLDMw= 215 - github.com/bluenviron/mediacommon/v2 v2.4.0/go.mod h1:a6MbPmXtYda9mKibKVMZlW20GYLLrX2R7ZkUE+1pwV0= 218 + github.com/bluenviron/gortsplib/v5 v5.2.1 h1:VrFC5RU8npiyKiqLFKXZmdUFChSmbjO5eZBfOUgxwjo= 219 + github.com/bluenviron/gortsplib/v5 v5.2.1/go.mod h1:sK4+00XQaSpU2iPIKjmhj6Yye+sVbNWEU2IJWYEZI9U= 220 + github.com/bluenviron/mediacommon/v2 v2.5.2 h1:eq7LHJFksDAVtVdTrwOUl7dO7LE8eKwLgYKYi5MmYaY= 221 + github.com/bluenviron/mediacommon/v2 v2.5.2/go.mod h1:5V15TiOfeaNVmZPVuOqAwqQSWyvMV86/dijDKu5q9Zs= 216 222 github.com/bluesky-social/indigo v0.0.0-20251206005924-d49b45419635 h1:kNeRrgGJH2g5OvjLqtaQ744YXqduliZYpFkJ/ld47c0= 217 223 github.com/bluesky-social/indigo v0.0.0-20251206005924-d49b45419635/go.mod h1:Pm2I1+iDXn/hLbF7XCg/DsZi6uDCiOo7hZGWprSM7k0= 218 224 github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= ··· 299 305 github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= 300 306 github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= 301 307 github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= 308 + github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 302 309 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 303 310 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 304 311 github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= ··· 860 867 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 861 868 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 862 869 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 870 + github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 863 871 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 864 872 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 865 873 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= ··· 1053 1061 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 1054 1062 github.com/orandin/slog-gorm v1.4.0 h1:FgA8hJufF9/jeNSYoEXmHPPBwET2gwlF3B85JdpsTUU= 1055 1063 github.com/orandin/slog-gorm v1.4.0/go.mod h1:MoZ51+b7xE9lwGNPYEhxcUtRNrYzjdcKvA8QXQQGEPA= 1064 + github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw= 1065 + github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0= 1056 1066 github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= 1057 1067 github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= 1058 1068 github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= ··· 1094 1104 github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= 1095 1105 github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= 1096 1106 github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= 1097 - github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= 1098 - github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= 1099 - github.com/pion/rtp v1.8.21 h1:3yrOwmZFyUpcIosNcWRpQaU+UXIJ6yxLuJ8Bx0mw37Y= 1100 - github.com/pion/rtp v1.8.21/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= 1107 + github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo= 1108 + github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo= 1109 + github.com/pion/rtp v1.8.26 h1:VB+ESQFQhBXFytD+Gk8cxB6dXeVf2WQzg4aORvAvAAc= 1110 + github.com/pion/rtp v1.8.26/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM= 1101 1111 github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs= 1102 1112 github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= 1103 - github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA= 1104 - github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= 1105 - github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= 1106 - github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ= 1113 + github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo= 1114 + github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo= 1115 + github.com/pion/srtp/v3 v3.0.9 h1:lRGF4G61xxj+m/YluB3ZnBpiALSri2lTzba0kGZMrQY= 1116 + github.com/pion/srtp/v3 v3.0.9/go.mod h1:E+AuWd7Ug2Fp5u38MKnhduvpVkveXJX6J4Lq4rxUYt8= 1107 1117 github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= 1108 1118 github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= 1109 1119 github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= ··· 1112 1122 github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= 1113 1123 github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= 1114 1124 github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= 1115 - github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= 1116 - github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= 1125 + github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM= 1126 + github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ= 1117 1127 github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= 1118 1128 github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= 1119 1129 github.com/pion/webrtc/v4 v4.0.11 h1:0i7BNFH2n8LVp08q/dqM5iyZBXW4TITbD1+RwNqk/iY= ··· 1307 1317 github.com/streamplace/atproto-oauth-golang v0.0.0-20250619231223-a9c04fb888ac/go.mod h1:9LlKkqciiO5lRfbX0n4Wn5KNY9nvFb4R3by8FdW2TWc= 1308 1318 github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4 h1:L1fS4HJSaAyNnkwfuZubgfeZy8rkWmA0cMtH5Z0HqNc= 1309 1319 github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4/go.mod h1:bGUXY9Wd4mnd+XUrOYZr358J2f6z9QO/dLhL1SsiD+0= 1310 - github.com/streamplace/oatproxy v0.0.0-20251201231246-9e9aa13c659d h1:xVIF467970izRZBXBC+/XWyCB6zBtxwZ1KGytEp1rTc= 1311 - github.com/streamplace/oatproxy v0.0.0-20251201231246-9e9aa13c659d/go.mod h1:pXi24hA7xBHj8eEywX6wGqJOR9FaEYlGwQ/72rN6okw= 1312 1320 github.com/streamplace/oatproxy v0.0.0-20251207230012-236e9bd6dae6 h1:Y81F18H+qQGWk58Vqangsw75XQ6G1shJOsUEqgKQdYI= 1313 1321 github.com/streamplace/oatproxy v0.0.0-20251207230012-236e9bd6dae6/go.mod h1:pXi24hA7xBHj8eEywX6wGqJOR9FaEYlGwQ/72rN6okw= 1314 1322 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= ··· 1328 1336 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 1329 1337 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 1330 1338 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 1331 - github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 1332 - github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 1339 + github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 1340 + github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 1333 1341 github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= 1334 1342 github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= 1335 1343 github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 1344 + github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I= 1336 1345 github.com/supranational/blst v0.3.15 h1:rd9viN6tfARE5wv3KZJ9H8e1cg0jXW8syFCcsbHa76o= 1337 1346 github.com/supranational/blst v0.3.15/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 1338 1347 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= ··· 1520 1529 golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 1521 1530 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 1522 1531 golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 1523 - golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= 1524 - golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= 1532 + golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= 1533 + golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= 1525 1534 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 1526 1535 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 1527 1536 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= ··· 1573 1582 golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 1574 1583 golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 1575 1584 golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 1576 - golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= 1577 - golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= 1585 + golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= 1586 + golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= 1578 1587 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 1579 1588 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 1580 1589 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= ··· 1627 1636 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 1628 1637 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 1629 1638 golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 1630 - golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= 1631 - golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= 1639 + golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= 1640 + golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 1632 1641 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 1633 1642 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 1634 1643 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= ··· 1658 1667 golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 1659 1668 golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 1660 1669 golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 1661 - golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= 1662 - golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 1670 + golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= 1671 + golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 1663 1672 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 1664 1673 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 1665 1674 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= ··· 1722 1731 golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 1723 1732 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 1724 1733 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 1725 - golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= 1726 - golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 1734 + golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 1735 + golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 1727 1736 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 1737 + golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= 1738 + golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= 1728 1739 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 1729 1740 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 1730 1741 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= ··· 1736 1747 golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 1737 1748 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 1738 1749 golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 1739 - golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= 1740 - golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= 1750 + golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= 1751 + golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 1741 1752 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 1742 1753 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 1743 1754 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= ··· 1754 1765 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 1755 1766 golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 1756 1767 golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 1757 - golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= 1758 - golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= 1768 + golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= 1769 + golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 1759 1770 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 1760 1771 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 1761 1772 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= ··· 1823 1834 golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 1824 1835 golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= 1825 1836 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 1826 - golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= 1827 - golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= 1837 + golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= 1838 + golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= 1828 1839 golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= 1829 1840 golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= 1830 1841 golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= ··· 1946 1957 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 1947 1958 gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 1948 1959 gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 1960 + gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= 1961 + gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= 1949 1962 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 1950 1963 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 1951 1964 gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
+4
pkg/cmd/streamplace.go
··· 448 448 }) 449 449 450 450 group.Go(func() error { 451 + return media.StartRTMPServer(ctx) 452 + }) 453 + 454 + group.Go(func() error { 451 455 return replicator.Start(ctx, &cli) 452 456 }) 453 457
+160
pkg/media/rtmp.go
··· 1 + // Package main contains an example. 2 + package media 3 + 4 + import ( 5 + "context" 6 + "fmt" 7 + "net" 8 + "time" 9 + 10 + "github.com/bluenviron/gortmplib" 11 + "github.com/bluenviron/gortsplib/v5/pkg/format" 12 + "stream.place/streamplace/pkg/log" 13 + ) 14 + 15 + // This example shows how to: 16 + // 1. create a RTMP server 17 + // 2. accept a stream from a reader. 18 + // 3. broadcast the stream to readers. 19 + 20 + // var ( 21 + // mutex sync.Mutex 22 + // publisher *gortmplib.ServerConn 23 + // tracks []format.Format 24 + // readers []*gortmplib.Writer 25 + // ) 26 + 27 + func handlePublisher(ctx context.Context, sc *gortmplib.ServerConn) error { 28 + sc.RW.(net.Conn).SetReadDeadline(time.Now().Add(10 * time.Second)) 29 + 30 + r := &gortmplib.Reader{ 31 + Conn: sc, 32 + } 33 + err := r.Initialize() 34 + if err != nil { 35 + return err 36 + } 37 + 38 + log.Log(ctx, "conn %v is publishing:", sc.RW.(net.Conn).RemoteAddr()) 39 + 40 + for _, track := range r.Tracks() { 41 + log.Log(ctx, "get track", "track", track) 42 + 43 + switch track := track.(type) { 44 + case *format.AV1: 45 + r.OnDataAV1(track, func(pts time.Duration, tu [][]byte) { 46 + log.Log(ctx, "got AV1", "len", len(tu), "pts", pts) 47 + }) 48 + 49 + case *format.VP9: 50 + r.OnDataVP9(track, func(pts time.Duration, frame []byte) { 51 + 52 + log.Log(ctx, "got VP9", "len", len(frame), "pts", pts) 53 + }) 54 + 55 + case *format.H265: 56 + r.OnDataH265(track, func(pts time.Duration, dts time.Duration, au [][]byte) { 57 + log.Log(ctx, "got H265", "len", len(au), "pts", pts, "dts", dts) 58 + }) 59 + 60 + case *format.H264: 61 + r.OnDataH264(track, func(pts time.Duration, dts time.Duration, au [][]byte) { 62 + log.Log(ctx, "got H264", "len", len(au), "pts", pts, "dts", dts) 63 + }) 64 + 65 + case *format.Opus: 66 + r.OnDataOpus(track, func(pts time.Duration, packet []byte) { 67 + log.Log(ctx, "got Opus", "len", len(packet), "pts", pts) 68 + }) 69 + 70 + case *format.MPEG4Audio: 71 + r.OnDataMPEG4Audio(track, func(pts time.Duration, au []byte) { 72 + log.Log(ctx, "got MPEG4Au", "len", len(au), "pts", pts) 73 + }) 74 + 75 + case *format.MPEG1Audio: 76 + r.OnDataMPEG1Audio(track, func(pts time.Duration, frame []byte) { 77 + log.Log(ctx, "got MPEG1Au", "len", len(frame), "pts", pts) 78 + }) 79 + 80 + case *format.AC3: 81 + r.OnDataAC3(track, func(pts time.Duration, frame []byte) { 82 + log.Log(ctx, "got AC3", "len", len(frame), "pts", pts) 83 + }) 84 + 85 + case *format.G711: 86 + r.OnDataG711(track, func(pts time.Duration, samples []byte) { 87 + log.Log(ctx, "got G711", "len", len(samples), "pts", pts) 88 + }) 89 + 90 + case *format.LPCM: 91 + r.OnDataLPCM(track, func(pts time.Duration, samples []byte) { 92 + log.Log(ctx, "got LPCM", "len", len(samples), "pts", pts) 93 + }) 94 + } 95 + } 96 + 97 + for { 98 + sc.RW.(net.Conn).SetReadDeadline(time.Now().Add(10 * time.Second)) 99 + err = r.Read() 100 + if err != nil { 101 + return err 102 + } 103 + } 104 + } 105 + 106 + func handleConnInner(ctx context.Context, conn net.Conn) error { 107 + conn.SetReadDeadline(time.Now().Add(10 * time.Second)) 108 + 109 + sc := &gortmplib.ServerConn{ 110 + RW: conn, 111 + } 112 + err := sc.Initialize() 113 + if err != nil { 114 + return err 115 + } 116 + 117 + err = sc.Accept() 118 + if err != nil { 119 + return err 120 + } 121 + 122 + if sc.Publish { 123 + return handlePublisher(ctx, sc) 124 + } 125 + return fmt.Errorf("RTMP playback is not supported") 126 + } 127 + 128 + func handleConn(ctx context.Context, conn net.Conn) { 129 + defer conn.Close() 130 + 131 + log.Log(ctx, "conn %v opened", conn.RemoteAddr()) 132 + err := handleConnInner(ctx, conn) 133 + log.Log(ctx, "conn %v closed: %v", conn.RemoteAddr(), err) 134 + } 135 + 136 + func StartRTMPServer(ctx context.Context) error { 137 + ln, err := net.Listen("tcp", ":1935") 138 + if err != nil { 139 + return fmt.Errorf("failed to listen: %w", err) 140 + } 141 + defer ln.Close() 142 + 143 + log.Log(ctx, "listening on :1935") 144 + 145 + // Accept loop in a goroutine so we can select on context.Done 146 + go func() { 147 + for { 148 + conn, err := ln.Accept() 149 + if err != nil { 150 + log.Error(ctx, "error accepting RTMP connection", "error", err) 151 + } 152 + 153 + go handleConn(ctx, conn) 154 + } 155 + }() 156 + 157 + <-ctx.Done() 158 + 159 + return ln.Close() 160 + }