1. [refactor] 使用新的unique id生成器,提高性能 2. [refactor] 统一各Session接口

pull/49/head
q191201771 4 years ago
parent c5f756a51d
commit 3df6ee4027

@ -71,6 +71,11 @@ var (
)
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
url := parseFlag()
session := httpflv.NewPullSession()

@ -34,25 +34,49 @@ const (
PullTypeHTTPFLV
)
func (pt PullType) Readable() string {
switch pt {
case PullTypeUnknown:
return "unknown"
case PullTypeRTMP:
return "rtmp"
case PullTypeHTTPFLV:
return "httpflv"
}
// never reach here
return "fxxk"
}
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
tagKey2writeTime := make(map[string]time.Time)
var delays []int64
var mu sync.Mutex
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
filename, pushURL, pullURL, pullType := parseFlag()
nazalog.Infof("parse flag succ. filename=%s, pushURL=%s, pullURL=%s, pullType=%s",
filename, pushURL, pullURL, pullType.Readable())
tags, err := httpflv.ReadAllTagsFromFLVFile(filename)
nazalog.Assert(nil, err)
if err != nil {
nazalog.Fatalf("read tags from flv file failed. err=%+v", err)
}
nazalog.Infof("read tags from flv file succ. len of tags=%d", len(tags))
pushSession := rtmp.NewPushSession()
pushSession := rtmp.NewPushSession(func(option *rtmp.PushSessionOption) {
option.PushTimeoutMS = 10000
})
err = pushSession.Push(pushURL)
nazalog.Assert(nil, err)
nazalog.Info("push succ.")
//defer pushSession.Dispose()
if err != nil {
nazalog.Fatalf("push rtmp failed. url=%s, err=%+v", pushURL, err)
}
nazalog.Info("push rtmp succ.")
defer pushSession.Dispose()
var rtmpPullSession *rtmp.PullSession
var httpflvPullSession *httpflv.PullSession
@ -77,18 +101,23 @@ func main() {
err = httpflvPullSession.Pull(pullURL, func(tag httpflv.Tag) {
handleReadPayloadFn(tag.Payload())
})
nazalog.Assert(nil, err)
if err != nil {
nazalog.Fatalf("pull flv failed. err=%+v", err)
}
nazalog.Info("pull flv succ.")
defer httpflvPullSession.Dispose()
case PullTypeRTMP:
rtmpPullSession = rtmp.NewPullSession()
err = rtmpPullSession.Pull(pullURL, func(msg base.RTMPMsg) {
handleReadPayloadFn(msg.Payload)
})
nazalog.Assert(nil, err)
//defer pullSession.Dispose()
if err != nil {
nazalog.Fatalf("pull rtmp failed. err=%+v", err)
}
nazalog.Info("pull rtmp succ.")
defer rtmpPullSession.Dispose()
}
nazalog.Info("pull succ.")
go func() {
for {
time.Sleep(5 * time.Second)
@ -125,10 +154,14 @@ func main() {
tagKey2writeTime[tagKey] = time.Now()
mu.Unlock()
err = pushSession.AsyncWrite(chunks)
nazalog.Assert(nil, err)
err = pushSession.Write(chunks)
if err != nil {
nazalog.Fatalf("write failed. err=%+v", err)
}
//nazalog.Debugf("sent. %d", i)
}
_ = pushSession.Flush()
time.Sleep(300 * time.Millisecond)
min := int64(2147483647)
max := int64(0)

@ -198,6 +198,11 @@ func logHandler(w http.ResponseWriter, r *http.Request) {
}
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
dataManager = datamanager.NewDataManager(datamanager.DMTMemory, config.ServerTimeoutSec)
l, err := net.Listen("tcp", config.ListenAddr)

@ -27,6 +27,11 @@ import (
// TODO chef 做HEVC的支持
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
var err error
flvFileName, aacFileName, avcFileName := parseFlag()

@ -62,6 +62,11 @@ func getTSURL(m3u8URL string, tsFilename string) string {
}
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
m3u8URL := parseFlag()
nazalog.Infof("m3u8 url=%s", m3u8URL)

@ -82,6 +82,11 @@ func handlePacket(packet []byte) {
}
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
pid2stream = make(map[uint16]*Stream)
content, err := ioutil.ReadFile(filename)

@ -16,7 +16,7 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/httpflv"
log "github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/nazalog"
)
// 修改flv文件的一些信息比如某些tag的时间戳后另存文件
@ -29,44 +29,49 @@ var countV int
var exitFlag bool
func hookTag(tag *httpflv.Tag) {
log.Infof("%+v", tag.Header)
nazalog.Infof("%+v", tag.Header)
if tag.Header.Timestamp != 0 {
tag.ModTagTimestamp(tag.Header.Timestamp + uint32(time.Now().Unix()/1e6))
}
}
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
var err error
inFileName, outFileName := parseFlag()
var ffr httpflv.FLVFileReader
err = ffr.Open(inFileName)
log.Assert(nil, err)
nazalog.Assert(nil, err)
defer ffr.Dispose()
log.Infof("open input flv file succ.")
nazalog.Infof("open input flv file succ.")
var ffw httpflv.FLVFileWriter
err = ffw.Open(outFileName)
log.Assert(nil, err)
nazalog.Assert(nil, err)
defer ffw.Dispose()
log.Infof("open output flv file succ.")
nazalog.Infof("open output flv file succ.")
flvHeader, err := ffr.ReadFLVHeader()
log.Assert(nil, err)
nazalog.Assert(nil, err)
err = ffw.WriteRaw(flvHeader)
log.Assert(nil, err)
nazalog.Assert(nil, err)
for {
tag, err := ffr.ReadTag()
if err == io.EOF {
log.Infof("EOF.")
nazalog.Infof("EOF.")
break
}
log.Assert(nil, err)
nazalog.Assert(nil, err)
hookTag(&tag)
err = ffw.WriteRaw(tag.Raw)
log.Assert(nil, err)
nazalog.Assert(nil, err)
}
}

@ -14,7 +14,7 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/httpflv"
log "github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/nazalog"
)
// 拉取HTTP-FLV的流
@ -24,17 +24,22 @@ import (
// - 拉取HTTP-FLV流进行分析参见另外一个demoanalyseflvts。 这个demo可能可以删除掉了。
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
url := parseFlag()
session := httpflv.NewPullSession()
err := session.Pull(url, func(tag httpflv.Tag) {
switch tag.Header.Type {
case httpflv.TagTypeMetadata:
log.Info(hex.Dump(tag.Payload()))
nazalog.Info(hex.Dump(tag.Payload()))
case httpflv.TagTypeAudio:
case httpflv.TagTypeVideo:
}
})
log.Assert(nil, err)
nazalog.Assert(nil, err)
}
func parseFlag() string {

@ -48,7 +48,13 @@ import (
var aliveSessionCount int32
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
urlTmpl, fileNameTmpl, num := parseFlag()
nazalog.Infof("parse flag succ. urlTmpl=%s, fileNameTmpl=%s, num=%d", urlTmpl, fileNameTmpl, num)
urls, filenames := collect(urlTmpl, fileNameTmpl, num)
go func() {
@ -69,7 +75,7 @@ func main() {
}
wg.Wait()
time.Sleep(1 * time.Second)
nazalog.Info("bye.")
nazalog.Info("< main.")
}
func pull(url string, filename string) {
@ -87,28 +93,25 @@ func pull(url string, filename string) {
}
session := rtmp.NewPullSession(func(option *rtmp.PullSessionOption) {
option.PullTimeoutMS = 30000
option.PullTimeoutMS = 10000
option.ReadAVTimeoutMS = 10000
})
err = session.Pull(
url,
func(msg base.RTMPMsg) {
//nazalog.Debugf("header=%+v", msg.Header)
if filename != "" {
tag := remux.RTMPMsg2FLVTag(msg)
err := w.WriteTag(*tag)
nazalog.Assert(nil, err)
}
})
err = session.Pull(url, func(msg base.RTMPMsg) {
if filename != "" {
tag := remux.RTMPMsg2FLVTag(msg)
err := w.WriteTag(*tag)
nazalog.Assert(nil, err)
}
})
if err != nil {
nazalog.Errorf("pull failed. err=%v", err)
nazalog.Errorf("pull failed. err=%+v", err)
return
}
atomic.AddInt32(&aliveSessionCount, 1)
err = <-session.Wait()
nazalog.Debug(err)
err = <-session.WaitChan()
nazalog.Debugf("< session.WaitChan. [%s] err=%+v", session.UniqueKey(), err)
}
func collect(urlTmpl string, fileNameTmpl string, num int) (urls []string, filenames []string) {

@ -13,7 +13,6 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hls"
@ -22,7 +21,14 @@ import (
)
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
url, hlsOutPath, fragmentDurationMS, fragmentNum := parseFlag()
nazalog.Infof("parse flag succ. url=%s, hlsOutPath=%s, fragmentDurationMS=%d, fragmentNum=%d",
url, hlsOutPath, fragmentDurationMS, fragmentNum)
hlsMuxerConfig := hls.MuxerConfig{
Enable: true,
@ -31,26 +37,28 @@ func main() {
FragmentNum: fragmentNum,
}
index := strings.LastIndexByte(url, '/')
if index == -1 {
nazalog.Error("rtmp url invalid.")
os.Exit(1)
ctx, err := base.ParseRTMPURL(url)
if err != nil {
nazalog.Fatalf("parse rtmp url failed. url=%s, err=%+v", url, err)
}
streamName := url[index:]
streamName := ctx.LastItemOfPath
pullSession := rtmp.NewPullSession()
hlsMuexer := hls.NewMuxer(streamName, &hlsMuxerConfig, nil)
hlsMuexer.Start()
err := pullSession.Pull(url, func(msg base.RTMPMsg) {
pullSession := rtmp.NewPullSession(func(option *rtmp.PullSessionOption) {
option.PullTimeoutMS = 10000
option.ReadAVTimeoutMS = 10000
})
err = pullSession.Pull(url, func(msg base.RTMPMsg) {
hlsMuexer.FeedRTMPMessage(msg)
})
if err != nil {
nazalog.Errorf("pull error. err=%+v", err)
os.Exit(1)
nazalog.Fatalf("pull rtmp failed. err=%+v", err)
}
err = <-pullSession.Wait()
nazalog.Errorf("pull error. err=%+v", err)
err = <-pullSession.WaitChan()
nazalog.Errorf("< session.Wait [%s] err=%+v", pullSession.UniqueKey(), err)
}
func parseFlag() (url string, hlsOutPath string, fragmentDurationMS int, fragmentNum int) {

@ -19,6 +19,11 @@ import (
)
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
i := flag.String("i", "", "specify pull rtmp url")
o := flag.String("o", "", "specify push rtmp url list, separated by a comma")
flag.Parse()

@ -118,7 +118,7 @@ func (t *Tunnel) Start() (ret ErrorCode) {
nazalog.Debugf("[%s] > pull event loop. %s", t.uk, t.pullSession.UniqueKey())
for {
select {
case err := <-t.pullSession.Wait():
case err := <-t.pullSession.WaitChan():
t.notifyPullEC(ErrorCode{-1, err})
nazalog.Debugf("[%s] < pull event loop. %s", t.uk, t.pullSession.UniqueKey())
return
@ -133,7 +133,7 @@ func (t *Tunnel) Start() (ret ErrorCode) {
nazalog.Debugf("[%s] > push event loop. %s", t.uk, s.UniqueKey())
for {
select {
case err := <-s.Wait():
case err := <-s.WaitChan():
nazalog.Errorf("[%s] push wait error. [%s] err=%+v", t.uk, s.UniqueKey(), err)
t.notifyPushEC(ErrorCode{ii, err})
nazalog.Debugf("[%s] < push event loop. %s", t.uk, s.UniqueKey())
@ -171,7 +171,7 @@ func (t *Tunnel) Start() (ret ErrorCode) {
}
for i, pushSession := range t.pushSessionList {
err := pushSession.AsyncWrite(chunks)
err := pushSession.Write(chunks)
if err != nil {
nazalog.Errorf("[%s] exit main event loop, write error. err=%+v", t.uk, err)
t.notifyWait(ErrorCode{i, err})

@ -88,7 +88,7 @@ func main() {
}
}()
err = <-pullSession.Wait()
err = <-pullSession.WaitChan()
nazalog.Infof("< pullSession.Wait(). err=%+v", err)
}

@ -36,16 +36,16 @@ func (o *Observer) OnAVConfig(asc, vps, sps, pps []byte) {
metadata, ash, vsh, err := remux.AVConfig2RTMPMsg(asc, vps, sps, pps)
nazalog.Assert(nil, err)
err = pushSession.AsyncWrite(rtmp.Message2Chunks(metadata.Payload, &metadata.Header))
err = pushSession.Write(rtmp.Message2Chunks(metadata.Payload, &metadata.Header))
nazalog.Assert(nil, err)
if ash != nil {
err = pushSession.AsyncWrite(rtmp.Message2Chunks(ash.Payload, &ash.Header))
err = pushSession.Write(rtmp.Message2Chunks(ash.Payload, &ash.Header))
nazalog.Assert(nil, err)
}
if vsh != nil {
err = pushSession.AsyncWrite(rtmp.Message2Chunks(vsh.Payload, &vsh.Header))
err = pushSession.Write(rtmp.Message2Chunks(vsh.Payload, &vsh.Header))
nazalog.Assert(nil, err)
}
}
@ -53,7 +53,7 @@ func (o *Observer) OnAVConfig(asc, vps, sps, pps []byte) {
func (o *Observer) OnAVPacket(pkt base.AVPacket) {
msg, err := remux.AVPacket2RTMPMsg(pkt)
nazalog.Assert(nil, err)
err = pushSession.AsyncWrite(rtmp.Message2Chunks(msg.Payload, &msg.Header))
err = pushSession.Write(rtmp.Message2Chunks(msg.Payload, &msg.Header))
nazalog.Assert(nil, err)
}
@ -96,11 +96,11 @@ func main() {
}()
select {
case err = <-pullSession.Wait():
case err = <-pullSession.WaitChan():
nazalog.Infof("< pullSession.Wait(). err=%+v", err)
time.Sleep(1 * time.Second)
return
case err = <-pushSession.Wait():
case err = <-pushSession.WaitChan():
nazalog.Infof("< pushSession.Wait(). err=%+v", err)
time.Sleep(1 * time.Second)
return

@ -84,11 +84,11 @@ func main() {
for {
select {
case err = <-pullSession.Wait():
case err = <-pullSession.WaitChan():
nazalog.Infof("< pullSession.Wait(). err=%+v", err)
time.Sleep(1 * time.Second) // 不让程序立即退出只是为了测试session内部资源是否正常及时释放
return
case err = <-pushSession.Wait():
case err = <-pushSession.WaitChan():
nazalog.Infof("< pushSession.Wait(). err=%+v", err)
time.Sleep(1 * time.Second)
return

@ -52,27 +52,17 @@ import (
var aliveSessionCount int32
func main() {
defer nazalog.Sync()
filename, urlTmpl, num, isRecursive, logfile := parseFlag()
if logfile != "" {
err := nazalog.Init(func(option *nazalog.Option) {
option.IsRotateDaily = false
option.Filename = logfile
option.IsToStdout = false
})
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "init nazalog failed. err=%+v", err)
os.Exit(1)
}
}
initLog(logfile)
urls := collect(urlTmpl, num)
tags, err := httpflv.ReadAllTagsFromFLVFile(filename)
if err != nil {
nazalog.Errorf("read tags from flv file failed. err=%+v", err)
os.Exit(0)
nazalog.Fatalf("read tags from flv file failed. err=%+v", err)
}
nazalog.Infof("read all tag done. tag num=%d", len(tags))
nazalog.Infof("read tags from flv file succ. len of tags=%d", len(tags))
go func() {
for {
@ -92,7 +82,7 @@ func main() {
}
wg.Wait()
time.Sleep(1 * time.Second)
nazalog.Info("bye.")
nazalog.Info("< main.")
}
func push(tags []httpflv.Tag, urls []string, isRecursive bool) {
@ -214,7 +204,7 @@ func push(tags []httpflv.Tag, urls []string, isRecursive bool) {
func send(sessionList []*rtmp.PushSession, b []byte) {
var s []*rtmp.PushSession
for _, ps := range sessionList {
if err := ps.AsyncWrite(b); err != nil {
if err := ps.Write(b); err != nil {
nazalog.Errorf("write data error. err=%v", err)
continue
}
@ -240,6 +230,24 @@ func collect(urlTmpl string, num int) (urls []string) {
return
}
func initLog(logfile string) {
if logfile != "" {
err := nazalog.Init(func(option *nazalog.Option) {
option.IsRotateDaily = false
option.Filename = logfile
option.IsToStdout = false
})
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "init nazalog failed. err=%+v", err)
os.Exit(1)
}
} else {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
}
}
func parseFlag() (filename string, urlTmpl string, num int, isRecursive bool, logfile string) {
i := flag.String("i", "", "specify flv file")
o := flag.String("o", "", "specify rtmp push url")

@ -59,6 +59,11 @@ func parsePacket(packet []byte) {
}
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
content1, err := ioutil.ReadFile(filename1)
nazalog.Assert(nil, err)

@ -16,9 +16,15 @@ mkdir -p ${ROOT_DIR}/${OUT_DIR}/${prefix}macos/conf
mkdir -p ${ROOT_DIR}/${OUT_DIR}/${prefix}windows/bin
mkdir -p ${ROOT_DIR}/${OUT_DIR}/${prefix}windows/conf
echo ${v} >> ${ROOT_DIR}/${OUT_DIR}/${prefix}linux/VERSION.txt
echo ${v} >> ${ROOT_DIR}/${OUT_DIR}/${prefix}macos/VERSION.txt
echo ${v} >> ${ROOT_DIR}/${OUT_DIR}/${prefix}windows/VERSION.txt
echo ${v} >> ${ROOT_DIR}/${OUT_DIR}/${prefix}linux/README.txt
echo ${v} >> ${ROOT_DIR}/${OUT_DIR}/${prefix}macos/README.txt
echo ${v} >> ${ROOT_DIR}/${OUT_DIR}/${prefix}windows/README.txt
echo 'github: https://github.com/q191201771/lal' >> ${ROOT_DIR}/${OUT_DIR}/${prefix}linux/README.txt
echo 'github: https://github.com/q191201771/lal' >> ${ROOT_DIR}/${OUT_DIR}/${prefix}macos/README.txt
echo 'github: https://github.com/q191201771/lal' >> ${ROOT_DIR}/${OUT_DIR}/${prefix}windows/README.txt
echo 'doc: https://pengrl.com/lal' >> ${ROOT_DIR}/${OUT_DIR}/${prefix}linux/README.txt
echo 'doc: https://pengrl.com/lal' >> ${ROOT_DIR}/${OUT_DIR}/${prefix}macos/README.txt
echo 'doc: https://pengrl.com/lal' >> ${ROOT_DIR}/${OUT_DIR}/${prefix}windows/README.txt
cp conf/lalserver.conf.json ${ROOT_DIR}/${OUT_DIR}/${prefix}linux/conf
cp conf/lalserver.conf.json ${ROOT_DIR}/${OUT_DIR}/${prefix}macos/conf

@ -2,4 +2,4 @@ module github.com/q191201771/lal
go 1.13
require github.com/q191201771/naza v0.17.1
require github.com/q191201771/naza v0.18.0

@ -1,2 +1,2 @@
github.com/q191201771/naza v0.17.1 h1:gWk9jsybJoZRc4Bz39QMvtKoqLNQl+TY0mvWnuEcl34=
github.com/q191201771/naza v0.17.1/go.mod h1:5LeGupZZFtYP1g/S203n9vXoUNVdlRnPIfM6rExjqt0=
github.com/q191201771/naza v0.18.0 h1:YehvnnliSvJOc6HkLMXwEwslBHwjhWapw6iZ5kd91yo=
github.com/q191201771/naza v0.18.0/go.mod h1:5LeGupZZFtYP1g/S203n9vXoUNVdlRnPIfM6rExjqt0=

@ -8,13 +8,53 @@
package base
type ISessionURLContext interface {
URL() string
AppName() string
StreamName() string
RawQuery() string
import "errors"
var (
ErrSessionNotStarted = errors.New("lal.base: session has not been started yet")
)
type IClientSession interface {
// PushSession:
// Push()
// Write()
// Flush()
// PullSession:
// Pull()
IClientSessionLifecycle
ISessionURLContext
IObject
ISessionStat
}
type IServerSession interface {
IServerSessionLifecycle
ISessionURLContext
IObject
ISessionStat
}
// 调用约束对于Client类型的Session调用Start函数并返回成功后才能调用否则行为未定义
type IClientSessionLifecycle interface {
// 关闭session
// 业务方想主动关闭session时调用
// 注意Start成功后的session必须显示调用Dispose释放资源即使是被动接收到了WaitChan信号
Dispose() error
// Start成功后可使用这个channel来接收session结束的信号
WaitChan() <-chan error
}
type IServerSessionLifecycle interface {
// 开启session的事件循环阻塞直到session结束
RunLoop() error
// 主动关闭session时调用
Dispose() error
}
// 调用约束对于Client类型的Session调用Start函数并返回成功后才能调用否则行为未定义
type ISessionStat interface {
// 周期性调用该函数用于计算bitrate
//
@ -35,67 +75,20 @@ type ISessionStat interface {
IsAlive() (readAlive, writeAlive bool)
}
type ISession interface {
RemoteAddr() string
}
// TODO chef: rtmp.ClientSession修改为BaseClientSession更好些
//
// | . | rtmp pub | rtmp sub | rtmp push | rtmp pull |
// | - | - | - | - | - |
// | file | server_session.go | server_session.go | client_push_session.go | client_pull_session.go |
// | struct | ServerSession | ServerSession | PushSession/ClientSession | PullSession/ClientSession |
// 获取和流地址相关的信息
//
//
// | . | flv sub | flv pull |
// | - | - | - |
// | file | server_sub_session.go | client_pull_session.go |
// | struct | SubSession | PullSession |
//
//
// | . | ts sub |
// | - | - |
// | file | server_sub_session.go |
// | struct | SubSession |
//
//
// | . | rtmppub | rtsppub | rtmpsub | flvsub | tssub | rtspsub | - | rtmppush | rtmppull | flvpull | rtsppull | rtsppush | hls |
// | - | - | - | - | - | - | - | - | - | - | - | - | | |
// | x | x | x | x | x | x | x | - | x | x | x | x | | |
// | UniqueKey<all> | √ | √ | √ | √ | √ | √ | - | x√ | x√ | √ | √ | | |
// | AppName()<all> | √ | √ | √ | √ | √ | √ | - | √ | √ | √ | √ | | |
// | StreamName()<all> | √ | √ | √ | √ | √ | √ | - | √ | √ | √ | √ | | |
// | RawQuery()<all> | √ | √ | √ | √ | √ | √ | - | √ | √ | √ | √ | | |
// 调用约束对于Client类型的Session调用Start函数并返回成功后才能调用否则行为未定义
type ISessionURLContext interface {
URL() string
AppName() string
StreamName() string
RawQuery() string
}
// | GetStat()<all> | √ | √ | √ | √ | √ | √ | - | √ | √ | √ | √ | | |
// | UpdateStat()<all> | √ | √ | √ | √ | √ | √ | - | √ | √ | √ | √ | | |
// | IsAlive()<all> | √ | √ | √ | √ | √ | √ | - | √ | √ | √ | √ | | |
type IObject interface {
// 对象的全局唯一标识
UniqueKey() string
}
// | RunLoop() | √ | x√ | √ | √ | √ | x&√ | - | x | x | x | x | | |
// | Dispose() | √ | √ | √ | √ | √ | √ | - | √ | √ | √ | √ | | |
// TODO chef: rtmp.ClientSession修改为BaseClientSession更好些
// | RemoteAddr() | √ | x | √ | √ | x | x | - | x | x | x | x | | |
// | SingleConn | √ | x | √ | √ | √ | √ | - | √ | √ | √ | x | | |
//
// | Opt.Push/PullTimeoutMS | - | - | - | - | - | - | - | √ | √ | √ | √ | √ | |
// | Wait() | - | - | - | - | - | - | - | √ | √ | √ | √ | √ | |
//
// Dispose由外部调用表示主动关闭正常的session
// 外部调用Dispose后不应继续使用该session
// Dispose后RunLoop结束阻塞
//
// 对端关闭或session内部关闭也会导致RunLoop结束阻塞
//
// RunLoop结束阻塞后可通知上层告知session生命周期结束
//
// ---
//
// 对于rtsp.PushSession和rtsp.PullSession
// Push()或Pull成功后可调用Dispose()主动关闭session
// 当对端关闭导致Wait()触发时也需要调用Dispose()
//
// 对于rtsp.PubSession和rtsp.SubSession
// ServerCommandSession通知上层上层调用session的Dispose()
// 当然session也支持主动调用Dispose()

@ -47,10 +47,11 @@ type StatPull struct {
}
type StatSession struct {
Protocol string `json:"protocol"`
SessionID string `json:"session_id"`
RemoteAddr string `json:"remote_addr"`
StartTime string `json:"start_time"`
Protocol string `json:"protocol"`
SessionID string `json:"session_id"`
RemoteAddr string `json:"remote_addr"`
StartTime string `json:"start_time"`
ReadBytesSum uint64 `json:"read_bytes_sum"`
WroteBytesSum uint64 `json:"wrote_bytes_sum"`
Bitrate int `json:"bitrate"`

@ -11,23 +11,115 @@ package base
import "github.com/q191201771/naza/pkg/unique"
const (
UKPRTMPServerSession = "RTMPPUBSUB"
UKPRTMPPushSession = "RTMPPUSH"
UKPRTMPPullSession = "RTMPPULL"
UKPRTSPServerCommandSession = "RTSPSRVCMD"
UKPRTSPPubSession = "RTSPPUB"
UKPRTSPSubSession = "RTSPSUB"
UKPRTSPPushSession = "RTSPPUSH"
UKPRTSPPullSession = "RTSPPULL"
UKPFLVSubSession = "FLVSUB"
UKPTSSubSession = "TSSUB"
UKPFLVPullSession = "FLVPULL"
UKPGroup = "GROUP"
UKPHLSMuxer = "HLSMUXER"
UKPStreamer = "STREAMER"
UKPreRTMPServerSession = "RTMPPUBSUB"
UKPreRTMPPushSession = "RTMPPUSH"
UKPreRTMPPullSession = "RTMPPULL"
UKPreRTSPServerCommandSession = "RTSPSRVCMD"
UKPreRTSPPubSession = "RTSPPUB"
UKPreRTSPSubSession = "RTSPSUB"
UKPreRTSPPushSession = "RTSPPUSH"
UKPreRTSPPullSession = "RTSPPULL"
UKPreFLVSubSession = "FLVSUB"
UKPreTSSubSession = "TSSUB"
UKPreFLVPullSession = "FLVPULL"
UKPreGroup = "GROUP"
UKPreHLSMuxer = "HLSMUXER"
UKPreStreamer = "STREAMER"
)
//func GenUK(prefix string) string {
// return unique.GenUniqueKey(prefix)
//}
func GenUKRTMPServerSession() string {
return siUKRTMPServerSession.GenUniqueKey()
}
func GenUKRTMPPushSession() string {
return siUKRTMPPushSession.GenUniqueKey()
}
func GenUKRTMPPullSession() string {
return siUKRTMPPullSession.GenUniqueKey()
}
func GenUKRTSPServerCommandSession() string {
return siUKRTSPServerCommandSession.GenUniqueKey()
}
func GenUKRTSPPubSession() string {
return siUKRTSPPubSession.GenUniqueKey()
}
func GenUKRTSPSubSession() string {
return siUKRTSPSubSession.GenUniqueKey()
}
func GenUKRTSPPushSession() string {
return siUKRTSPPushSession.GenUniqueKey()
}
func GenUKRTSPPullSession() string {
return siUKRTSPPullSession.GenUniqueKey()
}
func GenUKFLVSubSession() string {
return siUKFLVSubSession.GenUniqueKey()
}
func GenUKTSSubSession() string {
return siUKTSSubSession.GenUniqueKey()
}
func GenUKFLVPullSession() string {
return siUKFLVPullSession.GenUniqueKey()
}
func GenUKGroup() string {
return siUKGroup.GenUniqueKey()
}
func GenUKHLSMuxer() string {
return siUKHLSMuxer.GenUniqueKey()
}
func GenUKStreamer() string {
return siUKStreamer.GenUniqueKey()
}
var (
siUKRTMPServerSession *unique.SingleGenerator
siUKRTMPPushSession *unique.SingleGenerator
siUKRTMPPullSession *unique.SingleGenerator
siUKRTSPServerCommandSession *unique.SingleGenerator
siUKRTSPPubSession *unique.SingleGenerator
siUKRTSPSubSession *unique.SingleGenerator
siUKRTSPPushSession *unique.SingleGenerator
siUKRTSPPullSession *unique.SingleGenerator
siUKFLVSubSession *unique.SingleGenerator
siUKTSSubSession *unique.SingleGenerator
siUKFLVPullSession *unique.SingleGenerator
siUKGroup *unique.SingleGenerator
siUKHLSMuxer *unique.SingleGenerator
siUKStreamer *unique.SingleGenerator
)
func GenUniqueKey(prefix string) string {
return unique.GenUniqueKey(prefix)
func init() {
siUKRTMPServerSession = unique.NewSingleGenerator(UKPreRTMPServerSession)
siUKRTMPPushSession = unique.NewSingleGenerator(UKPreRTMPPushSession)
siUKRTMPPullSession = unique.NewSingleGenerator(UKPreRTMPPullSession)
siUKRTSPServerCommandSession = unique.NewSingleGenerator(UKPreRTSPServerCommandSession)
siUKRTSPPubSession = unique.NewSingleGenerator(UKPreRTSPPubSession)
siUKRTSPSubSession = unique.NewSingleGenerator(UKPreRTSPSubSession)
siUKRTSPPushSession = unique.NewSingleGenerator(UKPreRTSPPushSession)
siUKRTSPPullSession = unique.NewSingleGenerator(UKPreRTSPPullSession)
siUKFLVSubSession = unique.NewSingleGenerator(UKPreFLVSubSession)
siUKTSSubSession = unique.NewSingleGenerator(UKPreTSSubSession)
siUKFLVPullSession = unique.NewSingleGenerator(UKPreFLVPullSession)
siUKGroup = unique.NewSingleGenerator(UKPreGroup)
siUKHLSMuxer = unique.NewSingleGenerator(UKPreHLSMuxer)
siUKStreamer = unique.NewSingleGenerator(UKPreStreamer)
}

@ -76,7 +76,7 @@ type fragmentInfo struct {
// @param observer 可以为nil如果不为nilTS流将回调给上层
func NewMuxer(streamName string, config *MuxerConfig, observer MuxerObserver) *Muxer {
uk := base.GenUniqueKey(base.UKPHLSMuxer)
uk := base.GenUKHLSMuxer()
op := getMuxerOutPath(config.OutPath, streamName)
playlistFilename := getM3U8Filename(op, streamName)
playlistFilenameBak := fmt.Sprintf("%s.bak", playlistFilename)

@ -41,7 +41,7 @@ type Streamer struct {
}
func NewStreamer(observer StreamerObserver) *Streamer {
uk := base.GenUniqueKey(base.UKPStreamer)
uk := base.GenUKStreamer()
videoOut := make([]byte, 1024*1024)
videoOut = videoOut[0:0]
return &Streamer{

@ -36,7 +36,7 @@ var defaultPullSessionOption = PullSessionOption{
}
type PullSession struct {
UniqueKey string // const after ctor
uniqueKey string // const after ctor
option PullSessionOption // const after ctor
conn connection.Connection
@ -46,7 +46,7 @@ type PullSession struct {
urlCtx base.URLContext
waitErrChan chan error
waitChan chan error
}
type ModPullSessionOption func(option *PullSessionOption)
@ -57,11 +57,11 @@ func NewPullSession(modOptions ...ModPullSessionOption) *PullSession {
fn(&option)
}
uk := base.GenUniqueKey(base.UKPFLVPullSession)
uk := base.GenUKFLVPullSession()
s := &PullSession{
UniqueKey: uk,
option: option,
waitErrChan: make(chan error, 1),
uniqueKey: uk,
option: option,
waitChan: make(chan error, 1),
}
nazalog.Infof("[%s] lifecycle new httpflv PullSession. session=%p", uk, s)
return s
@ -69,7 +69,7 @@ func NewPullSession(modOptions ...ModPullSessionOption) *PullSession {
type OnReadFLVTag func(tag Tag)
// 如果没有错误发生阻塞直到接收音视频数据的前一步也即发送完HTTP请求
// 阻塞直到和对端完成拉流前握手部分的工作也即发送完HTTP Request或者发生错误
//
// @param rawURL 支持如下两种格式。(当然,关键点是对端支持)
// http://{domain}/{app_name}/{stream_name}.flv
@ -77,7 +77,7 @@ type OnReadFLVTag func(tag Tag)
//
// @param onReadFLVTag 读取到 flv tag 数据时回调。回调结束后PullSession 不会再使用这块 <tag> 数据。
func (session *PullSession) Pull(rawURL string, onReadFLVTag OnReadFLVTag) error {
nazalog.Debugf("[%s] pull. url=%s", session.UniqueKey, rawURL)
nazalog.Debugf("[%s] pull. url=%s", session.uniqueKey, rawURL)
var (
ctx context.Context
@ -92,57 +92,57 @@ func (session *PullSession) Pull(rawURL string, onReadFLVTag OnReadFLVTag) error
return session.pullContext(ctx, rawURL, onReadFLVTag)
}
// Pull成功后调用该函数可阻塞直到拉流结束
func (session *PullSession) Wait() <-chan error {
return session.waitErrChan
// 文档请参考: interface IClientSessionLifecycle
func (session *PullSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose httpflv PullSession.", session.uniqueKey)
if session.conn == nil {
return base.ErrSessionNotStarted
}
return session.conn.Close()
}
func (session *PullSession) pullContext(ctx context.Context, rawURL string, onReadFLVTag OnReadFLVTag) error {
errChan := make(chan error, 1)
// 文档请参考: interface IClientSessionLifecycle
func (session *PullSession) WaitChan() <-chan error {
return session.waitChan
}
go func() {
if err := session.connect(rawURL); err != nil {
errChan <- err
return
}
if err := session.writeHTTPRequest(); err != nil {
errChan <- err
return
}
// 文档请参考: interface ISessionURLContext
func (session *PullSession) URL() string {
return session.urlCtx.URL
}
errChan <- nil
}()
// 文档请参考: interface ISessionURLContext
func (session *PullSession) AppName() string {
return session.urlCtx.PathWithoutLastItem
}
select {
case <-ctx.Done():
return ctx.Err()
case err := <-errChan:
if err != nil {
return err
}
}
// 文档请参考: interface ISessionURLContext
func (session *PullSession) StreamName() string {
return session.urlCtx.LastItemOfPath
}
go session.runReadLoop(onReadFLVTag)
return nil
// 文档请参考: interface ISessionURLContext
func (session *PullSession) RawQuery() string {
return session.urlCtx.RawQuery
}
func (session *PullSession) Dispose() {
nazalog.Infof("[%s] lifecycle dispose httpflv PullSession.", session.UniqueKey)
if session.conn != nil {
_ = session.conn.Close()
}
// 文档请参考: interface IObject
func (session *PullSession) UniqueKey() string {
return session.uniqueKey
}
func (session *PullSession) UpdateStat(interval uint32) {
// 文档请参考: interface ISessionStat
func (session *PullSession) UpdateStat(intervalSec uint32) {
currStat := session.conn.GetStat()
rDiff := currStat.ReadBytesSum - session.prevConnStat.ReadBytesSum
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(interval))
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := currStat.WroteBytesSum - session.prevConnStat.WroteBytesSum
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(interval))
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
session.stat.Bitrate = session.stat.ReadBitrate
session.prevConnStat = currStat
}
// 文档请参考: interface ISessionStat
func (session *PullSession) GetStat() base.StatSession {
connStat := session.conn.GetStat()
session.stat.ReadBytesSum = connStat.ReadBytesSum
@ -150,6 +150,7 @@ func (session *PullSession) GetStat() base.StatSession {
return session.stat
}
// 文档请参考: interface ISessionStat
func (session *PullSession) IsAlive() (readAlive, writeAlive bool) {
currStat := session.conn.GetStat()
if session.staleStat == nil {
@ -164,16 +165,33 @@ func (session *PullSession) IsAlive() (readAlive, writeAlive bool) {
return
}
func (session *PullSession) AppName() string {
return session.urlCtx.PathWithoutLastItem
}
func (session *PullSession) pullContext(ctx context.Context, rawURL string, onReadFLVTag OnReadFLVTag) error {
errChan := make(chan error, 1)
func (session *PullSession) StreamName() string {
return session.urlCtx.LastItemOfPath
}
go func() {
if err := session.connect(rawURL); err != nil {
errChan <- err
return
}
if err := session.writeHTTPRequest(); err != nil {
errChan <- err
return
}
func (session *PullSession) RawQuery() string {
return session.urlCtx.RawQuery
errChan <- nil
}()
select {
case <-ctx.Done():
return ctx.Err()
case err := <-errChan:
if err != nil {
return err
}
}
go session.runReadLoop(onReadFLVTag)
return nil
}
func (session *PullSession) connect(rawURL string) (err error) {
@ -182,7 +200,7 @@ func (session *PullSession) connect(rawURL string) (err error) {
return
}
nazalog.Debugf("[%s] > tcp connect.", session.UniqueKey)
nazalog.Debugf("[%s] > tcp connect.", session.uniqueKey)
// # 建立连接
conn, err := net.Dial("tcp", session.urlCtx.HostWithPort)
@ -199,7 +217,7 @@ func (session *PullSession) connect(rawURL string) (err error) {
func (session *PullSession) writeHTTPRequest() error {
// # 发送 http GET 请求
nazalog.Debugf("[%s] > W http request. GET %s", session.UniqueKey, session.urlCtx.PathWithRawQuery)
nazalog.Debugf("[%s] > W http request. GET %s", session.uniqueKey, session.urlCtx.PathWithRawQuery)
req := fmt.Sprintf("GET %s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\nRange: byte=0-\r\nConnection: close\r\nHost: %s\r\nIcy-MetaData: 1\r\n\r\n",
session.urlCtx.PathWithRawQuery, base.LALHTTPFLVPullSessionUA, session.urlCtx.StdHost)
_, err := session.conn.Write([]byte(req))
@ -216,7 +234,7 @@ func (session *PullSession) readHTTPRespHeader() (statusLine string, headers map
return
}
nazalog.Debugf("[%s] < R http response header. code=%s", session.UniqueKey, code)
nazalog.Debugf("[%s] < R http response header. code=%s", session.uniqueKey, code)
return
}
@ -226,7 +244,7 @@ func (session *PullSession) readFLVHeader() ([]byte, error) {
if err != nil {
return flvHeader, err
}
nazalog.Debugf("[%s] < R http flv header.", session.UniqueKey)
nazalog.Debugf("[%s] < R http flv header.", session.uniqueKey)
// TODO chef: check flv header's value
return flvHeader, nil
@ -238,19 +256,19 @@ func (session *PullSession) readTag() (Tag, error) {
func (session *PullSession) runReadLoop(onReadFLVTag OnReadFLVTag) {
if _, _, err := session.readHTTPRespHeader(); err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
if _, err := session.readFLVHeader(); err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
for {
tag, err := session.readTag()
if err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
onReadFLVTag(tag)

@ -128,16 +128,16 @@ func (server *Server) handleConnect(conn net.Conn, scheme string) {
nazalog.Infof("accept a httpflv connection. remoteAddr=%s", conn.RemoteAddr().String())
session := NewSubSession(conn, scheme)
if err := session.ReadRequest(); err != nil {
nazalog.Errorf("[%s] read httpflv SubSession request error. err=%v", session.UniqueKey, err)
nazalog.Errorf("[%s] read httpflv SubSession request error. err=%v", session.uniqueKey, err)
return
}
nazalog.Debugf("[%s] < read http request. url=%s", session.UniqueKey, session.URL())
nazalog.Debugf("[%s] < read http request. url=%s", session.uniqueKey, session.URL())
if !server.observer.OnNewHTTPFLVSubSession(session) {
session.Dispose()
}
err := session.RunLoop()
nazalog.Debugf("[%s] httpflv sub session loop done. err=%v", session.UniqueKey, err)
nazalog.Debugf("[%s] httpflv sub session loop done. err=%v", session.uniqueKey, err)
server.observer.OnDelHTTPFLVSubSession(session)
}

@ -26,7 +26,7 @@ import (
var flvHTTPResponseHeader []byte
type SubSession struct {
UniqueKey string
uniqueKey string
IsFresh bool
scheme string
@ -42,9 +42,9 @@ type SubSession struct {
}
func NewSubSession(conn net.Conn, scheme string) *SubSession {
uk := base.GenUniqueKey(base.UKPFLVSubSession)
uk := base.GenUKFLVSubSession()
s := &SubSession{
UniqueKey: uk,
uniqueKey: uk,
scheme: scheme,
IsFresh: true,
conn: connection.New(conn, func(option *connection.Option) {
@ -93,12 +93,12 @@ func (session *SubSession) RunLoop() error {
}
func (session *SubSession) WriteHTTPResponseHeader() {
nazalog.Debugf("[%s] > W http response header.", session.UniqueKey)
nazalog.Debugf("[%s] > W http response header.", session.uniqueKey)
session.WriteRawPacket(flvHTTPResponseHeader)
}
func (session *SubSession) WriteFLVHeader() {
nazalog.Debugf("[%s] > W http flv header.", session.UniqueKey)
nazalog.Debugf("[%s] > W http flv header.", session.uniqueKey)
session.WriteRawPacket(FLVHeader)
}
@ -110,9 +110,9 @@ func (session *SubSession) WriteRawPacket(pkt []byte) {
_, _ = session.conn.Write(pkt)
}
func (session *SubSession) Dispose() {
nazalog.Infof("[%s] lifecycle dispose httpflv SubSession.", session.UniqueKey)
_ = session.conn.Close()
func (session *SubSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose httpflv SubSession.", session.uniqueKey)
return session.conn.Close()
}
func (session *SubSession) URL() string {
@ -131,6 +131,10 @@ func (session *SubSession) RawQuery() string {
return session.urlCtx.RawQuery
}
func (session *SubSession) UniqueKey() string {
return session.uniqueKey
}
func (session *SubSession) GetStat() base.StatSession {
currStat := session.conn.GetStat()
session.stat.ReadBytesSum = currStat.ReadBytesSum
@ -138,12 +142,12 @@ func (session *SubSession) GetStat() base.StatSession {
return session.stat
}
func (session *SubSession) UpdateStat(interval uint32) {
func (session *SubSession) UpdateStat(intervalSec uint32) {
currStat := session.conn.GetStat()
rDiff := currStat.ReadBytesSum - session.prevConnStat.ReadBytesSum
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(interval))
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := currStat.WroteBytesSum - session.prevConnStat.WroteBytesSum
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(interval))
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
session.stat.Bitrate = session.stat.WriteBitrate
session.prevConnStat = currStat
}
@ -162,10 +166,6 @@ func (session *SubSession) IsAlive() (readAlive, writeAlive bool) {
return
}
func (session *SubSession) RemoteAddr() string {
return session.conn.RemoteAddr().String()
}
func init() {
flvHTTPResponseHeaderStr := "HTTP/1.1 200 OK\r\n" +
"Server: " + base.LALHTTPFLVSubSessionServer + "\r\n" +

@ -66,16 +66,16 @@ func (server *Server) handleConnect(conn net.Conn) {
log.Infof("accept a httpts connection. remoteAddr=%s", conn.RemoteAddr().String())
session := NewSubSession(conn, "http")
if err := session.ReadRequest(); err != nil {
log.Errorf("[%s] read httpts SubSession request error. err=%v", session.UniqueKey, err)
log.Errorf("[%s] read httpts SubSession request error. err=%v", session.uniqueKey, err)
return
}
log.Debugf("[%s] < read http request. url=%s", session.UniqueKey, session.URL())
log.Debugf("[%s] < read http request. url=%s", session.uniqueKey, session.URL())
if !server.observer.OnNewHTTPTSSubSession(session) {
session.Dispose()
}
err := session.RunLoop()
log.Debugf("[%s] httpts sub session loop done. err=%v", session.UniqueKey, err)
log.Debugf("[%s] httpts sub session loop done. err=%v", session.uniqueKey, err)
server.observer.OnDelHTTPTSSubSession(session)
}

@ -25,7 +25,7 @@ import (
var tsHTTPResponseHeader []byte
type SubSession struct {
UniqueKey string
uniqueKey string
IsFresh bool
scheme string
@ -41,9 +41,9 @@ type SubSession struct {
}
func NewSubSession(conn net.Conn, scheme string) *SubSession {
uk := base.GenUniqueKey(base.UKPTSSubSession)
uk := base.GenUKTSSubSession()
s := &SubSession{
UniqueKey: uk,
uniqueKey: uk,
scheme: scheme,
IsFresh: true,
conn: connection.New(conn, func(option *connection.Option) {
@ -92,12 +92,12 @@ func (session *SubSession) RunLoop() error {
}
func (session *SubSession) WriteHTTPResponseHeader() {
nazalog.Debugf("[%s] > W http response header.", session.UniqueKey)
nazalog.Debugf("[%s] > W http response header.", session.uniqueKey)
session.WriteRawPacket(tsHTTPResponseHeader)
}
func (session *SubSession) WriteFragmentHeader() {
nazalog.Debugf("[%s] > W http response header.", session.UniqueKey)
nazalog.Debugf("[%s] > W http response header.", session.uniqueKey)
session.WriteRawPacket(mpegts.FixedFragmentHeader)
}
@ -105,17 +105,17 @@ func (session *SubSession) WriteRawPacket(pkt []byte) {
_, _ = session.conn.Write(pkt)
}
func (session *SubSession) Dispose() {
nazalog.Infof("[%s] lifecycle dispose httpts SubSession.", session.UniqueKey)
_ = session.conn.Close()
func (session *SubSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose httpts SubSession.", session.uniqueKey)
return session.conn.Close()
}
func (session *SubSession) UpdateStat(interval uint32) {
func (session *SubSession) UpdateStat(intervalSec uint32) {
currStat := session.conn.GetStat()
rDiff := currStat.ReadBytesSum - session.prevConnStat.ReadBytesSum
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(interval))
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := currStat.WroteBytesSum - session.prevConnStat.WroteBytesSum
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(interval))
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
session.stat.Bitrate = session.stat.WriteBitrate
session.prevConnStat = currStat
}
@ -157,8 +157,8 @@ func (session *SubSession) RawQuery() string {
return session.urlCtx.RawQuery
}
func (session *SubSession) RemoteAddr() string {
return session.conn.RemoteAddr().String()
func (session *SubSession) UniqueKey() string {
return session.uniqueKey
}
func init() {

@ -114,7 +114,7 @@ func InnerTestEntry(t *testing.T) {
if err != nil {
nazalog.Error(err)
}
err = <-rtmpPullSession.Wait()
err = <-rtmpPullSession.WaitChan()
nazalog.Debug(err)
}()
@ -145,7 +145,7 @@ func InnerTestEntry(t *testing.T) {
fileTagCount.Increment()
msg := remux.FLVTag2RTMPMsg(tag)
chunks := rtmp.Message2Chunks(msg.Payload, &msg.Header)
err = pushSession.AsyncWrite(chunks)
err = pushSession.Write(chunks)
assert.Equal(t, nil, err)
}
err = pushSession.Flush()

@ -147,6 +147,15 @@ func LoadConf(confFile string) (*Config, error) {
if !j.Exist("log.short_file_flag") {
config.LogConfig.ShortFileFlag = true
}
if !j.Exist("log.timestamp_flag") {
config.LogConfig.TimestampFlag = true
}
if !j.Exist("log.timestamp_with_ms_flag") {
config.LogConfig.TimestampWithMSFlag = true
}
if !j.Exist("log.level_flag") {
config.LogConfig.LevelFlag = true
}
if !j.Exist("log.assert_behavior") {
config.LogConfig.AssertBehavior = nazalog.AssertError
}

@ -86,7 +86,7 @@ type pushProxy struct {
}
func NewGroup(appName string, streamName string, pullEnable bool, pullURL string) *Group {
uk := base.GenUniqueKey(base.UKPGroup)
uk := base.GenUKGroup()
nazalog.Infof("[%s] lifecycle new group. appName=%s, streamName=%s", uk, appName, streamName)
url2PushProxy := make(map[string]*pushProxy)
@ -145,13 +145,13 @@ func (group *Group) Tick() {
if group.tickCount%checkSessionAliveIntervalSec == 0 {
if group.rtmpPubSession != nil {
if readAlive, _ := group.rtmpPubSession.IsAlive(); !readAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtmpPubSession.UniqueKey)
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtmpPubSession.UniqueKey())
group.rtmpPubSession.Dispose()
}
}
if group.rtspPubSession != nil {
if readAlive, _ := group.rtspPubSession.IsAlive(); !readAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtspPubSession.UniqueKey)
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtspPubSession.UniqueKey())
group.rtspPubSession.Dispose()
group.rtspPubSession = nil
}
@ -165,28 +165,28 @@ func (group *Group) Tick() {
}
for session := range group.rtmpSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey)
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.delRTMPSubSession(session)
}
}
for session := range group.httpflvSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey)
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.delHTTPFLVSubSession(session)
}
}
for session := range group.httptsSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey)
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.delHTTPTSSubSession(session)
}
}
for session := range group.rtspSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey)
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.DelRTSPSubSession(session)
}
@ -271,13 +271,13 @@ func (group *Group) Dispose() {
}
func (group *Group) AddRTMPPubSession(session *rtmp.ServerSession) bool {
nazalog.Debugf("[%s] [%s] add PubSession into group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] add PubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
if group.hasInSession() {
nazalog.Errorf("[%s] in stream already exist. wanna add=%s", group.UniqueKey, session.UniqueKey)
nazalog.Errorf("[%s] in stream already exist. wanna add=%s", group.UniqueKey, session.UniqueKey())
return false
}
@ -296,13 +296,13 @@ func (group *Group) DelRTMPPubSession(session *rtmp.ServerSession) {
// TODO chef: rtsp package中增加回调返回值判断如果是false将连接关掉
func (group *Group) AddRTSPPubSession(session *rtsp.PubSession) bool {
nazalog.Debugf("[%s] [%s] add RTSP PubSession into group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] add RTSP PubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
if group.hasInSession() {
nazalog.Errorf("[%s] in stream already exist. wanna add=%s", group.UniqueKey, session.UniqueKey)
nazalog.Errorf("[%s] in stream already exist. wanna add=%s", group.UniqueKey, session.UniqueKey())
return false
}
@ -342,7 +342,7 @@ func (group *Group) DelRTMPPullSession(session *rtmp.PullSession) {
}
func (group *Group) AddRTMPSubSession(session *rtmp.ServerSession) {
nazalog.Debugf("[%s] [%s] add SubSession into group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] add SubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
group.rtmpSubSessionSet[session] = struct{}{}
@ -357,7 +357,7 @@ func (group *Group) DelRTMPSubSession(session *rtmp.ServerSession) {
}
func (group *Group) AddHTTPFLVSubSession(session *httpflv.SubSession) {
nazalog.Debugf("[%s] [%s] add httpflv SubSession into group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] add httpflv SubSession into group.", group.UniqueKey, session.UniqueKey())
session.WriteHTTPResponseHeader()
session.WriteFLVHeader()
@ -378,7 +378,7 @@ func (group *Group) DelHTTPFLVSubSession(session *httpflv.SubSession) {
// 这里应该也要考虑触发hls muxer开启
// 也即HTTPTS sub需要使用hls muxerhls muxer开启和关闭都要考虑HTTPTS sub
func (group *Group) AddHTTPTSSubSession(session *httpts.SubSession) {
nazalog.Debugf("[%s] [%s] add httpflv SubSession into group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] add httpflv SubSession into group.", group.UniqueKey, session.UniqueKey())
session.WriteHTTPResponseHeader()
session.WriteFragmentHeader()
@ -399,7 +399,8 @@ func (group *Group) HandleNewRTSPSubSessionDescribe(session *rtsp.SubSession) (o
group.mutex.Lock()
defer group.mutex.Unlock()
if group.rtspPubSession == nil {
nazalog.Warnf("[%s] close rtsp subSession while describe but pubSession not exist. [%s]", group.UniqueKey, session.UniqueKey)
nazalog.Warnf("[%s] close rtsp subSession while describe but pubSession not exist. [%s]",
group.UniqueKey, session.UniqueKey())
return false, nil
}
@ -408,7 +409,7 @@ func (group *Group) HandleNewRTSPSubSessionDescribe(session *rtsp.SubSession) (o
}
func (group *Group) HandleNewRTSPSubSessionPlay(session *rtsp.SubSession) bool {
nazalog.Debugf("[%s] [%s] add rtsp SubSession into group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] add rtsp SubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
@ -417,7 +418,7 @@ func (group *Group) HandleNewRTSPSubSessionPlay(session *rtsp.SubSession) bool {
}
func (group *Group) DelRTSPSubSession(session *rtsp.SubSession) {
nazalog.Debugf("[%s] [%s] del rtsp SubSession from group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] del rtsp SubSession from group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
delete(group.rtspSubSessionSet, session)
@ -597,32 +598,32 @@ func (group *Group) KickOutSession(sessionID string) bool {
nazalog.Infof("[%s] kick out session. session id=%s", group.UniqueKey, sessionID)
if strings.HasPrefix(sessionID, base.UKPRTMPServerSession) {
if strings.HasPrefix(sessionID, base.UKPreRTMPServerSession) {
if group.rtmpPubSession != nil {
group.rtmpPubSession.Dispose()
return true
}
} else if strings.HasPrefix(sessionID, base.UKPRTSPPubSession) {
} else if strings.HasPrefix(sessionID, base.UKPreRTSPPubSession) {
if group.rtspPubSession != nil {
group.rtspPubSession.Dispose()
return true
}
} else if strings.HasPrefix(sessionID, base.UKPFLVSubSession) {
} else if strings.HasPrefix(sessionID, base.UKPreFLVSubSession) {
// TODO chef: 考虑数据结构改成sessionIDzuokey的map
for s := range group.httpflvSubSessionSet {
if s.UniqueKey == sessionID {
if s.UniqueKey() == sessionID {
s.Dispose()
return true
}
}
} else if strings.HasPrefix(sessionID, base.UKPTSSubSession) {
} else if strings.HasPrefix(sessionID, base.UKPreTSSubSession) {
for s := range group.httptsSubSessionSet {
if s.UniqueKey == sessionID {
if s.UniqueKey() == sessionID {
s.Dispose()
return true
}
}
} else if strings.HasPrefix(sessionID, base.UKPRTSPSubSession) {
} else if strings.HasPrefix(sessionID, base.UKPreRTSPSubSession) {
// TODO chef: impl me
} else {
nazalog.Errorf("[%s] kick out session while session id format invalid. %s", group.UniqueKey, sessionID)
@ -632,10 +633,10 @@ func (group *Group) KickOutSession(sessionID string) bool {
}
func (group *Group) delRTMPPubSession(session *rtmp.ServerSession) {
nazalog.Debugf("[%s] [%s] del rtmp PubSession from group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] del rtmp PubSession from group.", group.UniqueKey, session.UniqueKey())
if session != group.rtmpPubSession {
nazalog.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey, group.rtmpPubSession)
nazalog.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey(), group.rtmpPubSession)
return
}
@ -644,10 +645,10 @@ func (group *Group) delRTMPPubSession(session *rtmp.ServerSession) {
}
func (group *Group) delRTSPPubSession(session *rtsp.PubSession) {
nazalog.Debugf("[%s] [%s] del rtsp PubSession from group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] del rtsp PubSession from group.", group.UniqueKey, session.UniqueKey())
if session != group.rtspPubSession {
nazalog.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey, group.rtmpPubSession)
nazalog.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey(), group.rtmpPubSession)
return
}
@ -665,17 +666,17 @@ func (group *Group) delRTMPPullSession(session *rtmp.PullSession) {
}
func (group *Group) delRTMPSubSession(session *rtmp.ServerSession) {
nazalog.Debugf("[%s] [%s] del rtmp SubSession from group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] del rtmp SubSession from group.", group.UniqueKey, session.UniqueKey())
delete(group.rtmpSubSessionSet, session)
}
func (group *Group) delHTTPFLVSubSession(session *httpflv.SubSession) {
nazalog.Debugf("[%s] [%s] del httpflv SubSession from group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] del httpflv SubSession from group.", group.UniqueKey, session.UniqueKey())
delete(group.httpflvSubSessionSet, session)
}
func (group *Group) delHTTPTSSubSession(session *httpts.SubSession) {
nazalog.Debugf("[%s] [%s] del httpts SubSession from group.", group.UniqueKey, session.UniqueKey)
nazalog.Debugf("[%s] [%s] del httpts SubSession from group.", group.UniqueKey, session.UniqueKey())
delete(group.httptsSubSessionSet, session)
}
@ -711,19 +712,19 @@ func (group *Group) broadcastRTMP(msg base.RTMPMsg) {
// TODO chef: 头信息和full gop也可以在SubSession刚加入时发送
if group.gopCache.Metadata != nil {
//nazalog.Debugf("[%s] [%s] write metadata", group.UniqueKey, session.UniqueKey)
_ = session.AsyncWrite(group.gopCache.Metadata)
_ = session.Write(group.gopCache.Metadata)
}
if group.gopCache.VideoSeqHeader != nil {
//nazalog.Debugf("[%s] [%s] write vsh", group.UniqueKey, session.UniqueKey)
_ = session.AsyncWrite(group.gopCache.VideoSeqHeader)
_ = session.Write(group.gopCache.VideoSeqHeader)
}
if group.gopCache.AACSeqHeader != nil {
//nazalog.Debugf("[%s] [%s] write ash", group.UniqueKey, session.UniqueKey)
_ = session.AsyncWrite(group.gopCache.AACSeqHeader)
_ = session.Write(group.gopCache.AACSeqHeader)
}
for i := 0; i < group.gopCache.GetGOPCount(); i++ {
for _, item := range group.gopCache.GetGOPDataAt(i) {
_ = session.AsyncWrite(item)
_ = session.Write(item)
}
}
@ -731,7 +732,7 @@ func (group *Group) broadcastRTMP(msg base.RTMPMsg) {
}
// ## 3.2. 转发本次数据
_ = session.AsyncWrite(lcd.Get())
_ = session.Write(lcd.Get())
}
// TODO chef: rtmp sub, rtmp push, httpflv sub 的发送逻辑都差不多,可以考虑封装一下
@ -743,24 +744,24 @@ func (group *Group) broadcastRTMP(msg base.RTMPMsg) {
if v.pushSession.IsFresh {
if group.gopCache.Metadata != nil {
_ = v.pushSession.AsyncWrite(group.gopCache.Metadata)
_ = v.pushSession.Write(group.gopCache.Metadata)
}
if group.gopCache.VideoSeqHeader != nil {
_ = v.pushSession.AsyncWrite(group.gopCache.VideoSeqHeader)
_ = v.pushSession.Write(group.gopCache.VideoSeqHeader)
}
if group.gopCache.AACSeqHeader != nil {
_ = v.pushSession.AsyncWrite(group.gopCache.AACSeqHeader)
_ = v.pushSession.Write(group.gopCache.AACSeqHeader)
}
for i := 0; i < group.gopCache.GetGOPCount(); i++ {
for _, item := range group.gopCache.GetGOPDataAt(i) {
_ = v.pushSession.AsyncWrite(item)
_ = v.pushSession.Write(item)
}
}
v.pushSession.IsFresh = false
}
_ = v.pushSession.AsyncWrite(lcd.Get())
_ = v.pushSession.Write(lcd.Get())
}
}
@ -874,7 +875,7 @@ func (group *Group) pullIfNeeded() {
}
res := group.AddRTMPPullSession(pullSession)
if res {
err = <-pullSession.Wait()
err = <-pullSession.WaitChan()
nazalog.Infof("[%s] relay pull done. err=%v", pullSession.UniqueKey(), err)
group.DelRTMPPullSession(pullSession)
} else {
@ -925,7 +926,7 @@ func (group *Group) pushIfNeeded() {
return
}
group.AddRTMPPushSession(u, pushSession)
err = <-pushSession.Wait()
err = <-pushSession.WaitChan()
nazalog.Infof("[%s] relay push done. err=%v", pushSession.UniqueKey(), err)
group.DelRTMPPushSession(u, pushSession)
}(url, urlWithParam)

@ -1,82 +0,0 @@
// Copyright 2021, 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 (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hls"
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/httpts"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/lal/pkg/rtsp"
)
var _ base.ISession = &rtmp.ServerSession{}
var _ base.ISession = &rtsp.PubSession{}
var _ base.ISession = &httpflv.SubSession{}
var _ base.ISession = &httpts.SubSession{}
var _ base.ISession = &rtsp.SubSession{}
var _ base.ISessionURLContext = &rtmp.ServerSession{}
var _ base.ISessionURLContext = &rtsp.PubSession{}
var _ base.ISessionURLContext = &httpflv.SubSession{}
var _ base.ISessionURLContext = &httpts.SubSession{}
var _ base.ISessionURLContext = &rtsp.SubSession{}
//var _ base.ISessionURLContext = &rtmp.PushSession{} //
//var _ base.ISessionURLContext = &rtmp.PullSession{}
//var _ base.ISessionURLContext = &rtsp.PushSession{}
//var _ base.ISessionURLContext = &rtsp.PullSession{}
//var _ base.ISessionURLContext = &httpflv.PullSession{}
//var _ base.ISessionURLContext = &rtmp.ClientSession{} //
//var _ base.ISessionURLContext = &rtsp.ClientCommandSession{}
var _ base.ISessionStat = &rtmp.PushSession{} //
var _ base.ISessionStat = &rtmp.PullSession{}
var _ base.ISessionStat = &rtmp.ServerSession{}
var _ base.ISessionStat = &rtsp.PushSession{}
var _ base.ISessionStat = &rtsp.PullSession{}
var _ base.ISessionStat = &rtsp.PubSession{}
var _ base.ISessionStat = &rtsp.SubSession{}
var _ base.ISessionStat = &httpflv.PullSession{}
var _ base.ISessionStat = &httpflv.SubSession{}
var _ base.ISessionStat = &httpts.SubSession{}
var _ base.ISessionStat = &rtmp.ClientSession{} //
var _ base.ISessionStat = &rtsp.BaseInSession{}
var _ base.ISessionStat = &rtsp.BaseOutSession{}
var _ base.ISessionStat = &rtsp.ServerCommandSession{}
var _ rtmp.ServerObserver = &ServerManager{}
var _ rtsp.ServerObserver = &ServerManager{}
var _ httpflv.ServerObserver = &ServerManager{}
var _ httpts.ServerObserver = &ServerManager{}
var _ HTTPAPIServerObserver = &ServerManager{}
var _ rtmp.PubSessionObserver = &Group{} //
var _ rtsp.PullSessionObserver = &Group{}
var _ rtsp.PubSessionObserver = &Group{}
var _ hls.MuxerObserver = &Group{}
var _ rtsp.BaseInSessionObserver = &Group{} //
var _ rtmp.ServerSessionObserver = &rtmp.Server{}
var _ rtmp.HandshakeClient = &rtmp.HandshakeClientSimple{}
var _ rtmp.HandshakeClient = &rtmp.HandshakeClientComplex{}
var _ rtsp.ServerCommandSessionObserver = &rtsp.Server{}
var _ rtsp.ClientCommandSessionObserver = &rtsp.PushSession{}
var _ rtsp.ClientCommandSessionObserver = &rtsp.PullSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.PushSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.PullSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.PubSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.SubSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.ClientCommandSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.ServerCommandSession{}
var _ hls.StreamerObserver = &hls.Muxer{}

@ -0,0 +1,160 @@
// Copyright 2021, 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 (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hls"
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/httpts"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/lal/pkg/rtsp"
)
// client.pub: rtmp, rtsp
// client.sub: rtmp, rtsp, flv, ts
// server.push: rtmp, rtsp
// server.pull: rtmp, rtsp, flv
// other: rtmp.ClientSession rtsp.BaseInSession rtsp.BaseOutSession rtsp.ClientCommandSession rtsp.ServerCommandSession
// IClientSession: 所有Client Session都满足
var (
_ base.IClientSession = &rtmp.PushSession{}
_ base.IClientSession = &rtmp.PullSession{}
_ base.IClientSession = &rtsp.PushSession{}
_ base.IClientSession = &rtsp.PullSession{}
_ base.IClientSession = &httpflv.PullSession{}
)
// IServerSession
var (
_ base.IServerSession = &rtmp.ServerSession{}
_ base.IServerSession = &httpflv.SubSession{}
_ base.IServerSession = &httpts.SubSession{}
// 这两个比较特殊它们没有RunLoop函数RunLoop在rtsp.ServerCommandSession上
//_ base.IServerSession = &rtsp.PubSession{}
//_ base.IServerSession = &rtsp.SubSession{}
)
// IClientSessionLifecycle: 所有Client Session都满足
var (
_ base.IClientSessionLifecycle = &rtmp.PushSession{}
_ base.IClientSessionLifecycle = &rtmp.PullSession{}
_ base.IClientSessionLifecycle = &rtsp.PushSession{}
_ base.IClientSessionLifecycle = &rtsp.PullSession{}
_ base.IClientSessionLifecycle = &httpflv.PullSession{}
)
// IServerSessionLifecycle
var (
// server session
_ base.IServerSessionLifecycle = &rtmp.ServerSession{}
_ base.IServerSessionLifecycle = &httpflv.SubSession{}
_ base.IServerSessionLifecycle = &httpts.SubSession{}
// 这两个比较特殊它们没有RunLoop函数RunLoop在rtsp.ServerCommandSession上
//_ base.IServerSessionLifecycle = &rtsp.PubSession{}
//_ base.IServerSessionLifecycle = &rtsp.SubSession{}
// other
_ base.IServerSessionLifecycle = &rtsp.ServerCommandSession{}
)
// ISessionStat: 所有Session(client/server)都满足
var (
// client
_ base.ISessionStat = &rtmp.PushSession{}
_ base.ISessionStat = &rtsp.PushSession{}
_ base.ISessionStat = &rtmp.PullSession{}
_ base.ISessionStat = &rtsp.PullSession{}
_ base.ISessionStat = &httpflv.PullSession{}
// server session
_ base.ISessionStat = &rtmp.ServerSession{}
_ base.ISessionStat = &rtsp.PubSession{}
_ base.ISessionStat = &rtsp.SubSession{}
_ base.ISessionStat = &httpflv.SubSession{}
_ base.ISessionStat = &httpts.SubSession{}
// other
_ base.ISessionStat = &rtmp.ClientSession{}
_ base.ISessionStat = &rtsp.BaseInSession{}
_ base.ISessionStat = &rtsp.BaseOutSession{}
_ base.ISessionStat = &rtsp.ServerCommandSession{}
)
// ISessionURLContext: 所有Session(client/server)都满足
var (
// client
_ base.ISessionURLContext = &rtmp.PushSession{}
_ base.ISessionURLContext = &rtsp.PushSession{}
_ base.ISessionURLContext = &rtmp.PullSession{}
_ base.ISessionURLContext = &rtsp.PullSession{}
_ base.ISessionURLContext = &httpflv.PullSession{}
// server session
_ base.ISessionURLContext = &rtmp.ServerSession{}
_ base.ISessionURLContext = &rtsp.PubSession{}
_ base.ISessionURLContext = &httpflv.SubSession{}
_ base.ISessionURLContext = &httpts.SubSession{}
_ base.ISessionURLContext = &rtsp.SubSession{}
// other
_ base.ISessionURLContext = &rtmp.ClientSession{}
_ base.ISessionURLContext = &rtsp.ClientCommandSession{}
)
// IObject: 所有Session(client/server)都满足
var (
//// client
_ base.IObject = &rtmp.PushSession{}
_ base.IObject = &rtsp.PushSession{}
_ base.IObject = &rtmp.PullSession{}
_ base.IObject = &rtsp.PullSession{}
_ base.IObject = &httpflv.PullSession{}
// server session
_ base.IObject = &rtmp.ServerSession{}
_ base.IObject = &rtsp.PubSession{}
_ base.IObject = &rtsp.SubSession{}
_ base.IObject = &httpflv.SubSession{}
_ base.IObject = &httpts.SubSession{}
//// other
_ base.IObject = &rtmp.ClientSession{}
_ base.IObject = &rtsp.BaseInSession{}
_ base.IObject = &rtsp.BaseOutSession{}
_ base.IObject = &rtsp.ClientCommandSession{}
_ base.IObject = &rtsp.ServerCommandSession{}
)
// ---------------------------------------------------------------------------------------------------------------------
var _ rtmp.ServerObserver = &ServerManager{}
var _ rtsp.ServerObserver = &ServerManager{}
var _ httpflv.ServerObserver = &ServerManager{}
var _ httpts.ServerObserver = &ServerManager{}
var _ HTTPAPIServerObserver = &ServerManager{}
var _ rtmp.PubSessionObserver = &Group{} //
var _ rtsp.PullSessionObserver = &Group{}
var _ rtsp.PubSessionObserver = &Group{}
var _ hls.MuxerObserver = &Group{}
var _ rtsp.BaseInSessionObserver = &Group{} //
var _ rtmp.ServerSessionObserver = &rtmp.Server{}
var _ rtmp.HandshakeClient = &rtmp.HandshakeClientSimple{}
var _ rtmp.HandshakeClient = &rtmp.HandshakeClientComplex{}
var _ rtsp.ServerCommandSessionObserver = &rtsp.Server{}
var _ rtsp.ClientCommandSessionObserver = &rtsp.PushSession{}
var _ rtsp.ClientCommandSessionObserver = &rtsp.PullSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.PushSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.PullSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.PubSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.SubSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.ClientCommandSession{}
var _ rtsp.IInterleavedPacketWriter = &rtsp.ServerCommandSession{}
var _ hls.StreamerObserver = &hls.Muxer{}

@ -218,8 +218,8 @@ func (sm *ServerManager) OnRTMPConnect(session *rtmp.ServerSession, opa rtmp.Obj
var info base.RTMPConnectInfo
info.ServerID = config.ServerID
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
if app, err := opa.FindString("app"); err == nil {
info.App = app
}
@ -248,8 +248,8 @@ func (sm *ServerManager) OnNewRTMPPubSession(session *rtmp.ServerSession) bool {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnPubStart(info)
@ -274,8 +274,8 @@ func (sm *ServerManager) OnDelRTMPPubSession(session *rtmp.ServerSession) {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnPubStop(info)
@ -295,8 +295,8 @@ func (sm *ServerManager) OnNewRTMPSubSession(session *rtmp.ServerSession) bool {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStart(info)
@ -321,8 +321,8 @@ func (sm *ServerManager) OnDelRTMPSubSession(session *rtmp.ServerSession) {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStop(info)
@ -342,8 +342,8 @@ func (sm *ServerManager) OnNewHTTPFLVSubSession(session *httpflv.SubSession) boo
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStart(info)
@ -368,8 +368,8 @@ func (sm *ServerManager) OnDelHTTPFLVSubSession(session *httpflv.SubSession) {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStop(info)
@ -389,8 +389,8 @@ func (sm *ServerManager) OnNewHTTPTSSubSession(session *httpts.SubSession) bool
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStart(info)
@ -415,8 +415,8 @@ func (sm *ServerManager) OnDelHTTPTSSubSession(session *httpts.SubSession) {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStop(info)
@ -446,8 +446,8 @@ func (sm *ServerManager) OnNewRTSPPubSession(session *rtsp.PubSession) bool {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnPubStart(info)
@ -474,8 +474,8 @@ func (sm *ServerManager) OnDelRTSPPubSession(session *rtsp.PubSession) {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnPubStop(info)
@ -504,8 +504,8 @@ func (sm *ServerManager) OnNewRTSPSubSessionPlay(session *rtsp.SubSession) bool
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStart(info)
@ -532,8 +532,8 @@ func (sm *ServerManager) OnDelRTSPSubSession(session *rtsp.SubSession) {
info.AppName = session.AppName()
info.StreamName = session.StreamName()
info.URLParam = session.RawQuery()
info.SessionID = session.UniqueKey
info.RemoteAddr = session.RemoteAddr()
info.SessionID = session.UniqueKey()
info.RemoteAddr = session.GetStat().RemoteAddr
info.HasInSession = group.HasInSession()
info.HasOutSession = group.HasOutSession()
httpNotify.OnSubStop(info)

@ -27,7 +27,7 @@ type PullSessionOption struct {
}
var defaultPullSessionOption = PullSessionOption{
PullTimeoutMS: 0,
PullTimeoutMS: 10000,
ReadAVTimeoutMS: 0,
}
@ -47,7 +47,7 @@ func NewPullSession(modOptions ...ModPullSessionOption) *PullSession {
}
}
// 如果没有发生错误阻塞直到到接收音视频数据的前一步也即收到服务端返回的rtmp play对应结果的信令
// 阻塞直到和对端完成拉流前握手部分的工作也即收到RTMP Play response或者发生错误
//
// @param onReadRTMPAVMsg: 注意回调结束后内存块会被PullSession重复使用
func (s *PullSession) Pull(rawURL string, onReadRTMPAVMsg OnReadRTMPAVMsg) error {
@ -55,39 +55,52 @@ func (s *PullSession) Pull(rawURL string, onReadRTMPAVMsg OnReadRTMPAVMsg) error
return s.core.Do(rawURL)
}
// Pull成功后调用该函数可阻塞直到拉流结束
func (s *PullSession) Wait() <-chan error {
return s.core.Wait()
// 文档请参考: interface IClientSessionLifecycle
func (s *PullSession) Dispose() error {
return s.core.Dispose()
}
func (s *PullSession) Dispose() {
s.core.Dispose()
// 文档请参考: interface IClientSessionLifecycle
func (s *PullSession) WaitChan() <-chan error {
return s.core.WaitChan()
}
func (s *PullSession) UniqueKey() string {
return s.core.UniqueKey
// 文档请参考: interface ISessionURLContext
func (s *PullSession) URL() string {
return s.core.URL()
}
// 文档请参考: interface ISessionURLContext
func (s *PullSession) AppName() string {
return s.core.AppName()
}
// 文档请参考: interface ISessionURLContext
func (s *PullSession) StreamName() string {
return s.core.StreamName()
}
// 文档请参考: interface ISessionURLContext
func (s *PullSession) RawQuery() string {
return s.core.RawQuery()
}
// 文档请参考: interface IObject
func (s *PullSession) UniqueKey() string {
return s.core.uniqueKey
}
// 文档请参考: interface ISessionStat
func (s *PullSession) GetStat() base.StatSession {
return s.core.GetStat()
}
func (s *PullSession) UpdateStat(interval uint32) {
s.core.UpdateStat(interval)
// 文档请参考: interface ISessionStat
func (s *PullSession) UpdateStat(intervalSec uint32) {
s.core.UpdateStat(intervalSec)
}
// 文档请参考: interface ISessionStat
func (s *PullSession) IsAlive() (readAlive, writeAlive bool) {
return s.core.IsAlive()
}

@ -8,9 +8,7 @@
package rtmp
import (
"github.com/q191201771/lal/pkg/base"
)
import "github.com/q191201771/lal/pkg/base"
type PushSession struct {
IsFresh bool
@ -27,7 +25,7 @@ type PushSessionOption struct {
}
var defaultPushSessionOption = PushSessionOption{
PushTimeoutMS: 0,
PushTimeoutMS: 10000,
WriteAVTimeoutMS: 0,
}
@ -47,51 +45,69 @@ func NewPushSession(modOptions ...ModPushSessionOption) *PushSession {
}
}
// 如果没有错误发生阻塞到接收音视频数据的前一步也即收到服务端返回的rtmp publish对应结果的信令
// 阻塞直到和对端完成推流前握手部分的工作也即收到RTMP Publish response或者发生错误
func (s *PushSession) Push(rawURL string) error {
return s.core.Do(rawURL)
}
func (s *PushSession) AsyncWrite(msg []byte) error {
return s.core.AsyncWrite(msg)
// 发送数据
// 注意业务方需将数据打包成rtmp chunk格式后再调用该函数发送
func (s *PushSession) Write(msg []byte) error {
return s.core.Write(msg)
}
// 将缓存的数据立即刷新发送
// 是否有缓存策略,请参见配置及内部实现
func (s *PushSession) Flush() error {
return s.core.Flush()
}
func (s *PushSession) Dispose() {
s.core.Dispose()
// 文档请参考: interface IClientSessionLifecycle
func (s *PushSession) Dispose() error {
return s.core.Dispose()
}
func (s *PushSession) GetStat() base.StatSession {
return s.core.GetStat()
}
func (s *PushSession) UpdateStat(interval uint32) {
s.core.UpdateStat(interval)
// 文档请参考: interface IClientSessionLifecycle
func (s *PushSession) WaitChan() <-chan error {
return s.core.WaitChan()
}
func (s *PushSession) IsAlive() (readAlive, writeAlive bool) {
return s.core.IsAlive()
// 文档请参考: interface ISessionURLContext
func (s *PushSession) URL() string {
return s.core.URL()
}
// 文档请参考: interface ISessionURLContext
func (s *PushSession) AppName() string {
return s.core.AppName()
}
// 文档请参考: interface ISessionURLContext
func (s *PushSession) StreamName() string {
return s.core.StreamName()
}
// 文档请参考: interface ISessionURLContext
func (s *PushSession) RawQuery() string {
return s.core.RawQuery()
}
func (s *PushSession) Wait() <-chan error {
return s.core.Wait()
// 文档请参考: interface IObject
func (s *PushSession) UniqueKey() string {
return s.core.uniqueKey
}
func (s *PushSession) UniqueKey() string {
return s.core.UniqueKey
// 文档请参考: interface ISessionStat
func (s *PushSession) GetStat() base.StatSession {
return s.core.GetStat()
}
// 文档请参考: interface ISessionStat
func (s *PushSession) UpdateStat(intervalSec uint32) {
s.core.UpdateStat(intervalSec)
}
// 文档请参考: interface ISessionStat
func (s *PushSession) IsAlive() (readAlive, writeAlive bool) {
return s.core.IsAlive()
}

@ -30,7 +30,7 @@ var ErrClientSessionTimeout = errors.New("lal.rtmp: client session timeout")
// rtmp 客户端类型连接的底层实现
// package rtmp 的使用者应该优先使用基于 ClientSession 实现的 PushSession 和 PullSession
type ClientSession struct {
UniqueKey string
uniqueKey string
t ClientSessionType
option ClientSessionOption
@ -81,9 +81,9 @@ func NewClientSession(t ClientSessionType, modOptions ...ModClientSessionOption)
var uk string
switch t {
case CSTPullSession:
uk = base.GenUniqueKey(base.UKPRTMPPullSession)
uk = base.GenUKRTMPPullSession()
case CSTPushSession:
uk = base.GenUniqueKey(base.UKPRTMPPushSession)
uk = base.GenUKRTMPPushSession()
}
option := defaultClientSessOption
@ -92,7 +92,7 @@ func NewClientSession(t ClientSessionType, modOptions ...ModClientSessionOption)
}
s := &ClientSession{
UniqueKey: uk,
uniqueKey: uk,
t: t,
option: option,
doResultChan: make(chan struct{}, 1),
@ -111,7 +111,7 @@ func NewClientSession(t ClientSessionType, modOptions ...ModClientSessionOption)
// 阻塞直到收到服务端返回的 publish / play 对应结果的信令或者发生错误
func (s *ClientSession) Do(rawURL string) error {
nazalog.Debugf("[%s] Do. url=%s", s.UniqueKey, rawURL)
nazalog.Debugf("[%s] Do. url=%s", s.uniqueKey, rawURL)
var (
ctx context.Context
@ -126,25 +126,35 @@ func (s *ClientSession) Do(rawURL string) error {
return s.doContext(ctx, rawURL)
}
// Do成功后调用该函数可阻塞直到推流或拉流结束
func (s *ClientSession) Wait() <-chan error {
return s.conn.Done()
}
func (s *ClientSession) AsyncWrite(msg []byte) error {
func (s *ClientSession) Write(msg []byte) error {
if s.conn == nil {
return base.ErrSessionNotStarted
}
_, err := s.conn.Write(msg)
return err
}
func (s *ClientSession) Flush() error {
if s.conn == nil {
return base.ErrSessionNotStarted
}
return s.conn.Flush()
}
func (s *ClientSession) Dispose() {
nazalog.Infof("[%s] lifecycle dispose rtmp ClientSession.", s.UniqueKey)
if s.conn != nil {
_ = s.conn.Close()
func (s *ClientSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtmp ClientSession.", s.uniqueKey)
if s.conn == nil {
return base.ErrSessionNotStarted
}
return s.conn.Close()
}
func (s *ClientSession) WaitChan() <-chan error {
return s.conn.Done()
}
func (s *ClientSession) URL() string {
return s.urlCtx.URL
}
func (s *ClientSession) AppName() string {
@ -159,6 +169,10 @@ func (s *ClientSession) RawQuery() string {
return s.urlCtx.RawQuery
}
func (s *ClientSession) UniqueKey() string {
return s.uniqueKey
}
func (s *ClientSession) GetStat() base.StatSession {
connStat := s.conn.GetStat()
s.stat.ReadBytesSum = connStat.ReadBytesSum
@ -166,12 +180,12 @@ func (s *ClientSession) GetStat() base.StatSession {
return s.stat
}
func (s *ClientSession) UpdateStat(interval uint32) {
func (s *ClientSession) UpdateStat(intervalSec uint32) {
currStat := s.conn.GetStat()
rDiff := currStat.ReadBytesSum - s.prevConnStat.ReadBytesSum
s.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(interval))
s.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := currStat.WroteBytesSum - s.prevConnStat.WroteBytesSum
s.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(interval))
s.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
switch s.t {
case CSTPushSession:
s.stat.Bitrate = s.stat.WriteBitrate
@ -213,13 +227,13 @@ func (s *ClientSession) doContext(ctx context.Context, rawURL string) error {
return
}
nazalog.Infof("[%s] > W SetChunkSize %d.", s.UniqueKey, LocalChunkSize)
nazalog.Infof("[%s] > W SetChunkSize %d.", s.uniqueKey, LocalChunkSize)
if err := s.packer.writeChunkSize(s.conn, LocalChunkSize); err != nil {
errChan <- err
return
}
nazalog.Infof("[%s] > W connect('%s'). tcUrl=%s", s.UniqueKey, s.appName(), s.tcURL())
nazalog.Infof("[%s] > W connect('%s'). tcUrl=%s", s.uniqueKey, s.appName(), s.tcURL())
if err := s.packer.writeConnect(s.conn, s.appName(), s.tcURL(), s.t == CSTPushSession); err != nil {
errChan <- err
return
@ -260,7 +274,7 @@ func (s *ClientSession) streamNameWithRawQuery() string {
}
func (s *ClientSession) tcpConnect() error {
nazalog.Infof("[%s] > tcp connect.", s.UniqueKey)
nazalog.Infof("[%s] > tcp connect.", s.uniqueKey)
var err error
s.stat.RemoteAddr = s.urlCtx.HostWithPort
@ -278,7 +292,7 @@ func (s *ClientSession) tcpConnect() error {
}
func (s *ClientSession) handshake() error {
nazalog.Infof("[%s] > W Handshake C0+C1.", s.UniqueKey)
nazalog.Infof("[%s] > W Handshake C0+C1.", s.uniqueKey)
if err := s.hc.WriteC0C1(s.conn); err != nil {
return err
}
@ -286,9 +300,9 @@ func (s *ClientSession) handshake() error {
if err := s.hc.ReadS0S1S2(s.conn); err != nil {
return err
}
nazalog.Infof("[%s] < R Handshake S0+S1+S2.", s.UniqueKey)
nazalog.Infof("[%s] < R Handshake S0+S1+S2.", s.uniqueKey)
nazalog.Infof("[%s] > W Handshake C2.", s.UniqueKey)
nazalog.Infof("[%s] > W Handshake C2.", s.uniqueKey)
if err := s.hc.WriteC2(s.conn); err != nil {
return err
}
@ -318,14 +332,14 @@ func (s *ClientSession) doMsg(stream *Stream) error {
s.debugLogReadUserCtrlMsgCount++
if s.debugLogReadUserCtrlMsgCount <= s.debugLogReadUserCtrlMsgMax {
nazalog.Warnf("[%s] read user control message, ignore. buf=%s",
s.UniqueKey, hex.Dump(nazastring.SubSliceSafety(stream.msg.buf[stream.msg.b:stream.msg.e], 32)))
s.uniqueKey, hex.Dump(nazastring.SubSliceSafety(stream.msg.buf[stream.msg.b:stream.msg.e], 32)))
}
case base.RTMPTypeIDAudio:
fallthrough
case base.RTMPTypeIDVideo:
s.onReadRTMPAVMsg(stream.toAVMsg())
default:
nazalog.Errorf("[%s] read unknown message. typeid=%d, %s", s.UniqueKey, stream.header.MsgTypeID, stream.toDebugString())
nazalog.Errorf("[%s] read unknown message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeID, stream.toDebugString())
panic(0)
}
return nil
@ -333,7 +347,7 @@ func (s *ClientSession) doMsg(stream *Stream) error {
func (s *ClientSession) doAck(stream *Stream) error {
seqNum := bele.BEUint32(stream.msg.buf[stream.msg.b:stream.msg.e])
nazalog.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.UniqueKey, seqNum)
nazalog.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.uniqueKey, seqNum)
return nil
}
@ -345,7 +359,7 @@ func (s *ClientSession) doDataMessageAMF0(stream *Stream) error {
switch val {
case "|RtmpSampleAccess":
nazalog.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.UniqueKey)
nazalog.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey)
return nil
default:
}
@ -366,13 +380,13 @@ func (s *ClientSession) doCommandMessage(stream *Stream) error {
switch cmd {
case "onBWDone":
nazalog.Warnf("[%s] < R onBWDone. ignore.", s.UniqueKey)
nazalog.Warnf("[%s] < R onBWDone. ignore.", s.uniqueKey)
case "_result":
return s.doResultMessage(stream, tid)
case "onStatus":
return s.doOnStatusMessage(stream, tid)
default:
nazalog.Errorf("[%s] read unknown command message. cmd=%s, %s", s.UniqueKey, cmd, stream.toDebugString())
nazalog.Errorf("[%s] read unknown command message. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
}
return nil
@ -394,18 +408,18 @@ func (s *ClientSession) doOnStatusMessage(stream *Stream, tid int) error {
case CSTPushSession:
switch code {
case "NetStream.Publish.Start":
nazalog.Infof("[%s] < R onStatus('NetStream.Publish.Start').", s.UniqueKey)
nazalog.Infof("[%s] < R onStatus('NetStream.Publish.Start').", s.uniqueKey)
s.notifyDoResultSucc()
default:
nazalog.Warnf("[%s] read on status message but code field unknown. code=%s", s.UniqueKey, code)
nazalog.Warnf("[%s] read on status message but code field unknown. code=%s", s.uniqueKey, code)
}
case CSTPullSession:
switch code {
case "NetStream.Play.Start":
nazalog.Infof("[%s] < R onStatus('NetStream.Play.Start').", s.UniqueKey)
nazalog.Infof("[%s] < R onStatus('NetStream.Play.Start').", s.uniqueKey)
s.notifyDoResultSucc()
default:
nazalog.Warnf("[%s] read on status message but code field unknown. code=%s", s.UniqueKey, code)
nazalog.Warnf("[%s] read on status message but code field unknown. code=%s", s.uniqueKey, code)
}
}
@ -429,13 +443,13 @@ func (s *ClientSession) doResultMessage(stream *Stream, tid int) error {
}
switch code {
case "NetConnection.Connect.Success":
nazalog.Infof("[%s] < R _result(\"NetConnection.Connect.Success\").", s.UniqueKey)
nazalog.Infof("[%s] > W createStream().", s.UniqueKey)
nazalog.Infof("[%s] < R _result(\"NetConnection.Connect.Success\").", s.uniqueKey)
nazalog.Infof("[%s] > W createStream().", s.uniqueKey)
if err := s.packer.writeCreateStream(s.conn); err != nil {
return err
}
default:
nazalog.Errorf("[%s] unknown code. code=%v", s.UniqueKey, code)
nazalog.Errorf("[%s] unknown code. code=%v", s.uniqueKey, code)
}
case tidClientCreateStream:
err := stream.msg.readNull()
@ -446,21 +460,21 @@ func (s *ClientSession) doResultMessage(stream *Stream, tid int) error {
if err != nil {
return err
}
nazalog.Infof("[%s] < R _result().", s.UniqueKey)
nazalog.Infof("[%s] < R _result().", s.uniqueKey)
switch s.t {
case CSTPullSession:
nazalog.Infof("[%s] > W play('%s').", s.UniqueKey, s.streamNameWithRawQuery())
nazalog.Infof("[%s] > W play('%s').", s.uniqueKey, s.streamNameWithRawQuery())
if err := s.packer.writePlay(s.conn, s.streamNameWithRawQuery(), sid); err != nil {
return err
}
case CSTPushSession:
nazalog.Infof("[%s] > W publish('%s').", s.UniqueKey, s.streamNameWithRawQuery())
nazalog.Infof("[%s] > W publish('%s').", s.uniqueKey, s.streamNameWithRawQuery())
if err := s.packer.writePublish(s.conn, s.appName(), s.streamNameWithRawQuery(), sid); err != nil {
return err
}
}
default:
nazalog.Errorf("[%s] unknown tid. tid=%d", s.UniqueKey, tid)
nazalog.Errorf("[%s] unknown tid. tid=%d", s.uniqueKey, tid)
}
return nil
}
@ -474,15 +488,15 @@ func (s *ClientSession) doProtocolControlMessage(stream *Stream) error {
switch stream.header.MsgTypeID {
case base.RTMPTypeIDWinAckSize:
s.peerWinAckSize = val
nazalog.Infof("[%s] < R Window Acknowledgement Size: %d", s.UniqueKey, s.peerWinAckSize)
nazalog.Infof("[%s] < R Window Acknowledgement Size: %d", s.uniqueKey, s.peerWinAckSize)
case base.RTMPTypeIDBandwidth:
// TODO chef: 是否需要关注这个信令
nazalog.Warnf("[%s] < R Set Peer Bandwidth. ignore.", s.UniqueKey)
nazalog.Warnf("[%s] < R Set Peer Bandwidth. ignore.", s.uniqueKey)
case base.RTMPTypeIDSetChunkSize:
// composer内部会自动更新peer chunk size.
nazalog.Infof("[%s] < R Set Chunk Size %d.", s.UniqueKey, val)
nazalog.Infof("[%s] < R Set Chunk Size %d.", s.uniqueKey, val)
default:
nazalog.Errorf("[%s] read unknown protocol control message. typeid=%d, %s", s.UniqueKey, stream.header.MsgTypeID, stream.toDebugString())
nazalog.Errorf("[%s] read unknown protocol control message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeID, stream.toDebugString())
}
return nil
}

@ -12,7 +12,9 @@ import (
"errors"
)
var ErrRTMP = errors.New("lal.rtmp: fxxk")
var (
ErrRTMP = errors.New("lal.rtmp: fxxk")
)
const (
CSIDAMF = 5

@ -66,7 +66,7 @@ func (server *Server) handleTCPConnect(conn net.Conn) {
log.Infof("accept a rtmp connection. remoteAddr=%s", conn.RemoteAddr().String())
session := NewServerSession(server, conn)
err := session.RunLoop()
log.Infof("[%s] rtmp loop done. err=%v", session.UniqueKey, err)
log.Infof("[%s] rtmp loop done. err=%v", session.uniqueKey, err)
switch session.t {
case ServerSessionTypeUnknown:
// noop

@ -47,7 +47,7 @@ const (
)
type ServerSession struct {
UniqueKey string // const after ctor
uniqueKey string // const after ctor
url string
tcURL string
streamNameWithRawQuery string // const after set
@ -74,7 +74,7 @@ type ServerSession struct {
}
func NewServerSession(observer ServerSessionObserver, conn net.Conn) *ServerSession {
uk := base.GenUniqueKey(base.UKPRTMPServerSession)
uk := base.GenUKRTMPServerSession()
s := &ServerSession{
conn: connection.New(conn, func(option *connection.Option) {
option.ReadBufSize = readBufSize
@ -85,7 +85,7 @@ func NewServerSession(observer ServerSessionObserver, conn net.Conn) *ServerSess
StartTime: time.Now().Format("2006-01-02 15:04:05.999"),
RemoteAddr: conn.RemoteAddr().String(),
},
UniqueKey: uk,
uniqueKey: uk,
observer: observer,
t: ServerSessionTypeUnknown,
chunkComposer: NewChunkComposer(),
@ -104,7 +104,7 @@ func (s *ServerSession) RunLoop() (err error) {
return s.runReadLoop()
}
func (s *ServerSession) AsyncWrite(msg []byte) error {
func (s *ServerSession) Write(msg []byte) error {
_, err := s.conn.Write(msg)
return err
}
@ -113,9 +113,9 @@ func (s *ServerSession) Flush() error {
return s.conn.Flush()
}
func (s *ServerSession) Dispose() {
nazalog.Infof("[%s] lifecycle dispose rtmp ServerSession.", s.UniqueKey)
_ = s.conn.Close()
func (s *ServerSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtmp ServerSession.", s.uniqueKey)
return s.conn.Close()
}
func (s *ServerSession) URL() string {
@ -134,12 +134,16 @@ func (s *ServerSession) RawQuery() string {
return s.rawQuery
}
func (s *ServerSession) UpdateStat(interval uint32) {
func (s *ServerSession) UniqueKey() string {
return s.uniqueKey
}
func (s *ServerSession) UpdateStat(intervalSec uint32) {
currStat := s.conn.GetStat()
rDiff := currStat.ReadBytesSum - s.prevConnStat.ReadBytesSum
s.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(interval))
s.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := currStat.WroteBytesSum - s.prevConnStat.WroteBytesSum
s.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(interval))
s.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
switch s.t {
case ServerSessionTypePub:
s.stat.Bitrate = s.stat.ReadBitrate
@ -170,10 +174,6 @@ func (s *ServerSession) IsAlive() (readAlive, writeAlive bool) {
return
}
func (s *ServerSession) RemoteAddr() string {
return s.conn.RemoteAddr().String()
}
func (s *ServerSession) runReadLoop() error {
return s.chunkComposer.RunLoop(s.conn, s.doMsg)
}
@ -182,9 +182,9 @@ func (s *ServerSession) handshake() error {
if err := s.hs.ReadC0C1(s.conn); err != nil {
return err
}
nazalog.Infof("[%s] < R Handshake C0+C1.", s.UniqueKey)
nazalog.Infof("[%s] < R Handshake C0+C1.", s.uniqueKey)
nazalog.Infof("[%s] > W Handshake S0+S1+S2.", s.UniqueKey)
nazalog.Infof("[%s] > W Handshake S0+S1+S2.", s.uniqueKey)
if err := s.hs.WriteS0S1S2(s.conn); err != nil {
return err
}
@ -192,7 +192,7 @@ func (s *ServerSession) handshake() error {
if err := s.hs.ReadC2(s.conn); err != nil {
return err
}
nazalog.Infof("[%s] < R Handshake C2.", s.UniqueKey)
nazalog.Infof("[%s] < R Handshake C2.", s.uniqueKey)
return nil
}
@ -214,12 +214,12 @@ func (s *ServerSession) doMsg(stream *Stream) error {
fallthrough
case base.RTMPTypeIDVideo:
if s.t != ServerSessionTypePub {
nazalog.Errorf("[%s] read audio/video message but server session not pub type.", s.UniqueKey)
nazalog.Errorf("[%s] read audio/video message but server session not pub type.", s.uniqueKey)
return ErrRTMP
}
s.avObserver.OnReadRTMPAVMsg(stream.toAVMsg())
default:
nazalog.Warnf("[%s] read unknown message. typeid=%d, %s", s.UniqueKey, stream.header.MsgTypeID, stream.toDebugString())
nazalog.Warnf("[%s] read unknown message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeID, stream.toDebugString())
}
return nil
@ -227,13 +227,13 @@ func (s *ServerSession) doMsg(stream *Stream) error {
func (s *ServerSession) doACK(stream *Stream) error {
seqNum := bele.BEUint32(stream.msg.buf[stream.msg.b:stream.msg.e])
nazalog.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.UniqueKey, seqNum)
nazalog.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.uniqueKey, seqNum)
return nil
}
func (s *ServerSession) doDataMessageAMF0(stream *Stream) error {
if s.t != ServerSessionTypePub {
nazalog.Errorf("[%s] read audio/video message but server session not pub type.", s.UniqueKey)
nazalog.Errorf("[%s] read audio/video message but server session not pub type.", s.uniqueKey)
return ErrRTMP
}
@ -244,7 +244,7 @@ func (s *ServerSession) doDataMessageAMF0(stream *Stream) error {
switch val {
case "|RtmpSampleAccess":
nazalog.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.UniqueKey)
nazalog.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey)
return nil
default:
}
@ -259,7 +259,7 @@ func (s *ServerSession) doDataMessageAMF0(stream *Stream) error {
//
//switch val {
//case "|RtmpSampleAccess":
// nazalog.Warnf("[%s] read data message, ignore it. val=%s", s.UniqueKey, val)
// nazalog.Warnf("[%s] read data message, ignore it. val=%s", s.uniqueKey, val)
// return nil
//case "@setDataFrame":
// // macos obs and ffmpeg
@ -271,13 +271,13 @@ func (s *ServerSession) doDataMessageAMF0(stream *Stream) error {
// return err
// }
// if val != "onMetaData" {
// nazalog.Errorf("[%s] read unknown data message. val=%s, %s", s.UniqueKey, val, stream.toDebugString())
// nazalog.Errorf("[%s] read unknown data message. val=%s, %s", s.uniqueKey, val, stream.toDebugString())
// return ErrRTMP
// }
//case "onMetaData":
// // noop
//default:
// nazalog.Errorf("[%s] read unknown data message. val=%s, %s", s.UniqueKey, val, stream.toDebugString())
// nazalog.Errorf("[%s] read unknown data message. val=%s, %s", s.uniqueKey, val, stream.toDebugString())
// return nil
//}
//
@ -313,9 +313,9 @@ func (s *ServerSession) doCommandMessage(stream *Stream) error {
case "getStreamLength":
fallthrough
case "deleteStream":
nazalog.Debugf("[%s] read command message, ignore it. cmd=%s, %s", s.UniqueKey, cmd, stream.toDebugString())
nazalog.Debugf("[%s] read command message, ignore it. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
default:
nazalog.Errorf("[%s] read unknown command message. cmd=%s, %s", s.UniqueKey, cmd, stream.toDebugString())
nazalog.Errorf("[%s] read unknown command message. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
}
return nil
}
@ -337,28 +337,28 @@ func (s *ServerSession) doConnect(tid int, stream *Stream) error {
}
s.tcURL, err = val.FindString("tcUrl")
if err != nil {
nazalog.Warnf("[%s] tcUrl not exist.", s.UniqueKey)
nazalog.Warnf("[%s] tcUrl not exist.", s.uniqueKey)
}
nazalog.Infof("[%s] < R connect('%s'). tcUrl=%s", s.UniqueKey, s.appName, s.tcURL)
nazalog.Infof("[%s] < R connect('%s'). tcUrl=%s", s.uniqueKey, s.appName, s.tcURL)
s.observer.OnRTMPConnect(s, val)
nazalog.Infof("[%s] > W Window Acknowledgement Size %d.", s.UniqueKey, windowAcknowledgementSize)
nazalog.Infof("[%s] > W Window Acknowledgement Size %d.", s.uniqueKey, windowAcknowledgementSize)
if err := s.packer.writeWinAckSize(s.conn, windowAcknowledgementSize); err != nil {
return err
}
nazalog.Infof("[%s] > W Set Peer Bandwidth.", s.UniqueKey)
nazalog.Infof("[%s] > W Set Peer Bandwidth.", s.uniqueKey)
if err := s.packer.writePeerBandwidth(s.conn, peerBandwidth, peerBandwidthLimitTypeDynamic); err != nil {
return err
}
nazalog.Infof("[%s] > W SetChunkSize %d.", s.UniqueKey, LocalChunkSize)
nazalog.Infof("[%s] > W SetChunkSize %d.", s.uniqueKey, LocalChunkSize)
if err := s.packer.writeChunkSize(s.conn, LocalChunkSize); err != nil {
return err
}
nazalog.Infof("[%s] > W _result('NetConnection.Connect.Success').", s.UniqueKey)
nazalog.Infof("[%s] > W _result('NetConnection.Connect.Success').", s.uniqueKey)
oe, err := val.FindNumber("objectEncoding")
if oe != 0 && oe != 3 {
oe = 0
@ -370,8 +370,8 @@ func (s *ServerSession) doConnect(tid int, stream *Stream) error {
}
func (s *ServerSession) doCreateStream(tid int, stream *Stream) error {
nazalog.Infof("[%s] < R createStream().", s.UniqueKey)
nazalog.Infof("[%s] > W _result().", s.UniqueKey)
nazalog.Infof("[%s] < R createStream().", s.uniqueKey)
nazalog.Infof("[%s] > W _result().", s.uniqueKey)
if err := s.packer.writeCreateStreamResult(s.conn, tid); err != nil {
return err
}
@ -398,10 +398,10 @@ func (s *ServerSession) doPublish(tid int, stream *Stream) (err error) {
if err != nil {
return err
}
nazalog.Debugf("[%s] pubType=%s", s.UniqueKey, pubType)
nazalog.Infof("[%s] < R publish('%s')", s.UniqueKey, s.streamNameWithRawQuery)
nazalog.Debugf("[%s] pubType=%s", s.uniqueKey, pubType)
nazalog.Infof("[%s] < R publish('%s')", s.uniqueKey, s.streamNameWithRawQuery)
nazalog.Infof("[%s] > W onStatus('NetStream.Publish.Start').", s.UniqueKey)
nazalog.Infof("[%s] > W onStatus('NetStream.Publish.Start').", s.uniqueKey)
if err := s.packer.writeOnStatusPublish(s.conn, MSID1); err != nil {
return err
}
@ -431,7 +431,7 @@ func (s *ServerSession) doPlay(tid int, stream *Stream) (err error) {
s.url = fmt.Sprintf("%s/%s", s.tcURL, s.streamNameWithRawQuery)
nazalog.Infof("[%s] < R play('%s').", s.UniqueKey, s.streamNameWithRawQuery)
nazalog.Infof("[%s] < R play('%s').", s.uniqueKey, s.streamNameWithRawQuery)
// TODO chef: start duration reset
if err := s.packer.writeStreamIsRecorded(s.conn, MSID1); err != nil {
@ -441,7 +441,7 @@ func (s *ServerSession) doPlay(tid int, stream *Stream) (err error) {
return err
}
nazalog.Infof("[%s] > W onStatus('NetStream.Play.Start').", s.UniqueKey)
nazalog.Infof("[%s] > W onStatus('NetStream.Play.Start').", s.uniqueKey)
if err := s.packer.writeOnStatusPlay(s.conn, MSID1); err != nil {
return err
}

@ -44,7 +44,7 @@ type BaseInSessionObserver interface {
}
type BaseInSession struct {
UniqueKey string // 使用上层Session的值
uniqueKey string // 使用上层Session的值
cmdSession IInterleavedPacketWriter
observer BaseInSessionObserver
@ -85,7 +85,7 @@ type BaseInSession struct {
func NewBaseInSession(uniqueKey string, cmdSession IInterleavedPacketWriter) *BaseInSession {
s := &BaseInSession{
UniqueKey: uniqueKey,
uniqueKey: uniqueKey,
stat: base.StatSession{
Protocol: base.ProtocolRTSP,
SessionID: uniqueKey,
@ -113,12 +113,12 @@ func (session *BaseInSession) InitWithSDP(rawSDP []byte, sdpLogicCtx sdp.LogicCo
if session.sdpLogicCtx.IsAudioUnpackable() {
session.audioUnpacker = rtprtcp.NewRTPUnpacker(session.sdpLogicCtx.GetAudioPayloadTypeBase(), session.sdpLogicCtx.AudioClockRate, unpackerItemMaxSize, session.onAVPacketUnpacked)
} else {
nazalog.Warnf("[%s] audio unpacker not support for this type yet.", session.UniqueKey)
nazalog.Warnf("[%s] audio unpacker not support for this type yet.", session.uniqueKey)
}
if session.sdpLogicCtx.IsVideoUnpackable() {
session.videoUnpacker = rtprtcp.NewRTPUnpacker(session.sdpLogicCtx.GetVideoPayloadTypeBase(), session.sdpLogicCtx.VideoClockRate, unpackerItemMaxSize, session.onAVPacketUnpacked)
} else {
nazalog.Warnf("[%s] video unpacker not support this type yet.", session.UniqueKey)
nazalog.Warnf("[%s] video unpacker not support this type yet.", session.uniqueKey)
}
session.audioRRProducer = rtprtcp.NewRRProducer(session.sdpLogicCtx.AudioClockRate)
@ -171,7 +171,7 @@ func (session *BaseInSession) SetupWithChannel(uri string, rtpChannel, rtcpChann
}
func (session *BaseInSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp BaseInSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp BaseInSession. session=%p", session.uniqueKey, session)
var e1, e2, e3, e4 error
if session.audioRTPConn != nil {
e1 = session.audioRTPConn.Dispose()
@ -205,7 +205,7 @@ func (session *BaseInSession) HandleInterleavedPacket(b []byte, channel int) {
case session.videoRTCPChannel:
_ = session.handleRTCPPacket(b, nil)
default:
nazalog.Errorf("[%s] read interleaved packet but channel invalid. channel=%d", session.UniqueKey, channel)
nazalog.Errorf("[%s] read interleaved packet but channel invalid. channel=%d", session.uniqueKey, channel)
}
}
@ -231,13 +231,13 @@ func (session *BaseInSession) GetStat() base.StatSession {
return session.stat
}
func (session *BaseInSession) UpdateStat(interval uint32) {
func (session *BaseInSession) UpdateStat(intervalSec uint32) {
readBytesSum := session.currConnStat.ReadBytesSum.Load()
wroteBytesSum := session.currConnStat.WroteBytesSum.Load()
rDiff := readBytesSum - session.prevConnStat.ReadBytesSum
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(interval))
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := wroteBytesSum - session.prevConnStat.WroteBytesSum
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(interval))
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
session.stat.Bitrate = session.stat.ReadBitrate
session.prevConnStat.ReadBytesSum = readBytesSum
session.prevConnStat.WroteBytesSum = wroteBytesSum
@ -260,6 +260,10 @@ func (session *BaseInSession) IsAlive() (readAlive, writeAlive bool) {
return
}
func (session *BaseInSession) UniqueKey() string {
return session.uniqueKey
}
// callback by RTPUnpacker
func (session *BaseInSession) onAVPacketUnpacked(pkt base.AVPacket) {
if session.avPacketQueue != nil {
@ -277,7 +281,7 @@ func (session *BaseInSession) onAVPacket(pkt base.AVPacket) {
// callback by UDPConnection
func (session *BaseInSession) onReadRTPPacket(b []byte, rAddr *net.UDPAddr, err error) bool {
if err != nil {
nazalog.Errorf("[%s] read udp packet failed. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] read udp packet failed. err=%+v", session.uniqueKey, err)
return true
}
@ -288,7 +292,7 @@ func (session *BaseInSession) onReadRTPPacket(b []byte, rAddr *net.UDPAddr, err
// callback by UDPConnection
func (session *BaseInSession) onReadRTCPPacket(b []byte, rAddr *net.UDPAddr, err error) bool {
if err != nil {
nazalog.Errorf("[%s] read udp packet failed. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] read udp packet failed. err=%+v", session.uniqueKey, err)
return true
}
@ -301,12 +305,12 @@ func (session *BaseInSession) handleRTCPPacket(b []byte, rAddr *net.UDPAddr) err
session.currConnStat.ReadBytesSum.Add(uint64(len(b)))
if len(b) <= 0 {
nazalog.Errorf("[%s] handleRTCPPacket but length invalid. len=%d", session.UniqueKey, len(b))
nazalog.Errorf("[%s] handleRTCPPacket but length invalid. len=%d", session.uniqueKey, len(b))
return ErrRTSP
}
if session.loggedReadRTCPCount < session.debugLogMaxCount {
nazalog.Debugf("[%s] LOGPACKET. read rtcp=%s", session.UniqueKey, hex.Dump(nazastring.SubSliceSafety(b, 32)))
nazalog.Debugf("[%s] LOGPACKET. read rtcp=%s", session.uniqueKey, hex.Dump(nazastring.SubSliceSafety(b, 32)))
session.loggedReadRTCPCount++
}
@ -316,7 +320,7 @@ func (session *BaseInSession) handleRTCPPacket(b []byte, rAddr *net.UDPAddr) err
case rtprtcp.RTCPPacketTypeSR:
sr := rtprtcp.ParseSR(b)
if session.loggedReadSRCount < session.debugLogMaxCount {
nazalog.Debugf("[%s] LOGPACKET. %+v", session.UniqueKey, sr)
nazalog.Debugf("[%s] LOGPACKET. %+v", session.uniqueKey, sr)
session.loggedReadSRCount++
}
var rrBuf []byte
@ -344,11 +348,11 @@ func (session *BaseInSession) handleRTCPPacket(b []byte, rAddr *net.UDPAddr) err
default:
// ffmpeg推流时会在发送第一个RTP包之前就发送一个SR所以关闭这个警告日志
//nazalog.Warnf("[%s] read rtcp sr but senderSSRC invalid. senderSSRC=%d, audio=%d, video=%d",
// p.UniqueKey, sr.SenderSSRC, p.audioSSRC, p.videoSSRC)
// p.uniqueKey, sr.SenderSSRC, p.audioSSRC, p.videoSSRC)
return ErrRTSP
}
default:
nazalog.Warnf("[%s] handleRTCPPacket but type unknown. type=%d", session.UniqueKey, b[1])
nazalog.Warnf("[%s] handleRTCPPacket but type unknown. type=%d", session.uniqueKey, b[1])
return ErrRTSP
}
@ -359,19 +363,19 @@ func (session *BaseInSession) handleRTPPacket(b []byte) error {
session.currConnStat.ReadBytesSum.Add(uint64(len(b)))
if len(b) < rtprtcp.RTPFixedHeaderLength {
nazalog.Errorf("[%s] handleRTPPacket but length invalid. len=%d", session.UniqueKey, len(b))
nazalog.Errorf("[%s] handleRTPPacket but length invalid. len=%d", session.uniqueKey, len(b))
return ErrRTSP
}
packetType := int(b[1] & 0x7F)
if !session.sdpLogicCtx.IsPayloadTypeOrigin(packetType) {
nazalog.Errorf("[%s] handleRTPPacket but type invalid. type=%d", session.UniqueKey, packetType)
nazalog.Errorf("[%s] handleRTPPacket but type invalid. type=%d", session.uniqueKey, packetType)
return ErrRTSP
}
h, err := rtprtcp.ParseRTPPacket(b)
if err != nil {
nazalog.Errorf("[%s] handleRTPPacket invalid rtp packet. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] handleRTPPacket invalid rtp packet. err=%+v", session.uniqueKey, err)
return err
}
@ -382,7 +386,7 @@ func (session *BaseInSession) handleRTPPacket(b []byte) error {
// 接收数据时保证了sdp的原始类型对应
if session.sdpLogicCtx.IsAudioPayloadTypeOrigin(packetType) {
if session.loggedReadAudioRTPCount < session.debugLogMaxCount {
nazalog.Debugf("[%s] LOGPACKET. read audio rtp=%+v", session.UniqueKey, h)
nazalog.Debugf("[%s] LOGPACKET. read audio rtp=%+v", session.uniqueKey, h)
session.loggedReadAudioRTPCount++
}
@ -395,7 +399,7 @@ func (session *BaseInSession) handleRTPPacket(b []byte) error {
}
} else if session.sdpLogicCtx.IsVideoPayloadTypeOrigin(packetType) {
if session.loggedReadVideoRTPCount < session.debugLogMaxCount {
nazalog.Debugf("[%s] LOGPACKET. read video rtp=%+v", session.UniqueKey, h)
nazalog.Debugf("[%s] LOGPACKET. read video rtp=%+v", session.uniqueKey, h)
session.loggedReadVideoRTPCount++
}

@ -25,7 +25,7 @@ import (
)
type BaseOutSession struct {
UniqueKey string
uniqueKey string
cmdSession IInterleavedPacketWriter
rawSDP []byte
@ -54,7 +54,7 @@ type BaseOutSession struct {
func NewBaseOutSession(uniqueKey string, cmdSession IInterleavedPacketWriter) *BaseOutSession {
s := &BaseOutSession{
UniqueKey: uniqueKey,
uniqueKey: uniqueKey,
cmdSession: cmdSession,
stat: base.StatSession{
Protocol: base.ProtocolRTSP,
@ -106,7 +106,7 @@ func (session *BaseOutSession) SetupWithChannel(uri string, rtpChannel, rtcpChan
}
func (session *BaseOutSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp BaseOutSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp BaseOutSession. session=%p", session.uniqueKey, session)
var e1, e2, e3, e4 error
if session.audioRTPConn != nil {
e1 = session.audioRTPConn.Dispose()
@ -128,13 +128,13 @@ func (session *BaseOutSession) HandleInterleavedPacket(b []byte, channel int) {
case session.audioRTPChannel:
fallthrough
case session.videoRTPChannel:
nazalog.Warnf("[%s] not supposed to read packet in rtp channel of BaseOutSession. channel=%d, len=%d", session.UniqueKey, channel, len(b))
nazalog.Warnf("[%s] not supposed to read packet in rtp channel of BaseOutSession. channel=%d, len=%d", session.uniqueKey, channel, len(b))
case session.audioRTCPChannel:
fallthrough
case session.videoRTCPChannel:
nazalog.Debugf("[%s] read interleaved rtcp packet. b=%s", session.UniqueKey, hex.Dump(nazastring.SubSliceSafety(b, 32)))
nazalog.Debugf("[%s] read interleaved rtcp packet. b=%s", session.uniqueKey, hex.Dump(nazastring.SubSliceSafety(b, 32)))
default:
nazalog.Errorf("[%s] read interleaved packet but channel invalid. channel=%d", session.UniqueKey, channel)
nazalog.Errorf("[%s] read interleaved packet but channel invalid. channel=%d", session.uniqueKey, channel)
}
}
@ -145,7 +145,7 @@ func (session *BaseOutSession) WriteRTPPacket(packet rtprtcp.RTPPacket) {
t := int(packet.Header.PacketType)
if session.sdpLogicCtx.IsAudioPayloadTypeOrigin(t) {
if session.loggedWriteAudioRTPCount < session.debugLogMaxCount {
nazalog.Debugf("[%s] LOGPACKET. write audio rtp=%+v", session.UniqueKey, packet.Header)
nazalog.Debugf("[%s] LOGPACKET. write audio rtp=%+v", session.uniqueKey, packet.Header)
session.loggedWriteAudioRTPCount++
}
@ -157,7 +157,7 @@ func (session *BaseOutSession) WriteRTPPacket(packet rtprtcp.RTPPacket) {
}
} else if session.sdpLogicCtx.IsVideoPayloadTypeOrigin(t) {
if session.loggedWriteVideoRTPCount < session.debugLogMaxCount {
nazalog.Debugf("[%s] LOGPACKET. write video rtp=%+v", session.UniqueKey, packet.Header)
nazalog.Debugf("[%s] LOGPACKET. write video rtp=%+v", session.uniqueKey, packet.Header)
session.loggedWriteVideoRTPCount++
}
@ -168,7 +168,7 @@ func (session *BaseOutSession) WriteRTPPacket(packet rtprtcp.RTPPacket) {
_ = session.cmdSession.WriteInterleavedPacket(packet.Raw, session.videoRTPChannel)
}
} else {
nazalog.Errorf("[%s] write rtp packet but type invalid. type=%d", session.UniqueKey, t)
nazalog.Errorf("[%s] write rtp packet but type invalid. type=%d", session.uniqueKey, t)
}
}
@ -178,13 +178,13 @@ func (session *BaseOutSession) GetStat() base.StatSession {
return session.stat
}
func (session *BaseOutSession) UpdateStat(interval uint32) {
func (session *BaseOutSession) UpdateStat(intervalSec uint32) {
readBytesSum := session.currConnStat.ReadBytesSum.Load()
wroteBytesSum := session.currConnStat.WroteBytesSum.Load()
rDiff := readBytesSum - session.prevConnStat.ReadBytesSum
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(interval))
session.stat.ReadBitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := wroteBytesSum - session.prevConnStat.WroteBytesSum
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(interval))
session.stat.WriteBitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
session.stat.Bitrate = session.stat.WriteBitrate
session.prevConnStat.ReadBytesSum = readBytesSum
session.prevConnStat.WroteBytesSum = wroteBytesSum
@ -207,11 +207,15 @@ func (session *BaseOutSession) IsAlive() (readAlive, writeAlive bool) {
return
}
func (session *BaseOutSession) UniqueKey() string {
return session.uniqueKey
}
func (session *BaseOutSession) onReadUDPPacket(b []byte, rAddr *net.UDPAddr, err error) bool {
// TODO chef: impl me
if session.loggedReadUDPCount < session.debugLogMaxCount {
nazalog.Debugf("[%s] LOGPACKET. read udp=%s", session.UniqueKey, hex.Dump(nazastring.SubSliceSafety(b, 32)))
nazalog.Debugf("[%s] LOGPACKET. read udp=%s", session.uniqueKey, hex.Dump(nazastring.SubSliceSafety(b, 32)))
session.loggedReadUDPCount++
}
return true

@ -63,7 +63,7 @@ type ClientCommandSessionObserver interface {
// Push和Pull共用封装了客户端底层信令信令部分。
// 业务方应该使用PushSession和PullSession而不是直接使用ClientCommandSession除非你确定要这么做。
type ClientCommandSession struct {
UniqueKey string
uniqueKey string
t ClientCommandSessionType
observer ClientCommandSessionObserver
option ClientCommandSessionOption
@ -82,7 +82,7 @@ type ClientCommandSession struct {
sessionID string
channel int
waitErrChan chan error
waitChan chan error
}
type ModClientCommandSessionOption func(option *ClientCommandSessionOption)
@ -93,11 +93,11 @@ func NewClientCommandSession(t ClientCommandSessionType, uniqueKey string, obser
fn(&option)
}
s := &ClientCommandSession{
t: t,
UniqueKey: uniqueKey,
observer: observer,
option: option,
waitErrChan: make(chan error, 1),
t: t,
uniqueKey: uniqueKey,
observer: observer,
option: option,
waitChan: make(chan error, 1),
}
nazalog.Infof("[%s] lifecycle new rtsp ClientCommandSession. session=%p", uniqueKey, s)
return s
@ -123,27 +123,37 @@ func (session *ClientCommandSession) Do(rawURL string) error {
return session.doContext(ctx, rawURL)
}
func (session *ClientCommandSession) Wait() <-chan error {
return session.waitErrChan
func (session *ClientCommandSession) WaitChan() <-chan error {
return session.waitChan
}
func (session *ClientCommandSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp ClientCommandSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp ClientCommandSession. session=%p", session.uniqueKey, session)
if session.conn == nil {
return nil
return base.ErrSessionNotStarted
}
return session.conn.Close()
}
func (session *ClientCommandSession) WriteInterleavedPacket(packet []byte, channel int) error {
if session.conn == nil {
return base.ErrSessionNotStarted
}
_, err := session.conn.Write(packInterleaved(channel, packet))
return err
}
func (session *ClientCommandSession) RemoteAddr() string {
if session.conn == nil {
return ""
}
return session.conn.RemoteAddr().String()
}
func (session *ClientCommandSession) URL() string {
return session.urlCtx.URL
}
func (session *ClientCommandSession) AppName() string {
return session.urlCtx.PathWithoutLastItem
}
@ -156,6 +166,10 @@ func (session *ClientCommandSession) RawQuery() string {
return session.urlCtx.RawQuery
}
func (session *ClientCommandSession) UniqueKey() string {
return session.uniqueKey
}
func (session *ClientCommandSession) doContext(ctx context.Context, rawURL string) error {
errChan := make(chan error, 1)
@ -229,7 +243,7 @@ func (session *ClientCommandSession) runReadLoop() {
for {
isInterleaved, packet, channel, err := readInterleaved(r)
if err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
if isInterleaved {
@ -242,13 +256,13 @@ func (session *ClientCommandSession) runReadLoop() {
// 接收TCP对端关闭FIN信号
dummy := make([]byte, 1)
_, err := session.conn.Read(dummy)
session.waitErrChan <- err
session.waitChan <- err
return
}
// 对端支持get_parameter需要定时向对端发送get_parameter进行保活
nazalog.Debugf("[%s] start get_parameter timer.", session.UniqueKey)
nazalog.Debugf("[%s] start get_parameter timer.", session.uniqueKey)
var r = bufio.NewReader(session.conn)
t := time.NewTicker(writeGetParameterIntervalMSec * time.Millisecond)
defer t.Stop()
@ -259,7 +273,7 @@ func (session *ClientCommandSession) runReadLoop() {
case <-t.C:
session.cseq++
if err := session.writeCmd(MethodGetParameter, session.urlCtx.RawURLWithoutUserInfo, nil, ""); err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
default:
@ -268,14 +282,14 @@ func (session *ClientCommandSession) runReadLoop() {
isInterleaved, packet, channel, err := readInterleaved(r)
if err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
if isInterleaved {
session.observer.OnInterleavedPacket(packet, int(channel))
} else {
if _, err := nazahttp.ReadHTTPResponseMessage(r); err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
}
@ -288,7 +302,7 @@ func (session *ClientCommandSession) runReadLoop() {
case <-t.C:
session.cseq++
if _, err := session.writeCmdReadResp(MethodGetParameter, session.urlCtx.RawURLWithoutUserInfo, nil, ""); err != nil {
session.waitErrChan <- err
session.waitChan <- err
return
}
default:
@ -306,7 +320,7 @@ func (session *ClientCommandSession) connect(rawURL string) (err error) {
return err
}
nazalog.Debugf("[%s] > tcp connect.", session.UniqueKey)
nazalog.Debugf("[%s] > tcp connect.", session.uniqueKey)
// # 建立连接
conn, err := net.Dial("tcp", session.urlCtx.HostWithPort)
@ -316,7 +330,7 @@ func (session *ClientCommandSession) connect(rawURL string) (err error) {
session.conn = connection.New(conn, func(option *connection.Option) {
option.ReadBufSize = readBufSize
})
nazalog.Debugf("[%s] < tcp connect. laddr=%s, raddr=%s", session.UniqueKey, conn.LocalAddr().String(), conn.RemoteAddr().String())
nazalog.Debugf("[%s] < tcp connect. laddr=%s, raddr=%s", session.uniqueKey, conn.LocalAddr().String(), conn.RemoteAddr().String())
session.observer.OnConnectResult()
return nil
@ -422,7 +436,7 @@ func (session *ClientCommandSession) writeOneSetup(setupURI string) error {
}
nazalog.Debugf("[%s] init conn. lRTPPort=%d, lRTCPPort=%d, rRTPPort=%d, rRTCPPort=%d",
session.UniqueKey, lRTPPort, lRTCPPort, rRTPPort, rRTCPPort)
session.uniqueKey, lRTPPort, lRTCPPort, rRTPPort, rRTCPPort)
rtpConn, err := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.Conn = rtpC
@ -511,8 +525,8 @@ func (session *ClientCommandSession) writeCmd(method, uri string, headers map[st
}
req := PackRequest(method, uri, headers, body)
nazalog.Debugf("[%s] > write %s.", session.UniqueKey, method)
//nazalog.Debugf("[%s] > write %s. req=%s", session.UniqueKey, method, req)
nazalog.Debugf("[%s] > write %s.", session.uniqueKey, method)
//nazalog.Debugf("[%s] > write %s. req=%s", session.uniqueKey, method, req)
_, err := session.conn.Write([]byte(req))
return err
}
@ -530,7 +544,7 @@ func (session *ClientCommandSession) writeCmdReadResp(method, uri string, header
return
}
nazalog.Debugf("[%s] < read response. version=%s, code=%s, reason=%s, headers=%+v, body=%s",
session.UniqueKey, ctx.Version, ctx.StatusCode, ctx.Reason, ctx.Headers, string(ctx.Body))
session.uniqueKey, ctx.Version, ctx.StatusCode, ctx.Reason, ctx.Headers, string(ctx.Body))
if ctx.StatusCode != "401" {
return

@ -34,7 +34,7 @@ var defaultPullSessionOption = PullSessionOption{
}
type PullSession struct {
UniqueKey string // const after ctor
uniqueKey string // const after ctor
cmdSession *ClientCommandSession
baseInSession *BaseInSession
}
@ -47,9 +47,9 @@ func NewPullSession(observer PullSessionObserver, modOptions ...ModPullSessionOp
fn(&option)
}
uk := base.GenUniqueKey(base.UKPRTSPPullSession)
uk := base.GenUKRTSPPullSession()
s := &PullSession{
UniqueKey: uk,
uniqueKey: uk,
}
cmdSession := NewClientCommandSession(CCSTPullSession, uk, s, func(opt *ClientCommandSessionOption) {
opt.DoTimeoutMS = option.PullTimeoutMS
@ -62,50 +62,67 @@ func NewPullSession(observer PullSessionObserver, modOptions ...ModPullSessionOp
return s
}
// 如果没有错误发生阻塞直到接收音视频数据的前一步也即收到rtsp play response
// 阻塞直到和对端完成拉流前握手部分的工作也即收到RTSP Play response或者发生错误
func (session *PullSession) Pull(rawURL string) error {
nazalog.Debugf("[%s] pull. url=%s", session.UniqueKey, rawURL)
nazalog.Debugf("[%s] pull. url=%s", session.uniqueKey, rawURL)
return session.cmdSession.Do(rawURL)
}
// Pull成功后调用该函数可阻塞直到拉流结束
func (session *PullSession) Wait() <-chan error {
return session.cmdSession.Wait()
func (session *PullSession) GetSDP() ([]byte, sdp.LogicContext) {
return session.baseInSession.GetSDP()
}
// 文档请参考: interface IClientSessionLifecycle
func (session *PullSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp PullSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp PullSession. session=%p", session.uniqueKey, session)
e1 := session.cmdSession.Dispose()
e2 := session.baseInSession.Dispose()
return nazaerrors.CombineErrors(e1, e2)
}
func (session *PullSession) GetSDP() ([]byte, sdp.LogicContext) {
return session.baseInSession.GetSDP()
// 文档请参考: interface IClientSessionLifecycle
func (session *PullSession) WaitChan() <-chan error {
return session.cmdSession.WaitChan()
}
// 文档请参考: interface ISessionURLContext
func (session *PullSession) URL() string {
return session.cmdSession.URL()
}
// 文档请参考: interface ISessionURLContext
func (session *PullSession) AppName() string {
return session.cmdSession.AppName()
}
// 文档请参考: interface ISessionURLContext
func (session *PullSession) StreamName() string {
return session.cmdSession.StreamName()
}
// 文档请参考: interface ISessionURLContext
func (session *PullSession) RawQuery() string {
return session.cmdSession.RawQuery()
}
// 文档请参考: interface IObject
func (session *PullSession) UniqueKey() string {
return session.uniqueKey
}
// 文档请参考: interface ISessionStat
func (session *PullSession) GetStat() base.StatSession {
stat := session.baseInSession.GetStat()
stat.RemoteAddr = session.cmdSession.RemoteAddr()
return stat
}
func (session *PullSession) UpdateStat(interval uint32) {
session.baseInSession.UpdateStat(interval)
// 文档请参考: interface ISessionStat
func (session *PullSession) UpdateStat(intervalSec uint32) {
session.baseInSession.UpdateStat(intervalSec)
}
// 文档请参考: interface ISessionStat
func (session *PullSession) IsAlive() (readAlive, writeAlive bool) {
return session.baseInSession.IsAlive()
}

@ -28,7 +28,7 @@ var defaultPushSessionOption = PushSessionOption{
}
type PushSession struct {
UniqueKey string
uniqueKey string
cmdSession *ClientCommandSession
baseOutSession *BaseOutSession
}
@ -41,9 +41,9 @@ func NewPushSession(modOptions ...ModPushSessionOption) *PushSession {
fn(&option)
}
uk := base.GenUniqueKey(base.UKPRTSPPushSession)
uk := base.GenUKRTSPPushSession()
s := &PushSession{
UniqueKey: uk,
uniqueKey: uk,
}
cmdSession := NewClientCommandSession(CCSTPushSession, uk, s, func(opt *ClientCommandSessionOption) {
opt.DoTimeoutMS = option.PushTimeoutMS
@ -56,50 +56,69 @@ func NewPushSession(modOptions ...ModPushSessionOption) *PushSession {
return s
}
// 阻塞直到和对端完成推流前握手部分的工作也即收到RTSP Record response或者发生错误
func (session *PushSession) Push(rawURL string, rawSDP []byte, sdpLogicCtx sdp.LogicContext) error {
nazalog.Debugf("[%s] push. url=%s", session.UniqueKey, rawURL)
nazalog.Debugf("[%s] push. url=%s", session.uniqueKey, rawURL)
session.cmdSession.InitWithSDP(rawSDP, sdpLogicCtx)
session.baseOutSession.InitWithSDP(rawSDP, sdpLogicCtx)
return session.cmdSession.Do(rawURL)
}
func (session *PushSession) Wait() <-chan error {
return session.cmdSession.Wait()
}
func (session *PushSession) WriteRTPPacket(packet rtprtcp.RTPPacket) {
session.baseOutSession.WriteRTPPacket(packet)
}
// 文档请参考: interface IClientSessionLifecycle
func (session *PushSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp PushSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp PushSession. session=%p", session.uniqueKey, session)
e1 := session.cmdSession.Dispose()
e2 := session.baseOutSession.Dispose()
return nazaerrors.CombineErrors(e1, e2)
}
// 文档请参考: interface IClientSessionLifecycle
func (session *PushSession) WaitChan() <-chan error {
return session.cmdSession.WaitChan()
}
// 文档请参考: interface ISessionURLContext
func (session *PushSession) URL() string {
return session.cmdSession.URL()
}
// 文档请参考: interface ISessionURLContext
func (session *PushSession) AppName() string {
return session.cmdSession.AppName()
}
// 文档请参考: interface ISessionURLContext
func (session *PushSession) StreamName() string {
return session.cmdSession.StreamName()
}
// 文档请参考: interface ISessionURLContext
func (session *PushSession) RawQuery() string {
return session.cmdSession.RawQuery()
}
// 文档请参考: interface IObject
func (session *PushSession) UniqueKey() string {
return session.uniqueKey
}
// 文档请参考: interface ISessionStat
func (session *PushSession) GetStat() base.StatSession {
stat := session.baseOutSession.GetStat()
stat.RemoteAddr = session.cmdSession.RemoteAddr()
return stat
}
func (session *PushSession) UpdateStat(interval uint32) {
session.baseOutSession.UpdateStat(interval)
// 文档请参考: interface ISessionStat
func (session *PushSession) UpdateStat(intervalSec uint32) {
session.baseOutSession.UpdateStat(intervalSec)
}
// 文档请参考: interface ISessionStat
func (session *PushSession) IsAlive() (readAlive, writeAlive bool) {
return session.baseOutSession.IsAlive()
}

@ -38,7 +38,7 @@ type ServerCommandSessionObserver interface {
}
type ServerCommandSession struct {
UniqueKey string // const after ctor
uniqueKey string // const after ctor
observer ServerCommandSessionObserver // const after ctor
conn connection.Connection
prevConnStat connection.Stat
@ -50,9 +50,9 @@ type ServerCommandSession struct {
}
func NewServerCommandSession(observer ServerCommandSessionObserver, conn net.Conn) *ServerCommandSession {
uk := base.GenUniqueKey(base.UKPRTSPServerCommandSession)
uk := base.GenUKRTSPServerCommandSession()
s := &ServerCommandSession{
UniqueKey: uk,
uniqueKey: uk,
observer: observer,
conn: connection.New(conn, func(option *connection.Option) {
option.ReadBufSize = serverCommandSessionReadBufSize
@ -68,7 +68,7 @@ func (session *ServerCommandSession) RunLoop() error {
}
func (session *ServerCommandSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp ServerCommandSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp ServerCommandSession. session=%p", session.uniqueKey, session)
return session.conn.Close()
}
@ -82,12 +82,12 @@ func (session *ServerCommandSession) RemoteAddr() string {
return session.conn.RemoteAddr().String()
}
func (session *ServerCommandSession) UpdateStat(interval uint32) {
func (session *ServerCommandSession) UpdateStat(intervalSec uint32) {
currStat := session.conn.GetStat()
rDiff := currStat.ReadBytesSum - session.prevConnStat.ReadBytesSum
session.stat.Bitrate = int(rDiff * 8 / 1024 / uint64(interval))
session.stat.Bitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := currStat.WroteBytesSum - session.prevConnStat.WroteBytesSum
session.stat.Bitrate = int(wDiff * 8 / 1024 / uint64(interval))
session.stat.Bitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
session.prevConnStat = currStat
}
@ -112,6 +112,10 @@ func (session *ServerCommandSession) IsAlive() (readAlive, writeAlive bool) {
return
}
func (session *ServerCommandSession) UniqueKey() string {
return session.uniqueKey
}
func (session *ServerCommandSession) runCmdLoop() error {
var r = bufio.NewReader(session.conn)
@ -119,7 +123,7 @@ Loop:
for {
isInterleaved, packet, channel, err := readInterleaved(r)
if err != nil {
nazalog.Errorf("[%s] read interleaved error. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] read interleaved error. err=%+v", session.uniqueKey, err)
break Loop
}
if isInterleaved {
@ -128,7 +132,7 @@ Loop:
} else if session.subSession != nil {
session.subSession.HandleInterleavedPacket(packet, int(channel))
} else {
nazalog.Errorf("[%s] read interleaved packet but pub or sub not exist.", session.UniqueKey)
nazalog.Errorf("[%s] read interleaved packet but pub or sub not exist.", session.uniqueKey)
break Loop
}
continue
@ -137,12 +141,12 @@ Loop:
// 读取一个message
requestCtx, err := nazahttp.ReadHTTPRequestMessage(r)
if err != nil {
nazalog.Errorf("[%s] read rtsp message error. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] read rtsp message error. err=%+v", session.uniqueKey, err)
break Loop
}
nazalog.Debugf("[%s] read http request. method=%s, uri=%s, version=%s, headers=%+v, body=%s",
session.UniqueKey, requestCtx.Method, requestCtx.URI, requestCtx.Version, requestCtx.Headers, string(requestCtx.Body))
session.uniqueKey, requestCtx.Method, requestCtx.URI, requestCtx.Version, requestCtx.Headers, string(requestCtx.Body))
var handleMsgErr error
switch requestCtx.Method {
@ -169,48 +173,48 @@ Loop:
handleMsgErr = session.handleTeardown(requestCtx)
break Loop
default:
nazalog.Errorf("[%s] unknown rtsp message. method=%s", session.UniqueKey, requestCtx.Method)
nazalog.Errorf("[%s] unknown rtsp message. method=%s", session.uniqueKey, requestCtx.Method)
}
if handleMsgErr != nil {
nazalog.Errorf("[%s] handle rtsp message error. err=%+v", session.UniqueKey, handleMsgErr)
nazalog.Errorf("[%s] handle rtsp message error. err=%+v", session.uniqueKey, handleMsgErr)
break
}
}
_ = session.conn.Close()
nazalog.Debugf("[%s] < handleTCPConnect.", session.UniqueKey)
nazalog.Debugf("[%s] < handleTCPConnect.", session.uniqueKey)
return nil
}
func (session *ServerCommandSession) handleOptions(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R OPTIONS", session.UniqueKey)
nazalog.Infof("[%s] < R OPTIONS", session.uniqueKey)
resp := PackResponseOptions(requestCtx.Headers[HeaderCSeq])
_, err := session.conn.Write([]byte(resp))
return err
}
func (session *ServerCommandSession) handleAnnounce(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R ANNOUNCE", session.UniqueKey)
nazalog.Infof("[%s] < R ANNOUNCE", session.uniqueKey)
urlCtx, err := base.ParseRTSPURL(requestCtx.URI)
if err != nil {
nazalog.Errorf("[%s] parse presentation failed. uri=%s", session.UniqueKey, requestCtx.URI)
nazalog.Errorf("[%s] parse presentation failed. uri=%s", session.uniqueKey, requestCtx.URI)
return err
}
sdpLogicCtx, err := sdp.ParseSDP2LogicContext(requestCtx.Body)
if err != nil {
nazalog.Errorf("[%s] parse sdp failed. err=%v", session.UniqueKey, err)
nazalog.Errorf("[%s] parse sdp failed. err=%v", session.uniqueKey, err)
return err
}
session.pubSession = NewPubSession(urlCtx, session)
nazalog.Infof("[%s] link new PubSession. [%s]", session.UniqueKey, session.pubSession.UniqueKey)
nazalog.Infof("[%s] link new PubSession. [%s]", session.uniqueKey, session.pubSession.uniqueKey)
session.pubSession.InitWithSDP(requestCtx.Body, sdpLogicCtx)
if ok := session.observer.OnNewRTSPPubSession(session.pubSession); !ok {
nazalog.Warnf("[%s] force close pubsession.", session.pubSession.UniqueKey)
nazalog.Warnf("[%s] force close pubsession.", session.pubSession.uniqueKey)
return ErrRTSP
}
@ -220,19 +224,19 @@ func (session *ServerCommandSession) handleAnnounce(requestCtx nazahttp.HTTPReqM
}
func (session *ServerCommandSession) handleDescribe(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R DESCRIBE", session.UniqueKey)
nazalog.Infof("[%s] < R DESCRIBE", session.uniqueKey)
urlCtx, err := base.ParseRTSPURL(requestCtx.URI)
if err != nil {
nazalog.Errorf("[%s] parse presentation failed. uri=%s", session.UniqueKey, requestCtx.URI)
nazalog.Errorf("[%s] parse presentation failed. uri=%s", session.uniqueKey, requestCtx.URI)
return err
}
session.subSession = NewSubSession(urlCtx, session)
nazalog.Infof("[%s] link new SubSession. [%s]", session.UniqueKey, session.subSession.UniqueKey)
nazalog.Infof("[%s] link new SubSession. [%s]", session.uniqueKey, session.subSession.uniqueKey)
ok, rawSDP := session.observer.OnNewRTSPSubSessionDescribe(session.subSession)
if !ok {
nazalog.Warnf("[%s] force close subSession.", session.UniqueKey)
nazalog.Warnf("[%s] force close subSession.", session.uniqueKey)
return ErrRTSP
}
@ -246,7 +250,7 @@ func (session *ServerCommandSession) handleDescribe(requestCtx nazahttp.HTTPReqM
// 一次SETUP对应一路流音频或视频
func (session *ServerCommandSession) handleSetup(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R SETUP", session.UniqueKey)
nazalog.Infof("[%s] < R SETUP", session.uniqueKey)
remoteAddr := session.conn.RemoteAddr().String()
host, _, _ := net.SplitHostPort(remoteAddr)
@ -256,21 +260,21 @@ func (session *ServerCommandSession) handleSetup(requestCtx nazahttp.HTTPReqMsgC
if strings.Contains(htv, TransportFieldInterleaved) {
rtpChannel, rtcpChannel, err := parseRTPRTCPChannel(htv)
if err != nil {
nazalog.Errorf("[%s] parse rtp rtcp channel error. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] parse rtp rtcp channel error. err=%+v", session.uniqueKey, err)
return err
}
if session.pubSession != nil {
if err := session.pubSession.SetupWithChannel(requestCtx.URI, int(rtpChannel), int(rtcpChannel)); err != nil {
nazalog.Errorf("[%s] setup channel error. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] setup channel error. err=%+v", session.uniqueKey, err)
return err
}
} else if session.subSession != nil {
if err := session.subSession.SetupWithChannel(requestCtx.URI, int(rtpChannel), int(rtcpChannel)); err != nil {
nazalog.Errorf("[%s] setup channel error. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] setup channel error. err=%+v", session.uniqueKey, err)
return err
}
} else {
nazalog.Errorf("[%s] setup but session not exist.", session.UniqueKey)
nazalog.Errorf("[%s] setup but session not exist.", session.uniqueKey)
return ErrRTSP
}
@ -281,31 +285,31 @@ func (session *ServerCommandSession) handleSetup(requestCtx nazahttp.HTTPReqMsgC
rRTPPort, rRTCPPort, err := parseClientPort(requestCtx.Headers[HeaderTransport])
if err != nil {
nazalog.Errorf("[%s] parseClientPort failed. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] parseClientPort failed. err=%+v", session.uniqueKey, err)
return err
}
rtpConn, rtcpConn, lRTPPort, lRTCPPort, err := initConnWithClientPort(host, rRTPPort, rRTCPPort)
if err != nil {
nazalog.Errorf("[%s] initConnWithClientPort failed. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] initConnWithClientPort failed. err=%+v", session.uniqueKey, err)
return err
}
nazalog.Debugf("[%s] init conn. lRTPPort=%d, lRTCPPort=%d, rRTPPort=%d, rRTCPPort=%d",
session.UniqueKey, lRTPPort, lRTCPPort, rRTPPort, rRTCPPort)
session.uniqueKey, lRTPPort, lRTCPPort, rRTPPort, rRTCPPort)
if session.pubSession != nil {
if err = session.pubSession.SetupWithConn(requestCtx.URI, rtpConn, rtcpConn); err != nil {
nazalog.Errorf("[%s] setup conn error. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] setup conn error. err=%+v", session.uniqueKey, err)
return err
}
htv = fmt.Sprintf(HeaderTransportServerRecordTmpl, rRTPPort, rRTCPPort, lRTPPort, lRTCPPort)
} else if session.subSession != nil {
if err = session.subSession.SetupWithConn(requestCtx.URI, rtpConn, rtcpConn); err != nil {
nazalog.Errorf("[%s] setup conn error. err=%+v", session.UniqueKey, err)
nazalog.Errorf("[%s] setup conn error. err=%+v", session.uniqueKey, err)
return err
}
htv = fmt.Sprintf(HeaderTransportServerPlayTmpl, rRTPPort, rRTCPPort, lRTPPort, lRTCPPort)
} else {
nazalog.Errorf("[%s] setup but session not exist.", session.UniqueKey)
nazalog.Errorf("[%s] setup but session not exist.", session.uniqueKey)
return ErrRTSP
}
@ -315,14 +319,14 @@ func (session *ServerCommandSession) handleSetup(requestCtx nazahttp.HTTPReqMsgC
}
func (session *ServerCommandSession) handleRecord(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R RECORD", session.UniqueKey)
nazalog.Infof("[%s] < R RECORD", session.uniqueKey)
resp := PackResponseRecord(requestCtx.Headers[HeaderCSeq])
_, err := session.conn.Write([]byte(resp))
return err
}
func (session *ServerCommandSession) handlePlay(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R PLAY", session.UniqueKey)
nazalog.Infof("[%s] < R PLAY", session.uniqueKey)
if ok := session.observer.OnNewRTSPSubSessionPlay(session.subSession); !ok {
return ErrRTSP
}
@ -332,7 +336,7 @@ func (session *ServerCommandSession) handlePlay(requestCtx nazahttp.HTTPReqMsgCt
}
func (session *ServerCommandSession) handleTeardown(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R TEARDOWN", session.UniqueKey)
nazalog.Infof("[%s] < R TEARDOWN", session.uniqueKey)
resp := PackResponseTeardown(requestCtx.Headers[HeaderCSeq])
_, err := session.conn.Write([]byte(resp))
return err

@ -23,7 +23,7 @@ type PubSessionObserver interface {
}
type PubSession struct {
UniqueKey string
uniqueKey string
urlCtx base.URLContext
cmdSession *ServerCommandSession
baseInSession *BaseInSession
@ -32,9 +32,9 @@ type PubSession struct {
}
func NewPubSession(urlCtx base.URLContext, cmdSession *ServerCommandSession) *PubSession {
uk := base.GenUniqueKey(base.UKPRTSPPubSession)
uk := base.GenUKRTSPPubSession()
s := &PubSession{
UniqueKey: uk,
uniqueKey: uk,
urlCtx: urlCtx,
cmdSession: cmdSession,
}
@ -61,7 +61,7 @@ func (session *PubSession) SetupWithChannel(uri string, rtpChannel, rtcpChannel
}
func (session *PubSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp PubSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp PubSession. session=%p", session.uniqueKey, session)
e1 := session.cmdSession.Dispose()
e2 := session.baseInSession.Dispose()
return nazaerrors.CombineErrors(e1, e2)
@ -91,24 +91,24 @@ func (session *PubSession) RawQuery() string {
return session.urlCtx.RawQuery
}
func (session *PubSession) UniqueKey() string {
return session.uniqueKey
}
func (session *PubSession) GetStat() base.StatSession {
stat := session.baseInSession.GetStat()
stat.RemoteAddr = session.cmdSession.RemoteAddr()
return stat
}
func (session *PubSession) UpdateStat(interval uint32) {
session.baseInSession.UpdateStat(interval)
func (session *PubSession) UpdateStat(intervalSec uint32) {
session.baseInSession.UpdateStat(intervalSec)
}
func (session *PubSession) IsAlive() (readAlive, writeAlive bool) {
return session.baseInSession.IsAlive()
}
func (session *PubSession) RemoteAddr() string {
return session.cmdSession.RemoteAddr()
}
// IInterleavedPacketWriter, callback by BaseInSession
func (session *PubSession) WriteInterleavedPacket(packet []byte, channel int) error {
return session.cmdSession.WriteInterleavedPacket(packet, channel)

@ -19,16 +19,16 @@ import (
)
type SubSession struct {
UniqueKey string // const after ctor
uniqueKey string // const after ctor
urlCtx base.URLContext
cmdSession *ServerCommandSession
baseOutSession *BaseOutSession
}
func NewSubSession(urlCtx base.URLContext, cmdSession *ServerCommandSession) *SubSession {
uk := base.GenUniqueKey(base.UKPRTSPSubSession)
uk := base.GenUKRTSPSubSession()
s := &SubSession{
UniqueKey: uk,
uniqueKey: uk,
urlCtx: urlCtx,
cmdSession: cmdSession,
}
@ -55,7 +55,7 @@ func (session *SubSession) WriteRTPPacket(packet rtprtcp.RTPPacket) {
}
func (session *SubSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp SubSession. session=%p", session.UniqueKey, session)
nazalog.Infof("[%s] lifecycle dispose rtsp SubSession. session=%p", session.uniqueKey, session)
e1 := session.baseOutSession.Dispose()
e2 := session.cmdSession.Dispose()
return nazaerrors.CombineErrors(e1, e2)
@ -81,18 +81,18 @@ func (session *SubSession) RawQuery() string {
return session.urlCtx.RawQuery
}
func (session *SubSession) UniqueKey() string {
return session.uniqueKey
}
func (session *SubSession) GetStat() base.StatSession {
stat := session.baseOutSession.GetStat()
stat.RemoteAddr = session.cmdSession.RemoteAddr()
return stat
}
func (session *SubSession) UpdateStat(interval uint32) {
session.baseOutSession.UpdateStat(interval)
}
func (session *SubSession) RemoteAddr() string {
return session.cmdSession.RemoteAddr()
func (session *SubSession) UpdateStat(intervalSec uint32) {
session.baseOutSession.UpdateStat(intervalSec)
}
func (session *SubSession) IsAlive() (readAlive, writeAlive bool) {

Loading…
Cancel
Save