mirror of https://github.com/gwuhaolin/livego.git
69 changed files with 2000 additions and 964 deletions
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
name: Release |
||||
on: |
||||
release: |
||||
types: [published] |
||||
jobs: |
||||
goreleaser: |
||||
runs-on: ubuntu-latest |
||||
env: |
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
||||
steps: |
||||
- name: Set up Go 1.13 |
||||
uses: actions/setup-go@v1 |
||||
with: |
||||
go-version: 1.13 |
||||
id: go |
||||
- name: Check out code into the Go module directory |
||||
uses: actions/checkout@v1 |
||||
- name: Get dependencies |
||||
run: go mod tidy |
||||
- name: Go release |
||||
uses: goreleaser/goreleaser-action@v1 |
||||
- name: Docker release |
||||
uses: elgohr/Publish-Docker-Github-Action@master |
||||
with: |
||||
name: gwuhaolin/livego |
||||
username: ${{ secrets.DOCKER_USERNAME }} |
||||
password: ${{ secrets.DOCKER_PASSWORD }} |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
name: Test |
||||
on: [push] |
||||
jobs: |
||||
test: |
||||
name: Build |
||||
runs-on: ${{ matrix.os }} |
||||
strategy: |
||||
matrix: |
||||
os: [ubuntu-latest, windows-latest, macOS-latest] |
||||
|
||||
steps: |
||||
- name: Set up Go 1.13 |
||||
uses: actions/setup-go@v1 |
||||
with: |
||||
go-version: 1.13 |
||||
id: go |
||||
- name: Check out code into the Go module directory |
||||
uses: actions/checkout@v1 |
||||
- name: Get dependencies |
||||
run: go mod tidy |
||||
- name: Test |
||||
run: go test ./... |
@ -1,3 +1,7 @@
@@ -1,3 +1,7 @@
|
||||
# Created by .ignore support plugin (hsz.mobi) |
||||
.idea |
||||
dist |
||||
.vscode |
||||
tmp |
||||
vendor |
||||
livego |
||||
|
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
before: |
||||
hooks: |
||||
- go mod tidy |
||||
builds: |
||||
- binary: livego |
||||
id: livego |
||||
main: ./main.go |
||||
goos: |
||||
- windows |
||||
- darwin |
||||
- linux |
||||
- freebsd |
||||
goarch: |
||||
- amd64 |
||||
- 386 |
||||
- arm |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
# Changelog |
||||
All notable changes to this project will be documented in this file. |
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
||||
|
||||
## [Unreleased] |
||||
|
||||
### Added |
||||
- JSON Web Token support. |
||||
``` json |
||||
// livego.json |
||||
{ |
||||
"jwt": { |
||||
"secret": "testing", |
||||
"algorithm": "HS256" |
||||
}, |
||||
"server": [ |
||||
{ |
||||
"appname": "live", |
||||
"live": true, |
||||
"hls": true |
||||
} |
||||
] |
||||
} |
||||
``` |
||||
- Use redis for store room keys |
||||
``` json |
||||
// livego.json |
||||
{ |
||||
"redis_addr": "localhost:6379", |
||||
"server": [ |
||||
{ |
||||
"appname": "live", |
||||
"live": true, |
||||
"hls": true |
||||
} |
||||
] |
||||
} |
||||
``` |
||||
- Makefile |
||||
|
||||
### Changed |
||||
- Show `players`. |
||||
- Show `stream_id`. |
||||
- Deleted keys saved in physical file, now the keys are in cached using `go-cache` by default. |
||||
- Using `logrus` like log system. |
||||
- Using method `.Get(queryParamName)` to get an url query param. |
||||
- Replaced `errors.New(...)` to `fmt.Errorf(...)`. |
||||
- Replaced types string on config params `liveon` and `hlson` to booleans `live: true/false` and `hls: true/false` |
||||
- Using viper for config, allow use file, cloud providers, environment vars or flags. |
||||
- Using yaml config by default. |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
FROM golang:latest as builder |
||||
WORKDIR /app |
||||
ENV GOPROXY https://goproxy.io |
||||
COPY go.mod go.sum ./ |
||||
RUN go mod download |
||||
COPY . . |
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o livego . |
||||
|
||||
FROM alpine:latest |
||||
RUN mkdir -p /app/config |
||||
WORKDIR /app |
||||
ENV RTMP_PORT 1935 |
||||
ENV HTTP_FLV_PORT 7001 |
||||
ENV HLS_PORT 7002 |
||||
ENV HTTP_OPERATION_PORT 8090 |
||||
COPY --from=builder /app/livego . |
||||
EXPOSE ${RTMP_PORT} |
||||
EXPOSE ${HTTP_FLV_PORT} |
||||
EXPOSE ${HLS_PORT} |
||||
EXPOSE ${HTTP_OPERATION_PORT} |
||||
ENTRYPOINT ["./livego"] |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2017 吴浩麟 |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
GOCMD ?= go |
||||
GOBUILD = $(GOCMD) build |
||||
GOCLEAN = $(GOCMD) clean |
||||
GOTEST = $(GOCMD) test |
||||
GOGET = $(GOCMD) get |
||||
BINARY_NAME = livego |
||||
BINARY_UNIX = $(BINARY_NAME)_unix |
||||
|
||||
DOCKER_ACC ?= gwuhaolin |
||||
DOCKER_REPO ?= livego |
||||
|
||||
TAG ?= $(shell git describe --tags --abbrev=0 2>/dev/null) |
||||
|
||||
default: all |
||||
|
||||
all: test build dockerize |
||||
build: |
||||
$(GOBUILD) -o $(BINARY_NAME) -v -ldflags="-X main.VERSION=$(TAG)" |
||||
|
||||
test: |
||||
$(GOTEST) -v ./... |
||||
|
||||
clean: |
||||
$(GOCLEAN) |
||||
rm -f $(BINARY_NAME) |
||||
rm -f $(BINARY_UNIX) |
||||
|
||||
run: build |
||||
./$(BINARY_NAME) |
||||
|
||||
build-linux: |
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_UNIX) -v |
||||
|
||||
dockerize: |
||||
docker build -t $(DOCKER_ACC)/$(DOCKER_REPO):$(TAG) . |
||||
docker push $(DOCKER_ACC)/$(DOCKER_REPO):$(TAG) |
@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
|
||||
package configure |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/gwuhaolin/livego/utils/uid" |
||||
|
||||
"github.com/go-redis/redis/v7" |
||||
"github.com/patrickmn/go-cache" |
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
type RoomKeysType struct { |
||||
redisCli *redis.Client |
||||
localCache *cache.Cache |
||||
} |
||||
|
||||
var RoomKeys = &RoomKeysType{ |
||||
localCache: cache.New(cache.NoExpiration, 0), |
||||
} |
||||
|
||||
var saveInLocal = true |
||||
|
||||
func Init() { |
||||
saveInLocal = len(Config.GetString("redis_addr")) == 0 |
||||
if saveInLocal { |
||||
return |
||||
} |
||||
|
||||
RoomKeys.redisCli = redis.NewClient(&redis.Options{ |
||||
Addr: Config.GetString("redis_addr"), |
||||
Password: Config.GetString("redis_pwd"), |
||||
DB: 0, |
||||
}) |
||||
|
||||
_, err := RoomKeys.redisCli.Ping().Result() |
||||
if err != nil { |
||||
log.Panic("Redis: ", err) |
||||
} |
||||
|
||||
log.Info("Redis connected") |
||||
} |
||||
|
||||
// set/reset a random key for channel
|
||||
func (r *RoomKeysType) SetKey(channel string) (key string, err error) { |
||||
if !saveInLocal { |
||||
for { |
||||
key = uid.RandStringRunes(48) |
||||
if _, err = r.redisCli.Get(key).Result(); err == redis.Nil { |
||||
err = r.redisCli.Set(channel, key, 0).Err() |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
err = r.redisCli.Set(key, channel, 0).Err() |
||||
return |
||||
} else if err != nil { |
||||
return |
||||
} |
||||
} |
||||
} |
||||
|
||||
for { |
||||
key = uid.RandStringRunes(48) |
||||
if _, found := r.localCache.Get(key); !found { |
||||
r.localCache.SetDefault(channel, key) |
||||
r.localCache.SetDefault(key, channel) |
||||
break |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
func (r *RoomKeysType) GetKey(channel string) (newKey string, err error) { |
||||
if !saveInLocal { |
||||
if newKey, err = r.redisCli.Get(channel).Result(); err == redis.Nil { |
||||
newKey, err = r.SetKey(channel) |
||||
log.Debugf("[KEY] new channel [%s]: %s", channel, newKey) |
||||
return |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
var key interface{} |
||||
var found bool |
||||
if key, found = r.localCache.Get(channel); found { |
||||
return key.(string), nil |
||||
} |
||||
newKey, err = r.SetKey(channel) |
||||
log.Debugf("[KEY] new channel [%s]: %s", channel, newKey) |
||||
return |
||||
} |
||||
|
||||
func (r *RoomKeysType) GetChannel(key string) (channel string, err error) { |
||||
if !saveInLocal { |
||||
return r.redisCli.Get(key).Result() |
||||
} |
||||
|
||||
chann, found := r.localCache.Get(key) |
||||
if found { |
||||
return chann.(string), nil |
||||
} else { |
||||
return "", fmt.Errorf("%s does not exists", key) |
||||
} |
||||
} |
||||
|
||||
func (r *RoomKeysType) DeleteChannel(channel string) bool { |
||||
if !saveInLocal { |
||||
return r.redisCli.Del(channel).Err() != nil |
||||
} |
||||
|
||||
key, ok := r.localCache.Get(channel) |
||||
if ok { |
||||
r.localCache.Delete(channel) |
||||
r.localCache.Delete(key.(string)) |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func (r *RoomKeysType) DeleteKey(key string) bool { |
||||
if !saveInLocal { |
||||
return r.redisCli.Del(key).Err() != nil |
||||
} |
||||
|
||||
channel, ok := r.localCache.Get(key) |
||||
if ok { |
||||
r.localCache.Delete(channel.(string)) |
||||
r.localCache.Delete(key) |
||||
return true |
||||
} |
||||
return false |
||||
} |
@ -1,74 +1,161 @@
@@ -1,74 +1,161 @@
|
||||
package configure |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"io/ioutil" |
||||
"log" |
||||
"strings" |
||||
|
||||
"github.com/kr/pretty" |
||||
log "github.com/sirupsen/logrus" |
||||
"github.com/spf13/pflag" |
||||
"github.com/spf13/viper" |
||||
) |
||||
|
||||
/* |
||||
{ |
||||
[ |
||||
{ |
||||
"application":"live", |
||||
"live":"on", |
||||
"hls":"on", |
||||
"static_push":["rtmp://xx/live"] |
||||
} |
||||
] |
||||
"server": [ |
||||
{ |
||||
"appname": "live", |
||||
"live": true, |
||||
"hls": true, |
||||
"static_push": [] |
||||
} |
||||
] |
||||
} |
||||
*/ |
||||
|
||||
type Application struct { |
||||
Appname string |
||||
Liveon string |
||||
Hlson string |
||||
Static_push []string |
||||
Appname string `mapstructure:"appname"` |
||||
Live bool `mapstructure:"live"` |
||||
Hls bool `mapstructure:"hls"` |
||||
StaticPush []string `mapstructure:"static_push"` |
||||
} |
||||
|
||||
type Applications []Application |
||||
|
||||
type JWT struct { |
||||
Secret string `mapstructure:"secret"` |
||||
Algorithm string `mapstructure:"algorithm"` |
||||
} |
||||
type ServerCfg struct { |
||||
Server []Application |
||||
Level string `mapstructure:"level"` |
||||
ConfigFile string `mapstructure:"config_file"` |
||||
FLVDir string `mapstructure:"flv_dir"` |
||||
RTMPAddr string `mapstructure:"rtmp_addr"` |
||||
HTTPFLVAddr string `mapstructure:"httpflv_addr"` |
||||
HLSAddr string `mapstructure:"hls_addr"` |
||||
HLSKeepAfterEnd bool `mapstructure:"hls_keep_after_end"` |
||||
APIAddr string `mapstructure:"api_addr"` |
||||
RedisAddr string `mapstructure:"redis_addr"` |
||||
RedisPwd string `mapstructure:"redis_pwd"` |
||||
ReadTimeout int `mapstructure:"read_timeout"` |
||||
WriteTimeout int `mapstructure:"write_timeout"` |
||||
GopNum int `mapstructure:"gop_num"` |
||||
JWT JWT `mapstructure:"jwt"` |
||||
Server Applications `mapstructure:"server"` |
||||
} |
||||
|
||||
// default config
|
||||
var defaultConf = ServerCfg{ |
||||
ConfigFile: "livego.yaml", |
||||
RTMPAddr: ":1935", |
||||
HTTPFLVAddr: ":7001", |
||||
HLSAddr: ":7002", |
||||
HLSKeepAfterEnd: false, |
||||
APIAddr: ":8090", |
||||
WriteTimeout: 10, |
||||
ReadTimeout: 10, |
||||
GopNum: 1, |
||||
Server: Applications{{ |
||||
Appname: "live", |
||||
Live: true, |
||||
Hls: true, |
||||
StaticPush: nil, |
||||
}}, |
||||
} |
||||
|
||||
var RtmpServercfg ServerCfg |
||||
var Config = viper.New() |
||||
|
||||
func LoadConfig(configfilename string) error { |
||||
log.Printf("starting load configure file(%s)......", configfilename) |
||||
data, err := ioutil.ReadFile(configfilename) |
||||
if err != nil { |
||||
log.Printf("ReadFile %s error:%v", configfilename, err) |
||||
return err |
||||
func initLog() { |
||||
if l, err := log.ParseLevel(Config.GetString("level")); err == nil { |
||||
log.SetLevel(l) |
||||
log.SetReportCaller(l == log.DebugLevel) |
||||
} |
||||
} |
||||
|
||||
func init() { |
||||
defer Init() |
||||
|
||||
log.Printf("loadconfig: \r\n%s", string(data)) |
||||
// Default config
|
||||
b, _ := json.Marshal(defaultConf) |
||||
defaultConfig := bytes.NewReader(b) |
||||
viper.SetConfigType("json") |
||||
viper.ReadConfig(defaultConfig) |
||||
Config.MergeConfigMap(viper.AllSettings()) |
||||
|
||||
err = json.Unmarshal(data, &RtmpServercfg) |
||||
// Flags
|
||||
pflag.String("rtmp_addr", ":1935", "RTMP server listen address") |
||||
pflag.String("httpflv_addr", ":7001", "HTTP-FLV server listen address") |
||||
pflag.String("hls_addr", ":7002", "HLS server listen address") |
||||
pflag.String("api_addr", ":8090", "HTTP manage interface server listen address") |
||||
pflag.String("config_file", "livego.yaml", "configure filename") |
||||
pflag.String("level", "info", "Log level") |
||||
pflag.Bool("hls_keep_after_end", false, "Maintains the HLS after the stream ends") |
||||
pflag.String("flv_dir", "tmp", "output flv file at flvDir/APP/KEY_TIME.flv") |
||||
pflag.Int("read_timeout", 10, "read time out") |
||||
pflag.Int("write_timeout", 10, "write time out") |
||||
pflag.Int("gop_num", 1, "gop num") |
||||
pflag.Parse() |
||||
Config.BindPFlags(pflag.CommandLine) |
||||
|
||||
// File
|
||||
Config.SetConfigFile(Config.GetString("config_file")) |
||||
Config.AddConfigPath(".") |
||||
err := Config.ReadInConfig() |
||||
if err != nil { |
||||
log.Printf("json.Unmarshal error:%v", err) |
||||
return err |
||||
log.Warning(err) |
||||
log.Info("Using default config") |
||||
} else { |
||||
Config.MergeInConfig() |
||||
} |
||||
log.Printf("get config json data:%v", RtmpServercfg) |
||||
return nil |
||||
|
||||
// Environment
|
||||
replacer := strings.NewReplacer(".", "_") |
||||
Config.SetEnvKeyReplacer(replacer) |
||||
Config.AllowEmptyEnv(true) |
||||
Config.AutomaticEnv() |
||||
|
||||
// Log
|
||||
initLog() |
||||
|
||||
// Print final config
|
||||
c := ServerCfg{} |
||||
Config.Unmarshal(&c) |
||||
log.Debugf("Current configurations: \n%# v", pretty.Formatter(c)) |
||||
} |
||||
|
||||
func CheckAppName(appname string) bool { |
||||
for _, app := range RtmpServercfg.Server { |
||||
if (app.Appname == appname) && (app.Liveon == "on") { |
||||
return true |
||||
apps := Applications{} |
||||
Config.UnmarshalKey("server", &apps) |
||||
for _, app := range apps { |
||||
if app.Appname == appname { |
||||
return app.Live |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func GetStaticPushUrlList(appname string) ([]string, bool) { |
||||
for _, app := range RtmpServercfg.Server { |
||||
if (app.Appname == appname) && (app.Liveon == "on") { |
||||
if len(app.Static_push) > 0 { |
||||
return app.Static_push, true |
||||
apps := Applications{} |
||||
Config.UnmarshalKey("server", &apps) |
||||
for _, app := range apps { |
||||
if (app.Appname == appname) && app.Live { |
||||
if len(app.StaticPush) > 0 { |
||||
return app.StaticPush, true |
||||
} else { |
||||
return nil, false |
||||
} |
||||
} |
||||
|
||||
} |
||||
return nil, false |
||||
} |
||||
|
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
module github.com/gwuhaolin/livego |
||||
|
||||
go 1.13 |
||||
|
||||
require ( |
||||
github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b |
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible |
||||
github.com/go-redis/redis/v7 v7.2.0 |
||||
github.com/gorilla/mux v1.7.4 // indirect |
||||
github.com/kr/pretty v0.1.0 |
||||
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6 |
||||
github.com/patrickmn/go-cache v2.1.0+incompatible |
||||
github.com/satori/go.uuid v1.2.0 |
||||
github.com/sirupsen/logrus v1.5.0 |
||||
github.com/spf13/pflag v1.0.3 |
||||
github.com/spf13/viper v1.6.3 |
||||
github.com/stretchr/testify v1.4.0 |
||||
github.com/urfave/negroni v1.0.0 // indirect |
||||
) |
@ -0,0 +1,200 @@
@@ -0,0 +1,200 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= |
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= |
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= |
||||
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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= |
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= |
||||
github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b h1:CvoEHGmxWl5kONC5icxwqV899dkf4VjOScbxLpllEnw= |
||||
github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM= |
||||
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/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= |
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= |
||||
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/go-semver v0.2.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/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= |
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= |
||||
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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= |
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= |
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= |
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= |
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= |
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= |
||||
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs= |
||||
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= |
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= |
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= |
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= |
||||
github.com/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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= |
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= |
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= |
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= |
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= |
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= |
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= |
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= |
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= |
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= |
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= |
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= |
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= |
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= |
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= |
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= |
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= |
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= |
||||
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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= |
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= |
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= |
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= |
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= |
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= |
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= |
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= |
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= |
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= |
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= |
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= |
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= |
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= |
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= |
||||
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.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= |
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= |
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= |
||||
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6 h1:lNCW6THrCKBiJBpz8kbVGjC7MgdCGKwuvBgc7LoD6sw= |
||||
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= |
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= |
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= |
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= |
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= |
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||
github.com/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_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/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/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/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= |
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= |
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= |
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= |
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= |
||||
github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= |
||||
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= |
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= |
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= |
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= |
||||
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 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= |
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= |
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= |
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= |
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= |
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= |
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= |
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= |
||||
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= |
||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= |
||||
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= |
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= |
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= |
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= |
||||
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= |
||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= |
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= |
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= |
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= |
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= |
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= |
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= |
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= |
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
||||
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-20181114220301-adae6a3d119a/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= |
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= |
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= |
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= |
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= |
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= |
||||
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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= |
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= |
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= |
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= |
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= |
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= |
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= |
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= |
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= |
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= |
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= |
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= |
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= |
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= |
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= |
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
@ -1,11 +0,0 @@
@@ -1,11 +0,0 @@
|
||||
build: |
||||
binary: livego |
||||
goos: |
||||
- windows |
||||
- darwin |
||||
- linux |
||||
- freebsd |
||||
goarch: |
||||
- amd64 |
||||
- 386 |
||||
- arm |
@ -1,10 +0,0 @@
@@ -1,10 +0,0 @@
|
||||
{ |
||||
"server": [ |
||||
{ |
||||
"appname":"live", |
||||
"liveon":"on", |
||||
"hlson":"on" |
||||
} |
||||
] |
||||
} |
||||
|
@ -1,130 +0,0 @@
@@ -1,130 +0,0 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"flag" |
||||
"github.com/gwuhaolin/livego/configure" |
||||
"github.com/gwuhaolin/livego/protocol/hls" |
||||
"github.com/gwuhaolin/livego/protocol/httpflv" |
||||
"github.com/gwuhaolin/livego/protocol/httpopera" |
||||
"github.com/gwuhaolin/livego/protocol/rtmp" |
||||
"log" |
||||
"net" |
||||
"time" |
||||
) |
||||
|
||||
var ( |
||||
version = "master" |
||||
rtmpAddr = flag.String("rtmp-addr", ":1935", "RTMP server listen address") |
||||
httpFlvAddr = flag.String("httpflv-addr", ":7001", "HTTP-FLV server listen address") |
||||
hlsAddr = flag.String("hls-addr", ":7002", "HLS server listen address") |
||||
operaAddr = flag.String("manage-addr", ":8090", "HTTP manage interface server listen address") |
||||
configfilename = flag.String("cfgfile", "livego.cfg", "live configure filename") |
||||
) |
||||
|
||||
func init() { |
||||
log.SetFlags(log.Lshortfile | log.Ltime | log.Ldate) |
||||
flag.Parse() |
||||
} |
||||
|
||||
func startHls() *hls.Server { |
||||
hlsListen, err := net.Listen("tcp", *hlsAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
hlsServer := hls.NewServer() |
||||
go func() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Println("HLS server panic: ", r) |
||||
} |
||||
}() |
||||
log.Println("HLS listen On", *hlsAddr) |
||||
hlsServer.Serve(hlsListen) |
||||
}() |
||||
return hlsServer |
||||
} |
||||
|
||||
func startRtmp(stream *rtmp.RtmpStream, hlsServer *hls.Server) { |
||||
rtmpListen, err := net.Listen("tcp", *rtmpAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
var rtmpServer *rtmp.Server |
||||
|
||||
if hlsServer == nil { |
||||
rtmpServer = rtmp.NewRtmpServer(stream, nil) |
||||
log.Printf("hls server disable....") |
||||
} else { |
||||
rtmpServer = rtmp.NewRtmpServer(stream, hlsServer) |
||||
log.Printf("hls server enable....") |
||||
} |
||||
|
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Println("RTMP server panic: ", r) |
||||
} |
||||
}() |
||||
log.Println("RTMP Listen On", *rtmpAddr) |
||||
rtmpServer.Serve(rtmpListen) |
||||
} |
||||
|
||||
func startHTTPFlv(stream *rtmp.RtmpStream) { |
||||
flvListen, err := net.Listen("tcp", *httpFlvAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
hdlServer := httpflv.NewServer(stream) |
||||
go func() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Println("HTTP-FLV server panic: ", r) |
||||
} |
||||
}() |
||||
log.Println("HTTP-FLV listen On", *httpFlvAddr) |
||||
hdlServer.Serve(flvListen) |
||||
}() |
||||
} |
||||
|
||||
func startHTTPOpera(stream *rtmp.RtmpStream) { |
||||
if *operaAddr != "" { |
||||
opListen, err := net.Listen("tcp", *operaAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
opServer := httpopera.NewServer(stream, *rtmpAddr) |
||||
go func() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Println("HTTP-Operation server panic: ", r) |
||||
} |
||||
}() |
||||
log.Println("HTTP-Operation listen On", *operaAddr) |
||||
opServer.Serve(opListen) |
||||
}() |
||||
} |
||||
} |
||||
|
||||
func main() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Println("livego panic: ", r) |
||||
time.Sleep(1 * time.Second) |
||||
} |
||||
}() |
||||
log.Println("start livego, version", version) |
||||
err := configure.LoadConfig(*configfilename) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
stream := rtmp.NewRtmpStream() |
||||
hlsServer := startHls() |
||||
startHTTPFlv(stream) |
||||
startHTTPOpera(stream) |
||||
|
||||
startRtmp(stream, hlsServer) |
||||
//startRtmp(stream, nil)
|
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
# # Logger level |
||||
# level: info |
||||
|
||||
# # FLV Options |
||||
# flv_dir: "./tmp" |
||||
# httpflv_addr: ":7001" |
||||
|
||||
# # RTMP Options |
||||
# rtmp_addr: ":1935" |
||||
# read_timeout: 10 |
||||
# write_timeout: 10 |
||||
|
||||
# # HLS Options |
||||
# hls_addr: ":7002" |
||||
|
||||
# # API Options |
||||
# api_addr: ":8090" |
||||
server: |
||||
- appname: live |
||||
live: true |
||||
hls: true |
After Width: | Height: | Size: 5.4 KiB |
@ -0,0 +1,143 @@
@@ -0,0 +1,143 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"github.com/gwuhaolin/livego/configure" |
||||
"github.com/gwuhaolin/livego/protocol/api" |
||||
"github.com/gwuhaolin/livego/protocol/hls" |
||||
"github.com/gwuhaolin/livego/protocol/httpflv" |
||||
"github.com/gwuhaolin/livego/protocol/rtmp" |
||||
"net" |
||||
"path" |
||||
"runtime" |
||||
"time" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
var VERSION = "master" |
||||
|
||||
func startHls() *hls.Server { |
||||
hlsAddr := configure.Config.GetString("hls_addr") |
||||
hlsListen, err := net.Listen("tcp", hlsAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
hlsServer := hls.NewServer() |
||||
go func() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Error("HLS server panic: ", r) |
||||
} |
||||
}() |
||||
log.Info("HLS listen On ", hlsAddr) |
||||
hlsServer.Serve(hlsListen) |
||||
}() |
||||
return hlsServer |
||||
} |
||||
|
||||
var rtmpAddr string |
||||
|
||||
func startRtmp(stream *rtmp.RtmpStream, hlsServer *hls.Server) { |
||||
rtmpAddr = configure.Config.GetString("rtmp_addr") |
||||
|
||||
rtmpListen, err := net.Listen("tcp", rtmpAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
var rtmpServer *rtmp.Server |
||||
|
||||
if hlsServer == nil { |
||||
rtmpServer = rtmp.NewRtmpServer(stream, nil) |
||||
log.Info("HLS server disable....") |
||||
} else { |
||||
rtmpServer = rtmp.NewRtmpServer(stream, hlsServer) |
||||
log.Info("HLS server enable....") |
||||
} |
||||
|
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Error("RTMP server panic: ", r) |
||||
} |
||||
}() |
||||
log.Info("RTMP Listen On ", rtmpAddr) |
||||
rtmpServer.Serve(rtmpListen) |
||||
} |
||||
|
||||
func startHTTPFlv(stream *rtmp.RtmpStream) { |
||||
httpflvAddr := configure.Config.GetString("httpflv_addr") |
||||
|
||||
flvListen, err := net.Listen("tcp", httpflvAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
hdlServer := httpflv.NewServer(stream) |
||||
go func() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Error("HTTP-FLV server panic: ", r) |
||||
} |
||||
}() |
||||
log.Info("HTTP-FLV listen On ", httpflvAddr) |
||||
hdlServer.Serve(flvListen) |
||||
}() |
||||
} |
||||
|
||||
func startAPI(stream *rtmp.RtmpStream) { |
||||
apiAddr := configure.Config.GetString("api_addr") |
||||
|
||||
if apiAddr != "" { |
||||
opListen, err := net.Listen("tcp", apiAddr) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
opServer := api.NewServer(stream, rtmpAddr) |
||||
go func() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Error("HTTP-API server panic: ", r) |
||||
} |
||||
}() |
||||
log.Info("HTTP-API listen On ", apiAddr) |
||||
opServer.Serve(opListen) |
||||
}() |
||||
} |
||||
} |
||||
|
||||
func init() { |
||||
log.SetFormatter(&log.TextFormatter{ |
||||
FullTimestamp: true, |
||||
CallerPrettyfier: func(f *runtime.Frame) (string, string) { |
||||
filename := path.Base(f.File) |
||||
return fmt.Sprintf("%s()", f.Function), fmt.Sprintf(" %s:%d", filename, f.Line) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
func main() { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Error("livego panic: ", r) |
||||
time.Sleep(1 * time.Second) |
||||
} |
||||
}() |
||||
|
||||
log.Infof(` |
||||
_ _ ____ |
||||
| | (_)_ _____ / ___| ___ |
||||
| | | \ \ / / _ \ | _ / _ \ |
||||
| |___| |\ V / __/ |_| | (_) | |
||||
|_____|_| \_/ \___|\____|\___/ |
||||
version: %s |
||||
`, VERSION) |
||||
|
||||
stream := rtmp.NewRtmpStream() |
||||
hlsServer := startHls() |
||||
startHTTPFlv(stream) |
||||
startAPI(stream) |
||||
|
||||
startRtmp(stream, hlsServer) |
||||
} |
@ -0,0 +1,425 @@
@@ -0,0 +1,425 @@
|
||||
package api |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"net" |
||||
"net/http" |
||||
|
||||
"github.com/gwuhaolin/livego/av" |
||||
"github.com/gwuhaolin/livego/configure" |
||||
"github.com/gwuhaolin/livego/protocol/rtmp" |
||||
"github.com/gwuhaolin/livego/protocol/rtmp/rtmprelay" |
||||
|
||||
jwtmiddleware "github.com/auth0/go-jwt-middleware" |
||||
"github.com/dgrijalva/jwt-go" |
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
type Response struct { |
||||
w http.ResponseWriter |
||||
Status int `json:"status"` |
||||
Data interface{} `json:"data"` |
||||
} |
||||
|
||||
func (r *Response) SendJson() (int, error) { |
||||
resp, _ := json.Marshal(r) |
||||
r.w.Header().Set("Content-Type", "application/json") |
||||
r.w.WriteHeader(r.Status) |
||||
return r.w.Write(resp) |
||||
} |
||||
|
||||
type Operation struct { |
||||
Method string `json:"method"` |
||||
URL string `json:"url"` |
||||
Stop bool `json:"stop"` |
||||
} |
||||
|
||||
type OperationChange struct { |
||||
Method string `json:"method"` |
||||
SourceURL string `json:"source_url"` |
||||
TargetURL string `json:"target_url"` |
||||
Stop bool `json:"stop"` |
||||
} |
||||
|
||||
type ClientInfo struct { |
||||
url string |
||||
rtmpRemoteClient *rtmp.Client |
||||
rtmpLocalClient *rtmp.Client |
||||
} |
||||
|
||||
type Server struct { |
||||
handler av.Handler |
||||
session map[string]*rtmprelay.RtmpRelay |
||||
rtmpAddr string |
||||
} |
||||
|
||||
func NewServer(h av.Handler, rtmpAddr string) *Server { |
||||
return &Server{ |
||||
handler: h, |
||||
session: make(map[string]*rtmprelay.RtmpRelay), |
||||
rtmpAddr: rtmpAddr, |
||||
} |
||||
} |
||||
|
||||
func JWTMiddleware(next http.Handler) http.Handler { |
||||
isJWT := len(configure.Config.GetString("jwt.secret")) > 0 |
||||
if !isJWT { |
||||
return next |
||||
} |
||||
|
||||
log.Info("Using JWT middleware") |
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||
var algorithm jwt.SigningMethod |
||||
if len(configure.Config.GetString("jwt.algorithm")) > 0 { |
||||
algorithm = jwt.GetSigningMethod(configure.Config.GetString("jwt.algorithm")) |
||||
} |
||||
|
||||
if algorithm == nil { |
||||
algorithm = jwt.SigningMethodHS256 |
||||
} |
||||
|
||||
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{ |
||||
Extractor: jwtmiddleware.FromFirst(jwtmiddleware.FromAuthHeader, jwtmiddleware.FromParameter("jwt")), |
||||
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { |
||||
return []byte(configure.Config.GetString("jwt.secret")), nil |
||||
}, |
||||
SigningMethod: algorithm, |
||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err string) { |
||||
res := &Response{ |
||||
w: w, |
||||
Status: 403, |
||||
Data: err, |
||||
} |
||||
res.SendJson() |
||||
}, |
||||
}) |
||||
|
||||
jwtMiddleware.HandlerWithNext(w, r, next.ServeHTTP) |
||||
}) |
||||
} |
||||
|
||||
func (s *Server) Serve(l net.Listener) error { |
||||
mux := http.NewServeMux() |
||||
|
||||
mux.Handle("/statics/", http.StripPrefix("/statics/", http.FileServer(http.Dir("statics")))) |
||||
|
||||
mux.HandleFunc("/control/push", func(w http.ResponseWriter, r *http.Request) { |
||||
s.handlePush(w, r) |
||||
}) |
||||
mux.HandleFunc("/control/pull", func(w http.ResponseWriter, r *http.Request) { |
||||
s.handlePull(w, r) |
||||
}) |
||||
mux.HandleFunc("/control/get", func(w http.ResponseWriter, r *http.Request) { |
||||
s.handleGet(w, r) |
||||
}) |
||||
mux.HandleFunc("/control/reset", func(w http.ResponseWriter, r *http.Request) { |
||||
s.handleReset(w, r) |
||||
}) |
||||
mux.HandleFunc("/control/delete", func(w http.ResponseWriter, r *http.Request) { |
||||
s.handleDelete(w, r) |
||||
}) |
||||
mux.HandleFunc("/stat/livestat", func(w http.ResponseWriter, r *http.Request) { |
||||
s.GetLiveStatics(w, r) |
||||
}) |
||||
http.Serve(l, JWTMiddleware(mux)) |
||||
return nil |
||||
} |
||||
|
||||
type stream struct { |
||||
Key string `json:"key"` |
||||
Url string `json:"url"` |
||||
StreamId uint32 `json:"stream_id"` |
||||
VideoTotalBytes uint64 `json:"video_total_bytes"` |
||||
VideoSpeed uint64 `json:"video_speed"` |
||||
AudioTotalBytes uint64 `json:"audio_total_bytes"` |
||||
AudioSpeed uint64 `json:"audio_speed"` |
||||
} |
||||
|
||||
type streams struct { |
||||
Publishers []stream `json:"publishers"` |
||||
Players []stream `json:"players"` |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/stat/livestat
|
||||
func (server *Server) GetLiveStatics(w http.ResponseWriter, req *http.Request) { |
||||
res := &Response{ |
||||
w: w, |
||||
Data: nil, |
||||
Status: 200, |
||||
} |
||||
|
||||
defer res.SendJson() |
||||
|
||||
rtmpStream := server.handler.(*rtmp.RtmpStream) |
||||
if rtmpStream == nil { |
||||
res.Status = 500 |
||||
res.Data = "Get rtmp stream information error" |
||||
return |
||||
} |
||||
|
||||
msgs := new(streams) |
||||
|
||||
rtmpStream.GetStreams().Range(func(key, val interface{}) bool { |
||||
if s, ok := val.(*rtmp.Stream); ok { |
||||
if s.GetReader() != nil { |
||||
switch s.GetReader().(type) { |
||||
case *rtmp.VirReader: |
||||
v := s.GetReader().(*rtmp.VirReader) |
||||
msg := stream{key.(string), v.Info().URL, v.ReadBWInfo.StreamId, v.ReadBWInfo.VideoDatainBytes, v.ReadBWInfo.VideoSpeedInBytesperMS, |
||||
v.ReadBWInfo.AudioDatainBytes, v.ReadBWInfo.AudioSpeedInBytesperMS} |
||||
msgs.Publishers = append(msgs.Publishers, msg) |
||||
} |
||||
} |
||||
} |
||||
return true |
||||
}) |
||||
|
||||
rtmpStream.GetStreams().Range(func(key, val interface{}) bool { |
||||
ws := val.(*rtmp.Stream).GetWs() |
||||
ws.Range(func(k, v interface{}) bool { |
||||
if pw, ok := v.(*rtmp.PackWriterCloser); ok { |
||||
if pw.GetWriter() != nil { |
||||
switch pw.GetWriter().(type) { |
||||
case *rtmp.VirWriter: |
||||
v := pw.GetWriter().(*rtmp.VirWriter) |
||||
msg := stream{key.(string), v.Info().URL, v.WriteBWInfo.StreamId, v.WriteBWInfo.VideoDatainBytes, v.WriteBWInfo.VideoSpeedInBytesperMS, |
||||
v.WriteBWInfo.AudioDatainBytes, v.WriteBWInfo.AudioSpeedInBytesperMS} |
||||
msgs.Players = append(msgs.Players, msg) |
||||
} |
||||
} |
||||
} |
||||
return true |
||||
}) |
||||
return true |
||||
}) |
||||
|
||||
resp, _ := json.Marshal(msgs) |
||||
res.Data = resp |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/control/pull?&oper=start&app=live&name=123456&url=rtmp://192.168.16.136/live/123456
|
||||
func (s *Server) handlePull(w http.ResponseWriter, req *http.Request) { |
||||
var retString string |
||||
var err error |
||||
|
||||
res := &Response{ |
||||
w: w, |
||||
Data: nil, |
||||
Status: 200, |
||||
} |
||||
|
||||
defer res.SendJson() |
||||
|
||||
if req.ParseForm() != nil { |
||||
res.Status = 400 |
||||
res.Data = "url: /control/pull?&oper=start&app=live&name=123456&url=rtmp://192.168.16.136/live/123456" |
||||
return |
||||
} |
||||
|
||||
oper := req.Form.Get("oper") |
||||
app := req.Form.Get("app") |
||||
name := req.Form.Get("name") |
||||
url := req.Form.Get("url") |
||||
|
||||
log.Debugf("control pull: oper=%v, app=%v, name=%v, url=%v", oper, app, name, url) |
||||
if (len(app) <= 0) || (len(name) <= 0) || (len(url) <= 0) { |
||||
res.Status = 400 |
||||
res.Data = "control push parameter error, please check them." |
||||
return |
||||
} |
||||
|
||||
remoteurl := "rtmp://127.0.0.1" + s.rtmpAddr + "/" + app + "/" + name |
||||
localurl := url |
||||
|
||||
keyString := "pull:" + app + "/" + name |
||||
if oper == "stop" { |
||||
pullRtmprelay, found := s.session[keyString] |
||||
|
||||
if !found { |
||||
retString = fmt.Sprintf("session key[%s] not exist, please check it again.", keyString) |
||||
res.Status = 400 |
||||
res.Data = retString |
||||
return |
||||
} |
||||
log.Debugf("rtmprelay stop push %s from %s", remoteurl, localurl) |
||||
pullRtmprelay.Stop() |
||||
|
||||
delete(s.session, keyString) |
||||
retString = fmt.Sprintf("<h1>push url stop %s ok</h1></br>", url) |
||||
res.Status = 400 |
||||
res.Data = retString |
||||
log.Debugf("pull stop return %s", retString) |
||||
} else { |
||||
pullRtmprelay := rtmprelay.NewRtmpRelay(&localurl, &remoteurl) |
||||
log.Debugf("rtmprelay start push %s from %s", remoteurl, localurl) |
||||
err = pullRtmprelay.Start() |
||||
if err != nil { |
||||
retString = fmt.Sprintf("push error=%v", err) |
||||
} else { |
||||
s.session[keyString] = pullRtmprelay |
||||
retString = fmt.Sprintf("<h1>push url start %s ok</h1></br>", url) |
||||
} |
||||
res.Status = 400 |
||||
res.Data = retString |
||||
log.Debugf("pull start return %s", retString) |
||||
} |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/control/push?&oper=start&app=live&name=123456&url=rtmp://192.168.16.136/live/123456
|
||||
func (s *Server) handlePush(w http.ResponseWriter, req *http.Request) { |
||||
var retString string |
||||
var err error |
||||
|
||||
res := &Response{ |
||||
w: w, |
||||
Data: nil, |
||||
Status: 200, |
||||
} |
||||
|
||||
defer res.SendJson() |
||||
|
||||
if req.ParseForm() != nil { |
||||
res.Data = "url: /control/push?&oper=start&app=live&name=123456&url=rtmp://192.168.16.136/live/123456" |
||||
return |
||||
} |
||||
|
||||
oper := req.Form.Get("oper") |
||||
app := req.Form.Get("app") |
||||
name := req.Form.Get("name") |
||||
url := req.Form.Get("url") |
||||
|
||||
log.Debugf("control push: oper=%v, app=%v, name=%v, url=%v", oper, app, name, url) |
||||
if (len(app) <= 0) || (len(name) <= 0) || (len(url) <= 0) { |
||||
res.Data = "control push parameter error, please check them." |
||||
return |
||||
} |
||||
|
||||
localurl := "rtmp://127.0.0.1" + s.rtmpAddr + "/" + app + "/" + name |
||||
remoteurl := url |
||||
|
||||
keyString := "push:" + app + "/" + name |
||||
if oper == "stop" { |
||||
pushRtmprelay, found := s.session[keyString] |
||||
if !found { |
||||
retString = fmt.Sprintf("<h1>session key[%s] not exist, please check it again.</h1>", keyString) |
||||
res.Data = retString |
||||
return |
||||
} |
||||
log.Debugf("rtmprelay stop push %s from %s", remoteurl, localurl) |
||||
pushRtmprelay.Stop() |
||||
|
||||
delete(s.session, keyString) |
||||
retString = fmt.Sprintf("<h1>push url stop %s ok</h1></br>", url) |
||||
res.Data = retString |
||||
log.Debugf("push stop return %s", retString) |
||||
} else { |
||||
pushRtmprelay := rtmprelay.NewRtmpRelay(&localurl, &remoteurl) |
||||
log.Debugf("rtmprelay start push %s from %s", remoteurl, localurl) |
||||
err = pushRtmprelay.Start() |
||||
if err != nil { |
||||
retString = fmt.Sprintf("push error=%v", err) |
||||
} else { |
||||
retString = fmt.Sprintf("<h1>push url start %s ok</h1></br>", url) |
||||
s.session[keyString] = pushRtmprelay |
||||
} |
||||
|
||||
res.Data = retString |
||||
log.Debugf("push start return %s", retString) |
||||
} |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/control/reset?room=ROOM_NAME
|
||||
func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) { |
||||
res := &Response{ |
||||
w: w, |
||||
Data: nil, |
||||
Status: 200, |
||||
} |
||||
defer res.SendJson() |
||||
|
||||
if err := r.ParseForm(); err != nil { |
||||
res.Status = 400 |
||||
res.Data = "url: /control/reset?room=<ROOM_NAME>" |
||||
return |
||||
} |
||||
room := r.Form.Get("room") |
||||
|
||||
if len(room) == 0 { |
||||
res.Status = 400 |
||||
res.Data = "url: /control/reset?room=<ROOM_NAME>" |
||||
return |
||||
} |
||||
|
||||
msg, err := configure.RoomKeys.SetKey(room) |
||||
|
||||
if err != nil { |
||||
msg = err.Error() |
||||
res.Status = 400 |
||||
} |
||||
|
||||
res.Data = msg |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/control/get?room=ROOM_NAME
|
||||
func (s *Server) handleGet(w http.ResponseWriter, r *http.Request) { |
||||
res := &Response{ |
||||
w: w, |
||||
Data: nil, |
||||
Status: 200, |
||||
} |
||||
defer res.SendJson() |
||||
|
||||
if err := r.ParseForm(); err != nil { |
||||
res.Status = 400 |
||||
res.Data = "url: /control/get?room=<ROOM_NAME>" |
||||
return |
||||
} |
||||
|
||||
room := r.Form.Get("room") |
||||
|
||||
if len(room) == 0 { |
||||
res.Status = 400 |
||||
res.Data = "url: /control/get?room=<ROOM_NAME>" |
||||
return |
||||
} |
||||
|
||||
msg, err := configure.RoomKeys.GetKey(room) |
||||
if err != nil { |
||||
msg = err.Error() |
||||
res.Status = 400 |
||||
} |
||||
res.Data = msg |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/control/delete?room=ROOM_NAME
|
||||
func (s *Server) handleDelete(w http.ResponseWriter, r *http.Request) { |
||||
res := &Response{ |
||||
w: w, |
||||
Data: nil, |
||||
Status: 200, |
||||
} |
||||
defer res.SendJson() |
||||
|
||||
if err := r.ParseForm(); err != nil { |
||||
res.Status = 400 |
||||
res.Data = "url: /control/delete?room=<ROOM_NAME>" |
||||
return |
||||
} |
||||
|
||||
room := r.Form.Get("room") |
||||
|
||||
if len(room) == 0 { |
||||
res.Status = 400 |
||||
res.Data = "url: /control/delete?room=<ROOM_NAME>" |
||||
return |
||||
} |
||||
|
||||
if configure.RoomKeys.DeleteChannel(room) { |
||||
res.Data = "Ok" |
||||
return |
||||
} |
||||
res.Status = 404 |
||||
res.Data = "room not found" |
||||
} |
@ -1,239 +0,0 @@
@@ -1,239 +0,0 @@
|
||||
package httpopera |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"github.com/gwuhaolin/livego/protocol/rtmp/rtmprelay" |
||||
"io" |
||||
"net" |
||||
"net/http" |
||||
"log" |
||||
"github.com/gwuhaolin/livego/av" |
||||
"github.com/gwuhaolin/livego/protocol/rtmp" |
||||
) |
||||
|
||||
type Response struct { |
||||
w http.ResponseWriter |
||||
Status int `json:"status"` |
||||
Message string `json:"message"` |
||||
} |
||||
|
||||
func (r *Response) SendJson() (int, error) { |
||||
resp, _ := json.Marshal(r) |
||||
r.w.Header().Set("Content-Type", "application/json") |
||||
return r.w.Write(resp) |
||||
} |
||||
|
||||
type Operation struct { |
||||
Method string `json:"method"` |
||||
URL string `json:"url"` |
||||
Stop bool `json:"stop"` |
||||
} |
||||
|
||||
type OperationChange struct { |
||||
Method string `json:"method"` |
||||
SourceURL string `json:"source_url"` |
||||
TargetURL string `json:"target_url"` |
||||
Stop bool `json:"stop"` |
||||
} |
||||
|
||||
type ClientInfo struct { |
||||
url string |
||||
rtmpRemoteClient *rtmp.Client |
||||
rtmpLocalClient *rtmp.Client |
||||
} |
||||
|
||||
type Server struct { |
||||
handler av.Handler |
||||
session map[string]*rtmprelay.RtmpRelay |
||||
rtmpAddr string |
||||
} |
||||
|
||||
func NewServer(h av.Handler, rtmpAddr string) *Server { |
||||
return &Server{ |
||||
handler: h, |
||||
session: make(map[string]*rtmprelay.RtmpRelay), |
||||
rtmpAddr: rtmpAddr, |
||||
} |
||||
} |
||||
|
||||
func (s *Server) Serve(l net.Listener) error { |
||||
mux := http.NewServeMux() |
||||
|
||||
mux.Handle("/statics", http.FileServer(http.Dir("statics"))) |
||||
|
||||
mux.HandleFunc("/control/push", func(w http.ResponseWriter, r *http.Request) { |
||||
s.handlePush(w, r) |
||||
}) |
||||
mux.HandleFunc("/control/pull", func(w http.ResponseWriter, r *http.Request) { |
||||
s.handlePull(w, r) |
||||
}) |
||||
mux.HandleFunc("/stat/livestat", func(w http.ResponseWriter, r *http.Request) { |
||||
s.GetLiveStatics(w, r) |
||||
}) |
||||
http.Serve(l, mux) |
||||
return nil |
||||
} |
||||
|
||||
type stream struct { |
||||
Key string `json:"key"` |
||||
Url string `json:"Url"` |
||||
StreamId uint32 `json:"StreamId"` |
||||
VideoTotalBytes uint64 `json:123456` |
||||
VideoSpeed uint64 `json:123456` |
||||
AudioTotalBytes uint64 `json:123456` |
||||
AudioSpeed uint64 `json:123456` |
||||
} |
||||
|
||||
type streams struct { |
||||
Publishers []stream `json:"publishers"` |
||||
Players []stream `json:"players"` |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/stat/livestat
|
||||
func (server *Server) GetLiveStatics(w http.ResponseWriter, req *http.Request) { |
||||
rtmpStream := server.handler.(*rtmp.RtmpStream) |
||||
if rtmpStream == nil { |
||||
io.WriteString(w, "<h1>Get rtmp stream information error</h1>") |
||||
return |
||||
} |
||||
|
||||
msgs := new(streams) |
||||
for item := range rtmpStream.GetStreams().IterBuffered() { |
||||
if s, ok := item.Val.(*rtmp.Stream); ok { |
||||
if s.GetReader() != nil { |
||||
switch s.GetReader().(type) { |
||||
case *rtmp.VirReader: |
||||
v := s.GetReader().(*rtmp.VirReader) |
||||
msg := stream{item.Key, v.Info().URL, v.ReadBWInfo.StreamId, v.ReadBWInfo.VideoDatainBytes, v.ReadBWInfo.VideoSpeedInBytesperMS, |
||||
v.ReadBWInfo.AudioDatainBytes, v.ReadBWInfo.AudioSpeedInBytesperMS} |
||||
msgs.Publishers = append(msgs.Publishers, msg) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
for item := range rtmpStream.GetStreams().IterBuffered() { |
||||
ws := item.Val.(*rtmp.Stream).GetWs() |
||||
for s := range ws.IterBuffered() { |
||||
if pw, ok := s.Val.(*rtmp.PackWriterCloser); ok { |
||||
if pw.GetWriter() != nil { |
||||
switch pw.GetWriter().(type) { |
||||
case *rtmp.VirWriter: |
||||
v := pw.GetWriter().(*rtmp.VirWriter) |
||||
msg := stream{item.Key, v.Info().URL, v.WriteBWInfo.StreamId, v.WriteBWInfo.VideoDatainBytes, v.WriteBWInfo.VideoSpeedInBytesperMS, |
||||
v.WriteBWInfo.AudioDatainBytes, v.WriteBWInfo.AudioSpeedInBytesperMS} |
||||
msgs.Players = append(msgs.Players, msg) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
resp, _ := json.Marshal(msgs) |
||||
w.Header().Set("Content-Type", "application/json") |
||||
w.Write(resp) |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/control/push?&oper=start&app=live&name=123456&url=rtmp://192.168.16.136/live/123456
|
||||
func (s *Server) handlePull(w http.ResponseWriter, req *http.Request) { |
||||
var retString string |
||||
var err error |
||||
|
||||
req.ParseForm() |
||||
|
||||
oper := req.Form["oper"] |
||||
app := req.Form["app"] |
||||
name := req.Form["name"] |
||||
url := req.Form["url"] |
||||
|
||||
log.Printf("control pull: oper=%v, app=%v, name=%v, url=%v", oper, app, name, url) |
||||
if (len(app) <= 0) || (len(name) <= 0) || (len(url) <= 0) { |
||||
io.WriteString(w, "control push parameter error, please check them.</br>") |
||||
return |
||||
} |
||||
|
||||
remoteurl := "rtmp://127.0.0.1" + s.rtmpAddr + "/" + app[0] + "/" + name[0] |
||||
localurl := url[0] |
||||
|
||||
keyString := "pull:" + app[0] + "/" + name[0] |
||||
if oper[0] == "stop" { |
||||
pullRtmprelay, found := s.session[keyString] |
||||
|
||||
if !found { |
||||
retString = fmt.Sprintf("session key[%s] not exist, please check it again.", keyString) |
||||
io.WriteString(w, retString) |
||||
return |
||||
} |
||||
log.Printf("rtmprelay stop push %s from %s", remoteurl, localurl) |
||||
pullRtmprelay.Stop() |
||||
|
||||
delete(s.session, keyString) |
||||
retString = fmt.Sprintf("<h1>push url stop %s ok</h1></br>", url[0]) |
||||
io.WriteString(w, retString) |
||||
log.Printf("pull stop return %s", retString) |
||||
} else { |
||||
pullRtmprelay := rtmprelay.NewRtmpRelay(&localurl, &remoteurl) |
||||
log.Printf("rtmprelay start push %s from %s", remoteurl, localurl) |
||||
err = pullRtmprelay.Start() |
||||
if err != nil { |
||||
retString = fmt.Sprintf("push error=%v", err) |
||||
} else { |
||||
s.session[keyString] = pullRtmprelay |
||||
retString = fmt.Sprintf("<h1>push url start %s ok</h1></br>", url[0]) |
||||
} |
||||
io.WriteString(w, retString) |
||||
log.Printf("pull start return %s", retString) |
||||
} |
||||
} |
||||
|
||||
//http://127.0.0.1:8090/control/push?&oper=start&app=live&name=123456&url=rtmp://192.168.16.136/live/123456
|
||||
func (s *Server) handlePush(w http.ResponseWriter, req *http.Request) { |
||||
var retString string |
||||
var err error |
||||
|
||||
req.ParseForm() |
||||
|
||||
oper := req.Form["oper"] |
||||
app := req.Form["app"] |
||||
name := req.Form["name"] |
||||
url := req.Form["url"] |
||||
|
||||
log.Printf("control push: oper=%v, app=%v, name=%v, url=%v", oper, app, name, url) |
||||
if (len(app) <= 0) || (len(name) <= 0) || (len(url) <= 0) { |
||||
io.WriteString(w, "control push parameter error, please check them.</br>") |
||||
return |
||||
} |
||||
|
||||
localurl := "rtmp://127.0.0.1" + s.rtmpAddr + "/" + app[0] + "/" + name[0] |
||||
remoteurl := url[0] |
||||
|
||||
keyString := "push:" + app[0] + "/" + name[0] |
||||
if oper[0] == "stop" { |
||||
pushRtmprelay, found := s.session[keyString] |
||||
if !found { |
||||
retString = fmt.Sprintf("<h1>session key[%s] not exist, please check it again.</h1>", keyString) |
||||
io.WriteString(w, retString) |
||||
return |
||||
} |
||||
log.Printf("rtmprelay stop push %s from %s", remoteurl, localurl) |
||||
pushRtmprelay.Stop() |
||||
|
||||
delete(s.session, keyString) |
||||
retString = fmt.Sprintf("<h1>push url stop %s ok</h1></br>", url[0]) |
||||
io.WriteString(w, retString) |
||||
log.Printf("push stop return %s", retString) |
||||
} else { |
||||
pushRtmprelay := rtmprelay.NewRtmpRelay(&localurl, &remoteurl) |
||||
log.Printf("rtmprelay start push %s from %s", remoteurl, localurl) |
||||
err = pushRtmprelay.Start() |
||||
if err != nil { |
||||
retString = fmt.Sprintf("push error=%v", err) |
||||
} else { |
||||
retString = fmt.Sprintf("<h1>push url start %s ok</h1></br>", url[0]) |
||||
s.session[keyString] = pushRtmprelay |
||||
} |
||||
|
||||
io.WriteString(w, retString) |
||||
log.Printf("push start return %s", retString) |
||||
} |
||||
} |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package uid |
||||
|
||||
import "math/rand" |
||||
|
||||
var letterRunes = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") |
||||
|
||||
func RandStringRunes(n int) string { |
||||
b := make([]rune, n) |
||||
for i := range b { |
||||
b[i] = letterRunes[rand.Intn(len(letterRunes))] |
||||
} |
||||
return string(b) |
||||
} |
Loading…
Reference in new issue