]> git.feebdaed.xyz Git - gomehub.git/commitdiff
rtmp stream success
authorseantywork <seantywork@gmail.com>
Tue, 18 Jun 2024 12:05:23 +0000 (21:05 +0900)
committerseantywork <seantywork@gmail.com>
Tue, 18 Jun 2024 12:05:23 +0000 (21:05 +0900)
ctl/ctl.go
go.mod
go.sum
pkg/stream/stream_cctv.go
pkg/stream/stream_cctv_local.go [new file with mode: 0644]
public/css/cctv.css [deleted file]
public/css/cctv_local.css [new file with mode: 0644]
public/js/cctv.js [deleted file]
public/js/cctv_local.js [new file with mode: 0644]
view/cctv.html
view/cctv_local.html [new file with mode: 0644]

index fbf2a0ae8738977a9f4768f9c78b7c50facbaa1d..b4db9ed0d9dc78ef65402080e6d75e1b8fe1c2e0 100644 (file)
@@ -43,9 +43,15 @@ func CreateServer() *gin.Engine {
 
        genserver.GET("/cctv", pkgstream.GetCCTVIndex)
 
-       genserver.GET("/cctv/turn/address", pkgstream.GetCCTVTurnServeAddr)
+       genserver.POST("/cctv/create", pkgstream.PostCCTVCreate)
 
-       genserver.POST("/cctv/offer", pkgstream.PostCCTVOffer)
+       // cctv local
+
+       genserver.GET("/cctv/local", pkgstream.GetCCTVLocalIndex)
+
+       genserver.GET("/cctv/local/turn/address", pkgstream.GetCCTVLocalTurnServeAddr)
+
+       genserver.POST("/cctv/local/offer", pkgstream.PostCCTVLocalOffer)
 
        // video
 
diff --git a/go.mod b/go.mod
index 37214cd0856fa4ac6ba8d15d1b76e21e83ab0cf1..e0fd5618782bedc6dcf7ed123704ccdcaa06c3d2 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -4,33 +4,38 @@ go 1.21.5
 
 require (
        github.com/gin-gonic/gin v1.10.0
+       github.com/gorilla/websocket v1.5.3
        github.com/pion/rtcp v1.2.14
-       github.com/pion/webrtc/v4 v4.0.0-beta.19
+       github.com/pion/webrtc/v4 v4.0.0-beta.20
        github.com/wimspaargaren/yolov3 v0.3.1
        gocv.io/x/gocv v0.37.0
+       gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
-       github.com/bytedance/sonic v1.11.6 // indirect
+       github.com/bytedance/sonic v1.11.8 // indirect
        github.com/bytedance/sonic/loader v0.1.1 // indirect
        github.com/cloudwego/base64x v0.1.4 // indirect
        github.com/cloudwego/iasm v0.2.0 // indirect
-       github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+       github.com/gabriel-vasile/mimetype v1.4.4 // indirect
        github.com/gin-contrib/sse v0.1.0 // indirect
        github.com/go-playground/locales v0.14.1 // indirect
        github.com/go-playground/universal-translator v0.18.1 // indirect
-       github.com/go-playground/validator/v10 v10.20.0 // indirect
-       github.com/goccy/go-json v0.10.2 // indirect
+       github.com/go-playground/validator/v10 v10.22.0 // indirect
+       github.com/goccy/go-json v0.10.3 // indirect
        github.com/google/uuid v1.6.0 // indirect
+       github.com/hashicorp/errwrap v1.1.0 // indirect
+       github.com/hashicorp/go-multierror v1.1.0 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
-       github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+       github.com/klauspost/cpuid/v2 v2.2.8 // indirect
        github.com/leodido/go-urn v1.4.0 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
+       github.com/mitchellh/mapstructure v1.4.1 // indirect
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
        github.com/modern-go/reflect2 v1.0.2 // indirect
        github.com/pelletier/go-toml/v2 v2.2.2 // indirect
        github.com/pion/datachannel v1.5.6 // indirect
-       github.com/pion/dtls/v2 v2.2.10 // indirect
+       github.com/pion/dtls/v2 v2.2.11 // indirect
        github.com/pion/ice/v3 v3.0.7 // indirect
        github.com/pion/interceptor v0.1.29 // indirect
        github.com/pion/logging v0.2.2 // indirect
@@ -41,17 +46,21 @@ require (
        github.com/pion/sdp/v3 v3.0.9 // indirect
        github.com/pion/srtp/v3 v3.0.1 // indirect
        github.com/pion/stun/v2 v2.0.0 // indirect
-       github.com/pion/transport/v2 v2.2.4 // indirect
+       github.com/pion/transport/v2 v2.2.5 // indirect
        github.com/pion/transport/v3 v3.0.2 // indirect
        github.com/pion/turn/v3 v3.0.3 // indirect
+       github.com/pkg/errors v0.9.1 // indirect
        github.com/rogpeppe/go-internal v1.12.0 // indirect
+       github.com/sirupsen/logrus v1.9.3 // indirect
        github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
        github.com/ugorji/go/codec v1.2.12 // indirect
+       github.com/yutopp/go-amf0 v0.1.0 // indirect
+       github.com/yutopp/go-flv v0.3.1 // indirect
+       github.com/yutopp/go-rtmp v0.0.6 // indirect
        golang.org/x/arch v0.8.0 // indirect
-       golang.org/x/crypto v0.23.0 // indirect
-       golang.org/x/net v0.25.0 // indirect
-       golang.org/x/sys v0.20.0 // indirect
-       golang.org/x/text v0.15.0 // indirect
-       google.golang.org/protobuf v1.34.1 // indirect
-       gopkg.in/yaml.v3 v3.0.1 // indirect
+       golang.org/x/crypto v0.24.0 // indirect
+       golang.org/x/net v0.26.0 // indirect
+       golang.org/x/sys v0.21.0 // indirect
+       golang.org/x/text v0.16.0 // indirect
+       google.golang.org/protobuf v1.34.2 // indirect
 )
diff --git a/go.sum b/go.sum
index 1ec203ea5451fe70699d2c144cf243fa45216a76..f723dbdfca821e7b5e28288fb8220f77131c106f 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
 github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
+github.com/bytedance/sonic v1.11.8 h1:Zw/j1KfiS+OYTi9lyB3bb0CFxPJVkM17k1wyDG32LRA=
+github.com/bytedance/sonic v1.11.8/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
 github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
 github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
@@ -9,8 +11,11 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
 github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
+github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
@@ -23,8 +28,12 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
 github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
+github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
@@ -32,11 +41,20 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
 github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
+github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -48,6 +66,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
 github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -60,6 +80,8 @@ github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNI
 github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
 github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA=
 github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
+github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
+github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
 github.com/pion/ice/v3 v3.0.7 h1:dfMViRKblENqzorR2cQiiRKWqQfqKZ9+nT/sREX3ra8=
 github.com/pion/ice/v3 v3.0.7/go.mod h1:pBRcCoJRC0vwvFsemfRIqRLYukV4bPboGb0B4b8AhrQ=
 github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
@@ -88,6 +110,8 @@ github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLcc
 github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
 github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo=
 github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
+github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
+github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
 github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
 github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
 github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
@@ -95,16 +119,22 @@ github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE=
 github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc=
 github.com/pion/webrtc/v4 v4.0.0-beta.19 h1:qAmESbOR4C8Odv+FjGc7qS1sqhWAZJgmSjQFu8pahI8=
 github.com/pion/webrtc/v4 v4.0.0-beta.19/go.mod h1:yL80J6f59xyNERWAJXFdKZjjQXR4NchgtXr5JDA/2uQ=
+github.com/pion/webrtc/v4 v4.0.0-beta.20 h1:dt1th4CqqKV2APgPsapaskZuOKKvbYsNpgmgi8D6014=
+github.com/pion/webrtc/v4 v4.0.0-beta.20/go.mod h1:tBa/6GmW34fT7UURDdj7kwJlAW9NLEkeHKP1jAonhXY=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -121,6 +151,12 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ
 github.com/wimspaargaren/yolov3 v0.3.1 h1:6vna2f0+E2PA8CSt26AGCWhADpul0WJVAHNVj5nSCvY=
 github.com/wimspaargaren/yolov3 v0.3.1/go.mod h1:MQ+aZYPx4DF8UVCGlIVdETPxPnl0iPiZ0SIJ6ZdQnc4=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yutopp/go-amf0 v0.1.0 h1:a3UeBZG7nRF0zfvmPn2iAfNo1RGzUpHz1VyJD2oGrik=
+github.com/yutopp/go-amf0 v0.1.0/go.mod h1:QzDOBr9RV6sQh6E5GFEJROZbU0iQKijORBmprkb3FIk=
+github.com/yutopp/go-flv v0.3.1 h1:4ILK6OgCJgUNm2WOjaucWM5lUHE0+sLNPdjq3L0Xtjk=
+github.com/yutopp/go-flv v0.3.1/go.mod h1:pAlHPSVRMv5aCUKmGOS/dZn/ooTgnc09qOPmiUNMubs=
+github.com/yutopp/go-rtmp v0.0.6 h1:iH52Zc1AamOe/8E3+DwMVkV+cEuYfDkV0CRBhcipxG8=
+github.com/yutopp/go-rtmp v0.0.6/go.mod h1:KSwrC9Xj5Kf18EUlk1g7CScecjXfIqc0J5q+S0u6Irc=
 gocv.io/x/gocv v0.37.0 h1:sISHvnApErjoJodz1Dxb8UAkFdITOB3vXGslbVu6Knk=
 gocv.io/x/gocv v0.37.0/go.mod h1:lmS802zoQmnNvXETpmGriBqWrENPei2GxYx5KUxJsMA=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -135,6 +171,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
 golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
 golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
 golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -149,14 +187,19 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
 golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -167,6 +210,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -185,6 +230,8 @@ golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
 golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -194,6 +241,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
 google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
index bdd7becc41685589c5da322db453b11441e065e0..da81a2c640612d0a6222ef687bb561dfc01bcb12 100644 (file)
 package stream
 
 import (
-       "errors"
-       "fmt"
+       "bytes"
+       "encoding/binary"
+       "encoding/json"
        "io"
+       "log"
        "net"
-       "net/http"
+       "time"
 
        "github.com/gin-gonic/gin"
-       webrtcv4 "github.com/pion/webrtc/v4"
-       pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
+       "github.com/pion/webrtc/v4"
+       "github.com/pion/webrtc/v4/pkg/media"
+       "github.com/pkg/errors"
+       flvtag "github.com/yutopp/go-flv/tag"
+       "github.com/yutopp/go-rtmp"
+       rtmpmsg "github.com/yutopp/go-rtmp/message"
 )
 
-var UDP_BUFFER_BYTE_SIZE int
-
-var RTP_RECEIVE_ADDR string
-
-var RTP_RECEIVE_PORT string
-
-var RECV_STARTED int = 0
-
 func GetCCTVIndex(c *gin.Context) {
 
        c.HTML(200, "cctv.html", gin.H{
                "title": "CCTV",
        })
-}
-
-func GetCCTVTurnServeAddr(c *gin.Context) {
-
-       c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: TURN_SERVER_ADDR})
 
 }
 
