项目结构整理

This commit is contained in:
hkmadao
2021-07-04 22:33:40 +08:00
parent 2860ef21df
commit 2bfb9578a2
80 changed files with 2680 additions and 2253 deletions

12
.gitignore vendored
View File

@@ -1,7 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
.vscode
rtsp2rtmp*
__debug_bin __debug_bin
output/rtsp2rtmp* lastupdate.tmp
output/live/*.flv resources/output/live/*.flv
output/log/*.log resources/output/log/*.log
resources/output/releases
.idea
.vscode

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020 yumrano Copyright (c) 2021 hkmadao
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -3,22 +3,25 @@
![](./docs/images/rtsp2rtmpad.png) ![](./docs/images/rtsp2rtmpad.png)
##### 项目功能: ##### 项目功能:
1. rtsp转httpflv播放 1. rtsp转httpflv播放
2. rtsp转rtmp推送 2. rtsp转rtmp推送
3. rtsp视频录像录像文件为flv格式 3. rtsp视频录像录像文件为flv格式
##### 运行说明: ##### 运行说明:
1. 下载[程序文件](https://github.com/hkmadao/rtsp2rtmp/releases),解压 1. 下载[程序文件](https://github.com/hkmadao/rtsp2rtmp/releases),解压
2. 执行程序文件window下执行rtsp2rtmp.exelinux下执行rtsp2rtmp 2. 安装postgresql,根据配置文件"resources/conf/conf-prod.yml"创建数据库
3. 浏览器访问程序服务地址http://[server_ip]:8080/ 3. 根据"docs/init/rtsp2rtmp-postgresql.sql"文件创建表
4. 在网页配置摄像头的rtsp地址、要推送到的rtmp服务器地址等信息 4. 执行程序文件window下执行rtsp2rtmp.exelinux下执行rtsp2rtmp
5. 配置好后重启服务器 5. 浏览器访问程序服务地址http://[server_ip]:8080/rtsp2rtmp/#/ ,根据配置文件"resources/conf/conf-prod.yml"密码登录系统
6. 再次进入网页观看视频 6. 在网页配置摄像头的rtsp地址、要推送到的rtmp服务器地址等信息
7. 等待连接上摄像头约30秒左右观看视频
> 注意: > 注意:
> >
> 若只想查看项目功能可下载带_demo结尾的版本该版本为window64的演示版本使用的是sqlite3数据库无需安装数据库即可运行
>
> 程序目前支持h264视频编码、aac音频编码若不能正常播放关掉摄像头推送的音频再尝试 > 程序目前支持h264视频编码、aac音频编码若不能正常播放关掉摄像头推送的音频再尝试
##### 目录结构: ##### 目录结构:
@@ -26,40 +29,51 @@
``` ```
--rtsp2rtmp #linux执行文件 --rtsp2rtmp #linux执行文件
--rtsp2rtmp.exe #window执行文件 --rtsp2rtmp.exe #window执行文件
--statics #程序的网页文件夹 --resources
--conf #配置文件文件夹 --static #程序的网页文件夹
--conf.yml #配置文件 --conf #配置文件文件夹
--db #sqlite3 #数据库文件 --conf-dev.yml #配置文件
--rtsp2rtmp.db #sqlite3数据库文件存放摄像头的url、推送的rtmp服务器地址等信息 --conf-prod.yml #配置文件
--output #程序输出文件夹 --output #程序输出文件夹
--live #保存摄像头录像的文件夹录像格式为flv --live #保存摄像头录像的文件夹录像格式为flv
--log #程序输出的日志文件夹 --log #程序输出的日志文件夹
``` ```
##### 配置说明: ##### 配置说明:
``` ```
server: server:
user:
name: admin #网页登录用户名
password: admin #网页登录密码
httpflv: httpflv:
port: 8080 #程序的http端口 port: 8080
static:
path: ./resources/static #页面所在文件夹
fileflv: fileflv:
save: true #是否保存录像文件 path: ./resources/output/live #录像所在文件
path: ./output/live #录像文件夹
log: 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. 安装golangMinGW(sqlite3模块的使用到的cgo,window下开发需要使用window下可选择安装MinGW) 1. 安装golang
2. 获取[服务器源码](https://github.com/hkmadao/rtsp2rtmp.git) 2. 获取[服务器源码](https://github.com/hkmadao/rtsp2rtmp.git)
3. 进入项目目录 3. 安装postgresql数据库根据配置文件"resources/conf/conf-prod.yml"创建数据库
4. go build开发 4. 根据"docs/init/rtsp2rtmp-postgresql.sql"文件创建表
5. 进入项目目录
6. go build开发
###### 页面开发说明: ###### 页面开发说明:

View File

@@ -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)
}
}

View File

@@ -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, &param)
// 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, &param)
// 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
// }

View File

@@ -2,47 +2,56 @@
chcp 65001 chcp 65001
set /p ver=请输入版本: set /p ver=请输入版本:
echo 版本:%ver% 打包开始 echo 版本:%ver% 打包开始
@REM window_amd64 rmdir /S /Q .\resources\output\releases
@REM SET GOOS=windows @REM windows_amd64
@REM SET GOARCH=amd64 echo 打包windows_amd64平台
SET CGO_ENABLED=1 SET GOOS=windows
go build -o rtsp2rtmp_%ver%_window_amd64.exe main.go 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 xcopy /S /Y /E .\resources\static .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\static\
@REM rmdir /S /Q .\output\rtsp2rtmp_%ver%_window_amd64 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 linux_amd64
@REM md .\output\rtsp2rtmp_%ver%_window_amd64\output\log echo 打包linux_amd64平台
@REM md .\output\rtsp2rtmp_%ver%_window_amd64\conf 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\ xcopy /S /Y /E .\resources\static .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\static\
@REM xcopy /S /Y /E .\db .\output\rtsp2rtmp_%ver%_window_amd64\db\ xcopy /S /Y /E .\resources\conf .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
@REM xcopy .\conf\conf.yml .\output\rtsp2rtmp_%ver%_window_amd64\conf cd .\resources\output\releases\
@REM xcopy .\rtsp2rtmp_%ver%_window_amd64.exe .\output\rtsp2rtmp_%ver%_window_amd64\rtsp2rtmp.exe 7z a -ttar -so rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar rtsp2rtmp_%ver%_%GOOS%_%GOARCH%/ | 7z a -si rtsp2rtmp_%ver%_%GOOS%_%GOARCH%.tar.gz
@REM xcopy .\start.vbs .\output\rtsp2rtmp_%ver%_window_amd64\start.vbs cd ..\..\..\
@REM linux_adm64 @REM linux_arm
@REM rmdir /S /Q .\output\rtsp2rtmp_%ver%_linux_amd64 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 xcopy /S /Y /E .\resources\static .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\static\
@REM md .\output\rtsp2rtmp_%ver%_linux_amd64\output\log xcopy /S /Y /E .\resources\conf .\resources\output\releases\rtsp2rtmp_%ver%_%GOOS%_%GOARCH%\resources\conf
@REM md .\output\rtsp2rtmp_%ver%_linux_amd64\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
@REM xcopy /S /Y /E .\static .\output\rtsp2rtmp_%ver%_linux_amd64\static\ cd ..\..\..\
@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
pause pause

39
build.sh Normal file
View 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

View File

@@ -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/

View File

@@ -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/

View File

@@ -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
}
}

View File

@@ -1,9 +0,0 @@
server:
httpflv:
port: 8080
fileflv:
save: true
path: ./output/live
log:
path: ./output/log

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)
}

Binary file not shown.

View 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 '开始生效时间';

View File

@@ -22,26 +22,38 @@
``` ```
server: server:
user:
name: admin #网页登录用户名
password: admin #网页登录密码
rtmp:
httpflv: httpflv:
port: 8080 #程序的http端口 port: 8080
static:
path: ./resources/static #页面所在文件夹
fileflv: fileflv:
save: true #是否保存录像文件 path: ./resources/output/live #录像所在文件
path: ./output/live #录像文件夹
log: 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. 安装golanggc++编译器(sqlite3模块的需要用到window下可选择安装MinGW) 1. 安装golang
2. 获取[服务器源码](https://github.com/hkmadao/rtsp2rtmp.git) 2. 获取[服务器源码](https://github.com/hkmadao/rtsp2rtmp.git)
3. 进入项目目录 3. 安装postgresql数据库根据配置文件"resources/conf/conf-prod.yml"创建数据库
4. go build开发 4. 根据"docs/init/rtsp2rtmp-postgresql.sql"文件创建表
5. 进入项目目录
6. go build开发
###### 页面开发说明: ###### 页面开发说明:

40
go.mod
View File

@@ -1,37 +1,11 @@
module github.com/yumrano/rtsp2rtmp module github.com/hkmadao/rtsp2rtmp
go 1.13 go 1.16
require ( require (
github.com/beego/bee/v2 v2.0.2 // indirect github.com/beego/beego/v2 v2.0.1
github.com/beego/beego/v2 v2.0.1 // indirect github.com/deepch/vdk v0.0.0-20210523103705-5b25bda1a000
github.com/deepch/vdk v0.0.0-20210203031003-ae4ae47c04e1 github.com/gin-gonic/gin v1.7.2
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect github.com/google/uuid v1.2.0
github.com/gin-gonic/gin v1.6.3 github.com/lib/pq v1.0.0
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
) )

355
go.sum
View File

@@ -1,33 +1,12 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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/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/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-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/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-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/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/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/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 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE=
github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI= 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= 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 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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/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/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/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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= 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/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-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/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/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/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/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/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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepch/vdk v0.0.0-20210203031003-ae4ae47c04e1 h1:O7TrdSlVsYlQhfX7dcKU92VOVeaKmxQ24jyTUoqhgsc= github.com/deepch/vdk v0.0.0-20210523103705-5b25bda1a000 h1:yheim2C0sxxzQ61GYlbuKCnCq2hSFraFx4XlgcBC6FE=
github.com/deepch/vdk v0.0.0-20210203031003-ae4ae47c04e1/go.mod h1:mFKn6/jT2OgoMrw/2kgoVDN67kokS5T3kRn04x3eJXg= github.com/deepch/vdk v0.0.0-20210523103705-5b25bda1a000/go.mod h1:EZi580zgIZQOVwMcApOE/rTxuWCHvP/9yAFkUro14LI=
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/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 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/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/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/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/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.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/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 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 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.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 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/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.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.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.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 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/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 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 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 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 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.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 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 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-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.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/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.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/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/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.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.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.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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/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.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-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.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 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/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/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.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.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.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.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.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/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/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.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.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= 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/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/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 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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/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.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.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/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/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/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/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.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/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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ= 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 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 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.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/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/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 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 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 h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 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/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/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
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/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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-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/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 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/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/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/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/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.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.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/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.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= 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/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.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.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.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/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.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.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/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 v0.7.18/go.mod h1:+Bvnm3nYC6Nnp7VV6glUkuOfToB/AtMRZpOU8ihuf4c=
github.com/pion/ice/v2 v2.0.14/go.mod h1:wqaUbOq5ObDNU5ox1hRsEst0rWfsKuH1zXjQFEWiZwM= github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI=
github.com/pion/interceptor v0.0.8/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c= github.com/pion/interceptor v0.0.9/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/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/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k= 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.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/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.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/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.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/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.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/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/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/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
github.com/pion/srtp v1.5.1 h1:9Q3jAfslYZBt+C69SI/ZcONJh9049JUHZWYRRf5KEKw=
github.com/pion/srtp v1.5.1/go.mod h1:B+QgX5xPeQTNc1CJStJPHzOlHK66ViMDWTT0HZTCkcA= 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/srtp/v2 v2.0.1/go.mod h1:c8NWHhhkFf/drmHTAblkdu8++lsISEBBdAuiyxgqIsE=
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= github.com/pion/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.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.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.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A= 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.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
github.com/pion/transport v0.12.0/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.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/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/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.26/go.mod h1:XMZbZRNHyPDe1gzTIHFcQu02283YO45CbiwFgKvXnmc=
github.com/pion/webrtc/v2 v2.2.23/go.mod h1:1lN/3EcATkQxc7GJSQbISCGC2l64Xu2VSLpwEG3c/tM= github.com/pion/webrtc/v3 v3.0.5/go.mod h1:/EDCREM8y+JrJSkoCRHpoz//qtuBCOYV4E96vEK3bz0=
github.com/pion/webrtc/v3 v3.0.1/go.mod h1:ePzv8r2tzj95nJuPsns/7OiS5M8RiGSULdxzIRbXOtQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/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.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.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 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-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-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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 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-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.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 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/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/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 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= 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= 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/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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 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/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.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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-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/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 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 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 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/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 h1:0Ve0/CCjiAiyKddUMUn3RwIGlq2iTW4GuVzyoKBYO/8=
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= 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/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= 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.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/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/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/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= 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-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-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-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-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-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-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -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 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-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-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-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-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-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/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.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.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-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-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-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-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-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-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-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-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-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= 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-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-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-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-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-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-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-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-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-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-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/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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.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.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.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-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-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-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-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-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-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-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-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-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-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-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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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-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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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.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.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-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-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.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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 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-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-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 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.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.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 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 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/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 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-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-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-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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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/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/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.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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.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.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-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.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-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 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
View File

@@ -5,28 +5,26 @@ import (
"os/signal" "os/signal"
"syscall" "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/beego/beego/v2/core/logs"
"github.com/yumrano/rtsp2rtmp/app"
_ "github.com/yumrano/rtsp2rtmp/conf"
"github.com/yumrano/rtsp2rtmp/server"
) )
func main() { func main() {
go app.ServeHTTP() rtspclientmanager.GetSingleRtspClientManager().StartClient()
server.NewServer() task.GetSingleTask().StartTask()
web.GetSingleWeb().StartWeb()
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
done := make(chan bool, 1) done := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
// go func() {
// http.ListenAndServe("0.0.0.0:6060", nil)
// }()
logs.Info("Server Start Awaiting Signal") logs.Info("Server Start Awaiting Signal")
select { select {
case sig := <-sigs: case sig := <-sigs:
logs.Error(sig) logs.Info(sig)
case <-done: case <-done:
} }
logs.Error("Exiting") logs.Info("Exiting")
} }

View File

@@ -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")
}

View File

@@ -1,2 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
*.flv

View File

@@ -1,2 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
*.log

View 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

View 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
View File

@@ -0,0 +1 @@
*.flv

1
resources/output/log/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.log

View 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"
]
}

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View 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>

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

File diff suppressed because one or more lines are too long

View File

@@ -64,3 +64,5 @@ object-assign
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
//! moment.js

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

View File

@@ -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()}([]); !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.233489df.js.map //# sourceMappingURL=runtime-main.14fbbcda.js.map

View File

@@ -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)
}
}

View 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}`)
}

View 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
}

View 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)
}

View 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)
}

View 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
}

View 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)
}
}

View 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")
}

View 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)
}
}

View 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)
}

View 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
}

View File

@@ -7,26 +7,23 @@ import (
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
) )
func init() {
// 需要在init中注册定义的model
orm.RegisterModel(new(Camera))
}
type Camera struct { type Camera struct {
Id string `orm:"pk;column(id)" json:"id"` Id string `orm:"pk;column(id)" json:"id"`
Code string `orm:"column(code)" json:"code"` Code string `orm:"column(code)" json:"code"`
RtspURL string `orm:"column(rtsp_url)" json:"rtspURL"` RtspURL string `orm:"column(rtsp_url)" json:"rtspURL"`
RtmpURL string `orm:"column(rtmp_url)" json:"rtmpURL"` 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"` OnlineStatus int `orm:"column(online_status)" json:"onlineStatus"`
Enabled int `orm:"column(enabled)" json:"enabled"` 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"` Created time.Time `orm:"column(created)" json:"created"`
} }
func CameraInsert(e Camera) (i int64, err error) { func CameraInsert(e Camera) (i int64, err error) {
o := orm.NewOrm() o := orm.NewOrm()
i, err = o.Insert(&e) i, err = o.Insert(&e)
if err != nil { if err != nil && err != orm.ErrLastInsertIdUnavailable {
logs.Error("camera insert error : %v", err) logs.Error("camera insert error : %v", err)
return i, err return i, err
} }

View 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
}

View 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))
}

View 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
}

View 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
}

View 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)
}

View 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)
}
}

View 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
}

View File

@@ -1,12 +1,6 @@
package utils package utils
import ( func OrDone(done <-chan interface{}, c <-chan interface{}) <-chan interface{} {
"time"
"github.com/beego/beego/v2/core/logs"
)
func OrDone(done, c <-chan interface{}) <-chan interface{} {
valStream := make(chan interface{}) valStream := make(chan interface{})
go func() { go func() {
defer close(valStream) defer close(valStream)
@@ -15,7 +9,7 @@ func OrDone(done, c <-chan interface{}) <-chan interface{} {
case <-done: case <-done:
return return
case v, ok := <-c: case v, ok := <-c:
if ok == false { // 外界关闭数据流 if !ok { // 外界关闭数据流
return return
} }
select { // 防止写入阻塞 select { // 防止写入阻塞
@@ -28,7 +22,7 @@ func OrDone(done, c <-chan interface{}) <-chan interface{} {
return valStream 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{}) out1 := make(chan interface{})
out2 := make(chan interface{}) out2 := make(chan interface{})
go func() { go func() {
@@ -44,11 +38,24 @@ func Tee(done <-chan interface{}, in <-chan interface{}, timeout time.Duration)
out1 = nil // 置空阻塞机制完成select轮询 out1 = nil // 置空阻塞机制完成select轮询
case out2 <- val: case out2 <- val:
out2 = nil out2 = nil
case <-time.After(timeout): //设置超时发送时间,防止发送阻塞
logs.Error("Tee timeout")
} }
} }
} }
}() }()
return out1, out2 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
}

View File

@@ -33,3 +33,14 @@ func TokenTimeOut(token string, duration time.Duration) bool {
} }
return time.Now().After(tokenTime.Add(duration)) 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
View 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(&params)
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)
}

View File

@@ -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"
]
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
})
}
}
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}
}
}
}