Skip to content

Commit

Permalink
Merge pull request #86 from GNURub/master
Browse files Browse the repository at this point in the history
JWT for HTTP-Operation API
  • Loading branch information
gwuhaolin committed Apr 10, 2020
2 parents 36aaf27 + 24928da commit 2634d2b
Show file tree
Hide file tree
Showing 37 changed files with 684 additions and 145 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -1,3 +1,7 @@
# Created by .ignore support plugin (hsz.mobi)
.idea
dist
room_keys.json
.vscode
.tmp
vendor
44 changes: 44 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,44 @@
# 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": "HS256s"
},
"server": [
{
"appname": "live",
"liveon": "on",
"hlson": "on"
}
]
}
```
- Use redis for store room keys
``` json
// .livego.json
{
"redis_addr": "localhost:6379",
"server": [
{
"appname": "live",
"liveon": "on",
"hlson": "on"
}
]
}
```

### Changed
- Show `players`.
- Show `stream_id`.
9 changes: 6 additions & 3 deletions Dockerfile
Expand Up @@ -3,17 +3,20 @@ WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o livego ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o livego .

FROM alpine:latest
LABEL maintainer="gwuhaolin <gwuhaolin@gmail.com>"
LABEL maintainer="Ruben Cid Lara <rubencidlara@gmail.com>"
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/config ./config
COPY --from=builder /app/livego .
EXPOSE ${RTMP_PORT}
EXPOSE ${HTTP_FLV_PORT}
EXPOSE ${HLS_PORT}
EXPOSE ${HTTP_OPERATION_PORT}
CMD ./livego
ENTRYPOINT ["./livego"]
49 changes: 24 additions & 25 deletions README.md
@@ -1,43 +1,42 @@
# livego
简单高效的直播服务器:
- 安装和使用非常简单;
- Golang 编写,性能高,跨平台;
- 支持常用的传输协议、文件格式、编码格式;
Simple and efficient live broadcast server:
- Very simple to install and use;
- Pure Golang, high performance, cross-platform;
- Support commonly used transmission protocols, file formats, encoding formats;

#### 支持的传输协议
#### Supported transport protocols
- RTMP
- AMF
- HLS
- HTTP-FLV

#### 支持的容器格式
#### Supported container formats
- FLV
- TS

#### 支持的编码格式
#### Supported encoding formats
- H264
- AAC
- MP3
- sMP3

## 安装
直接下载编译好的[二进制文件](https://github.com/gwuhaolin/livego/releases)后,在命令行中执行。
## Installation
After directly downloading the compiled [binary file](https://github.com/gwuhaolin/livego/releases), execute it on the command line.

#### 从 Docker 启动
执行`docker run -p 1935:1935 -p 7001:7001 -p 7002:7002 -d --name livego gwuhaolin/livego`启动
#### Boot from Docker
Run `docker run -p 1935:1935 -p 7001:7001 -p 7002:7002 -d --name livego gnurub/livego` to start

#### 从源码编译
1. 下载源码 `git clone https://github.com/gwuhaolin/livego.git`
2. livego 目录中 执行 `go build`
#### 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. 启动服务:执行 `livego` 二进制文件启动 livego 服务;
3. 上行推流:通过 `RTMP` 协议把视频流推送到 `rtmp://localhost:1935/live/movie`,例如使用 `ffmpeg -re -i demo.flv -c copy -f flv rtmp://localhost:1935/live/movie` 推送;
4. 下行播放:支持以下三种播放协议,播放地址如下:
- `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`
## 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:
-`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`

### [Use with flv.js](https://github.com/gwuhaolin/blog/issues/3)

### [和 flv.js 搭配使用](https://github.com/gwuhaolin/blog/issues/3)

对Golang感兴趣?请看[Golang 中文学习资料汇总](http://go.wuhaolin.cn/)
Interested in Golang? Please see [Golang Chinese Learning Materials Summary](http://go.wuhaolin.cn/)
File renamed without changes.
200 changes: 200 additions & 0 deletions configure/channel.go
@@ -0,0 +1,200 @@
package configure

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

"github.com/go-redis/redis/v7"
)

var RoomKeys = LoadRoomKey(*GetKeyFile())

var roomUpdated = false

var saveInFile = true
var redisCli *redis.Client

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

return
}

redisCli = redis.NewClient(&redis.Options{
Addr: *GetRedisAddr(),
Password: *GetRedisPwd(),
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)
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
}
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
}
}

// set/reset a random key for channel
func (r *RoomKeysType) SetKey(channel string) (key string, err error) {
if !saveInFile {
for {
key = randStringRunes(48)
if _, err = redisCli.Get(key).Result(); err == redis.Nil {
err = redisCli.Set(channel, key, 0).Err()
if err != nil {
return
}

err = redisCli.Set(key, channel, 0).Err()
return
} else if err != nil {
return
}
}
}

for {
key = randStringRunes(48)
if _, found := r.mapKeyChan.Load(key); !found {
r.mapChanKey.Store(channel, key)
r.mapKeyChan.Store(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 {
newKey, err = r.SetKey(channel)
log.Printf("[KEY] new channel [%s]: %s", channel, newKey)
return
}

return
}

var key interface{}
var found bool
if key, found = r.mapChanKey.Load(channel); found {
return key.(string), nil
}
newKey, err = r.SetKey(channel)
log.Printf("[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()
}

chann, found := r.mapKeyChan.Load(key)
if found {
return chann.(string), nil
} else {
return "", fmt.Errorf("%s does not exists", key)
}
}

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

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

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

channel, ok := r.mapKeyChan.Load(key)
if ok {
r.mapChanKey.Delete(channel)
r.mapKeyChan.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)
}

0 comments on commit 2634d2b

Please sign in to comment.