-func PostCCTVOffer(c *gin.Context) {
-
-       if RECV_STARTED == 1 {
-
-               fmt.Println("recv already started")
-
-               c.JSON(http.StatusBadRequest, SERVER_RE{Status: "error", Reply: "invalid request"})
-
-               return
-       }
-
-       var offerjson CLIENT_REQ
-
-       if err := c.BindJSON(&offerjson); err != nil {
-
-               fmt.Println("failed to get request body")
-
-               c.JSON(http.StatusBadRequest, SERVER_RE{Status: "error", Reply: "invalid format"})
-
-               return
-
-       }
-
-       offer_out := make(chan string)
+func PostCCTVCreate(c *gin.Context) {
+       log.Println("Incoming HTTP Request")
 
-       go startCCTVReceiver(offerjson.Data, offer_out)
-
-       offer_out_str := <-offer_out
-
-       c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: offer_out_str})
-
-}
-func startCCTVReceiver(offer_in string, offer_out chan string) {
-
-       peerConnection, err := webrtcv4.NewPeerConnection(webrtcv4.Configuration{
-               ICEServers: []webrtcv4.ICEServer{
-                       {
-                               URLs: []string{TURN_SERVER_ADDR},
-                       },
-               },
-       })
+       peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{})
        if err != nil {
                panic(err)
        }
 
