mirror of https://github.com/q191201771/lal.git
Changes:
- [refactor] app/lals重命名为app/lalserver,避免描述时容易和lal造成混淆 - [refactor] 将app/lalserver的大部分逻辑代码移入pkg/logic中 - [test] 将所有package的Server、Session等内容的实例测试收缩至package innertest中,多个package都可以共用它做单元测试 - [refactor] lalserver配置中增加显式enable字段,用于开启关闭特定协议 - [refactor] 各package的Server对象增加独立的Listen函数,使得绑定监听端口失败时上层可以第一时间感知 - [feat] demo/analyseflv,增加I帧间隔检查,增加metadata分析 - [fix] package avc: 修复函数CalcSliceType解析I、P、B帧类型的bug - [refactor] package aac: 函数ADTS::PutAACSequenceHeader检查输入切片长度 - [reafactor] package aac: 删除函数CaptureAAC - [feat] 增加demo/learnrtsp,pkg/rtsp,开始学习rtsppull/8/head
parent
7e804f5f9e
commit
9af3b44753
@ -1,70 +0,0 @@
|
||||
// Copyright 2019, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/q191201771/lal/pkg/logic"
|
||||
|
||||
"github.com/q191201771/naza/pkg/nazajson"
|
||||
log "github.com/q191201771/naza/pkg/nazalog"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
logic.Config
|
||||
Log log.Option `json:"log"`
|
||||
PProf PProfConfig `json:"pprof"`
|
||||
}
|
||||
|
||||
type PProfConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
}
|
||||
|
||||
func LoadConf(confFile string) (*Config, error) {
|
||||
var config Config
|
||||
rawContent, err := ioutil.ReadFile(confFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(rawContent, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
j, err := nazajson.New(rawContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查配置必须项
|
||||
// 暂时无
|
||||
|
||||
// 配置不存在时,设置默认值
|
||||
if !j.Exist("log.level") {
|
||||
config.Log.Level = log.LevelDebug
|
||||
}
|
||||
if !j.Exist("log.filename") {
|
||||
config.Log.Filename = "./logs/lals.log"
|
||||
}
|
||||
if !j.Exist("log.is_to_stdout") {
|
||||
config.Log.IsToStdout = true
|
||||
}
|
||||
if !j.Exist("log.is_rotate_daily") {
|
||||
config.Log.IsRotateDaily = true
|
||||
}
|
||||
if !j.Exist("log.short_file_flag") {
|
||||
config.Log.ShortFileFlag = true
|
||||
}
|
||||
if !j.Exist("log.assert_behavior") {
|
||||
config.Log.AssertBehavior = log.AssertError
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
// Copyright 2019, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
|
||||
"github.com/q191201771/lal/pkg/logic"
|
||||
"github.com/q191201771/naza/pkg/bininfo"
|
||||
log "github.com/q191201771/naza/pkg/nazalog"
|
||||
)
|
||||
|
||||
var sm *logic.ServerManager
|
||||
|
||||
func main() {
|
||||
confFile := parseFlag()
|
||||
config := loadConf(confFile)
|
||||
initLog(config.Log)
|
||||
log.Infof("bininfo: %s", bininfo.StringifySingleLine())
|
||||
|
||||
sm = logic.NewServerManager(&config.Config)
|
||||
|
||||
if config.PProf.Addr != "" {
|
||||
go runWebPProf(config.PProf.Addr)
|
||||
}
|
||||
go runSignalHandler()
|
||||
|
||||
sm.RunLoop()
|
||||
}
|
||||
|
||||
func parseFlag() string {
|
||||
binInfoFlag := flag.Bool("v", false, "show bin info")
|
||||
cf := flag.String("c", "", "specify conf file")
|
||||
flag.Parse()
|
||||
if *binInfoFlag {
|
||||
_, _ = fmt.Fprint(os.Stderr, bininfo.StringifyMultiLine())
|
||||
os.Exit(0)
|
||||
}
|
||||
if *cf == "" {
|
||||
flag.Usage()
|
||||
_, _ = fmt.Fprintf(os.Stderr, `
|
||||
Example:
|
||||
./bin/lals -c ./conf/lals.conf.json
|
||||
`)
|
||||
os.Exit(1)
|
||||
}
|
||||
return *cf
|
||||
}
|
||||
|
||||
func loadConf(confFile string) *Config {
|
||||
config, err := LoadConf(confFile)
|
||||
if err != nil {
|
||||
log.Errorf("load conf failed. file=%s err=%+v", confFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Infof("load conf file succ. file=%s content=%+v", confFile, config)
|
||||
return config
|
||||
}
|
||||
|
||||
func initLog(opt log.Option) {
|
||||
if err := log.Init(func(option *log.Option) {
|
||||
*option = opt
|
||||
}); err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "initial log failed. err=%+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("initial log succ.")
|
||||
}
|
||||
|
||||
func runWebPProf(addr string) {
|
||||
log.Infof("start web pprof listen. addr=%s", addr)
|
||||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
// Copyright 2019, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/q191201771/lal/pkg/logic"
|
||||
"github.com/q191201771/naza/pkg/bininfo"
|
||||
)
|
||||
|
||||
var sm *logic.ServerManager
|
||||
|
||||
func main() {
|
||||
confFile := parseFlag()
|
||||
logic.Entry(confFile)
|
||||
}
|
||||
|
||||
func parseFlag() string {
|
||||
binInfoFlag := flag.Bool("v", false, "show bin info")
|
||||
cf := flag.String("c", "", "specify conf file")
|
||||
flag.Parse()
|
||||
if *binInfoFlag {
|
||||
_, _ = fmt.Fprint(os.Stderr, bininfo.StringifyMultiLine())
|
||||
os.Exit(0)
|
||||
}
|
||||
if *cf == "" {
|
||||
flag.Usage()
|
||||
_, _ = fmt.Fprintf(os.Stderr, `
|
||||
Example:
|
||||
./bin/lalserver -c ./conf/lalserver.conf.json
|
||||
`)
|
||||
os.Exit(1)
|
||||
}
|
||||
return *cf
|
||||
}
|
@ -1,27 +1,31 @@
|
||||
{
|
||||
"rtmp": {
|
||||
"enable": true,
|
||||
"addr": ":19350",
|
||||
"gop_num": 2
|
||||
},
|
||||
"httpflv": {
|
||||
"enable": true,
|
||||
"sub_listen_addr": ":8080",
|
||||
"gop_num": 2
|
||||
},
|
||||
"hls": {
|
||||
"enable": true,
|
||||
"sub_listen_addr": ":8081",
|
||||
"out_path": "/tmp/lal/hls/",
|
||||
"fragment_duration_ms": 3000,
|
||||
"fragment_num": 6
|
||||
},
|
||||
"pprof": {
|
||||
"enable": true,
|
||||
"addr": ":10001"
|
||||
},
|
||||
"log": {
|
||||
"level": 1,
|
||||
"filename": "./logs/lals.log",
|
||||
"filename": "./logs/lalserver.log",
|
||||
"is_to_stdout": true,
|
||||
"is_rotate_daily": true,
|
||||
"short_file_flag": true,
|
||||
"assert_behavior": 1
|
||||
},
|
||||
"pprof": {
|
||||
"addr": ":10001"
|
||||
}
|
||||
}
|
@ -1,27 +1,31 @@
|
||||
{
|
||||
"rtmp": {
|
||||
"enable": true,
|
||||
"addr": ":19350",
|
||||
"gop_num": 2
|
||||
},
|
||||
"httpflv": {
|
||||
"enable": true,
|
||||
"sub_listen_addr": ":8080",
|
||||
"gop_num": 2
|
||||
},
|
||||
"hls": {
|
||||
"enable": true,
|
||||
"sub_listen_addr": ":8081",
|
||||
"out_path": "/tmp/lal/hls/",
|
||||
"fragment_duration_ms": 3000,
|
||||
"fragment_num": 6
|
||||
},
|
||||
"pprof": {
|
||||
"enable": true,
|
||||
"addr": ":10001"
|
||||
},
|
||||
"log": {
|
||||
"level": 1,
|
||||
"filename": "./logs/lals.log",
|
||||
"filename": "./logs/lalserver.log",
|
||||
"is_to_stdout": true,
|
||||
"is_rotate_daily": true,
|
||||
"short_file_flag": true,
|
||||
"assert_behavior": 1
|
||||
},
|
||||
"pprof": {
|
||||
"addr": ":10001"
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2020, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/q191201771/lal/pkg/rtsp"
|
||||
"github.com/q191201771/naza/pkg/nazalog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := rtsp.NewServer(":5544")
|
||||
err := s.Listen()
|
||||
nazalog.Assert(nil, err)
|
||||
err = s.RunLoop()
|
||||
nazalog.Error(err)
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
github.com/q191201771/naza v0.12.3 h1:0Z8hMa5RYNqsG1GjGfYyLFkuPLfuZ21iDx3BJEPK0p8=
|
||||
github.com/q191201771/naza v0.12.3/go.mod h1:SE14GBGO9mAn6JZl3NlfWGtNOT7xQjxOG7f3YOdBThM=
|
||||
github.com/q191201771/naza v0.13.0 h1:tHgsMlMu9dHGmL26cGpFJDeP1qdFwbXVJHPg6IlAuvo=
|
||||
github.com/q191201771/naza v0.13.0/go.mod h1:SE14GBGO9mAn6JZl3NlfWGtNOT7xQjxOG7f3YOdBThM=
|
||||
|
@ -1,49 +0,0 @@
|
||||
// Copyright 2019, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package httpflv_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/q191201771/lal/pkg/httpflv"
|
||||
log "github.com/q191201771/naza/pkg/nazalog"
|
||||
)
|
||||
|
||||
// TODO chef: 后续加个 httpflv post 在做完整流程测试吧
|
||||
|
||||
var (
|
||||
serverAddr = ":10001"
|
||||
pullURL = "http://127.0.0.1:10001/live/11111.flv"
|
||||
)
|
||||
|
||||
type MockServerObserver struct {
|
||||
}
|
||||
|
||||
func (so *MockServerObserver) NewHTTPFLVSubSessionCB(session *httpflv.SubSession) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (so *MockServerObserver) DelHTTPFLVSubSessionCB(session *httpflv.SubSession) {
|
||||
}
|
||||
|
||||
func TestExample(t *testing.T) {
|
||||
var err error
|
||||
|
||||
var so MockServerObserver
|
||||
s := httpflv.NewServer(&so, serverAddr)
|
||||
go s.RunLoop()
|
||||
|
||||
pullSession := httpflv.NewPullSession(func(option *httpflv.PullSessionOption) {
|
||||
option.ConnectTimeoutMS = 1000
|
||||
option.ReadTimeoutMS = 1000
|
||||
})
|
||||
err = pullSession.Pull(pullURL, func(tag httpflv.Tag) {
|
||||
})
|
||||
log.Debugf("pull failed. err=%+v", err)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2020, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package httpflv_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/q191201771/lal/pkg/innertest"
|
||||
)
|
||||
|
||||
func TestRTMP(t *testing.T) {
|
||||
innertest.InnerTestEntry(t)
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
// Copyright 2020, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package logic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
|
||||
"github.com/q191201771/naza/pkg/bininfo"
|
||||
"github.com/q191201771/naza/pkg/nazalog"
|
||||
)
|
||||
|
||||
var (
|
||||
config *Config
|
||||
)
|
||||
|
||||
func Entry(confFile string) {
|
||||
config = loadConf(confFile)
|
||||
initLog(config.LogConfig)
|
||||
nazalog.Infof("bininfo: %s", bininfo.StringifySingleLine())
|
||||
|
||||
sm := NewServerManager(config)
|
||||
|
||||
if config.PProfConfig.Enable {
|
||||
go runWebPProf(config.PProfConfig.Addr)
|
||||
}
|
||||
go runSignalHandler(func() {
|
||||
sm.Dispose()
|
||||
})
|
||||
|
||||
sm.RunLoop()
|
||||
}
|
||||
|
||||
func loadConf(confFile string) *Config {
|
||||
config, err := LoadConf(confFile)
|
||||
if err != nil {
|
||||
nazalog.Errorf("load conf failed. file=%s err=%+v", confFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
nazalog.Infof("load conf file succ. file=%s content=%+v", confFile, config)
|
||||
return config
|
||||
}
|
||||
|
||||
func initLog(opt nazalog.Option) {
|
||||
if err := nazalog.Init(func(option *nazalog.Option) {
|
||||
*option = opt
|
||||
}); err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "initial log failed. err=%+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
nazalog.Info("initial log succ.")
|
||||
}
|
||||
|
||||
func runWebPProf(addr string) {
|
||||
nazalog.Infof("start web pprof listen. addr=%s", addr)
|
||||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||
nazalog.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2019, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package logic_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/q191201771/lal/pkg/innertest"
|
||||
)
|
||||
|
||||
func TestLogic(t *testing.T) {
|
||||
innertest.InnerTestEntry(t)
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
// Copyright 2019, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package rtmp_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/q191201771/lal/pkg/httpflv"
|
||||
"github.com/q191201771/lal/pkg/logic"
|
||||
"github.com/q191201771/lal/pkg/rtmp"
|
||||
"github.com/q191201771/naza/pkg/assert"
|
||||
log "github.com/q191201771/naza/pkg/nazalog"
|
||||
)
|
||||
|
||||
// 读取 flv 文件,使用 rtmp 协议发送至服务端,再使用 rtmp 协议从服务端拉流,转换 flv 格式存储为文件,
|
||||
// 检查两份 flv 文件是否完全一致。
|
||||
|
||||
var (
|
||||
serverAddr = ":10001"
|
||||
pushURL = "rtmp://127.0.0.1:10001/live/test"
|
||||
pullURL = "rtmp://127.0.0.1:10001/live/test"
|
||||
rFLVFile = "testdata/test.flv"
|
||||
wFLVFile = "testdata/out.flv"
|
||||
wgNum = 4 // FLVFileReader -> [push -> pub -> sub -> pull] -> FLVFileWriter
|
||||
)
|
||||
|
||||
var (
|
||||
pubSessionObs MockPubSessionObserver
|
||||
pullSession *rtmp.PullSession
|
||||
subSession *rtmp.ServerSession
|
||||
wg sync.WaitGroup
|
||||
w httpflv.FLVFileWriter
|
||||
//
|
||||
rc uint32
|
||||
bc uint32
|
||||
wc uint32
|
||||
)
|
||||
|
||||
type MockServerObserver struct {
|
||||
}
|
||||
|
||||
func (so *MockServerObserver) NewRTMPPubSessionCB(session *rtmp.ServerSession) bool {
|
||||
log.Debug("NewRTMPPubSessionCB")
|
||||
session.SetPubSessionObserver(&pubSessionObs)
|
||||
return true
|
||||
}
|
||||
func (so *MockServerObserver) NewRTMPSubSessionCB(session *rtmp.ServerSession) bool {
|
||||
log.Debug("NewRTMPSubSessionCB")
|
||||
subSession = session
|
||||
return true
|
||||
}
|
||||
func (so *MockServerObserver) DelRTMPPubSessionCB(session *rtmp.ServerSession) {
|
||||
log.Debug("DelRTMPPubSessionCB")
|
||||
subSession.Flush()
|
||||
subSession.Dispose()
|
||||
wg.Done()
|
||||
}
|
||||
func (so *MockServerObserver) DelRTMPSubSessionCB(session *rtmp.ServerSession) {
|
||||
log.Debug("DelRTMPSubSessionCB")
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
type MockPubSessionObserver struct {
|
||||
}
|
||||
|
||||
func (pso *MockPubSessionObserver) OnReadRTMPAVMsg(msg rtmp.AVMsg) {
|
||||
bc++
|
||||
// 转发
|
||||
currHeader := logic.Trans.MakeDefaultRTMPHeader(msg.Header)
|
||||
var absChunks []byte
|
||||
absChunks = rtmp.Message2Chunks(msg.Payload, &currHeader)
|
||||
subSession.AsyncWrite(absChunks)
|
||||
}
|
||||
|
||||
func TestExample(t *testing.T) {
|
||||
var err error
|
||||
|
||||
var r httpflv.FLVFileReader
|
||||
err = r.Open(rFLVFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
wg.Add(wgNum)
|
||||
|
||||
var so MockServerObserver
|
||||
s := rtmp.NewServer(&so, serverAddr)
|
||||
go s.RunLoop()
|
||||
|
||||
// 等待 server 开始监听
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
go func() {
|
||||
pullSession = rtmp.NewPullSession()
|
||||
err = pullSession.Pull(pullURL, func(msg rtmp.AVMsg) {
|
||||
tag := logic.Trans.RTMPMsg2FLVTag(msg)
|
||||
w.WriteTag(*tag)
|
||||
atomic.AddUint32(&wc, 1)
|
||||
})
|
||||
log.Error(err)
|
||||
}()
|
||||
|
||||
pushSession := rtmp.NewPushSession()
|
||||
err = pushSession.Push(pushURL)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
err = w.Open(wFLVFile)
|
||||
assert.Equal(t, nil, err)
|
||||
err = w.WriteRaw(httpflv.FLVHeader)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
for {
|
||||
tag, err := r.ReadTag()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
assert.Equal(t, nil, err)
|
||||
rc++
|
||||
//log.Debugf("send tag. %d", tag.Header.Timestamp)
|
||||
msg := logic.Trans.FLVTag2RTMPMsg(tag)
|
||||
//log.Debugf("send msg. %d %d", msg.Header.Timestamp, msg.Header.TimestampAbs)
|
||||
chunks := rtmp.Message2Chunks(msg.Payload, &msg.Header)
|
||||
//log.Debugf("%s", hex.Dump(chunks[:16]))
|
||||
err = pushSession.AsyncWrite(chunks)
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
r.Dispose()
|
||||
wg.Done()
|
||||
|
||||
err = pushSession.Flush()
|
||||
assert.Equal(t, nil, err)
|
||||
pushSession.Dispose()
|
||||
wg.Done()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// 等待 pull 完成
|
||||
for {
|
||||
if atomic.LoadUint32(&wc) == rc {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Nanosecond)
|
||||
}
|
||||
//time.Sleep(1 * time.Second)
|
||||
|
||||
pullSession.Dispose()
|
||||
w.Dispose()
|
||||
|
||||
s.Dispose()
|
||||
log.Debugf("rc=%d, bc=%d, wc=%d", rc, bc, atomic.LoadUint32(&wc))
|
||||
compareFile(t)
|
||||
}
|
||||
|
||||
func compareFile(t *testing.T) {
|
||||
r, err := ioutil.ReadFile(rFLVFile)
|
||||
assert.Equal(t, nil, err)
|
||||
w, err := ioutil.ReadFile(wFLVFile)
|
||||
assert.Equal(t, nil, err)
|
||||
res := bytes.Compare(r, w)
|
||||
assert.Equal(t, 0, res)
|
||||
//err = os.Remove(wFLVFile)
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2020, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package rtmp_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/q191201771/lal/pkg/innertest"
|
||||
)
|
||||
|
||||
func TestRTMP(t *testing.T) {
|
||||
innertest.InnerTestEntry(t)
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// Copyright 2020, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package rtsp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ResponseOptionsTmpl = "RTSP/1.0 200 OK\r\nCSeq: %s\r\nPublic:DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n"
|
||||
var ResponseSetupTmpl = "RTSP/1.0 200 OK\r\nCSeq: %s\r\nDate: %s\r\nSession: 47112344\r\nTransport:%s;server_port=6256-6257"
|
||||
|
||||
func PackResponseOptions(cseq string) string {
|
||||
return fmt.Sprintf(ResponseOptionsTmpl, cseq)
|
||||
}
|
||||
|
||||
func PackResponseSetup(cseq string, transportC string) string {
|
||||
date := time.Now().Format(time.RFC1123)
|
||||
return fmt.Sprintf(ResponseSetupTmpl, cseq, date, transportC)
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
// Copyright 2020, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/lal
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package rtsp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
|
||||
"github.com/q191201771/naza/pkg/nazahttp"
|
||||
"github.com/q191201771/naza/pkg/nazalog"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
addr string
|
||||
ln net.Listener
|
||||
}
|
||||
|
||||
func NewServer(addr string) *Server {
|
||||
return &Server{
|
||||
addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Listen() (err error) {
|
||||
s.ln, err = net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nazalog.Infof("start hls server listen. addr=%s", s.addr)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Server) RunLoop() error {
|
||||
for {
|
||||
conn, err := s.ln.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.handleTCPConnect(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleTCPConnect(conn net.Conn) {
|
||||
nazalog.Debugf("handleTCPConnect. conn=%p", conn)
|
||||
r := bufio.NewReader(conn)
|
||||
for {
|
||||
requestLine, headers, err := nazahttp.ReadHTTPHeader(r)
|
||||
if err != nil {
|
||||
nazalog.Error(err)
|
||||
break
|
||||
}
|
||||
nazalog.Debugf("requestLine=%s, headers=%+v", requestLine, headers)
|
||||
|
||||
method, _, _, err := nazahttp.ParseHTTPRequestLine(requestLine)
|
||||
if err != nil {
|
||||
nazalog.Error(err)
|
||||
break
|
||||
}
|
||||
|
||||
// TODO chef: header field not exist?
|
||||
switch method {
|
||||
case MethodOptions:
|
||||
resp := PackResponseOptions(headers[HeaderFieldCSeq])
|
||||
_, _ = conn.Write([]byte(resp))
|
||||
case MethodSetup:
|
||||
resp := PackResponseSetup(headers[HeaderFieldCSeq], headers[HeaderFieldTransport])
|
||||
_, _ = conn.Write([]byte(resp))
|
||||
default:
|
||||
nazalog.Error(method)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue