mirror of https://github.com/q191201771/lal.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
5.3 KiB
Go
205 lines
5.3 KiB
Go
6 years ago
|
package main
|
||
6 years ago
|
|
||
|
import (
|
||
|
"fmt"
|
||
6 years ago
|
"github.com/q191201771/lal/httpflv"
|
||
6 years ago
|
"github.com/q191201771/lal/log"
|
||
|
"github.com/q191201771/lal/util"
|
||
6 years ago
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
6 years ago
|
//
|
||
|
//type InSession interface {
|
||
|
// SetStartTick(tick int64)
|
||
|
// StartTick() int64
|
||
|
//}
|
||
|
|
||
6 years ago
|
type Group struct {
|
||
6 years ago
|
config *Config
|
||
6 years ago
|
appName string
|
||
|
streamName string
|
||
|
|
||
6 years ago
|
exitChan chan bool
|
||
6 years ago
|
pullSession *httpflv.PullSession
|
||
|
subSessionList map[*httpflv.SubSession]bool
|
||
6 years ago
|
turnToEmptyTick int64 // trace while sub session list turn to empty
|
||
6 years ago
|
gopCache *httpflv.GOPCache
|
||
6 years ago
|
mutex sync.Mutex
|
||
6 years ago
|
|
||
|
UniqueKey string
|
||
6 years ago
|
}
|
||
|
|
||
6 years ago
|
func NewGroup(appName string, streamName string, config *Config) *Group {
|
||
6 years ago
|
uk := util.GenUniqueKey("FLVGROUP")
|
||
|
log.Infof("lifecycle new Group. [%s] appName=%s streamName=%s", uk, appName, streamName)
|
||
|
|
||
6 years ago
|
return &Group{
|
||
6 years ago
|
config: config,
|
||
6 years ago
|
appName: appName,
|
||
|
streamName: streamName,
|
||
|
exitChan: make(chan bool),
|
||
6 years ago
|
subSessionList: make(map[*httpflv.SubSession]bool),
|
||
|
gopCache: httpflv.NewGOPCache(config.GOPCacheNum),
|
||
6 years ago
|
UniqueKey: uk,
|
||
6 years ago
|
}
|
||
|
}
|
||
|
|
||
|
func (group *Group) RunLoop() {
|
||
6 years ago
|
t := time.NewTicker(300 * time.Millisecond)
|
||
6 years ago
|
defer t.Stop()
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case <-group.exitChan:
|
||
|
return
|
||
|
case <-t.C:
|
||
|
now := time.Now().Unix()
|
||
|
|
||
6 years ago
|
// TODO chef: do timeout stuff. and do it fast.
|
||
|
|
||
|
group.mutex.Lock()
|
||
|
if group.pullSession != nil {
|
||
|
if isReadTimeout, _ := group.pullSession.ConnStat.Check(now); isReadTimeout {
|
||
|
log.Warnf("pull session read timeout. [%s]", group.pullSession.UniqueKey)
|
||
|
group.disposePullSession(lalErr)
|
||
6 years ago
|
}
|
||
|
}
|
||
6 years ago
|
group.mutex.Unlock()
|
||
6 years ago
|
|
||
6 years ago
|
group.mutex.Lock()
|
||
|
for session := range group.subSessionList {
|
||
|
if _, isWriteTimeout := session.ConnStat.Check(now); isWriteTimeout {
|
||
|
log.Warnf("sub session write timeout. [%s]", session)
|
||
|
delete(group.subSessionList, session)
|
||
|
session.Dispose(lalErr)
|
||
6 years ago
|
}
|
||
|
}
|
||
6 years ago
|
group.mutex.Unlock()
|
||
6 years ago
|
|
||
6 years ago
|
if group.config.Pull.StopPullWhileNoSubTimeout != 0 {
|
||
6 years ago
|
group.mutex.Lock()
|
||
|
if group.pullSession != nil && group.turnToEmptyTick != 0 && len(group.subSessionList) == 0 &&
|
||
6 years ago
|
now-group.turnToEmptyTick > group.config.Pull.StopPullWhileNoSubTimeout {
|
||
6 years ago
|
|
||
6 years ago
|
log.Infof("stop pull while no SubSession. [%s]", group.pullSession.UniqueKey)
|
||
|
group.disposePullSession(lalErr)
|
||
6 years ago
|
}
|
||
|
group.mutex.Unlock()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
6 years ago
|
func (group *Group) Dispose(err error) {
|
||
|
log.Infof("lifecycle dispose Group. [%s] reason=%v", group.UniqueKey, err)
|
||
6 years ago
|
group.exitChan <- true
|
||
|
}
|
||
|
|
||
6 years ago
|
func (group *Group) AddSubSession(session *httpflv.SubSession) {
|
||
6 years ago
|
group.mutex.Lock()
|
||
6 years ago
|
log.Debugf("add SubSession into group. [%s]", session.UniqueKey)
|
||
6 years ago
|
group.subSessionList[session] = true
|
||
|
group.turnToEmptyTick = 0
|
||
6 years ago
|
|
||
6 years ago
|
go func() {
|
||
|
if err := session.RunLoop(); err != nil {
|
||
6 years ago
|
log.Debugf("SubSession loop done. [%s] err=%v", session.UniqueKey, err)
|
||
6 years ago
|
}
|
||
|
|
||
|
group.mutex.Lock()
|
||
|
defer group.mutex.Unlock()
|
||
6 years ago
|
log.Infof("del SubSession out of group. [%s]", session.UniqueKey)
|
||
6 years ago
|
delete(group.subSessionList, session)
|
||
|
if len(group.subSessionList) == 0 {
|
||
|
group.turnToEmptyTick = time.Now().Unix()
|
||
|
}
|
||
|
}()
|
||
6 years ago
|
|
||
|
session.WriteHTTPResponseHeader()
|
||
|
session.WriteFlvHeader()
|
||
|
if group.gopCache.WriteWholeThings(session) {
|
||
|
session.HasKeyFrame = true
|
||
|
}
|
||
|
group.mutex.Unlock()
|
||
6 years ago
|
}
|
||
|
|
||
|
func (group *Group) PullIfNeeded(httpFlvPullAddr string) {
|
||
|
group.mutex.Lock()
|
||
|
if group.pullSession != nil {
|
||
|
return
|
||
|
}
|
||
6 years ago
|
pullSession := httpflv.NewPullSession(group, group.config.Pull.ConnectTimeout, group.config.Pull.ReadTimeout)
|
||
6 years ago
|
group.pullSession = pullSession
|
||
6 years ago
|
group.mutex.Unlock()
|
||
6 years ago
|
|
||
6 years ago
|
go func() {
|
||
|
defer func() {
|
||
|
group.mutex.Lock()
|
||
|
defer group.mutex.Unlock()
|
||
|
group.pullSession = nil
|
||
6 years ago
|
log.Infof("del PullSession out of group. [%s]", pullSession.UniqueKey)
|
||
6 years ago
|
}()
|
||
|
|
||
6 years ago
|
log.Infof("<----- connect. [%s]", pullSession.UniqueKey)
|
||
6 years ago
|
url := fmt.Sprintf("http://%s/%s/%s.flv", httpFlvPullAddr, group.appName, group.streamName)
|
||
6 years ago
|
err := pullSession.Connect(url)
|
||
6 years ago
|
if err != nil {
|
||
6 years ago
|
log.Errorf("-----> connect error. [%s] err=%v", pullSession.UniqueKey, err)
|
||
6 years ago
|
return
|
||
|
}
|
||
6 years ago
|
log.Infof("-----> connect succ. [%s]", pullSession.UniqueKey)
|
||
6 years ago
|
|
||
|
err = pullSession.RunLoop()
|
||
|
if err != nil {
|
||
6 years ago
|
log.Debugf("PullSession loop done. [%s] err=%v", pullSession.UniqueKey, err)
|
||
6 years ago
|
return
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
}
|
||
|
|
||
|
func (group *Group) IsTotalEmpty() bool {
|
||
|
group.mutex.Lock()
|
||
|
defer group.mutex.Unlock()
|
||
|
return group.pullSession == nil && len(group.subSessionList) == 0
|
||
|
}
|
||
|
|
||
6 years ago
|
func (group *Group) ReadHTTPRespHeaderCB() {
|
||
|
//log.Debugf("ReadHTTPRespHeaderCb. [%s]", group.UniqueKey)
|
||
6 years ago
|
}
|
||
|
|
||
6 years ago
|
func (group *Group) ReadFlvHeaderCB(flvHeader []byte) {
|
||
6 years ago
|
//log.Debugf("ReadFlvHeaderCb. [%s]", group.UniqueKey)
|
||
6 years ago
|
}
|
||
|
|
||
6 years ago
|
func (group *Group) ReadTagCB(tag *httpflv.Tag) {
|
||
6 years ago
|
//log.Debug(header.t, header.timestamp)
|
||
6 years ago
|
group.mutex.Lock()
|
||
|
defer group.mutex.Unlock()
|
||
|
// TODO chef: assume that write fast and would not block
|
||
|
for session := range group.subSessionList {
|
||
6 years ago
|
if session.HasKeyFrame {
|
||
|
session.WritePacket(tag.Raw)
|
||
|
} else {
|
||
6 years ago
|
if tag.IsMetadata() || tag.IsAVCKeySeqHeader() || tag.IsAACSeqHeader() || tag.IsAVCKeyNalu() {
|
||
|
if tag.IsAVCKeyNalu() {
|
||
6 years ago
|
session.HasKeyFrame = true
|
||
|
}
|
||
|
session.WritePacket(tag.Raw)
|
||
|
}
|
||
|
}
|
||
6 years ago
|
}
|
||
6 years ago
|
group.gopCache.Push(tag)
|
||
6 years ago
|
}
|
||
6 years ago
|
|
||
|
func (group *Group) disposePullSession(err error) {
|
||
|
group.pullSession.Dispose(err)
|
||
|
group.pullSession = nil
|
||
|
group.gopCache.ClearAll()
|
||
|
}
|
||
|
|
||
|
func (group *Group) isInExist() bool {
|
||
|
return group.pullSession != nil
|
||
|
}
|