-       var udp_port int
-
-       fmt.Sscanf(RTP_RECEIVE_PORT, "%d", &udp_port)
-
-       listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP(RTP_RECEIVE_ADDR), Port: udp_port})
+       videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264}, "video", "pion")
        if err != nil {
                panic(err)
        }
+       if _, err = peerConnection.AddTrack(videoTrack); err != nil {
+               panic(err)
+       }
 
-       // Increase the UDP receive buffer size
-       // Default UDP buffer sizes vary on different operating systems
-       bufferSize := UDP_BUFFER_BYTE_SIZE // 300KB
-       err = listener.SetReadBuffer(bufferSize)
+       audioTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypePCMA}, "audio", "pion")
        if err != nil {
                panic(err)
        }
+       if _, err = peerConnection.AddTrack(audioTrack); err != nil {
+               panic(err)
+       }
 
-       defer func() {
-               if err = listener.Close(); err != nil {
-                       panic(err)
-               }
-       }()
+       var offer webrtc.SessionDescription
+       if err := json.NewDecoder(c.Request.Body).Decode(&offer); err != nil {
+               panic(err)
+       }
 
-       // Create a video track
-       videoTrack, err := webrtcv4.NewTrackLocalStaticRTP(webrtcv4.RTPCodecCapability{MimeType: webrtcv4.MimeTypeVP8}, "video", "pion")
-       if err != nil {
+       if err := peerConnection.SetRemoteDescription(offer); err != nil {
                panic(err)
        }
-       rtpSender, err := peerConnection.AddTrack(videoTrack)
+
+       gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
+       answer, err := peerConnection.CreateAnswer(nil)
        if err != nil {
                panic(err)
+       } else if err = peerConnection.SetLocalDescription(answer); err != nil {
+               panic(err)
        }
+       <-gatherComplete
 
-       // Read incoming RTCP packets
-       // Before these packets are returned they are processed by interceptors. For things
-       // like NACK this needs to be called.
-       go func() {
-               rtcpBuf := make([]byte, 1500)
-               for {
-                       if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
-                               return
-                       }
-               }
-       }()
-
-       // Set the handler for ICE connection state
-       // This will notify you when the peer has connected/disconnected
-       peerConnection.OnICEConnectionStateChange(func(connectionState webrtcv4.ICEConnectionState) {
-               fmt.Printf("Connection State has changed %s \n", connectionState.String())
+       c.JSON(200, peerConnection.LocalDescription())
 
-               if connectionState == webrtcv4.ICEConnectionStateFailed {
-                       if closeErr := peerConnection.Close(); closeErr != nil {
-                               panic(closeErr)
-                       }
-               }
-       })
+       go startRTMPServer(peerConnection, videoTrack, audioTrack)
+}
 
