mirror of
https://github.com/hkmadao/rtsp2rtmp.git
synced 2025-09-26 19:31:19 +08:00
项目结构整理
This commit is contained in:
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
.vscode
|
||||
rtsp2rtmp*
|
||||
__debug_bin
|
||||
output/rtsp2rtmp*
|
||||
output/live/*.flv
|
||||
output/log/*.log
|
||||
lastupdate.tmp
|
||||
resources/output/live/*.flv
|
||||
resources/output/log/*.log
|
||||
resources/output/releases
|
||||
.idea
|
||||
.vscode
|
||||
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 yumrano
|
||||
Copyright (c) 2021 hkmadao
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
60
README.md
60
README.md
@@ -3,22 +3,25 @@
|
||||

|
||||
|
||||
##### 项目功能:
|
||||
|
||||
1. rtsp转httpflv播放
|
||||
2. rtsp转rtmp推送
|
||||
3. rtsp视频录像,录像文件为flv格式
|
||||
|
||||
|
||||
##### 运行说明:
|
||||
|
||||
1. 下载[程序文件](https://github.com/hkmadao/rtsp2rtmp/releases),解压
|
||||
2. 执行程序文件:window下执行rtsp2rtmp.exe,linux下执行rtsp2rtmp
|
||||
3. 浏览器访问程序服务地址:http://[server_ip]:8080/
|
||||
4. 在网页配置摄像头的rtsp地址、要推送到的rtmp服务器地址等信息
|
||||
5. 配置好后重启服务器
|
||||
6. 再次进入网页观看视频
|
||||
2. 安装postgresql,根据配置文件"resources/conf/conf-prod.yml"创建数据库
|
||||
3. 根据"docs/init/rtsp2rtmp-postgresql.sql"文件创建表
|
||||
4. 执行程序文件:window下执行rtsp2rtmp.exe,linux下执行rtsp2rtmp
|
||||
5. 浏览器访问程序服务地址:http://[server_ip]:8080/rtsp2rtmp/#/ ,根据配置文件"resources/conf/conf-prod.yml"密码登录系统
|
||||
6. 在网页配置摄像头的rtsp地址、要推送到的rtmp服务器地址等信息
|
||||
7. 等待连接上摄像头,约30秒左右,观看视频
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 若只想查看项目功能,可下载带_demo结尾的版本,该版本为window64的演示版本,使用的是sqlite3数据库,无需安装数据库即可运行
|
||||
>
|
||||
> 程序目前支持h264视频编码、aac音频编码,若不能正常播放,关掉摄像头推送的音频再尝试
|
||||
|
||||
##### 目录结构:
|
||||
@@ -26,40 +29,51 @@
|
||||
```
|
||||
--rtsp2rtmp #linux执行文件
|
||||
--rtsp2rtmp.exe #window执行文件
|
||||
--statics #程序的网页文件夹
|
||||
--conf #配置文件文件夹
|
||||
--conf.yml #配置文件
|
||||
--db #sqlite3 #数据库文件夹
|
||||
--rtsp2rtmp.db #sqlite3数据库文件(存放摄像头的url、推送的rtmp服务器地址等信息)
|
||||
--output #程序输出文件夹
|
||||
--live #保存摄像头录像的文件夹,录像格式为flv
|
||||
--log #程序输出的日志文件夹
|
||||
--resources
|
||||
--static #程序的网页文件夹
|
||||
--conf #配置文件文件夹
|
||||
--conf-dev.yml #配置文件
|
||||
--conf-prod.yml #配置文件
|
||||
--output #程序输出文件夹
|
||||
--live #保存摄像头录像的文件夹,录像格式为flv
|
||||
--log #程序输出的日志文件夹
|
||||
```
|
||||
|
||||
##### 配置说明:
|
||||
|
||||
```
|
||||
server:
|
||||
user:
|
||||
name: admin #网页登录用户名
|
||||
password: admin #网页登录密码
|
||||
httpflv:
|
||||
port: 8080 #程序的http端口
|
||||
port: 8080
|
||||
static:
|
||||
path: ./resources/static #页面所在文件夹
|
||||
fileflv:
|
||||
save: true #是否保存录像文件
|
||||
path: ./output/live #录像文件夹
|
||||
path: ./resources/output/live #录像所在文件夹
|
||||
log:
|
||||
path: ./output/log #日志文件夹
|
||||
|
||||
path: ./resources/output/log #日志所在文件夹
|
||||
level: 6 #1-7 7输出的信息最多
|
||||
database:
|
||||
driver-type: 4 #数据库类型
|
||||
driver: postgres #数据库驱动
|
||||
url: user=postgres password=123456 dbname=rtsp2rtmp host=localhost port=5432 sslmode=disable TimeZone=UTC #数据库url
|
||||
show-sql: false #是否打印sql
|
||||
```
|
||||
|
||||
##### 开发说明:
|
||||
|
||||
程序分为服务器和页面,服务端采用golang开发,前端采用react+materia-ui,完成后编译页面文件放入服务器的statics文件夹
|
||||
程序分为服务器和页面,服务端采用golang开发,前端采用react+materia-ui,完成后编译页面文件放入服务器的resources/static文件夹,或者修改配置文件页面所在文件夹的路径
|
||||
|
||||
###### 服务器开发说明:
|
||||
|
||||
1. 安装golang,MinGW(sqlite3模块的使用到的cgo,window下开发需要使用,window下可选择安装MinGW)
|
||||
1. 安装golang
|
||||
2. 获取[服务器源码](https://github.com/hkmadao/rtsp2rtmp.git)
|
||||
3. 进入项目目录
|
||||
4. go build开发
|
||||
3. 安装postgresql数据库,根据配置文件"resources/conf/conf-prod.yml"创建数据库
|
||||
4. 根据"docs/init/rtsp2rtmp-postgresql.sql"文件创建表
|
||||
5. 进入项目目录
|
||||
6. go build开发
|
||||
|
||||
###### 页面开发说明:
|
||||
|
||||
|
44
app/task.go
44
app/task.go
@@ -1,44 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/yumrano/rtsp2rtmp/models"
|
||||
"github.com/yumrano/rtsp2rtmp/server"
|
||||
)
|
||||
|
||||
type task struct {
|
||||
}
|
||||
|
||||
func NewTask() *task {
|
||||
t := &task{}
|
||||
go t.offlineCamera()
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *task) offlineCamera() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
css, err := models.CameraSelectAll()
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
}
|
||||
for _, cs := range css {
|
||||
if cs.OnlineStatus != 1 {
|
||||
continue
|
||||
}
|
||||
exist := server.ExistCamera(cs.Code)
|
||||
if !exist {
|
||||
cs.OnlineStatus = 0
|
||||
models.CameraUpdate(cs)
|
||||
}
|
||||
}
|
||||
<-time.After(10 * time.Minute)
|
||||
}
|
||||
}
|
259
app/web.go
259
app/web.go
@@ -1,259 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/yumrano/rtsp2rtmp/controllers"
|
||||
)
|
||||
|
||||
func ServeHTTP() error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("httpflv ServeHTTP panic %v", r)
|
||||
}
|
||||
}()
|
||||
port, err := config.Int("server.httpflv.port")
|
||||
if err != nil {
|
||||
logs.Error("get httpflv port error: %v. \n use default port : 8080", err)
|
||||
port = 8080
|
||||
}
|
||||
httpflvAddr := ":" + strconv.Itoa(port)
|
||||
flvListen, err := net.Listen("tcp", httpflvAddr)
|
||||
if err != nil {
|
||||
logs.Error("%v", err)
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/live/", controllers.Live)
|
||||
mux.HandleFunc("/camera/list", controllers.CameraList)
|
||||
mux.HandleFunc("/camera/edit", controllers.CameraEdit)
|
||||
mux.HandleFunc("/camera/delete", controllers.CameraDelete)
|
||||
mux.HandleFunc("/camera/enabled", controllers.CameraEnabled)
|
||||
mux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("static"))))
|
||||
if err := http.Serve(flvListen, mux); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// func cameraAll(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
// result := make(map[string]interface{})
|
||||
// result["code"] = 1
|
||||
// result["msg"] = ""
|
||||
// es, err := models.CameraSelectAll()
|
||||
// if err != nil {
|
||||
// logs.Error("get camera list error : %v", err)
|
||||
// result["code"] = 0
|
||||
// result["msg"] = ""
|
||||
// result["data"] = "{}"
|
||||
// rbytes, err := json.Marshal(result)
|
||||
// if err != nil {
|
||||
// logs.Error("parse json camera list error : %v", err)
|
||||
// w.Write([]byte("{}"))
|
||||
// return
|
||||
// }
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
|
||||
// data := make(map[string]interface{})
|
||||
// data["total"] = len(es)
|
||||
// data["page"] = es
|
||||
// result["data"] = data
|
||||
// rbytes, err := json.Marshal(result)
|
||||
// if err != nil {
|
||||
// logs.Error("parse json camera list error : %v", err)
|
||||
// w.Write([]byte("{}"))
|
||||
// return
|
||||
// }
|
||||
// w.Write(rbytes)
|
||||
// }
|
||||
|
||||
// func cameraEdit(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
// if r.Method == "OPTIONS" {
|
||||
// w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
|
||||
// w.Header().Set("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type")
|
||||
// w.Write([]byte("{}"))
|
||||
// return
|
||||
// }
|
||||
// result := make(map[string]interface{})
|
||||
// result["code"] = 1
|
||||
// result["msg"] = ""
|
||||
|
||||
// var param map[string]interface{}
|
||||
// body, err := ioutil.ReadAll(r.Body)
|
||||
// if err != nil {
|
||||
// logs.Error("read param error : %v", err)
|
||||
// }
|
||||
// logs.Error("%s", r.Method)
|
||||
// err = json.Unmarshal(body, ¶m)
|
||||
// if err != nil {
|
||||
// logs.Error("parse param to json error : %v", err)
|
||||
// }
|
||||
// if param["id"] == nil {
|
||||
// param["id"] = ""
|
||||
// }
|
||||
// e := models.Camera{
|
||||
// Id: param["id"].(string),
|
||||
// Code: param["code"].(string),
|
||||
// RtspURL: param["rtspURL"].(string),
|
||||
// RtmpURL: param["rtmpURL"].(string),
|
||||
// AuthCode: param["authCode"].(string),
|
||||
// }
|
||||
// var data int64
|
||||
// if e.Id == "" {
|
||||
// count, err := models.CameraCountByCode(e.Code)
|
||||
// if err != nil || count > 0 {
|
||||
// logs.Error("check code is exist camera error : %v", err)
|
||||
// result["code"] = 0
|
||||
// result["msg"] = "code exist !"
|
||||
// result["data"] = "{}"
|
||||
// rbytes, _ := json.Marshal(result)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
// e.Id = time.Now().Format("20060102150405")
|
||||
// _, err = models.CameraInsert(e)
|
||||
// if err != nil {
|
||||
// logs.Error("insert camera error : %v", err)
|
||||
// result["code"] = 0
|
||||
// result["msg"] = "insert camera error !"
|
||||
// result["data"] = "{}"
|
||||
// rbytes, _ := json.Marshal(result)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
// } else {
|
||||
// count, err := models.CameraCountByCode(e.Code)
|
||||
// if err != nil || count > 1 {
|
||||
// logs.Error("get camera list error : %v", err)
|
||||
// result["code"] = 0
|
||||
// result["msg"] = "code exist !"
|
||||
// result["data"] = "{}"
|
||||
// rbytes, _ := json.Marshal(result)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
// _, err = models.CameraUpdate(e)
|
||||
// if err != nil {
|
||||
// logs.Error("update camera error : %v", err)
|
||||
// result["code"] = 0
|
||||
// result["msg"] = "update camera error !"
|
||||
// result["data"] = "{}"
|
||||
// rbytes, _ := json.Marshal(result)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// result["data"] = data
|
||||
// rbytes, _ := json.Marshal(result)
|
||||
// w.Write(rbytes)
|
||||
// }
|
||||
|
||||
// func cameraDelete(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
// if r.Method == "OPTIONS" {
|
||||
// w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
|
||||
// w.Header().Set("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type")
|
||||
// w.Write([]byte("{}"))
|
||||
// return
|
||||
// }
|
||||
// result := make(map[string]interface{})
|
||||
// result["code"] = 1
|
||||
// result["msg"] = ""
|
||||
|
||||
// var param map[string]interface{}
|
||||
// body, err := ioutil.ReadAll(r.Body)
|
||||
// if err != nil {
|
||||
// logs.Error("read param error : %v", err)
|
||||
// }
|
||||
// logs.Error("%s", r.Method)
|
||||
// err = json.Unmarshal(body, ¶m)
|
||||
// if err != nil {
|
||||
// logs.Error("parse param to json error : %v", err)
|
||||
// }
|
||||
// e := models.Camera{Id: param["id"].(string)}
|
||||
// data, err := models.CameraDelete(e)
|
||||
// if err != nil {
|
||||
// logs.Error("delete camera error : %v", err)
|
||||
// result["code"] = 0
|
||||
// result["msg"] = "delete camera error !"
|
||||
// result["data"] = "{}"
|
||||
// rbytes, _ := json.Marshal(result)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
|
||||
// result["data"] = data
|
||||
// rbytes, _ := json.Marshal(result)
|
||||
// w.Write(rbytes)
|
||||
// }
|
||||
|
||||
// func reciver(w http.ResponseWriter, req *http.Request) {
|
||||
// w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
// r := result.Result{
|
||||
// Code: 1,
|
||||
// Msg: "",
|
||||
// }
|
||||
// uri := strings.TrimSuffix(strings.TrimLeft(req.RequestURI, "/"), ".flv")
|
||||
// uris := strings.Split(uri, "/")
|
||||
// if len(uris) < 2 || uris[0] != "live" {
|
||||
// http.Error(w, "invalid path", http.StatusBadRequest)
|
||||
// return
|
||||
// }
|
||||
// code := uris[1]
|
||||
// logs.Info("player [%s] addr [%s] connecting", code, req.RemoteAddr)
|
||||
// //管理员可以主动中断播放
|
||||
// endStream, heartbeatStream, _, err := httpflv.AddHttpFlvPlayer(code, w)
|
||||
// if err != nil {
|
||||
// logs.Error("camera [%s] add player error : %s", code)
|
||||
// r.Code = 0
|
||||
// r.Msg = "add player error"
|
||||
// rbytes, _ := json.Marshal(r)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
// Loop:
|
||||
// for {
|
||||
// select {
|
||||
// case <-endStream:
|
||||
// break Loop
|
||||
// case <-heartbeatStream:
|
||||
// continue
|
||||
// case <-time.After(10 * time.Second):
|
||||
// logs.Info("player [%s] addr [%s] timeout exit", code, req.RemoteAddr)
|
||||
// break Loop
|
||||
// }
|
||||
// }
|
||||
// logs.Info("player [%s] addr [%s] exit", code, req.RemoteAddr)
|
||||
// }
|
||||
|
||||
// func ServeHTTP() {
|
||||
// router := gin.Default()
|
||||
// router.GET("/recive", reciver)
|
||||
// port, err := conf.GetInt("server.httpflv.port")
|
||||
// if err != nil {
|
||||
// logs.Error("get httpflv port error: %v. \n use default port : 8080", err)
|
||||
// port = 8080
|
||||
// }
|
||||
// err = router.Run(":" + strconv.Itoa(port))
|
||||
// if err != nil {
|
||||
// rlog.Log.Fatalln("Start HTTP Server error", err)
|
||||
// }
|
||||
// }
|
||||
// func reciver(c *gin.Context) {
|
||||
// c.Header("Access-Control-Allow-Origin", "*")
|
||||
// fw := &FlvResponseWriter{
|
||||
// Key: "1",
|
||||
// IsStart: false,
|
||||
// writer: c.Writer,
|
||||
// }
|
||||
// httpFlvWriter.FlvResponseWriters["1"] = fw
|
||||
// }
|
83
build.bat
83
build.bat
@@ -2,47 +2,56 @@
|
||||
chcp 65001
|
||||
set /p ver=请输入版本:
|
||||
echo 版本:%ver% 打包开始
|
||||
@REM window_amd64
|
||||
@REM SET GOOS=windows
|
||||
@REM SET GOARCH=amd64
|
||||
SET CGO_ENABLED=1
|
||||
go build -o rtsp2rtmp_%ver%_window_amd64.exe main.go
|
||||
rmdir /S /Q .\resources\output\releases
|
||||
@REM windows_amd64
|
||||
echo 打包windows_amd64平台
|
||||
SET GOOS=windows
|
||||
SET GOARCH=amd64
|
||||
SET CGO_ENABLED=0
|
||||
go build -o .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\rtsp2rtmp.exe main.go
|
||||
echo =============%GOOS%_%GOARCH%
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\output\live
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\output\log
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
|
||||
|
||||
@REM window_amd64
|
||||
@REM rmdir /S /Q .\output\rtsp2rtmp_%ver%_window_amd64
|
||||
xcopy /S /Y /E .\resources\static .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\static\
|
||||
xcopy /S /Y /E .\resources\conf .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
|
||||
cd .\resources\output\releases\
|
||||
7z a -ttar -so rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar rtsp2rtmp_%ver%_%GOOS%_%GOARCH%/ | 7z a -si rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar.gz
|
||||
cd ..\..\..\
|
||||
|
||||
@REM md .\output\rtsp2rtmp_%ver%_window_amd64\output\live
|
||||
@REM md .\output\rtsp2rtmp_%ver%_window_amd64\output\log
|
||||
@REM md .\output\rtsp2rtmp_%ver%_window_amd64\conf
|
||||
@REM linux_amd64
|
||||
echo 打包linux_amd64平台
|
||||
SET GOOS=linux
|
||||
SET GOARCH=amd64
|
||||
SET CGO_ENABLED=0
|
||||
go build -o .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\rtsp2rtmp main.go
|
||||
echo =============%GOOS%_%GOARCH%
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\output\live
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\output\log
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
|
||||
|
||||
@REM xcopy /S /Y /E .\static .\output\rtsp2rtmp_%ver%_window_amd64\static\
|
||||
@REM xcopy /S /Y /E .\db .\output\rtsp2rtmp_%ver%_window_amd64\db\
|
||||
@REM xcopy .\conf\conf.yml .\output\rtsp2rtmp_%ver%_window_amd64\conf
|
||||
@REM xcopy .\rtsp2rtmp_%ver%_window_amd64.exe .\output\rtsp2rtmp_%ver%_window_amd64\rtsp2rtmp.exe
|
||||
@REM xcopy .\start.vbs .\output\rtsp2rtmp_%ver%_window_amd64\start.vbs
|
||||
xcopy /S /Y /E .\resources\static .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\static\
|
||||
xcopy /S /Y /E .\resources\conf .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
|
||||
cd .\resources\output\releases\
|
||||
7z a -ttar -so rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar rtsp2rtmp_%ver%_%GOOS%_%GOARCH%/ | 7z a -si rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar.gz
|
||||
cd ..\..\..\
|
||||
|
||||
@REM linux_adm64
|
||||
@REM rmdir /S /Q .\output\rtsp2rtmp_%ver%_linux_amd64
|
||||
@REM linux_arm
|
||||
echo 打包linux_arm平台
|
||||
SET GOOS=linux
|
||||
SET GOARCH=arm
|
||||
SET CGO_ENABLED=0
|
||||
go build -o .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\rtsp2rtmp main.go
|
||||
echo =============%GOOS%_%GOARCH%
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\output\live
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\output\log
|
||||
md .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
|
||||
|
||||
@REM md .\output\rtsp2rtmp_%ver%_linux_amd64\output\live
|
||||
@REM md .\output\rtsp2rtmp_%ver%_linux_amd64\output\log
|
||||
@REM md .\output\rtsp2rtmp_%ver%_linux_amd64\conf
|
||||
|
||||
@REM xcopy /S /Y /E .\static .\output\rtsp2rtmp_%ver%_linux_amd64\static\
|
||||
@REM xcopy /S /Y /E .\db .\output\rtsp2rtmp_%ver%_linux_amd64\db\
|
||||
@REM xcopy .\conf\conf.yml .\output\rtsp2rtmp_%ver%_linux_amd64\conf
|
||||
@REM xcopy .\rtsp2rtmp_%ver%_linux_amd64 .\output\rtsp2rtmp_%ver%_linux_amd64\rtsp2rtmp
|
||||
|
||||
@REM linux_armv6
|
||||
@REM rmdir /S /Q .\output\rtsp2rtmp_%ver%_linux_armv6
|
||||
|
||||
@REM md .\output\rtsp2rtmp_%ver%_linux_armv6\output\live
|
||||
@REM md .\output\rtsp2rtmp_%ver%_linux_armv6\output\log
|
||||
@REM md .\output\rtsp2rtmp_%ver%_linux_armv6\conf
|
||||
|
||||
@REM xcopy /S /Y /E .\static .\output\rtsp2rtmp_%ver%_linux_armv6\static\
|
||||
@REM xcopy /S /Y /E .\db .\output\rtsp2rtmp_%ver%_linux_armv6\db\
|
||||
@REM xcopy .\conf\conf.yml .\output\rtsp2rtmp_%ver%_linux_armv6\conf
|
||||
@REM xcopy .\rtsp2rtmp_%ver%_linux_armv6 .\output\rtsp2rtmp_%ver%_linux_armv6\rtsp2rtmp
|
||||
xcopy /S /Y /E .\resources\static .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\static\
|
||||
xcopy /S /Y /E .\resources\conf .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
|
||||
cd .\resources\output\releases\
|
||||
7z a -ttar -so rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar rtsp2rtmp_%ver%_%GOOS%_%GOARCH%/ | 7z a -si rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar.gz
|
||||
cd ..\..\..\
|
||||
|
||||
pause
|
39
build.sh
Normal file
39
build.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
#./build.sh 0.0.1
|
||||
ver=$1
|
||||
if [ -n "${ver}" ]; then
|
||||
echo package version "${ver}"
|
||||
else
|
||||
echo no version param
|
||||
exit 1
|
||||
fi
|
||||
#打多个平台的包
|
||||
platforms="windows_amd64 linux_amd64 linux_arm"
|
||||
rm -rf ./resources/output/releases/
|
||||
for platform in $platforms; do
|
||||
|
||||
export GOOS=$(echo "$platform" | gawk 'BEGIN{FS="_"} {print $1}')
|
||||
export GOARCH=$(echo "$platform" | gawk 'BEGIN{FS="_"} {print $2}')
|
||||
export CGO_ENABLED=0
|
||||
echo "${GOOS}"_"${GOARCH}"
|
||||
if [[ "${GOOS}" == "windows" ]]; then
|
||||
go build -o ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/rtsp2rtmp.exe main.go
|
||||
else
|
||||
go build -o ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/rtsp2rtmp main.go
|
||||
fi
|
||||
go build -o ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/rtsp2rtmp main.go
|
||||
|
||||
mkdir -p ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/resources/output/live
|
||||
mkdir -p ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/resources/output/log
|
||||
mkdir -p ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/resources/conf
|
||||
|
||||
cp -r ./resources/static ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/resources/static/
|
||||
cp -r ./resources/conf ./resources/output/releases/rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/resources/conf
|
||||
|
||||
cd ./resources/output/releases/ || exit
|
||||
rm -rf rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}".tar.gz
|
||||
tar -zcvf ./rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}".tar.gz rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/
|
||||
|
||||
# rm -rf ./rtsp2rtmp_"${ver}"_"${GOOS}"_"${GOARCH}"/
|
||||
cd ../../../
|
||||
done
|
@@ -1,66 +0,0 @@
|
||||
#/bin/bash
|
||||
ver=$1
|
||||
if [ -n "${ver}" ]
|
||||
then
|
||||
echo package version ${ver}
|
||||
else
|
||||
echo no version param
|
||||
exit 1
|
||||
fi
|
||||
# Linux
|
||||
# export GOOS=linux
|
||||
# export GOARCH=amd64
|
||||
export CGO_ENABLED=1
|
||||
go build -o rtsp2rtmp_${ver}_linux_amd64 main.go
|
||||
|
||||
# Windows
|
||||
# export GOOS=windows
|
||||
# export GOARCH=amd64
|
||||
# export CGO_ENABLED=1
|
||||
# go build -o rtsp2rtmp.exe main.go
|
||||
|
||||
#package linux_amd64
|
||||
rm -rf ./output/rtsp2rtmp_${ver}_linux_amd64
|
||||
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_amd64/output/live
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_amd64/output/log
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_amd64/conf
|
||||
|
||||
cp -r ./static ./output/rtsp2rtmp_${ver}_linux_amd64/static/
|
||||
cp -r ./db ./output/rtsp2rtmp_${ver}_linux_amd64/db/
|
||||
cp -r ./conf/conf.yml ./output/rtsp2rtmp_${ver}_linux_amd64/conf
|
||||
cp -r ./rtsp2rtmp_${ver}_linux_amd64 ./output/rtsp2rtmp_${ver}_linux_amd64/rtsp2rtmp
|
||||
|
||||
#package linux_armv6
|
||||
rm -rf ./output/rtsp2rtmp_${ver}_linux_armv6
|
||||
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_armv6/output/live
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_armv6/output/log
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_armv6/conf
|
||||
|
||||
cp -r ./static ./output/rtsp2rtmp_${ver}_linux_armv6/static/
|
||||
cp -r ./db ./output/rtsp2rtmp_${ver}_linux_armv6/db/
|
||||
cp -r ./conf/conf.yml ./output/rtsp2rtmp_${ver}_linux_armv6/conf
|
||||
cp -r ./rtsp2rtmp_${ver}_linux_armv6 ./output/rtsp2rtmp_${ver}_linux_armv6/rtsp2rtmp
|
||||
|
||||
#package window_amd64
|
||||
rm -rf ./output/rtsp2rtmp_${ver}_window_amd64
|
||||
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_window_amd64/output/live
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_window_amd64/output/log
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_window_amd64/conf
|
||||
|
||||
cp -r ./static ./output/rtsp2rtmp_${ver}_window_amd64/static/
|
||||
cp -r ./db ./output/rtsp2rtmp_${ver}_window_amd64/db/
|
||||
cp -r ./conf/conf.yml ./output/rtsp2rtmp_${ver}_window_amd64/conf
|
||||
cp -r ./rtsp2rtmp_${ver}_window_amd64.exe ./output/rtsp2rtmp_${ver}_window_amd64/rtsp2rtmp.exe
|
||||
cp -r ./start.vbs ./output/rtsp2rtmp_${ver}_window_amd64/start.vbs
|
||||
|
||||
cd ./output/
|
||||
tar -zcvf ./rtsp2rtmp_${ver}_linux_amd64.tar.gz ./rtsp2rtmp_${ver}_linux_amd64/
|
||||
tar -zcvf ./rtsp2rtmp_${ver}_linux_armv6.tar.gz ./rtsp2rtmp_${ver}_linux_armv6/
|
||||
tar -zcvf ./rtsp2rtmp_${ver}_window_amd64.tar.gz ./rtsp2rtmp_${ver}_window_amd64/
|
||||
|
||||
rm -rf ./rtsp2rtmp_${ver}_linux_amd64/
|
||||
rm -rf ./rtsp2rtmp_${ver}_linux_armv6/
|
||||
rm -rf ./rtsp2rtmp_${ver}_window_amd64/
|
@@ -1,66 +0,0 @@
|
||||
#/bin/bash
|
||||
ver=$1
|
||||
if [ -n "${ver}" ]
|
||||
then
|
||||
echo package version ${ver}
|
||||
else
|
||||
echo no version param
|
||||
exit 1
|
||||
fi
|
||||
# Linux
|
||||
# export GOOS=linux
|
||||
# export GOARCH=amd64
|
||||
export CGO_ENABLED=1
|
||||
go build -o rtsp2rtmp_${ver}_linux_armv6 main.go
|
||||
|
||||
# Windows
|
||||
# export GOOS=windows
|
||||
# export GOARCH=amd64
|
||||
# export CGO_ENABLED=1
|
||||
# go build -o rtsp2rtmp.exe main.go
|
||||
|
||||
#package linux_amd64
|
||||
rm -rf ./output/rtsp2rtmp_${ver}_linux_amd64
|
||||
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_amd64/output/live
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_amd64/output/log
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_amd64/conf
|
||||
|
||||
cp -r ./static ./output/rtsp2rtmp_${ver}_linux_amd64/static/
|
||||
cp -r ./db ./output/rtsp2rtmp_${ver}_linux_amd64/db/
|
||||
cp -r ./conf/conf.yml ./output/rtsp2rtmp_${ver}_linux_amd64/conf
|
||||
cp -r ./rtsp2rtmp_${ver}_linux_amd64 ./output/rtsp2rtmp_${ver}_linux_amd64/rtsp2rtmp
|
||||
|
||||
#package linux_armv6
|
||||
rm -rf ./output/rtsp2rtmp_${ver}_linux_armv6
|
||||
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_armv6/output/live
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_armv6/output/log
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_linux_armv6/conf
|
||||
|
||||
cp -r ./static ./output/rtsp2rtmp_${ver}_linux_armv6/static/
|
||||
cp -r ./db ./output/rtsp2rtmp_${ver}_linux_armv6/db/
|
||||
cp -r ./conf/conf.yml ./output/rtsp2rtmp_${ver}_linux_armv6/conf
|
||||
cp -r ./rtsp2rtmp_${ver}_linux_armv6 ./output/rtsp2rtmp_${ver}_linux_armv6/rtsp2rtmp
|
||||
|
||||
#package window_amd64
|
||||
rm -rf ./output/rtsp2rtmp_${ver}_window_amd64
|
||||
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_window_amd64/output/live
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_window_amd64/output/log
|
||||
mkdir -p ./output/rtsp2rtmp_${ver}_window_amd64/conf
|
||||
|
||||
cp -r ./static ./output/rtsp2rtmp_${ver}_window_amd64/static/
|
||||
cp -r ./db ./output/rtsp2rtmp_${ver}_window_amd64/db/
|
||||
cp -r ./conf/conf.yml ./output/rtsp2rtmp_${ver}_window_amd64/conf
|
||||
cp -r ./rtsp2rtmp_${ver}_window_amd64.exe ./output/rtsp2rtmp_${ver}_window_amd64/rtsp2rtmp.exe
|
||||
cp -r ./start.vbs ./output/rtsp2rtmp_${ver}_window_amd64/start.vbs
|
||||
|
||||
cd ./output/
|
||||
tar -zcvf ./rtsp2rtmp_${ver}_linux_amd64.tar.gz ./rtsp2rtmp_${ver}_linux_amd64/
|
||||
tar -zcvf ./rtsp2rtmp_${ver}_linux_armv6.tar.gz ./rtsp2rtmp_${ver}_linux_armv6/
|
||||
tar -zcvf ./rtsp2rtmp_${ver}_window_amd64.tar.gz ./rtsp2rtmp_${ver}_window_amd64/
|
||||
|
||||
rm -rf ./rtsp2rtmp_${ver}_linux_amd64/
|
||||
rm -rf ./rtsp2rtmp_${ver}_linux_armv6/
|
||||
rm -rf ./rtsp2rtmp_${ver}_window_amd64/
|
16
conf/conf.go
16
conf/conf.go
@@ -1,16 +0,0 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
_ "github.com/beego/beego/v2/core/config/yaml"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
func init() {
|
||||
filePath := "./conf/conf.yml"
|
||||
err := config.InitGlobalInstance("yaml", filePath)
|
||||
if err != nil {
|
||||
logs.Error("read conf file [%s] error : %v", filePath, err)
|
||||
return
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
server:
|
||||
httpflv:
|
||||
port: 8080
|
||||
fileflv:
|
||||
save: true
|
||||
path: ./output/live
|
||||
log:
|
||||
path: ./output/log
|
||||
|
@@ -1,17 +0,0 @@
|
||||
package conf
|
||||
|
||||
import "github.com/beego/beego/v2/core/logs"
|
||||
|
||||
func init() {
|
||||
// f := &logs.PatternLogFormatter{
|
||||
// Pattern: "%w %F:%n %t %m",
|
||||
// WhenFormat: "2006-01-02 15:04:05.000",
|
||||
// }
|
||||
// logs.RegisterFormatter("pattern", f)
|
||||
|
||||
// _ = logs.SetGlobalFormatter("pattern")
|
||||
// logs.SetLogger(logs.AdapterConsole)
|
||||
logs.SetLogger(logs.AdapterFile, `{"filename":"./output/log/rtsp2rtmp.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}`)
|
||||
logs.EnableFuncCallDepth(true)
|
||||
logs.SetLogFuncCallDepth(3)
|
||||
}
|
@@ -1,281 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/yumrano/rtsp2rtmp/models"
|
||||
"github.com/yumrano/rtsp2rtmp/result"
|
||||
"github.com/yumrano/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
func cros(w http.ResponseWriter, req *http.Request) {
|
||||
method := req.Method //请求方法
|
||||
origin := req.Header.Get("Origin") //请求头部
|
||||
if origin != "" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // 这是允许访问所有域
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
|
||||
// header的类型
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
|
||||
// 允许跨域设置 可以返回其他子段
|
||||
w.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
|
||||
w.Header().Set("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
|
||||
w.Header().Set("content-type", "application/json") // 设置返回格式是json
|
||||
}
|
||||
|
||||
//放行所有OPTIONS方法
|
||||
if method == "OPTIONS" {
|
||||
r := result.Result{Code: 1, Msg: "Options Request!"}
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
}
|
||||
}
|
||||
|
||||
func CameraList(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
cros(w, req)
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
cameras, err := models.CameraSelectAll()
|
||||
if err != nil {
|
||||
logs.Error("no camera found : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "no camera found"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
page := result.Page{Total: len(cameras), Page: cameras}
|
||||
r.Data = page
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
}
|
||||
|
||||
func CameraEdit(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
cros(w, req)
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{
|
||||
Code: 1,
|
||||
Msg: "",
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
logs.Error("read body err, %v", err)
|
||||
return
|
||||
}
|
||||
logs.Info("json:", string(body))
|
||||
|
||||
q := models.Camera{}
|
||||
if err = json.Unmarshal(body, &q); err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
|
||||
if q.Id == "" || len(q.Id) == 0 {
|
||||
id, _ := utils.NextToke()
|
||||
count, err := models.CameraCountByCode(q.Code)
|
||||
if err != nil {
|
||||
logs.Error("check camera is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "check camera is exist"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
if count > 0 {
|
||||
logs.Error("camera code is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera code is exist"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
q.Id = id
|
||||
q.Created = time.Now()
|
||||
playAuthCode, _ := utils.NextToke()
|
||||
q.AuthCode = playAuthCode
|
||||
_, err = models.CameraInsert(q)
|
||||
if err != nil {
|
||||
logs.Error("camera insert error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera insert error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
count, err := models.CameraCountByCode(q.Code)
|
||||
if err != nil {
|
||||
logs.Error("check camera is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "check camera is exist"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
if count > 1 {
|
||||
logs.Error("camera code is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera code is exist"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
camera, _ := models.CameraSelectById(q.Id)
|
||||
camera.Code = q.Code
|
||||
camera.AuthCode = q.AuthCode
|
||||
camera.RtmpURL = q.RtmpURL
|
||||
camera.RtspURL = q.RtspURL
|
||||
// camera.Enabled = q.Enabled
|
||||
_, err = models.CameraUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("camera insert error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera insert error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
}
|
||||
|
||||
func CameraDelete(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
cros(w, req)
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
logs.Error("read body err, %v", err)
|
||||
return
|
||||
}
|
||||
logs.Info("json:", string(body))
|
||||
|
||||
q := models.Camera{}
|
||||
if err = json.Unmarshal(body, &q); err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
camera := models.Camera{Id: q.Id}
|
||||
models.CameraDelete(camera)
|
||||
|
||||
if err != nil {
|
||||
logs.Error("delete camera error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "delete camera error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
//close camera conn
|
||||
select {
|
||||
case codeStream <- camera.Code:
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
}
|
||||
|
||||
func CameraEnabled(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
cros(w, req)
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
logs.Error("read body err, %v", err)
|
||||
return
|
||||
}
|
||||
logs.Info("json:", string(body))
|
||||
|
||||
q := models.Camera{}
|
||||
if err = json.Unmarshal(body, &q); err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
|
||||
camera, err := models.CameraSelectById(q.Id)
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "query camera error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
camera.Enabled = q.Enabled
|
||||
_, err = models.CameraUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("enabled camera status %d error : %v", camera.Enabled, err)
|
||||
r.Code = 0
|
||||
r.Msg = "enabled camera status %d error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
if q.Enabled != 1 {
|
||||
//close camera conn
|
||||
select {
|
||||
case codeStream <- camera.Code:
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
}
|
||||
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
}
|
||||
|
||||
var codeStream = make(chan string)
|
||||
|
||||
func CodeStream() <-chan string {
|
||||
return codeStream
|
||||
}
|
@@ -1,119 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/yumrano/rtsp2rtmp/models"
|
||||
"github.com/yumrano/rtsp2rtmp/result"
|
||||
"github.com/yumrano/rtsp2rtmp/writer/httpflv"
|
||||
)
|
||||
|
||||
func Live(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
cros(w, req)
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
uri := strings.TrimSuffix(strings.TrimLeft(req.RequestURI, "/"), ".flv")
|
||||
uris := strings.Split(uri, "/")
|
||||
if len(uris) < 3 || uris[0] != "live" {
|
||||
http.Error(w, "invalid path", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
method := uris[1]
|
||||
code := uris[2]
|
||||
authCode := uris[3]
|
||||
r := result.Result{
|
||||
Code: 1,
|
||||
Msg: "",
|
||||
}
|
||||
q := models.Camera{Code: code}
|
||||
camera, err := models.CameraSelectOne(q)
|
||||
if err != nil {
|
||||
logs.Error("camera query error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera query error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
if !(method == "temp" || method == "permanent") {
|
||||
logs.Error("method error : %s", method)
|
||||
r.Code = 0
|
||||
r.Msg = "method error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
// if method == "temp" {
|
||||
// csq := models.CameraShare{CameraId: camera.Id, AuthCode: authCode}
|
||||
// cs, err := models.CameraShareSelectOne(csq)
|
||||
// if err != nil {
|
||||
// logs.Error("CameraShareSelectOne error : %v", err)
|
||||
// r.Code = 0
|
||||
// r.Msg = "system error"
|
||||
// rbytes, _ := json.Marshal(r)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
// if time.Now().After(cs.Created.Add(7 * 24 * time.Hour)) {
|
||||
// logs.Error("camera [%s] AuthCodeTemp expired : %s", camera.Code, authCode)
|
||||
// r.Code = 0
|
||||
// r.Msg = "authCode expired"
|
||||
// rbytes, _ := json.Marshal(r)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
|
||||
// }
|
||||
if method == "permanent" && authCode != camera.AuthCode {
|
||||
logs.Error("AuthCodePermanent error : %s", authCode)
|
||||
r.Code = 0
|
||||
r.Msg = "authCode error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
// if !server.ExistCamera(code) {
|
||||
// logs.Error("camera [%s] no connection", code)
|
||||
// r.Code = 0
|
||||
// r.Msg = "camera no connection"
|
||||
// rbytes, _ := json.Marshal(r)
|
||||
// w.Write(rbytes)
|
||||
// return
|
||||
// }
|
||||
logs.Info("player [%s] addr [%s] connecting", code, req.RemoteAddr)
|
||||
//管理员可以主动中断播放
|
||||
endStream, heartbeatStream, _, err := httpflv.AddHttpFlvPlayer(code, w)
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] add player error : %s", code)
|
||||
r.Code = 0
|
||||
r.Msg = "add player error"
|
||||
rbytes, _ := json.Marshal(r)
|
||||
w.Write(rbytes)
|
||||
return
|
||||
}
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case <-endStream:
|
||||
logs.Info("player [%s] addr [%s] end", code, req.RemoteAddr)
|
||||
break Loop
|
||||
case <-heartbeatStream:
|
||||
// logs.Info("player [%s] addr [%s] continue", code, req.RemoteAddr)
|
||||
continue
|
||||
case <-time.After(10 * time.Second):
|
||||
logs.Info("player [%s] addr [%s] timeout", code, req.RemoteAddr)
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
logs.Info("player [%s] addr [%s] exit", code, req.RemoteAddr)
|
||||
}
|
BIN
db/rtsp2rtmp.db
BIN
db/rtsp2rtmp.db
Binary file not shown.
61
docs/init/rtsp2rtmp-postgresql.sql
Normal file
61
docs/init/rtsp2rtmp-postgresql.sql
Normal file
@@ -0,0 +1,61 @@
|
||||
-- public.camera definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.camera;
|
||||
|
||||
CREATE TABLE public.camera (
|
||||
id varchar NOT NULL, -- id
|
||||
code varchar NULL, -- 摄像头编号
|
||||
rtsp_url varchar NULL, -- rtsp地址
|
||||
rtmp_url varchar NULL, -- rtmp地址
|
||||
play_auth_code varchar NULL, -- 播放识别码
|
||||
online_status int2 NULL, -- 是否在线:1.在线;0.不在线;
|
||||
enabled int2 NULL, -- 是否启用:1.启用;0.禁用;
|
||||
created timestamp(0) NULL, -- 创建时间
|
||||
save_video int2 NULL, -- 是否保留录像:1.保留;0.不保留;
|
||||
live int2 NULL, -- 开启直播状态:1.开启;0.关闭;
|
||||
CONSTRAINT camera_pk PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
-- Column comments
|
||||
|
||||
COMMENT ON COLUMN public.camera.id IS 'id';
|
||||
COMMENT ON COLUMN public.camera.code IS '摄像头编号';
|
||||
COMMENT ON COLUMN public.camera.rtsp_url IS 'rtsp地址';
|
||||
COMMENT ON COLUMN public.camera.rtmp_url IS 'rtmp地址';
|
||||
COMMENT ON COLUMN public.camera.play_auth_code IS '播放识别码';
|
||||
COMMENT ON COLUMN public.camera.online_status IS '是否在线:1.在线;0.不在线;';
|
||||
COMMENT ON COLUMN public.camera.enabled IS '是否启用:1.启用;0.禁用;';
|
||||
COMMENT ON COLUMN public.camera.created IS '创建时间';
|
||||
COMMENT ON COLUMN public.camera.save_video IS '是否保留录像:1.保留;0.不保留;';
|
||||
COMMENT ON COLUMN public.camera.live IS '开启直播状态:1.开启;0.关闭;';
|
||||
|
||||
-- public.camera_share definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.camera_share;
|
||||
|
||||
CREATE TABLE public.camera_share (
|
||||
id varchar NOT NULL,
|
||||
camera_id varchar NULL, -- 摄像头标识
|
||||
auth_code varchar NULL, -- 播放权限码
|
||||
enabled varchar NULL, -- 启用状态:1.启用;0.禁用;
|
||||
created timestamp(0) NULL, -- 创建时间
|
||||
deadline timestamp(0) NULL, -- 截止日期
|
||||
"name" varchar NULL, -- 分享说明
|
||||
start_time timestamp(0) NULL, -- 开始生效时间
|
||||
CONSTRAINT camera_share_pk PRIMARY KEY (id)
|
||||
);
|
||||
COMMENT ON TABLE public.camera_share IS '摄像头分享表';
|
||||
|
||||
-- Column comments
|
||||
|
||||
COMMENT ON COLUMN public.camera_share.camera_id IS '摄像头标识';
|
||||
COMMENT ON COLUMN public.camera_share.auth_code IS '播放权限码';
|
||||
COMMENT ON COLUMN public.camera_share.enabled IS '启用状态:1.启用;0.禁用;';
|
||||
COMMENT ON COLUMN public.camera_share.created IS '创建时间';
|
||||
COMMENT ON COLUMN public.camera_share.deadline IS '截止日期';
|
||||
COMMENT ON COLUMN public.camera_share."name" IS '分享说明';
|
||||
COMMENT ON COLUMN public.camera_share.start_time IS '开始生效时间';
|
30
docs/man.md
30
docs/man.md
@@ -22,26 +22,38 @@
|
||||
|
||||
```
|
||||
server:
|
||||
user:
|
||||
name: admin #网页登录用户名
|
||||
password: admin #网页登录密码
|
||||
rtmp:
|
||||
httpflv:
|
||||
port: 8080 #程序的http端口
|
||||
port: 8080
|
||||
static:
|
||||
path: ./resources/static #页面所在文件夹
|
||||
fileflv:
|
||||
save: true #是否保存录像文件
|
||||
path: ./output/live #录像文件夹
|
||||
path: ./resources/output/live #录像所在文件夹
|
||||
log:
|
||||
path: ./output/log #日志文件夹
|
||||
|
||||
path: ./resources/output/log #日志所在文件夹
|
||||
level: 6 #1-7 7输出的信息最多
|
||||
database:
|
||||
driver-type: 4 #数据库类型
|
||||
driver: postgres #数据库驱动
|
||||
url: user=postgres password=123456 dbname=rtsp2rtmp host=localhost port=5432 sslmode=disable TimeZone=UTC #数据库url
|
||||
show-sql: false #是否打印sql
|
||||
```
|
||||
|
||||
##### 开发说明:
|
||||
|
||||
程序分为服务器和页面,服务端采用golang开发,前端采用react+materia-ui,完成后编译页面文件放入服务器的static文件夹
|
||||
程序分为服务器和页面,服务端采用golang开发,前端采用react+materia-ui,完成后编译页面文件放入服务器的resources/static文件夹,或者修改配置文件页面所在文件夹的路径
|
||||
|
||||
###### 服务器开发说明:
|
||||
|
||||
1. 安装golang,gc++编译器(sqlite3模块的需要用到,window下可选择安装MinGW)
|
||||
1. 安装golang
|
||||
2. 获取[服务器源码](https://github.com/hkmadao/rtsp2rtmp.git)
|
||||
3. 进入项目目录
|
||||
4. go build开发
|
||||
3. 安装postgresql数据库,根据配置文件"resources/conf/conf-prod.yml"创建数据库
|
||||
4. 根据"docs/init/rtsp2rtmp-postgresql.sql"文件创建表
|
||||
5. 进入项目目录
|
||||
6. go build开发
|
||||
|
||||
###### 页面开发说明:
|
||||
|
||||
|
40
go.mod
40
go.mod
@@ -1,37 +1,11 @@
|
||||
module github.com/yumrano/rtsp2rtmp
|
||||
module github.com/hkmadao/rtsp2rtmp
|
||||
|
||||
go 1.13
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/beego/bee/v2 v2.0.2 // indirect
|
||||
github.com/beego/beego/v2 v2.0.1 // indirect
|
||||
github.com/deepch/vdk v0.0.0-20210203031003-ae4ae47c04e1
|
||||
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-delve/delve v1.6.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.4.2 // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/lib/pq v1.10.2 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.1 // indirect
|
||||
github.com/peterh/liner v1.2.1 // indirect
|
||||
github.com/pion/webrtc/v2 v2.2.23
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.7.1 // indirect
|
||||
go.starlark.net v0.0.0-20210511153848-cca21e7857d4 // indirect
|
||||
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e // indirect
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
github.com/beego/beego/v2 v2.0.1
|
||||
github.com/deepch/vdk v0.0.0-20210523103705-5b25bda1a000
|
||||
github.com/gin-gonic/gin v1.7.2
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/lib/pq v1.0.0
|
||||
)
|
||||
|
355
go.sum
355
go.sum
@@ -1,33 +1,12 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ=
|
||||
github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
|
||||
github.com/beego/bee/v2 v2.0.2 h1:xWARyIqdnnbNMDBDUdb6Gvr9S/yGXC6Ni43kKdS1/eg=
|
||||
github.com/beego/bee/v2 v2.0.2/go.mod h1:rfZa899qLAF8SYBRvE7mWNPZTU7/qysOBhaCLmZrMX4=
|
||||
github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE=
|
||||
github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI=
|
||||
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd h1:jZtX5jh5IOMu0fpOTC3ayh6QGSPJ/KWOv1lgPvbRw1M=
|
||||
@@ -36,90 +15,60 @@ github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg=
|
||||
github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
|
||||
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
|
||||
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
|
||||
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
|
||||
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/deepch/vdk v0.0.0-20210203031003-ae4ae47c04e1 h1:O7TrdSlVsYlQhfX7dcKU92VOVeaKmxQ24jyTUoqhgsc=
|
||||
github.com/deepch/vdk v0.0.0-20210203031003-ae4ae47c04e1/go.mod h1:mFKn6/jT2OgoMrw/2kgoVDN67kokS5T3kRn04x3eJXg=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/deepch/vdk v0.0.0-20210523103705-5b25bda1a000 h1:yheim2C0sxxzQ61GYlbuKCnCq2hSFraFx4XlgcBC6FE=
|
||||
github.com/deepch/vdk v0.0.0-20210523103705-5b25bda1a000/go.mod h1:EZi580zgIZQOVwMcApOE/rTxuWCHvP/9yAFkUro14LI=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
|
||||
github.com/flosch/pongo2 v0.0.0-20200529170236-5abacdfa4915/go.mod h1:fB4mx6dzqFinCxIf3a7Mf5yLk+18Bia9mPAnuejcvDA=
|
||||
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 h1:fmFk0Wt3bBxxwZnu48jqMdaOR/IZ4vdtJFuaFV8MpIE=
|
||||
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3/go.mod h1:bJWSKrZyQvfTnb2OudyUjurSG4/edverV7n82+K3JiM=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
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.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
|
||||
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
|
||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
||||
github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ=
|
||||
github.com/go-delve/delve v1.6.1 h1:5jDODIhsD/1xQyq/EHZKWbu4n9WhaEX0bcWTROOUxiA=
|
||||
github.com/go-delve/delve v1.6.1/go.mod h1:2DpgGoHOW7r7MXyykmT7axp9IEEIc8EV/swa5m8rkbo=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -129,134 +78,64 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
||||
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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
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.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
|
||||
github.com/google/go-dap v0.5.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
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=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -269,99 +148,63 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pelletier/go-toml v1.9.1 h1:a6qW1EVNZWH9WGI6CsYdD8WAylkoXBS5yv0XHlh17Tc=
|
||||
github.com/pelletier/go-toml v1.9.1/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg=
|
||||
github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/pion/datachannel v1.4.19/go.mod h1:JzKF/zzeWgkOYwQ+KFb8JzbrUt8s63um+Qunu8VqTyw=
|
||||
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
||||
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
||||
github.com/pion/dtls/v2 v2.0.1/go.mod h1:uMQkz2W0cSqY00xav7WByQ4Hb+18xeQh2oH2fRezr5U=
|
||||
github.com/pion/dtls/v2 v2.0.2/go.mod h1:27PEO3MDdaCfo21heT59/vsdmZc0zMt9wQPcSlLu/1I=
|
||||
github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
|
||||
github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
|
||||
github.com/pion/ice v0.7.18 h1:KbAWlzWRUdX9SmehBh3gYpIFsirjhSQsCw6K2MjYMK0=
|
||||
github.com/pion/ice v0.7.18/go.mod h1:+Bvnm3nYC6Nnp7VV6glUkuOfToB/AtMRZpOU8ihuf4c=
|
||||
github.com/pion/ice/v2 v2.0.14/go.mod h1:wqaUbOq5ObDNU5ox1hRsEst0rWfsKuH1zXjQFEWiZwM=
|
||||
github.com/pion/interceptor v0.0.8/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI=
|
||||
github.com/pion/interceptor v0.0.9/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
|
||||
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
|
||||
github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k=
|
||||
github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
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.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I=
|
||||
github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
|
||||
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
||||
github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI=
|
||||
github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
|
||||
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||
github.com/pion/sctp v1.7.8/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sctp v1.7.11 h1:UCnj7MsobLKLuP/Hh+JMiI/6W5Bs/VF45lWKgHFjSIE=
|
||||
github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sdp/v2 v2.4.0 h1:luUtaETR5x2KNNpvEMv/r4Y+/kzImzbz4Lm1z8eQNQI=
|
||||
github.com/pion/sdp/v2 v2.4.0/go.mod h1:L2LxrOpSTJbAns244vfPChbciR/ReU1KWfG04OpkR7E=
|
||||
github.com/pion/sdp/v3 v3.0.3/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
||||
github.com/pion/srtp v1.5.1 h1:9Q3jAfslYZBt+C69SI/ZcONJh9049JUHZWYRRf5KEKw=
|
||||
github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
||||
github.com/pion/srtp v1.5.1/go.mod h1:B+QgX5xPeQTNc1CJStJPHzOlHK66ViMDWTT0HZTCkcA=
|
||||
github.com/pion/srtp/v2 v2.0.0/go.mod h1:QYOU1YKWaE/NJjR2WxtDMc+AfS9So0uz0RnadqOGJO8=
|
||||
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
||||
github.com/pion/srtp/v2 v2.0.1/go.mod h1:c8NWHhhkFf/drmHTAblkdu8++lsISEBBdAuiyxgqIsE=
|
||||
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||
github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE=
|
||||
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
||||
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
|
||||
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
|
||||
github.com/pion/transport v0.12.0 h1:UFmOBBZkTZ3LgvLRf/NGrfWdZEubcU6zkLU3PsA9YvU=
|
||||
github.com/pion/transport v0.12.0/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog=
|
||||
github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
|
||||
github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
|
||||
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
|
||||
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
||||
github.com/pion/webrtc/v2 v2.2.23 h1:rZdOC95fwUCoQFVjHooPAayx/vhs3SLHFz8J/iRkAuk=
|
||||
github.com/pion/webrtc/v2 v2.2.23/go.mod h1:1lN/3EcATkQxc7GJSQbISCGC2l64Xu2VSLpwEG3c/tM=
|
||||
github.com/pion/webrtc/v3 v3.0.1/go.mod h1:ePzv8r2tzj95nJuPsns/7OiS5M8RiGSULdxzIRbXOtQ=
|
||||
github.com/pion/webrtc/v2 v2.2.26/go.mod h1:XMZbZRNHyPDe1gzTIHFcQu02283YO45CbiwFgKvXnmc=
|
||||
github.com/pion/webrtc/v3 v3.0.5/go.mod h1:/EDCREM8y+JrJSkoCRHpoz//qtuBCOYV4E96vEK3bz0=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
||||
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
@@ -369,32 +212,6 @@ github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKz
|
||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartwalle/pongo2render v1.0.1 h1:rsPnDTu/+zIT5HEB5RbMjxKY5hisov26j0isZL/7YS0=
|
||||
github.com/smartwalle/pongo2render v1.0.1/go.mod h1:MGnTzND7nEMz7g194kjlnw8lx/V5JJlb1hr5kDXEO0I=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -403,11 +220,10 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
@@ -415,34 +231,17 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b h1:0Ve0/CCjiAiyKddUMUn3RwIGlq2iTW4GuVzyoKBYO/8=
|
||||
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
go.starlark.net v0.0.0-20200821142938-949cc6f4b097/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU=
|
||||
go.starlark.net v0.0.0-20210511153848-cca21e7857d4 h1:Jv3qnj+QmBjvAeyPwsTAm26oKmU/Z9F6EZIMDNznH6Q=
|
||||
go.starlark.net v0.0.0-20210511153848-cca21e7857d4/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e h1:pv3V0NlNSh5Q6AX/StwGLBjcLS7UN4m4Gq+V+uSecqM=
|
||||
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -450,40 +249,21 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -496,84 +276,50 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
|
||||
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20190911185100-cd5d95a43a6e/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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/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-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@@ -582,58 +328,30 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
@@ -641,14 +359,9 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
20
main.go
20
main.go
@@ -5,28 +5,26 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
// _ "net/http/pprof"
|
||||
_ "github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/conf" // 必须先导入配置文件
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/rtspclientmanager"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/task"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/web"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/yumrano/rtsp2rtmp/app"
|
||||
_ "github.com/yumrano/rtsp2rtmp/conf"
|
||||
"github.com/yumrano/rtsp2rtmp/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
go app.ServeHTTP()
|
||||
server.NewServer()
|
||||
rtspclientmanager.GetSingleRtspClientManager().StartClient()
|
||||
task.GetSingleTask().StartTask()
|
||||
web.GetSingleWeb().StartWeb()
|
||||
sigs := make(chan os.Signal, 1)
|
||||
done := make(chan bool, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
// go func() {
|
||||
// http.ListenAndServe("0.0.0.0:6060", nil)
|
||||
// }()
|
||||
logs.Info("Server Start Awaiting Signal")
|
||||
select {
|
||||
case sig := <-sigs:
|
||||
logs.Error(sig)
|
||||
logs.Info(sig)
|
||||
case <-done:
|
||||
}
|
||||
logs.Error("Exiting")
|
||||
logs.Info("Exiting")
|
||||
}
|
||||
|
@@ -1,11 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
orm.RegisterDriver("sqlite3", orm.DRSqlite)
|
||||
orm.RegisterDataBase("default", "sqlite3", "./db/rtsp2rtmp.db")
|
||||
}
|
2
output/live/.gitignore
vendored
2
output/live/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
*.flv
|
2
output/log/.gitignore
vendored
2
output/log/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
*.log
|
19
resources/conf/conf-dev.yml
Normal file
19
resources/conf/conf-dev.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
server:
|
||||
user:
|
||||
name: admin
|
||||
password: admin
|
||||
http:
|
||||
port: 8080
|
||||
static:
|
||||
path: D:\development\project\rtsp2rtmp-web\build
|
||||
fileflv:
|
||||
path: ./resources/output/live
|
||||
log:
|
||||
path: ./resources/output/log
|
||||
level: 6
|
||||
database:
|
||||
driver-type: 4
|
||||
driver: postgres
|
||||
url: user=postgres password=123456 dbname=rtsp2rtmp host=localhost port=5432 sslmode=disable TimeZone=UTC
|
||||
show-sql: false
|
||||
|
19
resources/conf/conf-prod.yml
Normal file
19
resources/conf/conf-prod.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
server:
|
||||
user:
|
||||
name: admin
|
||||
password: admin
|
||||
http:
|
||||
port: 8080
|
||||
static:
|
||||
path: ./resources/static
|
||||
fileflv:
|
||||
path: ./resources/output/live
|
||||
log:
|
||||
path: ./resources/output/log
|
||||
level: 6
|
||||
database:
|
||||
driver-type: 4
|
||||
driver: postgres
|
||||
url: user=postgres password=123456 dbname=rtsp2rtmp host=localhost port=5432 sslmode=disable TimeZone=UTC
|
||||
show-sql: false
|
||||
|
1
resources/output/live/.gitignore
vendored
Normal file
1
resources/output/live/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.flv
|
1
resources/output/log/.gitignore
vendored
Normal file
1
resources/output/log/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.log
|
17
resources/static/asset-manifest.json
Normal file
17
resources/static/asset-manifest.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"files": {
|
||||
"main.js": "/rtsp2rtmp/static/js/main.82132c58.chunk.js",
|
||||
"main.js.map": "/rtsp2rtmp/static/js/main.82132c58.chunk.js.map",
|
||||
"runtime-main.js": "/rtsp2rtmp/static/js/runtime-main.14fbbcda.js",
|
||||
"runtime-main.js.map": "/rtsp2rtmp/static/js/runtime-main.14fbbcda.js.map",
|
||||
"static/js/2.f4ce3ada.chunk.js": "/rtsp2rtmp/static/js/2.f4ce3ada.chunk.js",
|
||||
"static/js/2.f4ce3ada.chunk.js.map": "/rtsp2rtmp/static/js/2.f4ce3ada.chunk.js.map",
|
||||
"index.html": "/rtsp2rtmp/index.html",
|
||||
"static/js/2.f4ce3ada.chunk.js.LICENSE.txt": "/rtsp2rtmp/static/js/2.f4ce3ada.chunk.js.LICENSE.txt"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.14fbbcda.js",
|
||||
"static/js/2.f4ce3ada.chunk.js",
|
||||
"static/js/main.82132c58.chunk.js"
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
1
resources/static/index.html
Normal file
1
resources/static/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/rtsp2rtmp/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/rtsp2rtmp/logo192.png"/><link rel="manifest" href="/rtsp2rtmp/manifest.json"/><title>React App</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="app"></div><script>!function(e){function r(r){for(var n,p,l=r[0],f=r[1],i=r[2],c=0,s=[];c<l.length;c++)p=l[c],Object.prototype.hasOwnProperty.call(o,p)&&o[p]&&s.push(o[p][0]),o[p]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var f=t[l];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=p(p.s=t[0]))}return e}var n={},o={1:0},u=[];function p(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,p),t.l=!0,t.exports}p.m=e,p.c=n,p.d=function(e,r,t){p.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},p.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.t=function(e,r){if(1&r&&(e=p(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(p.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)p.d(t,n,function(r){return e[r]}.bind(null,n));return t},p.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(r,"a",r),r},p.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},p.p="/rtsp2rtmp/";var l=this.webpackJsonprtsp2rtmpweb=this.webpackJsonprtsp2rtmpweb||[],f=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var a=f;t()}([])</script><script src="/rtsp2rtmp/static/js/2.f4ce3ada.chunk.js"></script><script src="/rtsp2rtmp/static/js/main.82132c58.chunk.js"></script></body></html>
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
3
resources/static/static/js/2.f4ce3ada.chunk.js
Normal file
3
resources/static/static/js/2.f4ce3ada.chunk.js
Normal file
File diff suppressed because one or more lines are too long
@@ -64,3 +64,5 @@ object-assign
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
//! moment.js
|
1
resources/static/static/js/2.f4ce3ada.chunk.js.map
Normal file
1
resources/static/static/js/2.f4ce3ada.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
resources/static/static/js/main.82132c58.chunk.js
Normal file
2
resources/static/static/js/main.82132c58.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
resources/static/static/js/main.82132c58.chunk.js.map
Normal file
1
resources/static/static/js/main.82132c58.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
|
||||
!function(e){function r(r){for(var n,p,l=r[0],f=r[1],i=r[2],c=0,s=[];c<l.length;c++)p=l[c],Object.prototype.hasOwnProperty.call(o,p)&&o[p]&&s.push(o[p][0]),o[p]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var f=t[l];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=p(p.s=t[0]))}return e}var n={},o={1:0},u=[];function p(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,p),t.l=!0,t.exports}p.m=e,p.c=n,p.d=function(e,r,t){p.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},p.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.t=function(e,r){if(1&r&&(e=p(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(p.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)p.d(t,n,function(r){return e[r]}.bind(null,n));return t},p.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(r,"a",r),r},p.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},p.p="/";var l=this.webpackJsonprtsp2rtmpweb=this.webpackJsonprtsp2rtmpweb||[],f=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var a=f;t()}([]);
|
||||
//# sourceMappingURL=runtime-main.233489df.js.map
|
||||
!function(e){function r(r){for(var n,p,l=r[0],f=r[1],i=r[2],c=0,s=[];c<l.length;c++)p=l[c],Object.prototype.hasOwnProperty.call(o,p)&&o[p]&&s.push(o[p][0]),o[p]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var f=t[l];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=p(p.s=t[0]))}return e}var n={},o={1:0},u=[];function p(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,p),t.l=!0,t.exports}p.m=e,p.c=n,p.d=function(e,r,t){p.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},p.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.t=function(e,r){if(1&r&&(e=p(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(p.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)p.d(t,n,function(r){return e[r]}.bind(null,n));return t},p.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(r,"a",r),r},p.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},p.p="/rtsp2rtmp/";var l=this.webpackJsonprtsp2rtmpweb=this.webpackJsonprtsp2rtmpweb||[],f=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var a=f;t()}([]);
|
||||
//# sourceMappingURL=runtime-main.14fbbcda.js.map
|
File diff suppressed because one or more lines are too long
227
server/stream.go
227
server/stream.go
@@ -1,227 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/rtsp"
|
||||
"github.com/yumrano/rtsp2rtmp/controllers"
|
||||
"github.com/yumrano/rtsp2rtmp/models"
|
||||
"github.com/yumrano/rtsp2rtmp/writer/fileflv"
|
||||
"github.com/yumrano/rtsp2rtmp/writer/httpflv"
|
||||
"github.com/yumrano/rtsp2rtmp/writer/rtmpflv"
|
||||
)
|
||||
|
||||
var rms sync.Map
|
||||
|
||||
type Server struct {
|
||||
codeStream <-chan string //管理员关闭
|
||||
|
||||
}
|
||||
|
||||
func NewServer() *Server {
|
||||
codeStream := controllers.CodeStream()
|
||||
s := &Server{
|
||||
codeStream: codeStream,
|
||||
}
|
||||
go s.serveStreams()
|
||||
go s.stopConn()
|
||||
return s
|
||||
}
|
||||
|
||||
func ExistCamera(code string) bool {
|
||||
_, b := rms.Load(code)
|
||||
return b
|
||||
}
|
||||
|
||||
func (s *Server) stopConn() {
|
||||
codeStream := controllers.CodeStream()
|
||||
for {
|
||||
code := <-codeStream
|
||||
v, b := rms.Load(code)
|
||||
if b {
|
||||
r := v.(*RtspManager)
|
||||
err := r.conn.Close()
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] close error : %v", code, err)
|
||||
return
|
||||
}
|
||||
logs.Info("camera [%s] close success", code)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) serveStreams() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("rtspManager panic %v", r)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
es, err := models.CameraSelectAll()
|
||||
if err != nil {
|
||||
logs.Error("camera list is empty")
|
||||
return
|
||||
}
|
||||
for _, camera := range es {
|
||||
if v, b := rms.Load(camera.Code); b && v != nil {
|
||||
continue
|
||||
}
|
||||
if camera.Enabled != 1 {
|
||||
continue
|
||||
}
|
||||
go s.connRtsp(camera.Code)
|
||||
}
|
||||
<-time.After(30 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) connRtsp(code string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
q := models.Camera{Code: code}
|
||||
c, err := models.CameraSelectOne(q)
|
||||
if err != nil {
|
||||
logs.Error("find camera [%s] error : %v", code, err)
|
||||
return
|
||||
}
|
||||
if c.Enabled != 1 {
|
||||
logs.Error("camera [%s] disabled : %v", code)
|
||||
return
|
||||
}
|
||||
logs.Info(c.Code, "connect", c.RtspURL)
|
||||
rtsp.DebugRtsp = true
|
||||
session, err := rtsp.Dial(c.RtspURL)
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] conn : %v", c.Code, err)
|
||||
c.OnlineStatus = 0
|
||||
time.Sleep(5 * time.Second)
|
||||
if c.OnlineStatus == 1 {
|
||||
models.CameraUpdate(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
session.RtpKeepAliveTimeout = 10 * time.Second
|
||||
codec, err := session.Streams()
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] get streams : %v", c.Code, err)
|
||||
time.Sleep(5 * time.Second)
|
||||
return
|
||||
}
|
||||
|
||||
rm := NewRtmpManager(session, c.Code, codec)
|
||||
rms.Store(c.Code, rm)
|
||||
|
||||
c.OnlineStatus = 1
|
||||
models.CameraUpdate(c)
|
||||
for {
|
||||
pkt, err := session.ReadPacket()
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] ReadPacket : %v", c.Code, err)
|
||||
break
|
||||
}
|
||||
//不能开goroutine,不能保证包的顺序
|
||||
writeChan(pkt, rm.rfPktStream, rm.rfPktDone, "rfp")
|
||||
writeChan(pkt, rm.ffPktStream, rm.ffPktDone, "ffp")
|
||||
writeChan(pkt, rm.hfPktStream, rm.hfPktDone, "hfp")
|
||||
}
|
||||
close(rm.rfPktDone)
|
||||
close(rm.ffPktDone)
|
||||
close(rm.hfPktDone)
|
||||
err = session.Close()
|
||||
if err != nil {
|
||||
logs.Error("session Close error : %v", err)
|
||||
}
|
||||
//offline camera
|
||||
q = models.Camera{Code: code}
|
||||
c, err = models.CameraSelectOne(q)
|
||||
if err != nil {
|
||||
logs.Error("find camera [%s] error : %v", code, err)
|
||||
return
|
||||
}
|
||||
c.OnlineStatus = 0
|
||||
models.CameraUpdate(c)
|
||||
|
||||
rms.Delete(c.Code)
|
||||
logs.Info("camera [%s] reconnect wait 5s", c.Code)
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func writeChan(pkt av.Packet, c chan<- av.Packet, done <-chan interface{}, t string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("writeChan panic : %v", r)
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case c <- pkt:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
// if t == "hfp" {
|
||||
// logs.Info("lose pkt %s", t)
|
||||
// }
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
|
||||
type RtspManager struct {
|
||||
conn *rtsp.Client
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
rfPktDone chan interface{}
|
||||
ffPktDone chan interface{}
|
||||
hfPktDone chan interface{}
|
||||
rfPktStream chan av.Packet
|
||||
ffPktStream chan av.Packet
|
||||
hfPktStream chan av.Packet
|
||||
}
|
||||
|
||||
func NewRtmpManager(conn *rtsp.Client, code string, codecs []av.CodecData) *RtspManager {
|
||||
rfPktDone := make(chan interface{})
|
||||
ffPktDone := make(chan interface{})
|
||||
hfPktDone := make(chan interface{})
|
||||
rfPktStream := make(chan av.Packet, 10)
|
||||
ffPktStream := make(chan av.Packet, 10)
|
||||
hfPktStream := make(chan av.Packet, 10)
|
||||
rm := &RtspManager{
|
||||
conn: conn,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
rfPktDone: rfPktDone,
|
||||
ffPktDone: ffPktDone,
|
||||
hfPktDone: hfPktDone,
|
||||
rfPktStream: rfPktStream,
|
||||
ffPktStream: ffPktStream,
|
||||
hfPktStream: hfPktStream,
|
||||
}
|
||||
go rm.pktTransfer()
|
||||
return rm
|
||||
}
|
||||
|
||||
func (rm *RtspManager) pktTransfer() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
rtmpflv.NewRtmpFlvManager(rm.rfPktDone, rm.rfPktStream, rm.code, rm.codecs)
|
||||
httpflv.NewHttpFlvManager(rm.hfPktDone, rm.hfPktStream, rm.code, rm.codecs)
|
||||
save, err := config.Bool("server.fileflv.save")
|
||||
if err != nil {
|
||||
logs.Error("get server.fileflv.save error : %v", err)
|
||||
return
|
||||
}
|
||||
if save {
|
||||
fileflv.NewFileFlvManager(rm.ffPktDone, rm.ffPktStream, rm.code, rm.codecs)
|
||||
}
|
||||
}
|
75
src/rtsp2rtmp/conf/conf.go
Normal file
75
src/rtsp2rtmp/conf/conf.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
_ "github.com/beego/beego/v2/core/config/yaml"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var mode string
|
||||
var lout string
|
||||
flag.StringVar(&lout, "lout", logs.AdapterFile, "日志输出方式,默认file")
|
||||
flag.StringVar(&mode, "mode", "prod", "启动环境,默认prod")
|
||||
// 解析命令行参数写入注册的flag里
|
||||
flag.Parse()
|
||||
|
||||
loadConf(mode)
|
||||
logConfig(lout)
|
||||
}
|
||||
|
||||
func loadConf(mode string) {
|
||||
if mode == "dev" {
|
||||
filePath := "./resources/conf/conf-dev.yml"
|
||||
err := config.InitGlobalInstance("yaml", filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("read conf file [%s] error : %v", filePath, err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
filePath := "./resources/conf/conf-prod.yml"
|
||||
err := config.InitGlobalInstance("yaml", filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("read conf file [%s] error : %v", filePath, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//日志配置
|
||||
func logConfig(lout string) {
|
||||
logs.EnableFuncCallDepth(true)
|
||||
logs.SetLogFuncCallDepth(3)
|
||||
if lout == logs.AdapterConsole {
|
||||
f := &logs.PatternLogFormatter{
|
||||
Pattern: "%w %F:%n %t %m",
|
||||
WhenFormat: "2006-01-02 15:04:05.000",
|
||||
}
|
||||
logs.RegisterFormatter("pattern", f)
|
||||
|
||||
_ = logs.SetGlobalFormatter("pattern")
|
||||
level, err := config.Int("server.log.level")
|
||||
if err != nil {
|
||||
fmt.Printf("can not get log level : %v , use default level logs.LevelInformational", err)
|
||||
level = logs.LevelInformational
|
||||
}
|
||||
logs.SetLogger(logs.AdapterConsole, `{"level":`+strconv.Itoa(level)+`,"color":true}`)
|
||||
return
|
||||
}
|
||||
logPath, err := config.String("server.log.path")
|
||||
if err != nil {
|
||||
fmt.Printf("can not get log path : %v , use default path ./resources/output/log", err)
|
||||
logPath = "./output/log"
|
||||
}
|
||||
level, err := config.Int("server.log.level")
|
||||
if err != nil {
|
||||
fmt.Printf("can not get log level : %v , use default level logs.LevelInformational", err)
|
||||
level = logs.LevelInformational
|
||||
}
|
||||
logs.SetLogger(logs.AdapterFile, `{"filename":"`+logPath+`/rtsp2rtmp.log","level":`+strconv.Itoa(level)+`,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}`)
|
||||
}
|
335
src/rtsp2rtmp/controllers/camera.go
Normal file
335
src/rtsp2rtmp/controllers/camera.go
Normal file
@@ -0,0 +1,335 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/flvmanage"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/result"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
func CameraList(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
cameras, err := models.CameraSelectAll()
|
||||
if err != nil {
|
||||
logs.Error("no camera found : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "no camera found"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
page := result.Page{Total: len(cameras), Page: cameras}
|
||||
r.Data = page
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraDetail(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
cameraId := c.Query("id")
|
||||
if cameraId == "" {
|
||||
logs.Error("no cameraId found")
|
||||
r.Code = 0
|
||||
r.Msg = "no cameraId found"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera, err := models.CameraSelectById(cameraId)
|
||||
if err != nil {
|
||||
logs.Error("no camera found : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "no camera found"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
r.Data = camera
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraEdit(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{
|
||||
Code: 1,
|
||||
Msg: "",
|
||||
}
|
||||
q := models.Camera{}
|
||||
err := c.BindJSON(&q)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
if q.Id == "" || len(q.Id) == 0 {
|
||||
id, _ := utils.UUID()
|
||||
count, err := models.CameraCountByCode(q.Code)
|
||||
if err != nil {
|
||||
logs.Error("check camera is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "check camera is exist"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
if count > 0 {
|
||||
logs.Error("camera code is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera code is exist"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
q.Id = id
|
||||
q.Created = time.Now()
|
||||
playAuthCode, _ := utils.UUID()
|
||||
q.PlayAuthCode = playAuthCode
|
||||
_, err = models.CameraInsert(q)
|
||||
if err != nil {
|
||||
logs.Error("camera insert error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera insert error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
count, err := models.CameraCountByCode(q.Code)
|
||||
if err != nil {
|
||||
logs.Error("check camera is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "check camera is exist"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
if count > 1 {
|
||||
logs.Error("camera code is exist error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera code is exist"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera, _ := models.CameraSelectById(q.Id)
|
||||
camera.Code = q.Code
|
||||
camera.RtspURL = q.RtspURL
|
||||
camera.RtmpURL = q.RtmpURL
|
||||
// camera.Enabled = q.Enabled
|
||||
_, err = models.CameraUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("camera insert error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera insert error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraDelete(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
id, b := c.Params.Get("id")
|
||||
if !b {
|
||||
r.Code = 0
|
||||
r.Msg = "id is null"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera := models.Camera{Id: id}
|
||||
_, err := models.CameraDelete(camera)
|
||||
|
||||
if err != nil {
|
||||
logs.Error("delete camera error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "delete camera error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
//close camera conn
|
||||
select {
|
||||
case codeStream <- camera.Code:
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraEnabled(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
q := models.Camera{}
|
||||
err := c.BindJSON(&q)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
camera, err := models.CameraSelectById(q.Id)
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "query camera error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera.Enabled = q.Enabled
|
||||
_, err = models.CameraUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("enabled camera status %d error : %v", camera.Enabled, err)
|
||||
r.Code = 0
|
||||
r.Msg = "enabled camera status %d error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
if q.Enabled != 1 {
|
||||
//close camera conn
|
||||
select {
|
||||
case codeStream <- camera.Code:
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraSaveVideoChange(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
q := models.Camera{}
|
||||
err := c.BindJSON(&q)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
camera, err := models.CameraSelectById(q.Id)
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "query camera error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera.SaveVideo = q.SaveVideo
|
||||
_, err = models.CameraUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("enabled camera status %d error : %v", camera.Enabled, err)
|
||||
r.Code = 0
|
||||
r.Msg = "enabled camera status %d error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case q.Enabled != 1:
|
||||
|
||||
case q.SaveVideo == 1:
|
||||
flvmanage.GetSingleFileFlvManager().StartWrite(q.Code)
|
||||
logs.Info("camera [%s] start save video", q.Code)
|
||||
default:
|
||||
logs.Info("camera [%s] stop save video", q.Code)
|
||||
flvmanage.GetSingleFileFlvManager().StopWrite(q.Code)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraLiveChange(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
q := models.Camera{}
|
||||
err := c.BindJSON(&q)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
camera, err := models.CameraSelectById(q.Id)
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "query camera error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera.Live = q.Live
|
||||
_, err = models.CameraUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("enabled camera status %d error : %v", camera.Enabled, err)
|
||||
r.Code = 0
|
||||
r.Msg = "enabled camera status %d error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case q.Enabled != 1:
|
||||
|
||||
case q.Live == 1:
|
||||
flvmanage.GetSingleHttpflvAdmin().StartWrite(q.Code)
|
||||
default:
|
||||
flvmanage.GetSingleHttpflvAdmin().StopWrite(q.Code)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraPlayAuthCodeReset(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
q := models.Camera{}
|
||||
err := c.BindJSON(&q)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
camera, err := models.CameraSelectById(q.Id)
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "query camera error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
playAuthCode, _ := utils.UUID()
|
||||
camera.PlayAuthCode = playAuthCode
|
||||
_, err = models.CameraUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("enabled camera status %d error : %v", camera.Enabled, err)
|
||||
r.Code = 0
|
||||
r.Msg = "enabled camera status %d error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case q.Enabled != 1:
|
||||
|
||||
case q.Live == 1:
|
||||
flvmanage.GetSingleHttpflvAdmin().StopWrite(q.Code)
|
||||
flvmanage.GetSingleHttpflvAdmin().StartWrite(q.Code)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
var codeStream = make(chan string)
|
||||
|
||||
func CodeStream() <-chan string {
|
||||
return codeStream
|
||||
}
|
152
src/rtsp2rtmp/controllers/camerashare.go
Normal file
152
src/rtsp2rtmp/controllers/camerashare.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/result"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
func CameraShareList(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
cameraId := c.Query("cameraId")
|
||||
if cameraId == "" {
|
||||
logs.Error("no cameraId found")
|
||||
r.Code = 0
|
||||
r.Msg = "no cameraId found"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
cameraShares, err := models.CameraShareSelectByCameraId(cameraId)
|
||||
if err != nil {
|
||||
logs.Error("no camerashare found : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "no camerashare found"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
page := result.Page{Total: len(cameraShares), Page: cameraShares}
|
||||
r.Data = page
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraShareEdit(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{
|
||||
Code: 1,
|
||||
Msg: "",
|
||||
}
|
||||
q := models.CameraShare{}
|
||||
err := c.BindJSON(&q)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = models.CameraSelectById(q.CameraId)
|
||||
if err != nil {
|
||||
logs.Error("not fount camera : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "not fount camera"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
if q.Id == "" || len(q.Id) == 0 {
|
||||
id, _ := utils.UUID()
|
||||
q.Id = id
|
||||
q.Created = time.Now()
|
||||
playAuthCode, _ := utils.UUID()
|
||||
q.AuthCode = playAuthCode
|
||||
_, err = models.CameraShareInsert(q)
|
||||
if err != nil {
|
||||
logs.Error("camerashare insert error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camerashare insert error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
cameraShare, _ := models.CameraShareSelectById(q.Id)
|
||||
cameraShare.Name = q.Name
|
||||
cameraShare.StartTime = q.StartTime
|
||||
cameraShare.Deadline = q.Deadline
|
||||
// camera.Enabled = q.Enabled
|
||||
_, err = models.CameraShareUpdate(cameraShare)
|
||||
if err != nil {
|
||||
logs.Error("camerashare insert error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camerashare insert error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraShareDelete(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
id, b := c.Params.Get("id")
|
||||
if !b {
|
||||
r.Code = 0
|
||||
r.Msg = "id is null"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera := models.CameraShare{Id: id}
|
||||
_, err := models.CameraShareDelete(camera)
|
||||
|
||||
if err != nil {
|
||||
logs.Error("delete camerashare error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "delete camerashare error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
func CameraShareEnabled(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
r := result.Result{Code: 1, Msg: ""}
|
||||
q := models.CameraShare{}
|
||||
err := c.BindJSON(&q)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
camera, err := models.CameraShareSelectById(q.Id)
|
||||
if err != nil {
|
||||
logs.Error("query camerashare error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "query camerashare error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
camera.Enabled = q.Enabled
|
||||
_, err = models.CameraShareUpdate(camera)
|
||||
if err != nil {
|
||||
logs.Error("enabled camerashare status %d error : %v", camera.Enabled, err)
|
||||
r.Code = 0
|
||||
r.Msg = "enabled camerashare status %d error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
109
src/rtsp2rtmp/controllers/httpflv.go
Normal file
109
src/rtsp2rtmp/controllers/httpflv.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/flvmanage"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/result"
|
||||
)
|
||||
|
||||
func HttpFlvPlay(c *gin.Context) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Connection", "keep-alive")
|
||||
uri := strings.TrimSuffix(strings.TrimLeft(c.Request.RequestURI, "/"), ".flv")
|
||||
uris := strings.Split(uri, "/")
|
||||
if len(uris) < 3 || uris[0] != "live" {
|
||||
http.Error(c.Writer, "invalid path", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
method := uris[1]
|
||||
code := uris[2]
|
||||
authCode := uris[3]
|
||||
r := result.Result{
|
||||
Code: 1,
|
||||
Msg: "",
|
||||
}
|
||||
q := models.Camera{Code: code}
|
||||
camera, err := models.CameraSelectOne(q)
|
||||
if err != nil {
|
||||
logs.Error("camera query error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "camera query error"
|
||||
c.JSON(http.StatusBadRequest, r)
|
||||
return
|
||||
}
|
||||
if !(method == "temp" || method == "permanent") {
|
||||
logs.Error("method error : %s", method)
|
||||
r.Code = 0
|
||||
r.Msg = "method error"
|
||||
c.JSON(http.StatusBadRequest, r)
|
||||
return
|
||||
}
|
||||
if method == "temp" {
|
||||
csq := models.CameraShare{CameraId: camera.Id, AuthCode: authCode}
|
||||
cs, err := models.CameraShareSelectOne(csq)
|
||||
if err != nil {
|
||||
logs.Error("CameraShareSelectOne error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "system error"
|
||||
c.JSON(http.StatusBadRequest, r)
|
||||
return
|
||||
}
|
||||
if time.Now().After(cs.Created.Add(7 * 24 * time.Hour)) {
|
||||
logs.Error("camera [%s] AuthCodeTemp expired : %s", camera.Code, authCode)
|
||||
r.Code = 0
|
||||
r.Msg = "authCode expired"
|
||||
c.JSON(http.StatusBadRequest, r)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
if method == "permanent" && authCode != camera.PlayAuthCode {
|
||||
logs.Error("AuthCodePermanent error : %s", authCode)
|
||||
r.Code = 0
|
||||
r.Msg = "authCode error"
|
||||
c.JSON(http.StatusBadRequest, r)
|
||||
return
|
||||
}
|
||||
|
||||
logs.Info("player [%s] addr [%s] connecting", code, c.Request.RemoteAddr)
|
||||
//管理员可以主动中断播放
|
||||
playerDone := make(chan interface{})
|
||||
defer close(playerDone)
|
||||
const timeout = 10 * time.Second
|
||||
heartbeatStream, err := flvmanage.GetSingleHttpflvAdmin().AddHttpFlvPlayer(playerDone, timeout/2, code, c.Writer)
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] add player error : %s", code, err)
|
||||
r.Code = 0
|
||||
r.Msg = "add player error"
|
||||
c.JSON(http.StatusBadRequest, r)
|
||||
return
|
||||
}
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-heartbeatStream:
|
||||
if ok {
|
||||
logs.Debug("heartbeat")
|
||||
continue
|
||||
}
|
||||
logs.Info("heartbeatStream closed")
|
||||
break Loop
|
||||
case <-time.After(timeout):
|
||||
logs.Info("player [%s] addr [%s] timeout exit", code, c.Request.RemoteAddr)
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
logs.Info("player [%s] addr [%s] exit", code, c.Request.RemoteAddr)
|
||||
}
|
183
src/rtsp2rtmp/fileflvwriter/fileflvwriter.go
Normal file
183
src/rtsp2rtmp/fileflvwriter/fileflvwriter.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package fileflvwriter
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/flv"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
type IFileFlvManager interface {
|
||||
UpdateFFWS(string, *FileFlvWriter)
|
||||
}
|
||||
|
||||
type FileFlvWriter struct {
|
||||
selfDone chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
isStart bool
|
||||
fd *os.File
|
||||
iffm IFileFlvManager
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) GetPktStream() <-chan av.Packet {
|
||||
return ffw.pktStream
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) GetCodecs() []av.CodecData {
|
||||
return ffw.codecs
|
||||
}
|
||||
|
||||
func NewFileFlvWriter(
|
||||
pktStream <-chan av.Packet,
|
||||
code string,
|
||||
codecs []av.CodecData,
|
||||
iffm IFileFlvManager,
|
||||
) *FileFlvWriter {
|
||||
|
||||
ffw := &FileFlvWriter{
|
||||
selfDone: make(chan interface{}, 10),
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
iffm: iffm,
|
||||
isStart: false,
|
||||
}
|
||||
camera, err := models.CameraSelectOne(models.Camera{Code: code})
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
return ffw
|
||||
}
|
||||
if camera.OnlineStatus != 1 {
|
||||
return ffw
|
||||
}
|
||||
if camera.SaveVideo != 1 {
|
||||
return ffw
|
||||
}
|
||||
go ffw.flvWrite()
|
||||
go ffw.splitFile()
|
||||
return ffw
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) StopWrite() {
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
//有多个地方监听seleDone,需要写入多次才能退出多个goroutine
|
||||
for i := 0; i < 10; i++ {
|
||||
select {
|
||||
case ffw.selfDone <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) splitFile() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-ffw.selfDone:
|
||||
return
|
||||
case <-time.After(1 * time.Hour):
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
//有多个地方监听seleDone,需要写入多次才能退出多个goroutine
|
||||
for i := 0; i < 10; i++ {
|
||||
select {
|
||||
case ffw.selfDone <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
ffw.fd.Close()
|
||||
}()
|
||||
ffwn := NewFileFlvWriter(ffw.pktStream, ffw.code, ffw.codecs, ffw.iffm)
|
||||
ffwn.iffm.UpdateFFWS(ffwn.code, ffwn)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) Write(p []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
n, err = ffw.fd.Write(p)
|
||||
if err != nil {
|
||||
logs.Error("write file error : %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) createFlvFile() error {
|
||||
fd, err := os.OpenFile(getFileFlvPath()+"/"+ffw.code+"_"+time.Now().Format("20060102150405")+".flv", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
logs.Error("open file error :", err)
|
||||
return err
|
||||
}
|
||||
ffw.fd = fd
|
||||
return nil
|
||||
}
|
||||
|
||||
//Write extends to writer.Writer
|
||||
func (ffw *FileFlvWriter) flvWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
if err := ffw.createFlvFile(); err != nil {
|
||||
logs.Error("create file flv error : %v", err)
|
||||
return
|
||||
}
|
||||
defer close(ffw.selfDone)
|
||||
defer ffw.fd.Close()
|
||||
muxer := flv.NewMuxer(ffw)
|
||||
for pkt := range utils.OrDonePacket(ffw.selfDone, ffw.pktStream) {
|
||||
if ffw.isStart {
|
||||
if err := muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to flv file error : %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if pkt.IsKeyFrame {
|
||||
err := muxer.WriteHeader(ffw.codecs)
|
||||
if err != nil {
|
||||
logs.Error("writer header to flv file error : %v", err)
|
||||
}
|
||||
if err := muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to flv file error : %v", err)
|
||||
}
|
||||
ffw.isStart = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFileFlvPath() string {
|
||||
fileFlvPath, err := config.String("server.fileflv.path")
|
||||
if err != nil {
|
||||
logs.Error("get fileflv path error :", err)
|
||||
return ""
|
||||
}
|
||||
return fileFlvPath
|
||||
}
|
51
src/rtsp2rtmp/flvmanage/fileflvmanager.go
Normal file
51
src/rtsp2rtmp/flvmanage/fileflvmanager.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package flvmanage
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/fileflvwriter"
|
||||
)
|
||||
|
||||
type fileFlvManager struct {
|
||||
ffws sync.Map
|
||||
}
|
||||
|
||||
var ffmInstance *fileFlvManager
|
||||
|
||||
func init() {
|
||||
ffmInstance = &fileFlvManager{}
|
||||
}
|
||||
|
||||
func GetSingleFileFlvManager() *fileFlvManager {
|
||||
return ffmInstance
|
||||
}
|
||||
|
||||
func (ffm *fileFlvManager) FlvWrite(pktStream <-chan av.Packet, code string, codecs []av.CodecData) {
|
||||
ffw := fileflvwriter.NewFileFlvWriter(pktStream, code, codecs, ffm)
|
||||
ffm.ffws.Store(code, ffw)
|
||||
}
|
||||
|
||||
func (ffm *fileFlvManager) StopWrite(code string) {
|
||||
v, ok := ffm.ffws.Load(code)
|
||||
if ok {
|
||||
ffw := v.(*fileflvwriter.FileFlvWriter)
|
||||
ffw.StopWrite()
|
||||
}
|
||||
}
|
||||
|
||||
func (ffm *fileFlvManager) StartWrite(code string) {
|
||||
v, ok := ffm.ffws.Load(code)
|
||||
if ok {
|
||||
ffw := v.(*fileflvwriter.FileFlvWriter)
|
||||
ffw.StopWrite()
|
||||
ffm.FlvWrite(ffw.GetPktStream(), code, ffw.GetCodecs())
|
||||
}
|
||||
}
|
||||
|
||||
func (ffm *fileFlvManager) UpdateFFWS(code string, ffw *fileflvwriter.FileFlvWriter) {
|
||||
_, ok := ffm.ffws.LoadAndDelete(code)
|
||||
if ok {
|
||||
ffm.ffws.Store(code, ffw)
|
||||
}
|
||||
}
|
71
src/rtsp2rtmp/flvmanage/httpflvadmin.go
Normal file
71
src/rtsp2rtmp/flvmanage/httpflvadmin.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package flvmanage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/httpflvmanage"
|
||||
)
|
||||
|
||||
var hfas *HttpflvAdmin
|
||||
|
||||
type HttpflvAdmin struct {
|
||||
hfms sync.Map
|
||||
}
|
||||
|
||||
func init() {
|
||||
hfas = &HttpflvAdmin{}
|
||||
}
|
||||
|
||||
func GetSingleHttpflvAdmin() *HttpflvAdmin {
|
||||
return hfas
|
||||
}
|
||||
|
||||
// func (hfa *HttpflvAdmin) ExistsHttpFlvManager(code string) bool {
|
||||
// _, b := hfa.hfms.Load(code)
|
||||
// return b
|
||||
// }
|
||||
|
||||
func (hfa *HttpflvAdmin) AddHttpFlvManager(
|
||||
pktStream <-chan av.Packet,
|
||||
code string,
|
||||
codecs []av.CodecData,
|
||||
) {
|
||||
hfm := httpflvmanage.NewHttpFlvManager(pktStream, code, codecs)
|
||||
hfa.hfms.Store(code, hfm)
|
||||
}
|
||||
|
||||
func (hfa *HttpflvAdmin) StopWrite(code string) {
|
||||
v, ok := hfa.hfms.Load(code)
|
||||
if ok {
|
||||
ffw := v.(*httpflvmanage.HttpFlvManager)
|
||||
ffw.StopWrite()
|
||||
}
|
||||
}
|
||||
|
||||
func (hfa *HttpflvAdmin) StartWrite(code string) {
|
||||
v, ok := hfa.hfms.Load(code)
|
||||
if ok {
|
||||
ffw := v.(*httpflvmanage.HttpFlvManager)
|
||||
ffw.StopWrite()
|
||||
hfa.AddHttpFlvManager(ffw.GetPktStream(), code, ffw.GetCodecs())
|
||||
}
|
||||
}
|
||||
|
||||
//添加播放者
|
||||
func (hfa *HttpflvAdmin) AddHttpFlvPlayer(
|
||||
playerDone <-chan interface{},
|
||||
pulseInterval time.Duration,
|
||||
code string,
|
||||
writer io.Writer,
|
||||
) (<-chan interface{}, error) {
|
||||
v, b := hfa.hfms.Load(code)
|
||||
if b {
|
||||
hfm := v.(*httpflvmanage.HttpFlvManager)
|
||||
return hfm.AddHttpFlvPlayer(playerDone, pulseInterval, writer)
|
||||
}
|
||||
return nil, errors.New("camera no connection")
|
||||
}
|
51
src/rtsp2rtmp/flvmanage/rtmpflvmanager.go
Normal file
51
src/rtsp2rtmp/flvmanage/rtmpflvmanager.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package flvmanage
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/rtmpflvwriter"
|
||||
)
|
||||
|
||||
type rtmpFlvManager struct {
|
||||
rfms sync.Map
|
||||
}
|
||||
|
||||
var rfmInstance *rtmpFlvManager
|
||||
|
||||
func init() {
|
||||
rfmInstance = &rtmpFlvManager{}
|
||||
}
|
||||
|
||||
func GetSingleRtmpFlvManager() *rtmpFlvManager {
|
||||
return rfmInstance
|
||||
}
|
||||
|
||||
func (rfm *rtmpFlvManager) FlvWrite(pktStream <-chan av.Packet, code string, codecs []av.CodecData) {
|
||||
ffw := rtmpflvwriter.NewRtmpFlvWriter(pktStream, code, codecs, rfm)
|
||||
rfm.rfms.Store(code, ffw)
|
||||
}
|
||||
|
||||
func (rfm *rtmpFlvManager) StopWrite(code string) {
|
||||
v, ok := rfm.rfms.Load(code)
|
||||
if ok {
|
||||
ffw := v.(*rtmpflvwriter.RtmpFlvWriter)
|
||||
ffw.StopWrite()
|
||||
}
|
||||
}
|
||||
|
||||
func (rfm *rtmpFlvManager) StartWrite(code string) {
|
||||
v, ok := rfm.rfms.Load(code)
|
||||
if ok {
|
||||
ffw := v.(*rtmpflvwriter.RtmpFlvWriter)
|
||||
ffw.StopWrite()
|
||||
rfm.FlvWrite(ffw.GetPktStream(), code, ffw.GetCodecs())
|
||||
}
|
||||
}
|
||||
|
||||
func (rfm *rtmpFlvManager) UpdateFFWS(code string, rfw *rtmpflvwriter.RtmpFlvWriter) {
|
||||
_, ok := rfm.rfms.LoadAndDelete(code)
|
||||
if ok {
|
||||
rfm.rfms.Store(code, rfw)
|
||||
}
|
||||
}
|
109
src/rtsp2rtmp/httpflvmanage/httpflvmanager.go
Normal file
109
src/rtsp2rtmp/httpflvmanage/httpflvmanager.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package httpflvmanage
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/httpflvwriter"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
type HttpFlvManager struct {
|
||||
selfDone chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
hfws sync.Map
|
||||
}
|
||||
|
||||
func (ffw *HttpFlvManager) GetPktStream() <-chan av.Packet {
|
||||
return ffw.pktStream
|
||||
}
|
||||
|
||||
func (ffw *HttpFlvManager) GetCodecs() []av.CodecData {
|
||||
return ffw.codecs
|
||||
}
|
||||
|
||||
func NewHttpFlvManager(pktStream <-chan av.Packet, code string, codecs []av.CodecData) *HttpFlvManager {
|
||||
hfm := &HttpFlvManager{
|
||||
selfDone: make(chan interface{}, 10),
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
}
|
||||
camera, err := models.CameraSelectOne(models.Camera{Code: code})
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
return hfm
|
||||
}
|
||||
if camera.OnlineStatus != 1 {
|
||||
return hfm
|
||||
}
|
||||
if camera.Live != 1 {
|
||||
return hfm
|
||||
}
|
||||
go hfm.flvWrite()
|
||||
return hfm
|
||||
}
|
||||
|
||||
func (hfm *HttpFlvManager) StopWrite() {
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
//有多个地方监听seleDone,需要写入多次才能退出多个goroutine
|
||||
for i := 0; i < 10; i++ {
|
||||
select {
|
||||
case hfm.selfDone <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//Write extends to writer.Writer
|
||||
func (hfm *HttpFlvManager) flvWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for pkt := range utils.OrDonePacket(hfm.selfDone, hfm.pktStream) {
|
||||
hfm.hfws.Range(func(key, value interface{}) bool {
|
||||
wi := value.(*httpflvwriter.HttpFlvWriter)
|
||||
select {
|
||||
case wi.GetPktStream() <- pkt:
|
||||
// logs.Debug("flvWrite pkt")
|
||||
default:
|
||||
//当播放者速率跟不上时,会发生丢包
|
||||
logs.Debug("camera [%s] http flv sessionId [%s] write fail", hfm.code, wi.GetSessionId())
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//添加播放者
|
||||
func (hfm *HttpFlvManager) AddHttpFlvPlayer(
|
||||
playerDone <-chan interface{},
|
||||
pulseInterval time.Duration,
|
||||
writer io.Writer,
|
||||
) (<-chan interface{}, error) {
|
||||
sessionId := utils.NextValSnowflakeID()
|
||||
//添加缓冲,减少包到达速率震荡导致丢包
|
||||
pktStream := make(chan av.Packet, 1024)
|
||||
hfw := httpflvwriter.NewHttpFlvWriter(playerDone, pulseInterval, pktStream, hfm.code, hfm.codecs, writer, sessionId, hfm)
|
||||
hfm.hfws.Store(sessionId, hfw)
|
||||
return hfw.GetPlayerHeartbeatStream(), nil
|
||||
}
|
||||
|
||||
func (hfm *HttpFlvManager) DeleteHFW(sesessionId int64) {
|
||||
hfm.hfws.LoadAndDelete(sesessionId)
|
||||
}
|
225
src/rtsp2rtmp/httpflvwriter/httpflvwriter.go
Normal file
225
src/rtsp2rtmp/httpflvwriter/httpflvwriter.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package httpflvwriter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/flv"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
type IHttpFlvManager interface {
|
||||
DeleteHFW(sesessionId int64)
|
||||
}
|
||||
|
||||
type HttpFlvWriter struct {
|
||||
sessionId int64
|
||||
pulseInterval time.Duration
|
||||
pktStream chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
start bool
|
||||
writer io.Writer
|
||||
muxer *flv.Muxer
|
||||
playerDone <-chan interface{} //来自播放者的关闭
|
||||
heartbeatStream chan interface{} //心跳包通道
|
||||
playerHeartbeatStream <-chan interface{}
|
||||
selfHeartbeatStream <-chan interface{}
|
||||
ihfm IHttpFlvManager
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) SetCode(code string) {
|
||||
hfw.code = code
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) SetCodecs(codecs []av.CodecData) {
|
||||
hfw.codecs = codecs
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) GetPktStream() chan<- av.Packet {
|
||||
return hfw.pktStream
|
||||
}
|
||||
|
||||
// func (hfw *HttpFlvWriter) GetReadPktStream() <-chan av.Packet {
|
||||
// return hfw.pktStream
|
||||
// }
|
||||
|
||||
func (hfw *HttpFlvWriter) GetSessionId() int64 {
|
||||
return hfw.sessionId
|
||||
}
|
||||
|
||||
func NewHttpFlvWriter(
|
||||
playerDone <-chan interface{},
|
||||
pulseInterval time.Duration,
|
||||
pktStream chan av.Packet,
|
||||
code string,
|
||||
codecs []av.CodecData,
|
||||
writer io.Writer,
|
||||
sessionId int64,
|
||||
ihfm IHttpFlvManager,
|
||||
) *HttpFlvWriter {
|
||||
heartbeatStream := make(chan interface{})
|
||||
playerHeartbeatStream, selfHeartbeatStream := utils.Tee(playerDone, heartbeatStream)
|
||||
hfw := &HttpFlvWriter{
|
||||
sessionId: sessionId,
|
||||
pulseInterval: pulseInterval,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
writer: writer,
|
||||
playerDone: playerDone,
|
||||
heartbeatStream: heartbeatStream,
|
||||
playerHeartbeatStream: playerHeartbeatStream,
|
||||
selfHeartbeatStream: selfHeartbeatStream,
|
||||
ihfm: ihfm,
|
||||
start: false,
|
||||
}
|
||||
|
||||
camera, err := models.CameraSelectOne(models.Camera{Code: code})
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
return hfw
|
||||
}
|
||||
if camera.OnlineStatus != 1 {
|
||||
return hfw
|
||||
}
|
||||
if camera.Live != 1 {
|
||||
return hfw
|
||||
}
|
||||
|
||||
go hfw.httpWrite()
|
||||
go hfw.monitor()
|
||||
return hfw
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) GetPlayerHeartbeatStream() <-chan interface{} {
|
||||
return hfw.playerHeartbeatStream
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) httpWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
pulse := time.NewTicker(hfw.pulseInterval).C
|
||||
|
||||
sendPulse := func() {
|
||||
select {
|
||||
case hfw.heartbeatStream <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
sendPulse()
|
||||
done := make(chan interface{})
|
||||
go func(done <-chan interface{}) {
|
||||
defer func() {
|
||||
close(hfw.heartbeatStream)
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
writer := hfw.writer.(gin.ResponseWriter)
|
||||
for {
|
||||
select {
|
||||
case <-writer.CloseNotify():
|
||||
return
|
||||
case <-pulse:
|
||||
sendPulse()
|
||||
case <-done:
|
||||
return
|
||||
case <-hfw.playerDone:
|
||||
return
|
||||
}
|
||||
}
|
||||
}(done)
|
||||
|
||||
ticker := time.NewTicker(hfw.pulseInterval)
|
||||
pktStream := utils.OrDonePacket(hfw.playerDone, hfw.pktStream)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
close(done)
|
||||
return
|
||||
case pkt := <-pktStream:
|
||||
if err := hfw.writerPacket(pkt); err != nil {
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
ticker.Reset(hfw.pulseInterval)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
func (hfw *HttpFlvWriter) writerPacket(pkt av.Packet) error {
|
||||
if hfw.start {
|
||||
if err := hfw.muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to httpflv error : %v", err)
|
||||
return err
|
||||
}
|
||||
// logs.Debug("httpWrite")
|
||||
return nil
|
||||
}
|
||||
if pkt.IsKeyFrame {
|
||||
muxer := flv.NewMuxer(hfw)
|
||||
hfw.muxer = muxer
|
||||
err := hfw.muxer.WriteHeader(hfw.codecs)
|
||||
if err != nil {
|
||||
logs.Error("writer header to httpflv error : %v", err)
|
||||
return err
|
||||
}
|
||||
hfw.start = true
|
||||
if err := hfw.muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to httpflv error : %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) monitor() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-hfw.selfHeartbeatStream:
|
||||
if ok {
|
||||
logs.Debug("heartbeat")
|
||||
continue
|
||||
}
|
||||
hfw.ihfm.DeleteHFW(hfw.sessionId)
|
||||
return
|
||||
case <-time.After(2 * hfw.pulseInterval):
|
||||
//time out
|
||||
hfw.ihfm.DeleteHFW(hfw.sessionId)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Write extends to io.Writer
|
||||
func (hfw *HttpFlvWriter) Write(p []byte) (n int, err error) {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
logs.Debug(time.Since(start))
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
// logs.Debug("write pkt")
|
||||
n, err = hfw.writer.Write(p)
|
||||
if err != nil {
|
||||
logs.Error("write httpflv error : %v", err)
|
||||
}
|
||||
return
|
||||
}
|
@@ -7,26 +7,23 @@ import (
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 需要在init中注册定义的model
|
||||
orm.RegisterModel(new(Camera))
|
||||
}
|
||||
|
||||
type Camera struct {
|
||||
Id string `orm:"pk;column(id)" json:"id"`
|
||||
Code string `orm:"column(code)" json:"code"`
|
||||
RtspURL string `orm:"column(rtsp_url)" json:"rtspURL"`
|
||||
RtmpURL string `orm:"column(rtmp_url)" json:"rtmpURL"`
|
||||
AuthCode string `orm:"column(auth_code)" json:"authCode"`
|
||||
PlayAuthCode string `orm:"column(play_auth_code)" json:"playAuthCode"`
|
||||
OnlineStatus int `orm:"column(online_status)" json:"onlineStatus"`
|
||||
Enabled int `orm:"column(enabled)" json:"enabled"`
|
||||
SaveVideo int `orm:"column(save_video)" json:"saveVideo"`
|
||||
Live int `orm:"column(live)" json:"live"`
|
||||
Created time.Time `orm:"column(created)" json:"created"`
|
||||
}
|
||||
|
||||
func CameraInsert(e Camera) (i int64, err error) {
|
||||
o := orm.NewOrm()
|
||||
i, err = o.Insert(&e)
|
||||
if err != nil {
|
||||
if err != nil && err != orm.ErrLastInsertIdUnavailable {
|
||||
logs.Error("camera insert error : %v", err)
|
||||
return i, err
|
||||
}
|
111
src/rtsp2rtmp/models/camerashare.go
Normal file
111
src/rtsp2rtmp/models/camerashare.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
type CameraShare struct {
|
||||
Id string `orm:"pk;column(id)" json:"id"`
|
||||
CameraId string `orm:"column(camera_id)" json:"cameraId"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
AuthCode string `orm:"column(auth_code)" json:"authCode"`
|
||||
Enabled int `orm:"column(enabled)" json:"enabled"`
|
||||
Created time.Time `orm:"column(created)" json:"created"`
|
||||
StartTime time.Time `orm:"column(start_time)" json:"startTime"`
|
||||
Deadline time.Time `orm:"column(deadline)" json:"deadline"`
|
||||
}
|
||||
|
||||
func CameraShareInsert(e CameraShare) (i int64, err error) {
|
||||
o := orm.NewOrm()
|
||||
i, err = o.Insert(&e)
|
||||
if err != nil && err != orm.ErrLastInsertIdUnavailable {
|
||||
logs.Error("camera insert error : %v", err)
|
||||
return i, err
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func CameraShareDelete(e CameraShare) (i int64, err error) {
|
||||
o := orm.NewOrm()
|
||||
i, err = o.Delete(&e)
|
||||
if err != nil {
|
||||
logs.Error("camera delete error : %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func CameraShareUpdate(e CameraShare) (i int64, err error) {
|
||||
o := orm.NewOrm()
|
||||
i, err = o.Update(&e)
|
||||
if err != nil {
|
||||
logs.Error("camera update error : %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func CameraShareSelectById(id string) (e CameraShare, err error) {
|
||||
o := orm.NewOrm()
|
||||
e = CameraShare{Id: id}
|
||||
|
||||
err = o.Read(&e)
|
||||
|
||||
if err == orm.ErrNoRows {
|
||||
logs.Error("查询不到")
|
||||
return e, err
|
||||
} else if err == orm.ErrMissPK {
|
||||
logs.Error("找不到主键")
|
||||
return e, err
|
||||
} else if err != nil {
|
||||
logs.Error("错误: %v", err)
|
||||
return e, err
|
||||
} else {
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
func CameraShareSelectOne(q CameraShare) (e CameraShare, err error) {
|
||||
o := orm.NewOrm()
|
||||
err = o.QueryTable(new(CameraShare)).Filter("CameraId", q.CameraId).Filter("AuthCode", q.AuthCode).One(&e)
|
||||
if err != nil {
|
||||
logs.Error("查询出错:%v", err)
|
||||
return e, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func CameraShareCountByCode(code string) (count int64, err error) {
|
||||
o := orm.NewOrm()
|
||||
count, err = o.QueryTable(new(CameraShare)).Filter("code", code).Count()
|
||||
if err != nil {
|
||||
logs.Error("查询出错:%v", err)
|
||||
return count, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func CameraShareSelectAll() (es []CameraShare, err error) {
|
||||
o := orm.NewOrm()
|
||||
num, err := o.QueryTable(new(CameraShare)).All(&es)
|
||||
if err != nil {
|
||||
logs.Error("查询出错:%v", err)
|
||||
return es, err
|
||||
}
|
||||
logs.Info("查询到%d条记录", num)
|
||||
return es, nil
|
||||
}
|
||||
|
||||
func CameraShareSelectByCameraId(cameraId string) (es []CameraShare, err error) {
|
||||
o := orm.NewOrm()
|
||||
num, err := o.QueryTable(new(CameraShare)).Filter("CameraId", cameraId).All(&es)
|
||||
if err != nil {
|
||||
logs.Error("查询出错:%v", err)
|
||||
return es, err
|
||||
}
|
||||
logs.Info("查询到%d条记录", num)
|
||||
return es, nil
|
||||
}
|
43
src/rtsp2rtmp/models/models.go
Normal file
43
src/rtsp2rtmp/models/models.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
driver, err := config.String("server.database.driver")
|
||||
if err != nil {
|
||||
logs.Error("database driver param is null")
|
||||
return
|
||||
}
|
||||
url, err := config.String("server.database.url")
|
||||
if err != nil {
|
||||
logs.Error("database url param is null")
|
||||
return
|
||||
}
|
||||
driveType, err := config.Int("server.database.driver-type")
|
||||
if err != nil {
|
||||
logs.Error("database driver-type param is null")
|
||||
return
|
||||
}
|
||||
showSql, err := config.Bool("server.database.show-sql")
|
||||
if err != nil {
|
||||
logs.Error("database show-sql param error : %v", err)
|
||||
}
|
||||
if showSql {
|
||||
orm.Debug = showSql
|
||||
}
|
||||
logs.Info("user database %v", driver)
|
||||
orm.RegisterDriver(driver, orm.DriverType(driveType))
|
||||
orm.RegisterDataBase("default", driver, url)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// 需要在init中注册定义的model
|
||||
orm.RegisterModel(new(Camera))
|
||||
orm.RegisterModel(new(CameraShare))
|
||||
}
|
205
src/rtsp2rtmp/rtmpflvwriter/rtmpflvwriter.go
Normal file
205
src/rtsp2rtmp/rtmpflvwriter/rtmpflvwriter.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package rtmpflvwriter
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/rtmp"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
type IRtmpFlvManager interface {
|
||||
UpdateFFWS(string, *RtmpFlvWriter)
|
||||
}
|
||||
|
||||
type RtmpFlvWriter struct {
|
||||
selfDone chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
start bool
|
||||
conn *rtmp.Conn
|
||||
pulseInterval time.Duration
|
||||
heartbeatStream chan interface{}
|
||||
irfm IRtmpFlvManager
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) GetPktStream() <-chan av.Packet {
|
||||
return rfw.pktStream
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) GetCodecs() []av.CodecData {
|
||||
return rfw.codecs
|
||||
}
|
||||
|
||||
func NewRtmpFlvWriter(pktStream <-chan av.Packet, code string, codecs []av.CodecData, irfm IRtmpFlvManager) *RtmpFlvWriter {
|
||||
rfw := &RtmpFlvWriter{
|
||||
selfDone: make(chan interface{}, 10),
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
start: false,
|
||||
pulseInterval: 5 * time.Second,
|
||||
heartbeatStream: make(chan interface{}),
|
||||
irfm: irfm,
|
||||
}
|
||||
go rfw.flvWrite()
|
||||
go rfw.monitor()
|
||||
return rfw
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) StopWrite() {
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
//有多个地方监听seleDone,需要写入多次才能退出多个goroutine
|
||||
for i := 0; i < 10; i++ {
|
||||
select {
|
||||
case rfw.selfDone <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) monitor() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-rfw.selfDone:
|
||||
return
|
||||
case _, ok := <-rfw.heartbeatStream:
|
||||
if ok {
|
||||
logs.Debug("heartbeat")
|
||||
continue
|
||||
}
|
||||
rfwn := NewRtmpFlvWriter(rfw.pktStream, rfw.code, rfw.codecs, rfw.irfm)
|
||||
rfwn.irfm.UpdateFFWS(rfwn.code, rfwn)
|
||||
return
|
||||
case <-time.After(2 * rfw.pulseInterval):
|
||||
//time out
|
||||
rfwn := NewRtmpFlvWriter(rfw.pktStream, rfw.code, rfw.codecs, rfw.irfm)
|
||||
rfwn.irfm.UpdateFFWS(rfwn.code, rfwn)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) createConn() error {
|
||||
var camera models.Camera
|
||||
camera.Code = rfw.code
|
||||
camera, err := models.CameraSelectOne(camera)
|
||||
if err != nil {
|
||||
logs.Error("not found camera : %s", rfw.code)
|
||||
return err
|
||||
}
|
||||
rtmpConn, err := rtmp.Dial(camera.RtmpURL)
|
||||
if err != nil {
|
||||
logs.Error("rtmp client connection error : %v", err)
|
||||
return err
|
||||
}
|
||||
rfw.conn = rtmpConn
|
||||
return nil
|
||||
}
|
||||
|
||||
//Write extends to writer.Writer
|
||||
func (rfw *RtmpFlvWriter) flvWrite() {
|
||||
defer func() {
|
||||
close(rfw.heartbeatStream)
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
pulse := time.NewTicker(rfw.pulseInterval).C
|
||||
|
||||
sendPulse := func() {
|
||||
select {
|
||||
case rfw.heartbeatStream <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
sendPulse()
|
||||
done := make(chan interface{}, 10)
|
||||
go func(done <-chan interface{}) {
|
||||
defer func() {
|
||||
close(rfw.heartbeatStream)
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-pulse:
|
||||
sendPulse()
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}(done)
|
||||
|
||||
ticker := time.NewTicker(rfw.pulseInterval)
|
||||
defer close(rfw.selfDone)
|
||||
defer rfw.conn.Close()
|
||||
pktStream := utils.OrDonePacket(rfw.selfDone, rfw.pktStream)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
close(done)
|
||||
return
|
||||
case pkt := <-pktStream:
|
||||
if err := rfw.writerPacket(pkt); err != nil {
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
ticker.Reset(rfw.pulseInterval)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) writerPacket(pkt av.Packet) error {
|
||||
if rfw.start {
|
||||
if err := rfw.conn.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to rtmp server error : %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if pkt.IsKeyFrame {
|
||||
if err := rfw.createConn(); err != nil {
|
||||
logs.Error("conn rtmp server error : %v", err)
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
err = rfw.conn.WriteHeader(rfw.codecs)
|
||||
if err != nil {
|
||||
logs.Error("writer header to rtmp server error : %v", err)
|
||||
return err
|
||||
}
|
||||
rfw.start = true
|
||||
err = rfw.conn.WritePacket(pkt)
|
||||
if err != nil {
|
||||
logs.Error("writer packet to rtmp server error : %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
92
src/rtsp2rtmp/rtspclient/rtspclient.go
Normal file
92
src/rtsp2rtmp/rtspclient/rtspclient.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package rtspclient
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/flvmanage"
|
||||
)
|
||||
|
||||
type IRtspClientManager interface {
|
||||
Load(key interface{}) (interface{}, bool)
|
||||
Store(key, value interface{})
|
||||
Delete(key interface{})
|
||||
}
|
||||
|
||||
type RtspClient struct {
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
connDone <-chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
ffmPktStream <-chan av.Packet
|
||||
hfmPktStream <-chan av.Packet
|
||||
rfmPktStream <-chan av.Packet
|
||||
ircm IRtspClientManager
|
||||
}
|
||||
|
||||
func NewRtspClient(connDone <-chan interface{}, pktStream <-chan av.Packet, code string, codecs []av.CodecData, ircm IRtspClientManager) *RtspClient {
|
||||
r := &RtspClient{
|
||||
connDone: connDone,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
ffmPktStream: make(chan av.Packet),
|
||||
hfmPktStream: make(chan av.Packet),
|
||||
rfmPktStream: make(chan av.Packet),
|
||||
ircm: ircm,
|
||||
}
|
||||
r.pktTransfer()
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *RtspClient) Done() {
|
||||
<-r.connDone
|
||||
}
|
||||
|
||||
func (r *RtspClient) pktTransfer() {
|
||||
ffmPktStream, hfmPktStream, rfmPktStream := tee(r.connDone, r.pktStream)
|
||||
r.ffmPktStream = ffmPktStream
|
||||
r.hfmPktStream = hfmPktStream
|
||||
r.rfmPktStream = rfmPktStream
|
||||
logs.Debug("publisher [%s] create customer", r.code)
|
||||
flvmanage.GetSingleFileFlvManager().FlvWrite(r.ffmPktStream, r.code, r.codecs)
|
||||
flvmanage.GetSingleHttpflvAdmin().AddHttpFlvManager(r.hfmPktStream, r.code, r.codecs)
|
||||
flvmanage.GetSingleRtmpFlvManager().FlvWrite(r.rfmPktStream, r.code, r.codecs)
|
||||
}
|
||||
|
||||
// func (r *Publisher) GetFfmPktStream() (<-chan av.Packet, string, []av.CodecData) {
|
||||
// return r.ffmPktStream, r.code, r.codecs
|
||||
// }
|
||||
|
||||
// func (r *Publisher) GetHfmPktStream() (<-chan av.Packet, string, []av.CodecData) {
|
||||
// return r.ffmPktStream, r.code, r.codecs
|
||||
// }
|
||||
|
||||
func tee(done <-chan interface{}, in <-chan av.Packet) (<-chan av.Packet, <-chan av.Packet, <-chan av.Packet) {
|
||||
//设置缓冲,调节前后速率
|
||||
out1 := make(chan av.Packet, 50)
|
||||
out2 := make(chan av.Packet, 50)
|
||||
out3 := make(chan av.Packet, 50)
|
||||
go func() {
|
||||
defer close(out1)
|
||||
defer close(out2)
|
||||
defer close(out3)
|
||||
for val := range in {
|
||||
var out1, out2, out3 = out1, out2, out3 // 私有变量覆盖
|
||||
for i := 0; i < 3; i++ {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case out1 <- val:
|
||||
out1 = nil // 置空阻塞机制完成select轮询
|
||||
case out2 <- val:
|
||||
out2 = nil
|
||||
case out3 <- val:
|
||||
out3 = nil
|
||||
default:
|
||||
logs.Debug("RtspClient tee lose packet")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out1, out2, out3
|
||||
}
|
192
src/rtsp2rtmp/rtspclientmanager/rtspclientmanager.go
Normal file
192
src/rtsp2rtmp/rtspclientmanager/rtspclientmanager.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package rtspclientmanager
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/rtmp"
|
||||
"github.com/deepch/vdk/format/rtsp"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/controllers"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/rtspclient"
|
||||
)
|
||||
|
||||
var rcmInstance *RtspClientManager
|
||||
|
||||
func init() {
|
||||
rcmInstance = &RtspClientManager{}
|
||||
}
|
||||
|
||||
type RtspClientManager struct {
|
||||
rcs sync.Map
|
||||
conns sync.Map
|
||||
}
|
||||
|
||||
func GetSingleRtspClientManager() *RtspClientManager {
|
||||
return rcmInstance
|
||||
}
|
||||
|
||||
func (rs *RtspClientManager) StartClient() {
|
||||
go rs.serveStreams()
|
||||
done := make(chan interface{})
|
||||
go rs.stopConn(done, controllers.CodeStream())
|
||||
}
|
||||
|
||||
func (rc *RtspClientManager) ExistsPublisher(code string) bool {
|
||||
exists := false
|
||||
rc.rcs.Range(func(key, value interface{}) bool {
|
||||
codeKey := key.(string)
|
||||
if code == codeKey {
|
||||
exists = true
|
||||
return false
|
||||
}
|
||||
exists = codeKey == code
|
||||
return true
|
||||
})
|
||||
return exists
|
||||
}
|
||||
|
||||
func (rs *RtspClientManager) stopConn(done <-chan interface{}, codeStream <-chan string) {
|
||||
for code := range codeStream {
|
||||
v, b := rs.conns.Load(code)
|
||||
if b {
|
||||
r := v.(*rtmp.Conn)
|
||||
err := r.Close()
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] close error : %v", code, err)
|
||||
continue
|
||||
}
|
||||
logs.Info("camera [%s] close success", code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RtspClientManager) serveStreams() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("rtspManager panic %v", r)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
es, err := models.CameraSelectAll()
|
||||
if err != nil {
|
||||
logs.Error("camera list is empty")
|
||||
return
|
||||
}
|
||||
for _, camera := range es {
|
||||
if v, b := s.rcs.Load(camera.Code); b && v != nil {
|
||||
continue
|
||||
}
|
||||
if camera.Enabled != 1 {
|
||||
continue
|
||||
}
|
||||
go s.connRtsp(camera.Code)
|
||||
}
|
||||
<-time.After(30 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RtspClientManager) connRtsp(code string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
//放置信息表示已经开始
|
||||
s.rcs.Store(code, struct{}{})
|
||||
for {
|
||||
q := models.Camera{Code: code}
|
||||
c, err := models.CameraSelectOne(q)
|
||||
if err != nil {
|
||||
logs.Error("find camera [%s] error : %v", code, err)
|
||||
return
|
||||
}
|
||||
if c.Enabled != 1 {
|
||||
logs.Error("camera [%s] disabled : %v", code)
|
||||
return
|
||||
}
|
||||
logs.Info(c.Code, "connect", c.RtspURL)
|
||||
rtsp.DebugRtsp = false
|
||||
session, err := rtsp.Dial(c.RtspURL)
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] conn : %v", c.Code, err)
|
||||
c.OnlineStatus = 0
|
||||
time.Sleep(5 * time.Second)
|
||||
if c.OnlineStatus == 1 {
|
||||
models.CameraUpdate(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
session.RtpKeepAliveTimeout = 10 * time.Second
|
||||
codecs, err := session.Streams()
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] get streams : %v", c.Code, err)
|
||||
time.Sleep(5 * time.Second)
|
||||
return
|
||||
}
|
||||
|
||||
c.OnlineStatus = 1
|
||||
models.CameraUpdate(c)
|
||||
|
||||
done := make(chan interface{})
|
||||
//添加缓冲,缓解前后速率不一致问题,但是如果收包平均速率大于消费平均速率,依然会导致丢包
|
||||
pktStream := make(chan av.Packet, 50)
|
||||
defer func() {
|
||||
close(done)
|
||||
close(pktStream)
|
||||
}()
|
||||
|
||||
rc := rtspclient.NewRtspClient(done, pktStream, code, codecs, s)
|
||||
s.rcs.Store(code, rc)
|
||||
s.conns.Store(code, session)
|
||||
for {
|
||||
pkt, err := session.ReadPacket()
|
||||
if err != nil {
|
||||
logs.Error("camera [%s] ReadPacket : %v", c.Code, err)
|
||||
break
|
||||
}
|
||||
//不能开goroutine,不能保证包的顺序
|
||||
select {
|
||||
case pktStream <- pkt:
|
||||
default:
|
||||
//添加缓冲,缓解前后速率不一致问题,但是如果收包平均速率大于消费平均速率,依然会导致丢包
|
||||
logs.Debug("rtmpserver lose packet")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logs.Error("session Close error : %v", err)
|
||||
}
|
||||
//offline camera
|
||||
camera, err := models.CameraSelectOne(q)
|
||||
if err != nil {
|
||||
logs.Error("no camera error : %s", code)
|
||||
} else {
|
||||
camera.OnlineStatus = 0
|
||||
models.CameraUpdate(camera)
|
||||
}
|
||||
|
||||
s.rcs.Delete(code)
|
||||
s.conns.Delete(code)
|
||||
err = session.Close()
|
||||
if err != nil {
|
||||
logs.Error("close conn error : %v", err)
|
||||
}
|
||||
logs.Info("camera [%s] reconnect wait 5s", c.Code)
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RtspClientManager) Load(key interface{}) (interface{}, bool) {
|
||||
return r.rcs.Load(key)
|
||||
}
|
||||
func (r *RtspClientManager) Store(key, value interface{}) {
|
||||
r.rcs.Store(key, value)
|
||||
}
|
||||
func (r *RtspClientManager) Delete(key interface{}) {
|
||||
r.rcs.Delete(key)
|
||||
}
|
65
src/rtsp2rtmp/task/task.go
Normal file
65
src/rtsp2rtmp/task/task.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/models"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/rtspclientmanager"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/web"
|
||||
)
|
||||
|
||||
var taskInstance *task
|
||||
|
||||
func init() {
|
||||
taskInstance = &task{}
|
||||
}
|
||||
|
||||
type task struct {
|
||||
}
|
||||
|
||||
func GetSingleTask() *task {
|
||||
return taskInstance
|
||||
}
|
||||
|
||||
func (t *task) StartTask() {
|
||||
go t.clearToken()
|
||||
go t.offlineCamera()
|
||||
}
|
||||
|
||||
func (t *task) clearToken() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
web.ClearExipresToken()
|
||||
<-time.After(24 * time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *task) offlineCamera() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
css, err := models.CameraSelectAll()
|
||||
if err != nil {
|
||||
logs.Error("query camera error : %v", err)
|
||||
}
|
||||
for _, cs := range css {
|
||||
if cs.OnlineStatus != 1 {
|
||||
continue
|
||||
}
|
||||
if exists := rtspclientmanager.GetSingleRtspClientManager().ExistsPublisher(cs.Code); !exists {
|
||||
cs.OnlineStatus = 0
|
||||
models.CameraUpdate(cs)
|
||||
}
|
||||
}
|
||||
<-time.After(10 * time.Minute)
|
||||
}
|
||||
}
|
40
src/rtsp2rtmp/utils/avpacket.go
Normal file
40
src/rtsp2rtmp/utils/avpacket.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package utils
|
||||
|
||||
import "github.com/deepch/vdk/av"
|
||||
|
||||
func OrDonePacket(done <-chan interface{}, c <-chan av.Packet) <-chan av.Packet {
|
||||
valStream := make(chan av.Packet)
|
||||
go func() {
|
||||
defer close(valStream)
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case v, ok := <-c:
|
||||
if !ok { // 外界关闭数据流
|
||||
return
|
||||
}
|
||||
select { // 防止写入阻塞
|
||||
case valStream <- v:
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return valStream
|
||||
}
|
||||
|
||||
func ToPacket(done <-chan interface{}, valueStream <-chan interface{}) <-chan av.Packet {
|
||||
stringStream := make(chan av.Packet)
|
||||
go func() {
|
||||
defer close(stringStream)
|
||||
for v := range valueStream {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case stringStream <- v.(av.Packet):
|
||||
}
|
||||
}
|
||||
}()
|
||||
return stringStream
|
||||
}
|
@@ -1,12 +1,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
func OrDone(done, c <-chan interface{}) <-chan interface{} {
|
||||
func OrDone(done <-chan interface{}, c <-chan interface{}) <-chan interface{} {
|
||||
valStream := make(chan interface{})
|
||||
go func() {
|
||||
defer close(valStream)
|
||||
@@ -15,7 +9,7 @@ func OrDone(done, c <-chan interface{}) <-chan interface{} {
|
||||
case <-done:
|
||||
return
|
||||
case v, ok := <-c:
|
||||
if ok == false { // 外界关闭数据流
|
||||
if !ok { // 外界关闭数据流
|
||||
return
|
||||
}
|
||||
select { // 防止写入阻塞
|
||||
@@ -28,7 +22,7 @@ func OrDone(done, c <-chan interface{}) <-chan interface{} {
|
||||
return valStream
|
||||
}
|
||||
|
||||
func Tee(done <-chan interface{}, in <-chan interface{}, timeout time.Duration) (<-chan interface{}, <-chan interface{}) {
|
||||
func Tee(done <-chan interface{}, in <-chan interface{}) (<-chan interface{}, <-chan interface{}) {
|
||||
out1 := make(chan interface{})
|
||||
out2 := make(chan interface{})
|
||||
go func() {
|
||||
@@ -44,11 +38,24 @@ func Tee(done <-chan interface{}, in <-chan interface{}, timeout time.Duration)
|
||||
out1 = nil // 置空阻塞机制完成select轮询
|
||||
case out2 <- val:
|
||||
out2 = nil
|
||||
case <-time.After(timeout): //设置超时发送时间,防止发送阻塞
|
||||
logs.Error("Tee timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out1, out2
|
||||
}
|
||||
|
||||
func ToString(done <-chan interface{}, valueStream <-chan interface{}) <-chan string {
|
||||
stringStream := make(chan string)
|
||||
go func() {
|
||||
defer close(stringStream)
|
||||
for v := range valueStream {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case stringStream <- v.(string):
|
||||
}
|
||||
}
|
||||
}()
|
||||
return stringStream
|
||||
}
|
@@ -33,3 +33,14 @@ func TokenTimeOut(token string, duration time.Duration) bool {
|
||||
}
|
||||
return time.Now().After(tokenTime.Add(duration))
|
||||
}
|
||||
|
||||
func UUID() (string, error) {
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
logs.Error("Random error : %v", err)
|
||||
return "", err
|
||||
}
|
||||
idstring := id.String()
|
||||
idstring = strings.ReplaceAll(idstring, "-", "")
|
||||
return idstring, nil
|
||||
}
|
207
src/rtsp2rtmp/web/webapp.go
Normal file
207
src/rtsp2rtmp/web/webapp.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/controllers"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/result"
|
||||
"github.com/hkmadao/rtsp2rtmp/src/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
var tokens sync.Map
|
||||
|
||||
func ClearExipresToken() {
|
||||
deleteTokens := []string{}
|
||||
// 遍历所有sync.Map中的键值对
|
||||
tokens.Range(func(k, v interface{}) bool {
|
||||
if time.Now().After(v.(time.Time).Add(30 * time.Minute)) {
|
||||
deleteTokens = append(deleteTokens, k.(string))
|
||||
}
|
||||
return true
|
||||
})
|
||||
for _, v := range deleteTokens {
|
||||
tokens.Delete(v)
|
||||
}
|
||||
}
|
||||
|
||||
var webInstance *web
|
||||
|
||||
type web struct{}
|
||||
|
||||
func init() {
|
||||
webInstance = &web{}
|
||||
|
||||
}
|
||||
|
||||
func GetSingleWeb() *web {
|
||||
return webInstance
|
||||
}
|
||||
|
||||
func (w *web) StartWeb() {
|
||||
go w.webRun()
|
||||
}
|
||||
|
||||
func (w *web) webRun() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
router := gin.Default()
|
||||
router.Use(Cors())
|
||||
router.Use(Validate())
|
||||
|
||||
router.POST("/system/login", login)
|
||||
|
||||
router.GET("/live/:method/:code/:authCode.flv", controllers.HttpFlvPlay)
|
||||
|
||||
router.GET("/camera/list", controllers.CameraList)
|
||||
router.GET("/camera/detail", controllers.CameraDetail)
|
||||
router.POST("/camera/edit", controllers.CameraEdit)
|
||||
router.POST("/camera/delete/:id", controllers.CameraDelete)
|
||||
router.POST("/camera/enabled", controllers.CameraEnabled)
|
||||
router.POST("/camera/savevideochange", controllers.CameraSaveVideoChange)
|
||||
router.POST("/camera/livechange", controllers.CameraLiveChange)
|
||||
router.POST("/camera/playauthcodereset", controllers.CameraPlayAuthCodeReset)
|
||||
|
||||
router.GET("/camerashare/list", controllers.CameraShareList)
|
||||
router.POST("/camerashare/edit", controllers.CameraShareEdit)
|
||||
router.POST("/camerashare/delete/:id", controllers.CameraShareDelete)
|
||||
router.POST("/camerashare/enabled", controllers.CameraShareEnabled)
|
||||
|
||||
staticPath, err := config.String("server.http.static.path")
|
||||
if err != nil {
|
||||
logs.Error("get httpflv staticPath error: %v. \n use default staticPath : ./resources/static", err)
|
||||
staticPath = "./resources/static"
|
||||
}
|
||||
|
||||
router.StaticFS("/rtsp2rtmp", http.Dir(staticPath))
|
||||
|
||||
port, err := config.Int("server.http.port")
|
||||
if err != nil {
|
||||
logs.Error("get httpflv port error: %v. \n use default port : 9090", err)
|
||||
port = 9090
|
||||
}
|
||||
err = router.Run(":" + strconv.Itoa(port))
|
||||
if err != nil {
|
||||
logs.Error("Start HTTP Server error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 跨域
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method //请求方法
|
||||
origin := c.Request.Header.Get("Origin") //请求头部
|
||||
if origin != "" {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Origin", "*") // 这是允许访问所有域
|
||||
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
|
||||
// header的类型
|
||||
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
|
||||
// 允许跨域设置 可以返回其他子段
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
|
||||
c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
|
||||
c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
|
||||
c.Set("content-type", "application/json") // 设置返回格式是json
|
||||
}
|
||||
|
||||
//放行所有OPTIONS方法
|
||||
if method == "OPTIONS" {
|
||||
c.JSON(http.StatusOK, "Options Request!")
|
||||
}
|
||||
// 处理请求
|
||||
c.Next() // 处理请求
|
||||
}
|
||||
}
|
||||
|
||||
//验证token
|
||||
func Validate() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if c.Request.URL.Path == "/system/login" || strings.HasPrefix(c.Request.URL.Path, "/live/") ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/rtsp2rtmp") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
r := result.Result{
|
||||
Code: 1,
|
||||
Msg: "",
|
||||
}
|
||||
token := c.Request.Header.Get("token")
|
||||
if len(token) == 0 {
|
||||
logs.Error("token is null")
|
||||
r.Code = 0
|
||||
r.Msg = "token is null"
|
||||
c.JSON(http.StatusUnauthorized, r)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
tokenTime, b := tokens.Load(token)
|
||||
if !b {
|
||||
logs.Error("token error")
|
||||
r.Code = 0
|
||||
r.Msg = "token error"
|
||||
c.JSON(http.StatusUnauthorized, r)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
timeout := time.Now().After(tokenTime.(time.Time).Add(30 * time.Minute))
|
||||
if timeout {
|
||||
logs.Error("token is timeout")
|
||||
r.Code = 0
|
||||
r.Msg = "token is timeout"
|
||||
c.JSON(http.StatusUnauthorized, r)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
tokens.Store(token, time.Now())
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func login(c *gin.Context) {
|
||||
r := result.Result{
|
||||
Code: 1,
|
||||
Msg: "",
|
||||
}
|
||||
params := make(map[string]interface{})
|
||||
err := c.BindJSON(¶ms)
|
||||
if err != nil {
|
||||
logs.Error("param error : %v", err)
|
||||
r.Code = 0
|
||||
r.Msg = "param error"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
userNameParam := params["userName"].(string)
|
||||
passwordParam := params["userName"].(string)
|
||||
userName := config.DefaultString("server.user.name", "")
|
||||
password := config.DefaultString("server.user.password", "")
|
||||
if userNameParam != userName && passwordParam != password {
|
||||
logs.Error("userName : %s , password : %s error", userNameParam, passwordParam)
|
||||
r.Code = 0
|
||||
r.Msg = "userName or password error ! "
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
logs.Info("用户[%s]登录成功!", userName)
|
||||
token, err := utils.NextToke()
|
||||
if err != nil {
|
||||
logs.Error("create token fail")
|
||||
r.Code = 0
|
||||
r.Msg = "create token fail"
|
||||
c.JSON(http.StatusOK, r)
|
||||
return
|
||||
}
|
||||
r.Data = map[string]string{"token": token}
|
||||
tokens.Store(token, time.Now())
|
||||
c.JSON(http.StatusOK, r)
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"files": {
|
||||
"main.js": "/static/js/main.8767d5c1.chunk.js",
|
||||
"main.js.map": "/static/js/main.8767d5c1.chunk.js.map",
|
||||
"runtime-main.js": "/static/js/runtime-main.233489df.js",
|
||||
"runtime-main.js.map": "/static/js/runtime-main.233489df.js.map",
|
||||
"static/js/2.7116461d.chunk.js": "/static/js/2.7116461d.chunk.js",
|
||||
"static/js/2.7116461d.chunk.js.map": "/static/js/2.7116461d.chunk.js.map",
|
||||
"index.html": "/index.html",
|
||||
"static/js/2.7116461d.chunk.js.LICENSE.txt": "/static/js/2.7116461d.chunk.js.LICENSE.txt"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.233489df.js",
|
||||
"static/js/2.7116461d.chunk.js",
|
||||
"static/js/main.8767d5c1.chunk.js"
|
||||
]
|
||||
}
|
@@ -1 +0,0 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="app"></div><script>!function(e){function r(r){for(var n,p,l=r[0],f=r[1],i=r[2],c=0,s=[];c<l.length;c++)p=l[c],Object.prototype.hasOwnProperty.call(o,p)&&o[p]&&s.push(o[p][0]),o[p]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var f=t[l];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=p(p.s=t[0]))}return e}var n={},o={1:0},u=[];function p(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,p),t.l=!0,t.exports}p.m=e,p.c=n,p.d=function(e,r,t){p.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},p.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.t=function(e,r){if(1&r&&(e=p(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(p.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)p.d(t,n,function(r){return e[r]}.bind(null,n));return t},p.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(r,"a",r),r},p.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},p.p="/";var l=this.webpackJsonprtsp2rtmpweb=this.webpackJsonprtsp2rtmpweb||[],f=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var a=f;t()}([])</script><script src="/static/js/2.7116461d.chunk.js"></script><script src="/static/js/main.8767d5c1.chunk.js"></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,35 +0,0 @@
|
||||
package fileflv
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
)
|
||||
|
||||
type FileFlvManager struct {
|
||||
done <-chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
}
|
||||
|
||||
func NewFileFlvManager(done <-chan interface{}, pktStream <-chan av.Packet, code string, codecs []av.CodecData) *FileFlvManager {
|
||||
ffm := &FileFlvManager{
|
||||
done: done,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
}
|
||||
go ffm.flvWrite()
|
||||
return ffm
|
||||
}
|
||||
|
||||
func (ffm *FileFlvManager) flvWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
NewFileFlvWriter(ffm.done, ffm.pktStream, ffm.code, ffm.codecs)
|
||||
}
|
@@ -1,113 +0,0 @@
|
||||
package fileflv
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/config"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/flv"
|
||||
)
|
||||
|
||||
type FileFlvWriter struct {
|
||||
done <-chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
start bool
|
||||
prepare bool
|
||||
fd *os.File
|
||||
}
|
||||
|
||||
func NewFileFlvWriter(done <-chan interface{}, pktStream <-chan av.Packet, code string, codecs []av.CodecData) *FileFlvWriter {
|
||||
ffw := &FileFlvWriter{
|
||||
done: done,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
}
|
||||
go ffw.flvWrite()
|
||||
return ffw
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) Write(p []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
if ffw.prepare {
|
||||
return
|
||||
}
|
||||
n, err = ffw.fd.Write(p)
|
||||
if err != nil {
|
||||
logs.Error("write file error : %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ffw *FileFlvWriter) createFlvFile() {
|
||||
fd, err := os.OpenFile(getFileFlvPath()+"/"+ffw.code+"_"+time.Now().Format("20060102150405")+".flv", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
logs.Error("open file error :", err)
|
||||
}
|
||||
if ffw.fd == nil {
|
||||
ffw.fd = fd
|
||||
return
|
||||
}
|
||||
fdOld := ffw.fd
|
||||
ffw.prepare = true
|
||||
ffw.start = false
|
||||
ffw.fd = fd
|
||||
ffw.prepare = false
|
||||
fdOld.Close()
|
||||
}
|
||||
|
||||
//Write extends to writer.Writer
|
||||
func (ffw *FileFlvWriter) flvWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
ffw.createFlvFile()
|
||||
muxer := flv.NewMuxer(ffw)
|
||||
ticker := time.NewTicker(1 * time.Hour)
|
||||
for {
|
||||
select {
|
||||
case <-ffw.done:
|
||||
ffw.fd.Close()
|
||||
return
|
||||
case <-ticker.C: //split flvFile
|
||||
ffw.createFlvFile()
|
||||
case pkt := <-ffw.pktStream:
|
||||
if ffw.start {
|
||||
if err := muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to flv file error : %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if pkt.IsKeyFrame {
|
||||
err := muxer.WriteHeader(ffw.codecs)
|
||||
if err != nil {
|
||||
logs.Error("writer header to flv file error : %v", err)
|
||||
}
|
||||
if err := muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to flv file error : %v", err)
|
||||
}
|
||||
ffw.start = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFileFlvPath() string {
|
||||
fileFlvPath, err := config.String("server.fileflv.path")
|
||||
if err != nil {
|
||||
logs.Error("get fileflv path error :", err)
|
||||
return ""
|
||||
}
|
||||
return fileFlvPath
|
||||
}
|
@@ -1,137 +0,0 @@
|
||||
package httpflv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "net/http/pprof"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/yumrano/rtsp2rtmp/utils"
|
||||
)
|
||||
|
||||
var hfms sync.Map
|
||||
|
||||
type writerInfo struct {
|
||||
sessionId int64
|
||||
code string
|
||||
heartbeatStream <-chan interface{}
|
||||
endStream <-chan interface{}
|
||||
pktStream chan<- av.Packet
|
||||
}
|
||||
|
||||
//添加播放者
|
||||
func AddHttpFlvPlayer(code string, writer http.ResponseWriter) (endStream <-chan interface{}, heartbeatStream <-chan interface{}, playerDone chan<- interface{}, err error) {
|
||||
v, b := hfms.Load(code)
|
||||
if !b {
|
||||
err = errors.New("camera no connection")
|
||||
return
|
||||
}
|
||||
sessionId := utils.NextValSnowflakeID()
|
||||
hfm := v.(*HttpFlvManager)
|
||||
hfw := NewHttpFlvWriter(hfm.done, hfm.code, hfm.codecs, writer, sessionId)
|
||||
endStream = hfw.GetEndStream()
|
||||
//one2two chan
|
||||
heartbeatStream1, heartbeatStream2 := utils.Tee(endStream, hfw.GetHeartbeatStream(), 1*time.Millisecond)
|
||||
wi := &writerInfo{
|
||||
sessionId: sessionId,
|
||||
code: code,
|
||||
heartbeatStream: heartbeatStream1,
|
||||
endStream: endStream,
|
||||
pktStream: hfw.GetPktStream(),
|
||||
}
|
||||
hfm.wis.Store(sessionId, wi)
|
||||
heartbeatStream = heartbeatStream2
|
||||
playerDone = hfw.GetPlayerDone()
|
||||
go monitor(wi)
|
||||
return
|
||||
}
|
||||
|
||||
func monitor(wi *writerInfo) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-wi.heartbeatStream:
|
||||
continue
|
||||
case <-wi.endStream:
|
||||
//end info
|
||||
if v, b := hfms.Load(wi.code); b {
|
||||
hfm := v.(*HttpFlvManager)
|
||||
hfm.wis.Delete(wi.sessionId)
|
||||
}
|
||||
return
|
||||
case <-time.After(10 * time.Second):
|
||||
//time out
|
||||
if v, b := hfms.Load(wi.code); b {
|
||||
hfm := v.(*HttpFlvManager)
|
||||
hfm.wis.Delete(wi.sessionId)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExistsHttpFlvManager(code string) bool {
|
||||
_, b := hfms.Load(code)
|
||||
return b
|
||||
}
|
||||
|
||||
type HttpFlvManager struct {
|
||||
done <-chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
wis sync.Map
|
||||
}
|
||||
|
||||
func NewHttpFlvManager(done <-chan interface{}, pktStream <-chan av.Packet, code string, codecs []av.CodecData) *HttpFlvManager {
|
||||
hfm := &HttpFlvManager{
|
||||
done: done,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
}
|
||||
go hfm.flvWrite()
|
||||
hfms.Store(code, hfm)
|
||||
return hfm
|
||||
}
|
||||
|
||||
//Write extends to writer.Writer
|
||||
func (hfm *HttpFlvManager) flvWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-hfm.done:
|
||||
return
|
||||
case pkt := <-hfm.pktStream:
|
||||
hfm.wis.Range(func(key, value interface{}) bool {
|
||||
go func(pkt1 av.Packet) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
wi := value.(*writerInfo)
|
||||
select {
|
||||
case wi.pktStream <- pkt1:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
// logs.Info("lose pkt")
|
||||
}
|
||||
}(pkt)
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,149 +0,0 @@
|
||||
package httpflv
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/flv"
|
||||
)
|
||||
|
||||
type HttpFlvWriter struct {
|
||||
sessionId int64
|
||||
done <-chan interface{}
|
||||
pktStream chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
start bool
|
||||
writer http.ResponseWriter
|
||||
muxer *flv.Muxer
|
||||
close bool
|
||||
playerDone chan interface{} //来自播放者的关闭
|
||||
heartbeatStream chan interface{} //心跳包通道
|
||||
endStream chan interface{} //结束通道,告知父进程结束
|
||||
}
|
||||
|
||||
func NewHttpFlvWriter(done <-chan interface{}, code string, codecs []av.CodecData, writer http.ResponseWriter, sessionId int64) *HttpFlvWriter {
|
||||
playerDone := make(chan interface{})
|
||||
heartbeatStream := make(chan interface{})
|
||||
endStream := make(chan interface{})
|
||||
pktStream := make(chan av.Packet, 1*1024)
|
||||
hfw := &HttpFlvWriter{
|
||||
sessionId: sessionId,
|
||||
done: done,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
writer: writer,
|
||||
playerDone: playerDone,
|
||||
heartbeatStream: heartbeatStream,
|
||||
endStream: endStream,
|
||||
start: false,
|
||||
close: false,
|
||||
}
|
||||
go hfw.httpWrite()
|
||||
return hfw
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) GetEndStream() <-chan interface{} {
|
||||
return hfw.endStream
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) GetHeartbeatStream() <-chan interface{} {
|
||||
return hfw.heartbeatStream
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) GetPlayerDone() chan<- interface{} {
|
||||
return hfw.playerDone
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) GetPktStream() chan<- av.Packet {
|
||||
return hfw.pktStream
|
||||
}
|
||||
|
||||
func (hfw *HttpFlvWriter) httpWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-hfw.done:
|
||||
close(hfw.endStream)
|
||||
return
|
||||
case <-hfw.playerDone:
|
||||
close(hfw.endStream)
|
||||
return
|
||||
case pkt := <-hfw.pktStream:
|
||||
if hfw.start {
|
||||
if err := hfw.muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to httpflv error : %v", err)
|
||||
close(hfw.endStream)
|
||||
return
|
||||
}
|
||||
// logs.Info("start send heartbeat ")
|
||||
select {
|
||||
case hfw.heartbeatStream <- 1:
|
||||
// logs.Info("send heartbeat sucessful")
|
||||
continue
|
||||
case <-hfw.done:
|
||||
logs.Info("send heartbeat done")
|
||||
continue
|
||||
case <-hfw.playerDone:
|
||||
logs.Info("send heartbeat playerDone")
|
||||
continue
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
logs.Info("send heartbeat time out")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if pkt.IsKeyFrame {
|
||||
muxer := flv.NewMuxer(hfw)
|
||||
hfw.muxer = muxer
|
||||
err := hfw.muxer.WriteHeader(hfw.codecs)
|
||||
if err != nil {
|
||||
logs.Error("writer header to httpflv error : %v", err)
|
||||
close(hfw.endStream)
|
||||
return
|
||||
}
|
||||
hfw.start = true
|
||||
if err := hfw.muxer.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to httpflv error : %v", err)
|
||||
close(hfw.endStream)
|
||||
return
|
||||
}
|
||||
// logs.Info("start send heartbeat ")
|
||||
select {
|
||||
case hfw.heartbeatStream <- 1:
|
||||
// logs.Info("send heartbeat sucessful")
|
||||
continue
|
||||
case <-hfw.done:
|
||||
continue
|
||||
case <-hfw.playerDone:
|
||||
continue
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
logs.Info("send heartbeat time out")
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Write extends to io.Writer
|
||||
func (hfw *HttpFlvWriter) Write(p []byte) (n int, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
n, err = hfw.writer.Write(p)
|
||||
if err != nil {
|
||||
logs.Error("write httpflv error : %v", err)
|
||||
}
|
||||
return
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
package rtmpflv
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
)
|
||||
|
||||
type RtmpFlvManager struct {
|
||||
done <-chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
}
|
||||
|
||||
func NewRtmpFlvManager(done <-chan interface{}, pktStream <-chan av.Packet, code string, codecs []av.CodecData) *RtmpFlvManager {
|
||||
rfm := &RtmpFlvManager{
|
||||
done: done,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
}
|
||||
go rfm.flvWrite()
|
||||
return rfm
|
||||
}
|
||||
|
||||
func (rfm *RtmpFlvManager) flvWrite() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
NewRtmpFlvWriter(rfm.done, rfm.pktStream, rfm.code, rfm.codecs)
|
||||
}
|
@@ -1,178 +0,0 @@
|
||||
package rtmpflv
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/format/rtmp"
|
||||
"github.com/yumrano/rtsp2rtmp/models"
|
||||
)
|
||||
|
||||
type RtmpFlvWriter struct {
|
||||
done <-chan interface{}
|
||||
pktStream <-chan av.Packet
|
||||
code string
|
||||
codecs []av.CodecData
|
||||
start bool
|
||||
conn *rtmp.Conn
|
||||
heartbeatStream chan interface{}
|
||||
endStream chan interface{}
|
||||
}
|
||||
|
||||
func NewRtmpFlvWriter(done <-chan interface{}, pktStream <-chan av.Packet, code string, codecs []av.CodecData) *RtmpFlvWriter {
|
||||
rfw := &RtmpFlvWriter{
|
||||
done: done,
|
||||
pktStream: pktStream,
|
||||
code: code,
|
||||
codecs: codecs,
|
||||
start: false,
|
||||
heartbeatStream: make(chan interface{}),
|
||||
endStream: make(chan interface{}),
|
||||
}
|
||||
go rfw.flvWrite(rfw.endStream, rfw.heartbeatStream)
|
||||
go rfw.monitor(rfw.endStream, rfw.heartbeatStream)
|
||||
return rfw
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) monitor(endStream <-chan interface{}, heartbeatStream <-chan interface{}) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-endStream:
|
||||
return
|
||||
case <-rfw.done:
|
||||
if rfw.conn == nil {
|
||||
return
|
||||
}
|
||||
err := rfw.conn.Close()
|
||||
if err != nil {
|
||||
logs.Error("close rtmp client connection error : %v", err)
|
||||
}
|
||||
return
|
||||
case <-time.After(10 * time.Second):
|
||||
logs.Error("rtmp cliect time out , close")
|
||||
if rfw.conn != nil {
|
||||
err := rfw.conn.Close()
|
||||
if err != nil {
|
||||
logs.Error("close rtmp client connection error : %v", err)
|
||||
}
|
||||
}
|
||||
rfw.start = false
|
||||
go rfw.flvWrite(rfw.endStream, rfw.heartbeatStream)
|
||||
continue
|
||||
case <-heartbeatStream:
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (rfw *RtmpFlvWriter) createConn() error {
|
||||
var camera models.Camera
|
||||
camera.Code = rfw.code
|
||||
camera, err := models.CameraSelectOne(camera)
|
||||
if err != nil {
|
||||
logs.Error("not found camera : %s", rfw.code)
|
||||
return err
|
||||
}
|
||||
rtmpConn, err := rtmp.Dial(camera.RtmpURL)
|
||||
if err != nil {
|
||||
logs.Error("rtmp client connection error : %v", err)
|
||||
return err
|
||||
}
|
||||
rfw.conn = rtmpConn
|
||||
return nil
|
||||
}
|
||||
|
||||
//Write extends to writer.Writer
|
||||
func (rfw *RtmpFlvWriter) flvWrite(endStream chan<- interface{}, heartbeatStream chan<- interface{}) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logs.Error("system painc : %v \nstack : %v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-rfw.done:
|
||||
if rfw.conn == nil {
|
||||
return
|
||||
}
|
||||
err := rfw.conn.Close()
|
||||
if err != nil {
|
||||
logs.Error("close rtmp client connection error : %v", err)
|
||||
}
|
||||
return
|
||||
case pkt := <-rfw.pktStream:
|
||||
if rfw.start {
|
||||
if err := rfw.conn.WritePacket(pkt); err != nil {
|
||||
logs.Error("writer packet to rtmp server error : %v", err)
|
||||
select {
|
||||
case rfw.heartbeatStream <- 0:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
logs.Error("send heartbeat timeout")
|
||||
}
|
||||
err := rfw.conn.Close()
|
||||
if err != nil {
|
||||
logs.Error("close rtmp client connection error : %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
select {
|
||||
case rfw.heartbeatStream <- 1:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
logs.Error("send heartbeat timeout")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if pkt.IsKeyFrame {
|
||||
if err := rfw.createConn(); err != nil {
|
||||
logs.Error("conn rtmp server error : %v", err)
|
||||
return
|
||||
}
|
||||
var err error
|
||||
err = rfw.conn.WriteHeader(rfw.codecs)
|
||||
if err != nil {
|
||||
logs.Error("writer header to rtmp server error : %v", err)
|
||||
select {
|
||||
case rfw.heartbeatStream <- 0:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
logs.Error("send heartbeat timeout")
|
||||
}
|
||||
err := rfw.conn.Close()
|
||||
if err != nil {
|
||||
logs.Error("close rtmp client connection error : %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
rfw.start = true
|
||||
err = rfw.conn.WritePacket(pkt)
|
||||
if err != nil {
|
||||
logs.Error("writer packet to rtmp server error : %v", err)
|
||||
select {
|
||||
case rfw.heartbeatStream <- 0:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
logs.Error("send heartbeat timeout")
|
||||
}
|
||||
err := rfw.conn.Close()
|
||||
if err != nil {
|
||||
logs.Error("close rtmp client connection error : %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
select {
|
||||
case rfw.heartbeatStream <- 1:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
logs.Error("send heartbeat timeout")
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user