Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
吴浩麟 committed Apr 15, 2020
2 parents 6f30580 + 21175a2 commit e0f4213
Show file tree
Hide file tree
Showing 45 changed files with 846 additions and 647 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Created by .ignore support plugin (hsz.mobi)
.idea
dist
room_keys.json
.vscode
.tmp
vendor
tmp
vendor
livego
24 changes: 16 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- JSON Web Token support.
``` json
// .livego.json
// livego.json
{
"jwt": {
"secret": "testing",
"algorithm": "HS256s"
"algorithm": "HS256"
},
"server": [
{
"appname": "live",
"liveon": "on",
"hlson": "on"
"live": true,
"hls": true
}
]
}
```
- Use redis for store room keys
``` json
// .livego.json
// livego.json
{
"redis_addr": "localhost:6379",
"server": [
{
"appname": "live",
"liveon": "on",
"hlson": "on"
"live": true,
"hls": true
}
]
}
```
- Makefile

### Changed
- Show `players`.
- Show `stream_id`.
- 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.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
FROM golang:latest as builder
WORKDIR /app
ENV GOPROXY https://goproxy.io
COPY go.mod go.sum ./
RUN go mod download
COPY . .
Expand Down
36 changes: 36 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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)
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Simple and efficient live broadcast server:
#### Supported encoding formats
- H264
- AAC
- sMP3
- MP3

## Installation
After directly downloading the compiled [binary file](https://github.com/gwuhaolin/livego/releases), execute it on the command line.
Expand All @@ -30,12 +30,27 @@ Run `docker run -p 1935:1935 -p 7001:7001 -p 7002:7002 -d --name livego gwuhaoli

#### Compile from source
1. Download the source code `git clone https://github.com/gwuhaolin/livego.git`
2. Go to the livego directory and execute `go build`
2. Go to the livego directory and execute `go build` or `make build`

## Use
2. Start the service: execute the livego binary file to start the livego service;
3. Upstream push: Push the video stream to `rtmp://localhost:1935/live/movie` through the` RTMP` protocol, for example, use `ffmpeg -re -i demo.flv -c copy -f flv rtmp://localhost:1935/live/movie` push;
4. Downstream playback: The following three playback protocols are supported, and the playback address is as follows:
```bash
./livego -h
Usage of ./livego:
--api_addr string HTTP manage interface server listen address (default ":8090")
--config_file string configure filename (default "livego.yaml")
--flv_dir string output flv file at flvDir/APP/KEY_TIME.flv (default "tmp")
--gop_num int gop num (default 1)
--hls_addr string HLS server listen address (default ":7002")
--httpflv_addr string HTTP-FLV server listen address (default ":7001")
--level string Log level (default "info")
--read_timeout int read time out (default 10)
--rtmp_addr string RTMP server listen address (default ":1935")
--write_timeout int write time out (default 10)
```
2. Start the service: execute the livego binary file or `make run` to start the livego service;
3. Get a channelkey `curl http://localhost:8090/control/get?room=movie` and copy data like your channelkey.
4. Upstream push: Push the video stream to `rtmp://localhost:1935/live/movie`(`rtmp://localhost:1935/{appname}/{channelkey}`) through the` RTMP` protocol, for example, use `ffmpeg -re -i demo.flv -c copy -f flv rtmp://localhost:1935/live/movie` push;
5. Downstream playback: The following three playback protocols are supported, and the playback address is as follows:
-`RTMP`:`rtmp://localhost:1935/live/movie`
-`FLV`:`http://127.0.0.1:7001/live/movie.flv`
-`HLS`:`http://127.0.0.1:7002/live/movie.m3u8`
Expand Down
160 changes: 47 additions & 113 deletions configure/channel.go
Original file line number Diff line number Diff line change
@@ -1,112 +1,58 @@
package configure

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math/rand"
"sync"
"time"

"livego/utils/uid"

"github.com/go-redis/redis/v7"
"github.com/patrickmn/go-cache"
log "github.com/sirupsen/logrus"
)

var RoomKeys = LoadRoomKey(*GetKeyFile())
type RoomKeysType struct {
redisCli *redis.Client
localCache *cache.Cache
}

var roomUpdated = false
var RoomKeys = &RoomKeysType{
localCache: cache.New(cache.NoExpiration, 0),
}

var saveInFile = true
var redisCli *redis.Client
var saveInLocal = true