-       // Wait for the offer to be pasted
-       offer := webrtcv4.SessionDescription{}
-       pkgutils.Decode(offer_in, &offer)
+func startRTMPServer(peerConnection *webrtc.PeerConnection, videoTrack, audioTrack *webrtc.TrackLocalStaticSample) {
+       log.Println("Starting RTMP Server")
 
-       // Set the remote SessionDescription
-       if err = peerConnection.SetRemoteDescription(offer); err != nil {
-               panic(err)
+       tcpAddr, err := net.ResolveTCPAddr("tcp", ":8084")
+       if err != nil {
+               log.Panicf("Failed: %+v", err)
        }
 
-       // Create answer
-       answer, err := peerConnection.CreateAnswer(nil)
+       listener, err := net.ListenTCP("tcp", tcpAddr)
        if err != nil {
-               panic(err)
+               log.Panicf("Failed: %+v", err)
        }
 
-       // Create channel that is blocked until ICE Gathering is complete
-       gatherComplete := webrtcv4.GatheringCompletePromise(peerConnection)
-
-       // Sets the LocalDescription, and starts our UDP listeners
-       if err = peerConnection.SetLocalDescription(answer); err != nil {
-               panic(err)
+       srv := rtmp.NewServer(&rtmp.ServerConfig{
+               OnConnect: func(conn net.Conn) (io.ReadWriteCloser, *rtmp.ConnConfig) {
+                       return conn, &rtmp.ConnConfig{
+                               Handler: &Handler{
+                                       peerConnection: peerConnection,
+                                       videoTrack:     videoTrack,
+                                       audioTrack:     audioTrack,
+                               },
+
+                               ControlState: rtmp.StreamControlStateConfig{
+                                       DefaultBandwidthWindowSize: 6 * 1024 * 1024 / 8,
+                               },
+                       }
+               },
+       })
+       if err := srv.Serve(listener); err != nil {
+               log.Panicf("Failed: %+v", err)
        }
+}
 
-       // Block until ICE Gathering is complete, disabling trickle ICE
-       // we do this because we only can exchange one signaling message
-       // in a production application you should exchange ICE Candidates via OnICECandidate
-       <-gatherComplete
+type Handler struct {
+       rtmp.DefaultHandler
+       peerConnection         *webrtc.PeerConnection
+       videoTrack, audioTrack *webrtc.TrackLocalStaticSample
+}
 
-       // Output the answer in base64 so we can paste it in browser
+func (h *Handler) OnServe(conn *rtmp.Conn) {
+}
 
-       localdesc := pkgutils.Encode(peerConnection.LocalDescription())
+func (h *Handler) OnConnect(timestamp uint32, cmd *rtmpmsg.NetConnectionConnect) error {
+       log.Printf("OnConnect: %#v", cmd)
+       return nil
+}
 
-       offer_out <- localdesc
+func (h *Handler) OnCreateStream(timestamp uint32, cmd *rtmpmsg.NetConnectionCreateStream) error {
+       log.Printf("OnCreateStream: %#v", cmd)
+       return nil
+}
 
-       // Read RTP packets forever and send them to the WebRTC Client
-       inboundRTPPacket := make([]byte, 1600) // UDP MTU
+func (h *Handler) OnPublish(ctx *rtmp.StreamContext, timestamp uint32, cmd *rtmpmsg.NetStreamPublish) error {
+       log.Printf("OnPublish: %#v", cmd)
 
-       RECV_STARTED = 1
+       if cmd.PublishingName == "" {
+               return errors.New("PublishingName is empty")
+       }
+       return nil
+}
 
-       for {
-               n, _, err := listener.ReadFrom(inboundRTPPacket)
-               if err != nil {
+func (h *Handler) OnAudio(timestamp uint32, payload io.Reader) error {
+       var audio flvtag.AudioData
+       if err := flvtag.DecodeAudioData(payload, &audio); err != nil {
+               return err
+       }
 
-                       RECV_STARTED = 0
+       data := new(bytes.Buffer)
+       if _, err := io.Copy(data, audio.Data); err != nil {
+               return err
+       }
 
-                       panic(fmt.Sprintf("error during read: %s", err))
-               }
+       return h.audioTrack.WriteSample(media.Sample{
+               Data:     data.Bytes(),
+               Duration: 128 * time.Millisecond,
+       })
+}
 
-               if _, err = videoTrack.Write(inboundRTPPacket[:n]); err != nil {
-                       if errors.Is(err, io.ErrClosedPipe) {
-                               // The peerConnection has been closed.
+const headerLengthField = 4
 
-                               RECV_STARTED = 0
-                               return
-                       }
+func (h *Handler) OnVideo(timestamp uint32, payload io.Reader) error {
+       var video flvtag.VideoData
+       if err := flvtag.DecodeVideoData(payload, &video); err != nil {
+               return err
+       }
 
-                       RECV_STARTED = 0
+       data := new(bytes.Buffer)
+       if _, err := io.Copy(data, video.Data); err != nil {
+               return err
+       }
 
-                       panic(err)
+       outBuf := []byte{}
+       videoBuffer := data.Bytes()
+       for offset := 0; offset < len(videoBuffer); {
+               bufferLength := int(binary.BigEndian.Uint32(videoBuffer[offset : offset+headerLengthField]))
+               if offset+bufferLength >= len(videoBuffer) {
+                       break
                }
+
+               offset += headerLengthField
+               outBuf = append(outBuf, []byte{0x00, 0x00, 0x00, 0x01}...)
+               outBuf = append(outBuf, videoBuffer[offset:offset+bufferLength]...)
+
+               offset += int(bufferLength)
        }
+
+       return h.videoTrack.WriteSample(media.Sample{
+               Data:     outBuf,
+               Duration: time.Second / 30,
+       })
+}
+
+func (h *Handler) OnClose() {
+       log.Printf("OnClose")
 }
diff --git a/pkg/stream/stream_cctv_local.go b/pkg/stream/stream_cctv_local.go
new file mode 100644 (file)
index 0000000..64cc357
--- /dev/null
@@ -0,0 +1,209 @@
+package stream
+
+import (
+       "errors"
+       "fmt"
+       "io"
+       "net"
+       "net/http"
+
+       "github.com/gin-gonic/gin"
+       webrtcv4 "github.com/pion/webrtc/v4"
+       pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
+)
+
+var UDP_BUFFER_BYTE_SIZE int
+
+var RTP_RECEIVE_ADDR string
+
+var RTP_RECEIVE_PORT string
+
+var RECV_STARTED int = 0
+
+func GetCCTVLocalIndex(c *gin.Context) {
+
+       c.HTML(200, "cctv_local.html", gin.H{
+               "title": "CCTV",
+       })
+}
+
+func GetCCTVLocalTurnServeAddr(c *gin.Context) {
+
+       c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: TURN_SERVER_ADDR})
+
+}
+
+func PostCCTVLocalOffer(c *gin.Context) {
+
+       if RECV_STARTED == 1 {
+
+               fmt.Println("recv already started")
+
+               c.JSON(http.StatusBadRequest, SERVER_RE{Status: "error", Reply: "invalid request"})
+
+               return
+       }
+
+       var offerjson CLIENT_REQ
+
+       if err := c.BindJSON(&offerjson); err != nil {
+
+               fmt.Println("failed to get request body")
+
+               c.JSON(http.StatusBadRequest, SERVER_RE{Status: "error", Reply: "invalid format"})
+
+               return
+
+       }
+
+       offer_out := make(chan string)
+
+       go startCCTVReceiver(offerjson.Data, offer_out)
+
+       offer_out_str := <-offer_out
+
+       c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: offer_out_str})
+
+}
+func startCCTVReceiver(offer_in string, offer_out chan string) {
+
+       peerConnection, err := webrtcv4.NewPeerConnection(webrtcv4.Configuration{
+               ICEServers: []webrtcv4.ICEServer{
+                       {
+                               URLs: []string{TURN_SERVER_ADDR},
+                       },
+               },
+       })
+       if err != nil {
+               panic(err)
+       }
+
+       var udp_port int
+
+       fmt.Sscanf(RTP_RECEIVE_PORT, "%d", &udp_port)
+
+       listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP(RTP_RECEIVE_ADDR), Port: udp_port})
+       if err != nil {
+               panic(err)
+       }
+
+       // Increase the UDP receive buffer size
+       // Default UDP buffer sizes vary on different operating systems
+       //bufferSize := UDP_BUFFER_BYTE_SIZE // 300KB
+       //err = listener.SetReadBuffer(bufferSize)
+       //if err != nil {
+       //      panic(err)
+       //}
+
+       defer func() {
+               if err = listener.Close(); err != nil {
+                       panic(err)
+               }
+       }()
+
+       // Create a video track
+       videoTrack, err := webrtcv4.NewTrackLocalStaticRTP(webrtcv4.RTPCodecCapability{MimeType: webrtcv4.MimeTypeVP8}, "video", "pion")
+       if err != nil {
+               panic(err)
+       }
+       rtpSender, err := peerConnection.AddTrack(videoTrack)
+       if err != nil {
+               panic(err)
+       }
+
+       // Read incoming RTCP packets
+       // Before these packets are returned they are processed by interceptors. For things
+       // like NACK this needs to be called.
+       go func() {
+               rtcpBuf := make([]byte, 1500)
+               for {
+                       if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
+                               return
+                       }
+               }
+       }()
+
+       // Set the handler for ICE connection state
+       // This will notify you when the peer has connected/disconnected
+       peerConnection.OnICEConnectionStateChange(func(connectionState webrtcv4.ICEConnectionState) {
+               fmt.Printf("Connection State has changed %s \n", connectionState.String())
+
+               if connectionState == webrtcv4.ICEConnectionStateFailed {
+                       if closeErr := peerConnection.Close(); closeErr != nil {
+                               panic(closeErr)
+                       }
+               }
+       })
+
+       // Wait for the offer to be pasted
+       offer := webrtcv4.SessionDescription{}
+       pkgutils.Decode(offer_in, &offer)
+
+       // Set the remote SessionDescription
+       if err = peerConnection.SetRemoteDescription(offer); err != nil {
+               panic(err)
+       }
+
+       // Create answer
+       answer, err := peerConnection.CreateAnswer(nil)
+       if err != nil {
+               panic(err)
+       }
+
+       // Create channel that is blocked until ICE Gathering is complete
+       gatherComplete := webrtcv4.GatheringCompletePromise(peerConnection)
+
+       // Sets the LocalDescription, and starts our UDP listeners
+       if err = peerConnection.SetLocalDescription(answer); err != nil {
+               panic(err)
+       }
+
+       // Block until ICE Gathering is complete, disabling trickle ICE
+       // we do this because we only can exchange one signaling message
+       // in a production application you should exchange ICE Candidates via OnICECandidate
+       <-gatherComplete
+
+       // Output the answer in base64 so we can paste it in browser
+
+       localdesc := pkgutils.Encode(peerConnection.LocalDescription())
+
+       offer_out <- localdesc
+
+       // Read RTP packets forever and send them to the WebRTC Client
+       inboundRTPPacket := make([]byte, 1600) // UDP MTU
+
+       RECV_STARTED = 1
+
+       new_conn, err := listener.Accept()
+
+       if err != nil {
+
+               panic(err)
+
+       }
+
+       for {
+
+               n, err := new_conn.Read(inboundRTPPacket)
+               //n, _, err := listener.ReadFrom(inboundRTPPacket)
+               if err != nil {
+
+                       RECV_STARTED = 0
+
+                       panic(fmt.Sprintf("error during read: %s", err))
+               }
+
+               if _, err = videoTrack.Write(inboundRTPPacket[:n]); err != nil {
+                       if errors.Is(err, io.ErrClosedPipe) {
+                               // The peerConnection has been closed.
+
+                               RECV_STARTED = 0
+                               return
+                       }
+
+                       RECV_STARTED = 0
+
+                       panic(err)
+               }
+       }
+}
diff --git a/public/css/cctv.css b/public/css/cctv.css
deleted file mode 100644 (file)
index 9e43d34..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-textarea {
-    width: 500px;
-    min-height: 75px;
-}
\ No newline at end of file
diff --git a/public/css/cctv_local.css b/public/css/cctv_local.css
new file mode 100644 (file)
index 0000000..9e43d34
--- /dev/null
@@ -0,0 +1,4 @@
+textarea {
+    width: 500px;
+    min-height: 75px;
+}
\ No newline at end of file
diff --git a/public/js/cctv.js b/public/js/cctv.js
deleted file mode 100644 (file)
index 41eee6d..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-let pc = new RTCPeerConnection({
-    iceServers: [
-      {
-        urls: 'stun:stun.l.google.com:19302'
-      }
-    ]
-  })
-
-*/
-
-TURN_SERVER_ADDRESS = ""
-
-CLIENT_REQ = {
-    "data":""
-}
-
-pc = {}
-
-log = function(msg) {
-    document.getElementById('div').innerHTML += msg + '<br>'
-}
-
-
-
-
-async function startCCTVOffer(){
-
-
-    let offer_val = document.getElementById('localSessionDescription').value
-
-
-    let offerjson = JSON.parse(JSON.stringify(CLIENT_REQ))
-
-    offerjson.data = offer_val
-
-    let result = await axios.post("/cctv/offer", offerjson)
-
-    if (result.data.status != "success") {
-
-        alert("failed to start cctv offer")
-    }
-
-
-    document.getElementById('remoteSessionDescription').value = result.data.reply
-
-
-
-}
-
-
-function startSession() {
-    let sd = document.getElementById('remoteSessionDescription').value
-    if (sd === '') {
-        return alert('Session Description must not be empty')
-    }
-
-    try {
-        pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd))))
-    } catch (e) {
-        alert(e)
-    }
-}
-
-
-async function getTurnServerAddressAndInit(){
-
-    let result = await axios.get("/cctv/turn/address")
-
-    if(result.data.status != "success"){
-
-        alert("failed to get turn server address")
-
-        return
-    }
-
-
-    TURN_SERVER_ADDRESS = result.data.reply 
-
-    console.log("turnServerAddr: " + TURN_SERVER_ADDRESS)
-
-
-    pc = new RTCPeerConnection({
-        iceServers: [
-            {
-                urls: TURN_SERVER_ADDRESS
-            }
-        ]
-    })
-
-
-    pc.ontrack = function (event) {
-        let el = document.createElement(event.track.kind)
-        el.srcObject = event.streams[0]
-        el.autoplay = true
-        el.controls = true
-    
-        document.getElementById('remoteVideos').appendChild(el)
-    }
-    
-    pc.oniceconnectionstatechange = function(e) {log(pc.iceConnectionState)}
-    pc.onicecandidate = function(event){
-        if (event.candidate === null) {
-            document.getElementById('localSessionDescription').value = btoa(JSON.stringify(pc.localDescription))
-        }
-    }
-    
-    // Offer to receive 1 audio, and 2 video tracks
-    pc.addTransceiver('audio', {'direction': 'recvonly'})
-    pc.addTransceiver('video', {'direction': 'recvonly'})
-    pc.createOffer().then(function(d){ pc.setLocalDescription(d)}).catch(log)
-
-}
-
-
-getTurnServerAddressAndInit()
\ No newline at end of file
diff --git a/public/js/cctv_local.js b/public/js/cctv_local.js
new file mode 100644 (file)
index 0000000..41eee6d
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+let pc = new RTCPeerConnection({
+    iceServers: [
+      {
+        urls: 'stun:stun.l.google.com:19302'
+      }
+    ]
+  })
+
+*/
+
+TURN_SERVER_ADDRESS = ""
+
+CLIENT_REQ = {
+    "data":""
+}
+
+pc = {}
+
+log = function(msg) {
+    document.getElementById('div').innerHTML += msg + '<br>'
+}
+
+
+
+
+async function startCCTVOffer(){
+
+
+    let offer_val = document.getElementById('localSessionDescription').value
+
+
+    let offerjson = JSON.parse(JSON.stringify(CLIENT_REQ))
+
+    offerjson.data = offer_val
+
+    let result = await axios.post("/cctv/offer", offerjson)
+
+    if (result.data.status != "success") {
+
+        alert("failed to start cctv offer")
+    }
+
+
+    document.getElementById('remoteSessionDescription').value = result.data.reply
+
+
+
+}
+
+
+function startSession() {
+    let sd = document.getElementById('remoteSessionDescription').value
+    if (sd === '') {
+        return alert('Session Description must not be empty')
+    }
+
+    try {
+        pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd))))
+    } catch (e) {
+        alert(e)
+    }
+}
+
+
+async function getTurnServerAddressAndInit(){
+
+    let result = await axios.get("/cctv/turn/address")
+
+    if(result.data.status != "success"){
+
+        alert("failed to get turn server address")
+
+        return
+    }
+
+
+    TURN_SERVER_ADDRESS = result.data.reply 
+
+    console.log("turnServerAddr: " + TURN_SERVER_ADDRESS)
+
+
+    pc = new RTCPeerConnection({
+        iceServers: [
+            {
+                urls: TURN_SERVER_ADDRESS
+            }
+        ]
+    })
+
+
+    pc.ontrack = function (event) {
+        let el = document.createElement(event.track.kind)
+        el.srcObject = event.streams[0]
+        el.autoplay = true
+        el.controls = true
+    
+        document.getElementById('remoteVideos').appendChild(el)
+    }
+    
+    pc.oniceconnectionstatechange = function(e) {log(pc.iceConnectionState)}
+    pc.onicecandidate = function(event){
+        if (event.candidate === null) {
+            document.getElementById('localSessionDescription').value = btoa(JSON.stringify(pc.localDescription))
+        }
+    }
+    
+    // Offer to receive 1 audio, and 2 video tracks
+    pc.addTransceiver('audio', {'direction': 'recvonly'})
+    pc.addTransceiver('video', {'direction': 'recvonly'})
+    pc.createOffer().then(function(d){ pc.setLocalDescription(d)}).catch(log)
+
+}
+
+
+getTurnServerAddressAndInit()
\ No newline at end of file
index fe9f91d5b30a4d1a2d0fffcc192dbbb747c35024..6d80c73d4d13533c9f9cb56bab4c7d284d0700a6 100644 (file)
@@ -1,35 +1,41 @@
-<!doctype html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title>webrtc client</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link rel="icon" type="image/x-icon" href="favicon.ico">
+<html>
+  <head>
+    <title> RTMP to WebRTC </title>
+  </head>
 
