mirror of
https://github.com/zhoukk/rtsp2web.git
synced 2025-09-26 20:21:11 +08:00
first commit
This commit is contained in:
31
go.mod
Normal file
31
go.mod
Normal file
@@ -0,0 +1,31 @@
|
||||
module github.com/zhoukk/rtsp2web
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/deepch/vdk v0.0.19
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/pion/datachannel v1.5.2 // indirect
|
||||
github.com/pion/dtls/v2 v2.1.5 // indirect
|
||||
github.com/pion/ice/v2 v2.2.6 // indirect
|
||||
github.com/pion/interceptor v0.1.11 // indirect
|
||||
github.com/pion/logging v0.2.2 // indirect
|
||||
github.com/pion/mdns v0.0.5 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.9 // indirect
|
||||
github.com/pion/rtp v1.7.13 // indirect
|
||||
github.com/pion/sctp v1.8.2 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.5 // indirect
|
||||
github.com/pion/srtp/v2 v2.0.9 // indirect
|
||||
github.com/pion/stun v0.3.5 // indirect
|
||||
github.com/pion/transport v0.13.1 // indirect
|
||||
github.com/pion/turn/v2 v2.0.8 // indirect
|
||||
github.com/pion/udp v0.1.1 // indirect
|
||||
github.com/pion/webrtc/v3 v3.1.42 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8 // indirect
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
|
||||
)
|
154
go.sum
Normal file
154
go.sum
Normal file
@@ -0,0 +1,154 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deepch/vdk v0.0.19 h1:r6xYyBTtXEIEh+csO0XHT00sI7xLF+hQFkJE9/go5II=
|
||||
github.com/deepch/vdk v0.0.19/go.mod h1:7ydHfSkflMZxBXfWR79dMjrT54xzvLxnPaByOa9Jpzg=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E=
|
||||
github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ=
|
||||
github.com/pion/dtls/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus=
|
||||
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/ice/v2 v2.2.6 h1:R/vaLlI1J2gCx141L5PEwtuGAGcyS6e7E0hDeJFq5Ig=
|
||||
github.com/pion/ice/v2 v2.2.6/go.mod h1:SWuHiOGP17lGromHTFadUe1EuPgFh/oCU6FCMZHooVE=
|
||||
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/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw=
|
||||
github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g=
|
||||
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.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U=
|
||||
github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
|
||||
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/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/sdp/v3 v3.0.5 h1:ouvI7IgGl+V4CrqskVtr3AaTrPvPisEOxwgpdktctkU=
|
||||
github.com/pion/sdp/v3 v3.0.5/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw=
|
||||
github.com/pion/srtp/v2 v2.0.9 h1:JJq3jClmDFBPX/F5roEb0U19jSU7eUhyDqR/NZ34EKQ=
|
||||
github.com/pion/srtp/v2 v2.0.9/go.mod h1:5TtM9yw6lsH0ppNCehB/EjEUli7VkUgKSPJqWVqbhQ4=
|
||||
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
||||
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A=
|
||||
github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g=
|
||||
github.com/pion/transport v0.13.1 h1:/UH5yLeQtwm2VZIPjxwnNFxjS4DFhyLfS4GlfuKUzfA=
|
||||
github.com/pion/transport v0.13.1/go.mod h1:EBxbqzyv+ZrmDb82XswEE0BjfQFtuw1Nu6sjnjWCsGg=
|
||||
github.com/pion/turn/v2 v2.0.8 h1:KEstL92OUN3k5k8qxsXHpr7WWfrdp7iJZHx99ud8muw=
|
||||
github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw=
|
||||
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/webrtc/v3 v3.1.42 h1:wJEQFIXVanptnQcHOLTuIo4AtGB2+mG2x4OhIhnITOA=
|
||||
github.com/pion/webrtc/v3 v3.1.42/go.mod h1:ffD9DulDrPxyWvDPUIPAOSAWx9GUlOExiJPf7cCcMLA=
|
||||
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/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-20220516162934-403b01795ae8 h1:y+mHpWoQJNAHt26Nhh6JP7hvM71IRZureyvZhoVALIs=
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/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 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20211216021012-1d35b9e2eb4e/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-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
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.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
50
main/main.go
Normal file
50
main/main.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/zhoukk/rtsp2web"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
r2w := rtsp2web.NewRtsp2Web()
|
||||
|
||||
r2w.AddStream("live", "rtsp://192.168.1.3:554/live", false)
|
||||
|
||||
http.Handle("/", http.FileServer(http.Dir("web")))
|
||||
http.Handle("/web/", http.FileServer(http.Dir("../")))
|
||||
|
||||
http.HandleFunc("/api/stream", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost {
|
||||
param := make(map[string]string)
|
||||
if err := json.NewDecoder(r.Body).Decode(¶m); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
ret := make(map[string]string)
|
||||
|
||||
id := param["id"]
|
||||
url := param["url"]
|
||||
|
||||
if err := r2w.AddStream(id, url, false); err != nil {
|
||||
ret["code"] = "500"
|
||||
} else {
|
||||
ret["code"] = "200"
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json;charset=utf-8")
|
||||
json.NewEncoder(w).Encode(ret)
|
||||
}
|
||||
})
|
||||
|
||||
http.Handle("/stream/webrtc", r2w.WebRtcHander())
|
||||
|
||||
http.Handle("/stream/ws", r2w.WsMp4fHander())
|
||||
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
6
main/web/app.js
Normal file
6
main/web/app.js
Normal file
@@ -0,0 +1,6 @@
|
||||
document.onreadystatechange = () => {
|
||||
if (document.readyState == 'complete') {
|
||||
play_webrtc('live', 'video1')
|
||||
play_wsmp4f('live', 'video2')
|
||||
}
|
||||
}
|
11
main/web/index.html
Normal file
11
main/web/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
|
||||
<body>
|
||||
<video width="600" id="video1" autoplay muted controls></video>
|
||||
<video width="600" id="video2" autoplay muted controls></video>
|
||||
</body>
|
||||
<script type="text/javascript" src="../web/webrtc.js"></script>
|
||||
<script type="text/javascript" src="../web/wsmp4f.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
|
||||
</html>
|
357
rtsp2web.go
Normal file
357
rtsp2web.go
Normal file
@@ -0,0 +1,357 @@
|
||||
package rtsp2web
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/codec/h264parser"
|
||||
"github.com/deepch/vdk/format/mp4f"
|
||||
"github.com/deepch/vdk/format/rtspv2"
|
||||
webrtc "github.com/deepch/vdk/format/webrtcv3"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
type Viewer struct {
|
||||
c chan av.Packet
|
||||
}
|
||||
|
||||
type Stream struct {
|
||||
Url string
|
||||
EnableAudio bool
|
||||
EnableDebug bool
|
||||
Running bool
|
||||
Codecs []av.CodecData
|
||||
Viewers map[string]Viewer
|
||||
}
|
||||
|
||||
type Rtsp2Web struct {
|
||||
streams map[string]Stream
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) hasViewer(id string) bool {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok && len(s.Viewers) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) setCodec(id string, codecs []av.CodecData) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok {
|
||||
s.Codecs = codecs
|
||||
g.streams[id] = s
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) getCodec(id string) []av.CodecData {
|
||||
for i := 0; i < 1000; i++ {
|
||||
g.lock.RLock()
|
||||
s, ok := g.streams[id]
|
||||
g.lock.RUnlock()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if s.Codecs != nil && len(s.Codecs) > 0 {
|
||||
for _, codec := range s.Codecs {
|
||||
if codec.Type() == av.H264 {
|
||||
codecVideo := codec.(h264parser.CodecData)
|
||||
if codecVideo.SPS() == nil || codecVideo.PPS() == nil || len(codecVideo.SPS()) == 0 || len(codecVideo.PPS()) == 0 {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.Codecs
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) genViewerId() string {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
bytes := make([]byte, 16)
|
||||
for i := 0; i < 16; i++ {
|
||||
b := r.Intn(26) + 65
|
||||
bytes[i] = byte(b)
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) addViewer(id string) (string, chan av.Packet) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok {
|
||||
vid := g.genViewerId()
|
||||
ch := make(chan av.Packet, 100)
|
||||
s.Viewers[vid] = Viewer{c: ch}
|
||||
g.streams[id] = s
|
||||
return vid, ch
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) delViewer(id, vid string) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok {
|
||||
delete(s.Viewers, vid)
|
||||
g.streams[id] = s
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) castPkt(id string, pkt av.Packet) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok {
|
||||
for _, v := range s.Viewers {
|
||||
if len(v.c) < cap(v.c) {
|
||||
v.c <- pkt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) exist(id string) bool {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
_, ok := g.streams[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) work(id, url string, enableAudio, enableDebug bool) error {
|
||||
RTSPClient, err := rtspv2.Dial(rtspv2.RTSPClientOptions{URL: url, DisableAudio: !enableAudio, DialTimeout: 3 * time.Second, ReadWriteTimeout: 3 * time.Second, Debug: enableDebug})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer RTSPClient.Close()
|
||||
|
||||
if !RTSPClient.WaitCodec {
|
||||
g.setCodec(id, RTSPClient.CodecData)
|
||||
}
|
||||
|
||||
clientTimeout := time.NewTimer(60 * time.Second)
|
||||
viewerTimeout := time.NewTimer(20 * time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-viewerTimeout.C:
|
||||
if !g.hasViewer(id) {
|
||||
return fmt.Errorf("stream %s exit while no viewer", id)
|
||||
}
|
||||
case <-clientTimeout.C:
|
||||
return fmt.Errorf("stream %s exit while no video", id)
|
||||
case signals := <-RTSPClient.Signals:
|
||||
switch signals {
|
||||
case rtspv2.SignalCodecUpdate:
|
||||
g.setCodec(id, RTSPClient.CodecData)
|
||||
case rtspv2.SignalStreamRTPStop:
|
||||
return fmt.Errorf("stream %s exit while rtsp disconnect", id)
|
||||
}
|
||||
case packetAV := <-RTSPClient.OutgoingPacketQueue:
|
||||
if packetAV.IsKeyFrame {
|
||||
clientTimeout.Reset(60 * time.Second)
|
||||
}
|
||||
g.castPkt(id, *packetAV)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) start(id string) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok && !s.Running {
|
||||
s.Running = true
|
||||
g.streams[id] = s
|
||||
go func(id, url string, enableAudio, enableDebug bool) {
|
||||
for {
|
||||
err := g.work(id, url, enableAudio, enableDebug)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if !g.hasViewer(id) {
|
||||
log.Printf("stream %s exit while no viewer\n", id)
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok && s.Running {
|
||||
s.Running = false
|
||||
g.streams[id] = s
|
||||
}
|
||||
}(id, s.Url, s.EnableAudio, s.EnableDebug)
|
||||
}
|
||||
}
|
||||
|
||||
func NewRtsp2Web() *Rtsp2Web {
|
||||
return &Rtsp2Web{
|
||||
streams: make(map[string]Stream),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) WebRtcHander() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.FormValue("id")
|
||||
if !g.exist(id) {
|
||||
log.Printf("stream %s not exist\n", id)
|
||||
http.Error(w, "Stream Not Exist", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
g.start(id)
|
||||
|
||||
codecs := g.getCodec(id)
|
||||
if codecs == nil {
|
||||
log.Printf("stream %s no codec\n", id)
|
||||
http.Error(w, "Stream No Codec", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
sdp, _ := ioutil.ReadAll(r.Body)
|
||||
|
||||
muxerWebRTC := webrtc.NewMuxer(webrtc.Options{})
|
||||
answer, err := muxerWebRTC.WriteHeader(codecs, string(sdp))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Write([]byte(answer))
|
||||
|
||||
go func() {
|
||||
vid, ch := g.addViewer(id)
|
||||
defer g.delViewer(id, vid)
|
||||
defer muxerWebRTC.Close()
|
||||
|
||||
noVideoTimeout := time.NewTimer(60 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-noVideoTimeout.C:
|
||||
log.Println("key frame timeout")
|
||||
return
|
||||
case pkt := <-ch:
|
||||
if pkt.IsKeyFrame {
|
||||
noVideoTimeout.Reset(60 * time.Second)
|
||||
}
|
||||
if err = muxerWebRTC.WritePacket(pkt); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) WsMp4fHander() websocket.Handler {
|
||||
return websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
id := ws.Request().FormValue("id")
|
||||
if !g.exist(id) {
|
||||
log.Printf("stream %s not exist\n", id)
|
||||
return
|
||||
}
|
||||
|
||||
g.start(id)
|
||||
|
||||
codecs := g.getCodec(id)
|
||||
if codecs == nil {
|
||||
log.Printf("stream %s no codec\n", id)
|
||||
return
|
||||
}
|
||||
|
||||
ws.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
||||
|
||||
vid, ch := g.addViewer(id)
|
||||
defer g.delViewer(id, vid)
|
||||
|
||||
muxer := mp4f.NewMuxer(nil)
|
||||
err := muxer.WriteHeader(codecs)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
meta, init := muxer.GetInit(codecs)
|
||||
err = websocket.Message.Send(ws, append([]byte{9}, meta...))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = websocket.Message.Send(ws, init)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
var message string
|
||||
err := websocket.Message.Receive(ws, &message)
|
||||
if err != nil {
|
||||
ws.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
timeLine := make(map[int8]time.Duration)
|
||||
|
||||
noVideoTimeout := time.NewTimer(60 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-noVideoTimeout.C:
|
||||
log.Println("key frame timeout")
|
||||
return
|
||||
case pkt := <-ch:
|
||||
if pkt.IsKeyFrame {
|
||||
noVideoTimeout.Reset(60 * time.Second)
|
||||
}
|
||||
timeLine[pkt.Idx] += pkt.Duration
|
||||
pkt.Time = timeLine[pkt.Idx]
|
||||
if ready, buf, _ := muxer.WritePacket(pkt, false); ready {
|
||||
err = ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err := websocket.Message.Send(ws, buf)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Rtsp2Web) AddStream(id, url string, enable_audio bool) error {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
if s, ok := g.streams[id]; ok && s.Running {
|
||||
return errors.New("Stream already running")
|
||||
} else {
|
||||
g.streams[id] = Stream{
|
||||
Url: url,
|
||||
EnableAudio: false,
|
||||
EnableDebug: false,
|
||||
Codecs: make([]av.CodecData, 0),
|
||||
Viewers: make(map[string]Viewer),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
31
web/webrtc.js
Normal file
31
web/webrtc.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const play_webrtc = (id, dom) => {
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: [{
|
||||
urls: ["stun:stun.l.google.com:19302"]
|
||||
}]
|
||||
});
|
||||
|
||||
pc.onnegotiationneeded = async () => {
|
||||
let offer = await pc.createOffer();
|
||||
await pc.setLocalDescription(offer);
|
||||
const ret = await fetch('/stream/webrtc?id=' + id, {
|
||||
method: 'POST',
|
||||
body: btoa(pc.localDescription.sdp)
|
||||
})
|
||||
let sdp = await ret.text()
|
||||
pc.setRemoteDescription(new RTCSessionDescription({
|
||||
type: 'answer',
|
||||
sdp: atob(sdp)
|
||||
}))
|
||||
};
|
||||
|
||||
pc.ontrack = e => {
|
||||
document.getElementById(dom).srcObject = e.streams[0];
|
||||
}
|
||||
|
||||
pc.oniceconnectionstatechange = () => console.log(pc.iceConnectionState)
|
||||
|
||||
pc.addTransceiver('video', {
|
||||
'direction': 'sendrecv'
|
||||
})
|
||||
}
|
51
web/wsmp4f.js
Normal file
51
web/wsmp4f.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const play_wsmp4f = (id, dom) => {
|
||||
let hidden;
|
||||
if (typeof document.hidden !== "undefined") {
|
||||
hidden = "hidden";
|
||||
} else if (typeof document.msHidden !== "undefined") {
|
||||
hidden = "msHidden";
|
||||
} else if (typeof document.webkitHidden !== "undefined") {
|
||||
hidden = "webkitHidden";
|
||||
}
|
||||
const videoDom = document.getElementById(dom)
|
||||
const ms = new MediaSource()
|
||||
ms.addEventListener('sourceopen', () => {
|
||||
let queue = []
|
||||
let sourceBuffer
|
||||
let streamingStarted = false
|
||||
|
||||
let ws = new WebSocket("ws://" + window.location.host + "/stream/ws?id=" + id)
|
||||
ws.binaryType = "arraybuffer"
|
||||
ws.onopen = () => {
|
||||
console.log('connect')
|
||||
}
|
||||
ws.onmessage = e => {
|
||||
let data = new Uint8Array(e.data)
|
||||
if (data[0] == 9) {
|
||||
let codecs = new TextDecoder("utf-8").decode(data.slice(1))
|
||||
sourceBuffer = ms.addSourceBuffer('video/mp4; codecs="' + codecs + '"')
|
||||
sourceBuffer.mode = "segments"
|
||||
sourceBuffer.addEventListener("updateend", () => {
|
||||
if (queue.length > 0) {
|
||||
let data = queue.shift()
|
||||
sourceBuffer.appendBuffer(data)
|
||||
} else {
|
||||
streamingStarted = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (!streamingStarted) {
|
||||
sourceBuffer.appendBuffer(e.data)
|
||||
streamingStarted = true
|
||||
} else {
|
||||
queue.push(e.data)
|
||||
}
|
||||
}
|
||||
// fix stop when tab hidden
|
||||
if (document[hidden] && videoDom.buffered.length) {
|
||||
videoDom.currentTime = videoDom.buffered.end((videoDom.buffered.length - 1)) - 1;
|
||||
}
|
||||
}
|
||||
}, false)
|
||||
videoDom.src = URL.createObjectURL(ms)
|
||||
}
|
Reference in New Issue
Block a user