func Init() {
saveInFile = GetRedisAddr() == nil

rand.Seed(time.Now().UnixNano())
if saveInFile {
go func() {
for {
time.Sleep(15 * time.Second)
if roomUpdated {
RoomKeys.Save(*roomKeySaveFile)
roomUpdated = false
}
}
}()

saveInLocal = len(Config.GetString("redis_addr")) == 0
if saveInLocal {
return
}

redisCli = redis.NewClient(&redis.Options{
Addr: *GetRedisAddr(),
Password: *GetRedisPwd(),
RoomKeys.redisCli = redis.NewClient(&redis.Options{
Addr: Config.GetString("redis_addr"),
Password: Config.GetString("redis_pwd"),
DB: 0,
})

_, err := redisCli.Ping().Result()
if err != nil {
panic(err)
}

log.Printf("Redis connected")
}

type RoomKeysType struct {
mapChanKey sync.Map
mapKeyChan sync.Map
}

func LoadRoomKey(f string) *RoomKeysType {
result := &RoomKeysType{
mapChanKey: sync.Map{},
mapKeyChan: sync.Map{},
}
raw := map[string]string{}
content, err := ioutil.ReadFile(f)
_, err := RoomKeys.redisCli.Ping().Result()
if err != nil {
log.Printf("Failed to read file %s for room keys", f)
return result
}
if json.Unmarshal(content, &raw) != nil {
log.Printf("Failed to unmarshal file %s for room keys", f)
return result
log.Panic("Redis: ", err)
}
for room, key := range raw {
result.mapChanKey.Store(room, key)
result.mapKeyChan.Store(key, room)
}
return result
}

func (r *RoomKeysType) Save(f string) {
raw := map[string]string{}
r.mapChanKey.Range(func(channel, key interface{}) bool {
raw[channel.(string)] = key.(string)
return true
})
content, err := json.Marshal(raw)
if err != nil {
log.Println("Failed to marshal room keys")
return
}
if ioutil.WriteFile(f, content, 0644) != nil {
log.Println("Failed to save room keys")
return
}
log.Info("Redis connected")
}

// set/reset a random key for channel
func (r *RoomKeysType) SetKey(channel string) (key string, err error) {
if !saveInFile {
if !saveInLocal {
for {
key = randStringRunes(48)
if _, err = redisCli.Get(key).Result(); err == redis.Nil {
err = redisCli.Set(channel, key, 0).Err()
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 = redisCli.Set(key, channel, 0).Err()
err = r.redisCli.Set(key, channel, 0).Err()
return
} else if err != nil {
return
Expand All @@ -115,22 +61,21 @@ func (r *RoomKeysType) SetKey(channel string) (key string, err error) {
}

for {
key = randStringRunes(48)
if _, found := r.mapKeyChan.Load(key); !found {
r.mapChanKey.Store(channel, key)
r.mapKeyChan.Store(key, channel)
key = uid.RandStringRunes(48)
if _, found := r.localCache.Get(key); !found {
r.localCache.SetDefault(channel, key)
r.localCache.SetDefault(key, channel)
break
}
}
roomUpdated = true
return
}

func (r *RoomKeysType) GetKey(channel string) (newKey string, err error) {
if !saveInFile {
if newKey, err = redisCli.Get(channel).Result(); err == redis.Nil {
if !saveInLocal {
if newKey, err = r.redisCli.Get(channel).Result(); err == redis.Nil {
newKey, err = r.SetKey(channel)
log.Printf("[KEY] new channel [%s]: %s", channel, newKey)
log.Debugf("[KEY] new channel [%s]: %s", channel, newKey)
return
}

Expand All @@ -139,20 +84,20 @@ func (r *RoomKeysType) GetKey(channel string) (newKey string, err error) {

var key interface{}
var found bool
if key, found = r.mapChanKey.Load(channel); found {
if key, found = r.localCache.Get(channel); found {
return key.(string), nil
}
newKey, err = r.SetKey(channel)
log.Printf("[KEY] new channel [%s]: %s", channel, newKey)
log.Debugf("[KEY] new channel [%s]: %s", channel, newKey)
return
}

func (r *RoomKeysType) GetChannel(key string) (channel string, err error) {
if !saveInFile {
return redisCli.Get(key).Result()
if !saveInLocal {
return r.redisCli.Get(key).Result()
}

chann, found := r.mapKeyChan.Load(key)
chann, found := r.localCache.Get(key)
if found {
return chann.(string), nil
} else {
Expand All @@ -161,40 +106,29 @@ func (r *RoomKeysType) GetChannel(key string) (channel string, err error) {
}

func (r *RoomKeysType) DeleteChannel(channel string) bool {
if !saveInFile {
return redisCli.Del(channel).Err() != nil
if !saveInLocal {
return r.redisCli.Del(channel).Err() != nil
}

key, ok := r.mapChanKey.Load(channel)
key, ok := r.localCache.Get(channel)
if ok {
r.mapChanKey.Delete(channel)
r.mapKeyChan.Delete(key)
r.localCache.Delete(channel)
r.localCache.Delete(key.(string))
return true
}
return false
}

func (r *RoomKeysType) DeleteKey(key string) bool {
if !saveInFile {
return redisCli.Del(key).Err() != nil
if !saveInLocal {
return r.redisCli.Del(key).Err() != nil
}

channel, ok := r.mapKeyChan.Load(key)
channel, ok := r.localCache.Get(key)
if ok {
r.mapChanKey.Delete(channel)
r.mapKeyChan.Delete(key)
r.localCache.Delete(channel.(string))
r.localCache.Delete(key)
return true
}
return false
}

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

0 comments on commit e0f4213

Please sign in to comment.