-    <link rel="stylesheet" href="/public/css/cctv.css">
-    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
+  <body>
+    <h1> RTMP to WebRTC </h1>
+    <div id="rtmpFeed"></div>
+  </body>
 
-    <script src="/public/js/cctv.js"></script>
+  <script>
+    let pc = new RTCPeerConnection()
+    pc.ontrack = function (event) {
+      var el = document.createElement(event.track.kind)
+      el.srcObject = event.streams[0]
+      el.autoplay = true
+      el.controls = true
 
-</head>
-<body>
-    Browser base64 Session Description<br />
-    <textarea id="localSessionDescription" readonly="true"></textarea> <br />
-
-    <button onclick="startCCTVOffer()"> Start offer </button><br />
-    
-    Golang base64 Session Description<br />
-    <textarea id="remoteSessionDescription"> </textarea> <br/>
-    <button onclick="startSession()"> Start Session </button><br />
-    
-    <br />
-    
-    Video<br />
-    <div id="remoteVideos"></div> <br />
-    
-    Logs<br />
-    <div id="div"></div>
-    
-</body>
+      document.getElementById('rtmpFeed').appendChild(el)
+    }
 
+    pc.addTransceiver('video')
+    pc.addTransceiver('audio')
+    pc.createOffer()
+      .then(offer => {
+        pc.setLocalDescription(offer)
+        console.log(offer)
+        return fetch(`/cctv/create`, {
+          method: 'post',
+          headers: {
+            'Accept': 'application/json, text/plain, */*',
+            'Content-Type': 'application/json'
+          },
+          body: JSON.stringify(offer)
+        })
+      })
+      .then(res => res.json())
+      .then(res => pc.setRemoteDescription(res))
+      .catch(alert)
+  </script>
 </html>
\ No newline at end of file
diff --git a/view/cctv_local.html b/view/cctv_local.html
new file mode 100644 (file)
index 0000000..865cb1c
--- /dev/null
@@ -0,0 +1,35 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>webrtc client</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="icon" type="image/x-icon" href="favicon.ico">
+
+    <link rel="stylesheet" href="/public/css/cctv_local.css">
+    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
+
+    <script src="/public/js/cctv_local.js"></script>
+
+</head>
+<body>
+    Browser base64 Session Description<br />
+    <textarea id="localSessionDescription" readonly="true"></textarea> <br />
+
+    <button onclick="startCCTVOffer()"> Start offer </button><br />
+    
+    Golang base64 Session Description<br />
+    <textarea id="remoteSessionDescription"> </textarea> <br/>
+    <button onclick="startSession()"> Start Session </button><br />
+    
+    <br />
+    
+    Video<br />
+    <div id="remoteVideos"></div> <br />
+    
+    Logs<br />
+    <div id="div"></div>
+    
+</body>
+
+</html>
\ No newline at end of file