*) 新增 rtmp.PubSession 和 rtmp.SubSession *) 新增 rtmp/handshake_test.go *) 新增 rtmp/chunkdivider.go。用于将业务层的message切割成rtmp chunk

pull/200/head
q191201771 6 years ago
parent d9d752ce52
commit 8f52174fbd

@ -47,6 +47,7 @@ TODO 日志配置文件说明
#### 依赖
* cihub/seelog
* stretchr/testify/assert
#### roadmap

@ -16,11 +16,11 @@ type Group struct {
streamName string
exitChan chan bool
rtmpPubSession *rtmp.ServerSession
rtmpPubSession *rtmp.PubSession
rtmpPullSession *rtmp.PullSession
httpFlvPullSession *httpflv.PullSession
httpFlvSubSessionList map[*httpflv.SubSession]struct{}
rtmpSubSessionList map[*rtmp.ServerSession]struct{}
rtmpSubSessionList map[*rtmp.SubSession]struct{}
turnToEmptyTick int64 // trace while sub session list turn to empty
gopCache *httpflv.GOPCache
mutex sync.Mutex
@ -38,7 +38,7 @@ func NewGroup(appName string, streamName string, config *Config) *Group {
streamName: streamName,
exitChan: make(chan bool),
httpFlvSubSessionList: make(map[*httpflv.SubSession]struct{}),
rtmpSubSessionList: make(map[*rtmp.ServerSession]struct{}),
rtmpSubSessionList: make(map[*rtmp.SubSession]struct{}),
gopCache: httpflv.NewGOPCache(config.GOPCacheNum),
UniqueKey: uk,
}
@ -124,7 +124,7 @@ func (group *Group) AddHTTPFlvSubSession(session *httpflv.SubSession) {
}
}
func (group *Group) AddRTMPSubSession(session *rtmp.ServerSession) {
func (group *Group) AddRTMPSubSession(session *rtmp.SubSession) {
group.mutex.Lock()
defer group.mutex.Unlock()
log.Debugf("add SubSession into group. [%s]", session.UniqueKey)
@ -132,7 +132,7 @@ func (group *Group) AddRTMPSubSession(session *rtmp.ServerSession) {
group.turnToEmptyTick = 0
}
func (group *Group) AddRTMPPubSession(session *rtmp.ServerSession) {
func (group *Group) AddRTMPPubSession(session *rtmp.PubSession) {
// TODO chef: 如果已经存在输入,应该拒绝掉这次推流
group.mutex.Lock()
defer group.mutex.Unlock()
@ -170,15 +170,12 @@ func (group *Group) IsTotalEmpty() bool {
}
func (group *Group) ReadHTTPRespHeaderCB() {
//log.Debugf("ReadHTTPRespHeaderCb. [%s]", group.UniqueKey)
}
func (group *Group) ReadFlvHeaderCB(flvHeader []byte) {
//log.Debugf("ReadFlvHeaderCb. [%s]", group.UniqueKey)
}
func (group *Group) ReadTagCB(tag *httpflv.Tag) {
//log.Debug(header.t, header.timestamp)
group.mutex.Lock()
defer group.mutex.Unlock()
// TODO chef: assume that write fast and would not block
@ -198,11 +195,16 @@ func (group *Group) ReadTagCB(tag *httpflv.Tag) {
group.gopCache.Push(tag)
}
func (group *Group) ReadAVMessageCB(t int, timestampAbs int, message []byte) {
func (group *Group) ReadAVMessageCB(header rtmp.Header, timestampAbs int, message []byte) {
//log.Info(t)
group.mutex.Lock()
defer group.mutex.Unlock()
flvTag := httpflv.PackHTTPFlvTag(uint8(t), timestampAbs, message)
//for session := range group.rtmpSubSessionList {
//
//}
flvTag := httpflv.PackHTTPFlvTag(uint8(header.MsgTypeID), timestampAbs, message)
for session := range group.httpFlvSubSessionList {
if session.HasKeyFrame {
session.WritePacket(flvTag)

@ -114,16 +114,6 @@ func (session *PullSession) Dispose(err error) {
})
}
//func (session *PullSession) GetStat() (now PullSessionStat, diff PullSessionStat) {
// session.statMutex.Lock()
// defer session.statMutex.Unlock()
// now = session.stat
// diff.ReadCount = session.stat.ReadCount - session.prevStat.ReadCount
// diff.ReadByte = session.stat.ReadByte - session.prevStat.ReadByte
// session.prevStat = session.stat
// return
//}
func (session *PullSession) runReadLoop() error {
if err := session.readHTTPRespHeader(); err != nil {
return err

@ -27,6 +27,8 @@ var flvHeaderBuf13 = []byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0x0, 0x0, 0x0, 0x09, 0
var wChanSize = 1024 // TODO chef: 1024
type SubSession struct {
UniqueKey string
ConnStat util.ConnStat
writeTimeout int64
@ -45,8 +47,6 @@ type SubSession struct {
closeOnce sync.Once
exitChan chan struct{}
hasClosedFlag uint32
UniqueKey string
}
func NewSubSession(conn net.Conn, writeTimeout int64) *SubSession {
@ -142,7 +142,6 @@ func (session *SubSession) WritePacket(pkt []byte) {
if session.hasClosed() {
return
}
//session.addWannaWriteStat(len(pkt))
for {
select {
case session.wChan <- pkt:

@ -81,13 +81,13 @@ func (manager *Manager) NewHTTPFlvSubSessionCB(session *httpflv.SubSession) {
group.PullIfNeeded()
}
func (manager *Manager) NewRTMPSubSessionCB(session *rtmp.ServerSession) {
func (manager *Manager) NewRTMPSubSessionCB(session *rtmp.SubSession) {
group := manager.getOrCreateGroup(session.AppName, session.StreamName)
group.AddRTMPSubSession(session)
group.PullIfNeeded()
}
func (manager *Manager) NewRTMPPubSessionCB(session *rtmp.ServerSession) {
func (manager *Manager) NewRTMPPubSessionCB(session *rtmp.PubSession) {
group := manager.getOrCreateGroup(session.AppName, session.StreamName)
group.AddRTMPPubSession(session)
}

@ -5,29 +5,31 @@ import (
"io"
)
type Composer struct {
// 读取chunk并组织chunk生成message返回给上层
type ChunkComposer struct {
peerChunkSize int
csid2stream map[int]*Stream
}
func NewComposer() *Composer {
return &Composer{
func NewChunkComposer() *ChunkComposer {
return &ChunkComposer{
peerChunkSize: defaultChunkSize,
csid2stream: make(map[int]*Stream),
}
}
func (c *Composer) SetPeerChunkSize(val int) {
func (c *ChunkComposer) SetPeerChunkSize(val int) {
c.peerChunkSize = val
}
func (c *Composer) GetPeerChunkSize() int {
func (c *ChunkComposer) GetPeerChunkSize() int {
return c.peerChunkSize
}
type CompleteMessageCB func(stream *Stream) error
func (c *Composer) RunLoop(reader io.Reader, cb CompleteMessageCB) error {
func (c *ChunkComposer) RunLoop(reader io.Reader, cb CompleteMessageCB) error {
bootstrap := make([]byte, 11)
for {
@ -65,7 +67,7 @@ func (c *Composer) RunLoop(reader io.Reader, cb CompleteMessageCB) error {
stream.header.timestamp = int(bele.BEUint24(bootstrap))
stream.timestampAbs = stream.header.timestamp
stream.msgLen = int(bele.BEUint24(bootstrap[3:]))
stream.header.msgTypeID = int(bootstrap[6])
stream.header.MsgTypeID = int(bootstrap[6])
stream.header.msgStreamID = int(bele.LEUint32(bootstrap[7:]))
stream.msg.reserve(stream.msgLen)
@ -76,7 +78,7 @@ func (c *Composer) RunLoop(reader io.Reader, cb CompleteMessageCB) error {
stream.header.timestamp = int(bele.BEUint24(bootstrap))
stream.timestampAbs += stream.header.timestamp
stream.msgLen = int(bele.BEUint24(bootstrap[3:]))
stream.header.msgTypeID = int(bootstrap[6])
stream.header.MsgTypeID = int(bootstrap[6])
stream.msg.reserve(stream.msgLen)
case 2:
@ -125,10 +127,12 @@ func (c *Composer) RunLoop(reader io.Reader, cb CompleteMessageCB) error {
stream.msg.produced(neededSize)
if stream.msg.len() == stream.msgLen {
if stream.header.msgTypeID == typeidSetChunkSize {
if stream.header.MsgTypeID == typeidSetChunkSize {
val := int(bele.BEUint32(stream.msg.buf))
c.SetPeerChunkSize(val)
}
stream.header.csid = csid
stream.header.msgLen = stream.msgLen
if err := cb(stream); err != nil {
return err
}
@ -140,7 +144,7 @@ func (c *Composer) RunLoop(reader io.Reader, cb CompleteMessageCB) error {
}
}
func (c *Composer) getOrCreateStream(csid int) *Stream {
func (c *ChunkComposer) getOrCreateStream(csid int) *Stream {
stream, exist := c.csid2stream[csid]
if !exist {
stream = NewStream()

@ -0,0 +1,3 @@
package rtmp
//func Message2Chunks(message []byte, )

@ -0,0 +1,15 @@
package rtmp
type PullSession struct {
*ClientSession
}
func NewPullSession(obs AVMessageObserver, connectTimeout int64) *PullSession {
return &PullSession{
ClientSession: NewClientSession(CSTPullSession, obs, connectTimeout),
}
}
func (s *PullSession) Pull(rawURL string) error {
return s.Do(rawURL)
}

@ -12,16 +12,18 @@ import (
"time"
)
var chunkSize = 4096
// rtmp客户端类型连接的底层实现
// rtmp包的使用者应该优先使用基于ClientSession实现的PushSession和PullSession
type ClientSession struct {
t ClientSessionType
obs PullSessionObserver
obs AVMessageObserver // only for PullSession
connectTimeout int64
doResultChan chan struct{}
errChan chan error
packer *MessagePacker
composer *Composer
chunkComposer *ChunkComposer
url *url.URL
tcURL string
appName string
@ -43,7 +45,7 @@ const (
)
// set <obs> if <t> equal CSTPullSession
func NewClientSession(t ClientSessionType, obs PullSessionObserver, connectTimeout int64) *ClientSession {
func NewClientSession(t ClientSessionType, obs AVMessageObserver, connectTimeout int64) *ClientSession {
var uk string
switch t {
case CSTPullSession:
@ -61,7 +63,7 @@ func NewClientSession(t ClientSessionType, obs PullSessionObserver, connectTimeo
doResultChan: make(chan struct{}),
errChan: make(chan error),
packer: NewMessagePacker(),
composer: NewComposer(),
chunkComposer: NewChunkComposer(),
UniqueKey: util.GenUniqueKey(uk),
}
}
@ -107,11 +109,11 @@ func (s *ClientSession) WaitLoop() error {
}
func (s *ClientSession) runReadLoop() error {
return s.composer.RunLoop(s.rb, s.doMsg)
return s.chunkComposer.RunLoop(s.rb, s.doMsg)
}
func (s *ClientSession) doMsg(stream *Stream) error {
switch stream.header.msgTypeID {
switch stream.header.MsgTypeID {
case typeidWinAckSize:
fallthrough
case typeidBandwidth:
@ -121,15 +123,15 @@ func (s *ClientSession) doMsg(stream *Stream) error {
case typeidCommandMessageAMF0:
return s.doCommandMessage(stream)
case typeidUserControl:
log.Warn("user control message. ignore.")
log.Warn("read user control message, ignore. [%s]", s.UniqueKey)
case typeidDataMessageAMF0:
return s.doDataMessageAMF0(stream)
case typeidAudio:
fallthrough
case typeidVideo:
s.obs.ReadAVMessageCB(stream.header.msgTypeID, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
s.obs.ReadAVMessageCB(stream.header, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
default:
log.Errorf("unknown msg type id. typeid=%d", stream.header.msgTypeID)
log.Errorf("read unknown msg type id. [%s] typeid=%d", s.UniqueKey, stream.header)
panic(0)
}
return nil
@ -149,7 +151,7 @@ func (s *ClientSession) doDataMessageAMF0(stream *Stream) error {
log.Error(val)
log.Error(hex.Dump(stream.msg.buf[stream.msg.b:stream.msg.e]))
}
s.obs.ReadAVMessageCB(stream.header.msgTypeID, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
s.obs.ReadAVMessageCB(stream.header, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
return nil
}
@ -166,13 +168,13 @@ func (s *ClientSession) doCommandMessage(stream *Stream) error {
switch cmd {
case "onBWDone":
log.Warn("-----> onBWDone. ignore")
log.Warnf("-----> onBWDone. ignore. [%s]", s.UniqueKey)
case "_result":
return s.doResultMessage(stream, tid)
case "onStatus":
return s.doOnStatusMessage(stream, tid)
default:
log.Errorf("unknown cmd. cmd=%s", cmd)
log.Errorf("read unknown cmd. [%s] cmd=%s", s.UniqueKey, cmd)
}
return nil
@ -194,18 +196,18 @@ func (s *ClientSession) doOnStatusMessage(stream *Stream, tid int) error {
case CSTPushSession:
switch code {
case "NetStream.Publish.Start":
log.Info("-----> onStatus('NetStream.Publish.Start')")
log.Infof("-----> onStatus('NetStream.Publish.Start'). [%s]", s.UniqueKey)
s.notifyDoResultSucc()
default:
log.Errorf("unknown code. code=%s", code)
log.Errorf("read on status message but code field unknown. [%s] code=%s", s.UniqueKey, code)
}
case CSTPullSession:
switch code {
case "NetStream.Play.Start":
log.Info("-----> onStatus('NetStream.Play.Start')")
log.Infof("-----> onStatus('NetStream.Play.Start'). [%s]", s.UniqueKey)
s.notifyDoResultSucc()
default:
log.Errorf("unknown code. code=%s", code)
log.Errorf("read on status message but code field unknown. [%s] code=%s", s.UniqueKey, code)
}
}
@ -229,12 +231,12 @@ func (s *ClientSession) doResultMessage(stream *Stream, tid int) error {
}
switch code {
case "NetConnection.Connect.Success":
log.Info("-----> _result(\"NetConnection.Connect.Success\")")
log.Infof("-----> _result(\"NetConnection.Connect.Success\"). [%s]", s.UniqueKey)
if err := s.packer.writeCreateStream(s.Conn); err != nil {
return err
}
default:
log.Errorf("unknown code. code=%s", code)
log.Errorf("unknown code. [%s] code=%s", s.UniqueKey, code)
}
case tidClientCreateStream:
err := stream.msg.readNull()
@ -245,7 +247,7 @@ func (s *ClientSession) doResultMessage(stream *Stream, tid int) error {
if err != nil {
return err
}
log.Info("-----> _result()")
log.Infof("-----> _result(). [%s]", s.UniqueKey)
switch s.t {
case CSTPullSession:
if err := s.packer.writePlay(s.Conn, s.streamName, sid); err != nil {
@ -257,7 +259,7 @@ func (s *ClientSession) doResultMessage(stream *Stream, tid int) error {
}
}
default:
log.Errorf("unknown tid. tid=%d", tid)
log.Errorf("unknown tid. [%s] tid=%d", s.UniqueKey, tid)
}
return nil
}
@ -268,17 +270,17 @@ func (s *ClientSession) doProtocolControlMessage(stream *Stream) error {
}
val := int(bele.BEUint32(stream.msg.buf))
switch stream.header.msgTypeID {
switch stream.header.MsgTypeID {
case typeidWinAckSize:
s.peerWinAckSize = val
log.Infof("-----> Window Acknowledgement Size: %d", s.peerWinAckSize)
log.Infof("-----> Window Acknowledgement Size: %d. [%s]", s.peerWinAckSize, s.UniqueKey)
case typeidBandwidth:
log.Warn("-----> Set Peer Bandwidth. ignore")
log.Warnf("-----> Set Peer Bandwidth. ignore. [%s]", s.UniqueKey)
case typeidSetChunkSize:
// composer内部会自动更新peer chunk size.
log.Infof("-----> Set Chunk Size %d", val)
log.Infof("-----> Set Chunk Size %d. [%s]", val, s.UniqueKey)
default:
log.Errorf("unknown msg type id. id=%d", stream.header.msgTypeID)
log.Errorf("unknown msg type id. [%s] id=%d", s.UniqueKey, stream.header.MsgTypeID)
}
return nil
}

@ -12,6 +12,8 @@ import (
// TODO chef: doc
// TODO chef: HandshakeClient with complex mode
const version = uint8(3)
const (

@ -0,0 +1,26 @@
package rtmp
import (
"bytes"
"github.com/stretchr/testify/assert"
"testing"
)
func TestAll(t *testing.T) {
var err error
var hc HandshakeClient
var hs HandshakeServer
b := &bytes.Buffer{}
err = hc.WriteC0C1(b)
assert.Equal(t, nil, err, "fxxk.")
err = hs.ReadC0C1(b)
assert.Equal(t, nil, err, "fxxk.")
err = hs.WriteS0S1S2(b)
assert.Equal(t, nil, err, "fxxk.")
err = hc.ReadS0S1S2(b)
assert.Equal(t, nil, err, "fxxk.")
err = hc.WriteC2(b)
assert.Equal(t, nil, err, "fxxk.")
err = hs.ReadC2(b)
assert.Equal(t, nil, err, "fxxk.")
}

@ -1,23 +0,0 @@
package rtmp
var chunkSize = 4096
type PullSessionObserver interface {
// @param t: 8 audio, 9 video, 18 meta
// after cb, PullSession will use <message>
ReadAVMessageCB(t int, timestampAbs int, message []byte)
}
type PullSession struct {
*ClientSession
}
func NewPullSession(obs PullSessionObserver, connectTimeout int64) *PullSession {
return &PullSession{
ClientSession: NewClientSession(CSTPullSession, obs, connectTimeout),
}
}
func (s *PullSession) Pull(rawURL string) error {
return s.Do(rawURL)
}

@ -6,25 +6,31 @@ import (
var rtmpErr = errors.New("rtmp error")
var csidProtocolControl = 2
var csidOverConnection = 3
var csidOverStream = 5
var typeidSetChunkSize = 1
var typeidUserControl = 4
var typeidWinAckSize = 5
var typeidBandwidth = 6
var typeidAudio = 8
var typeidVideo = 9
var typeidDataMessageAMF0 = 18 // meta
var typeidCommandMessageAMF0 = 20
var tidClientConnect = 1
var tidClientCreateStream = 2
var tidClientPlay = 3
var tidClientPublish = 3
var maxTimestampInMessageHeader = 0xFFFFFF
const (
csidProtocolControl = 2
csidOverConnection = 3
csidOverStream = 5
)
const (
typeidSetChunkSize = 1
typeidUserControl = 4
typeidWinAckSize = 5
typeidBandwidth = 6
typeidAudio = 8
typeidVideo = 9
typeidDataMessageAMF0 = 18 // meta
typeidCommandMessageAMF0 = 20
)
const (
tidClientConnect = 1
tidClientCreateStream = 2
tidClientPlay = 3
tidClientPublish = 3
)
const maxTimestampInMessageHeader = 0xFFFFFF
var defaultChunkSize = 128
@ -36,3 +42,11 @@ var peerBandwidth = 5000000
var localChunkSize = 4096
var msid = 1
// 接收到音视频类型数据时的回调函数。目前被PullSession以及PubSession使用。
type AVMessageObserver interface {
// @param header:
// @param timestampAbs: 绝对时间戳
// @param message: 不包含头内容。回调结束后PullSession会继续使用这块内存。
ReadAVMessageCB(header Header, timestampAbs int, message []byte)
}

@ -6,8 +6,8 @@ import (
)
type ServerObserver interface {
NewRTMPPubSessionCB(session *ServerSession)
NewRTMPSubSessionCB(session *ServerSession)
NewRTMPPubSessionCB(session *PubSession)
NewRTMPSubSessionCB(session *SubSession)
}
type Server struct {
@ -52,14 +52,14 @@ func (server *Server) handleConnect(conn net.Conn) {
session.RunLoop()
}
func (server *Server) NewRTMPPubSessionCB(session *ServerSession) {
func (server *Server) NewRTMPPubSessionCB(session *PubSession) {
server.obs.NewRTMPPubSessionCB(session)
}
func (server *Server) NewRTMPSubSessionCB(session *ServerSession) {
func (server *Server) NewRTMPSubSessionCB(session *SubSession) {
server.obs.NewRTMPSubSessionCB(session)
}
func (server *Server) ReadAVMessageCB(t int, timestampAbs int, message []byte) {
func (server *Server) ReadAVMessageCB(header Header, timestampAbs int, message []byte) {
}

@ -0,0 +1,15 @@
package rtmp
type PubSession struct {
*ServerSession
}
func NewPubSession(ss *ServerSession) *PubSession {
return &PubSession{
ss,
}
}
func (s *ServerSession) SetAVMessageObserver(obs AVMessageObserver) {
s.avObs = obs
}

@ -6,17 +6,16 @@ import (
"github.com/q191201771/lal/log"
"github.com/q191201771/lal/util"
"net"
"strings"
)
type ServerSessionObserver interface {
NewRTMPPubSessionCB(session *ServerSession)
NewRTMPSubSessionCB(session *ServerSession)
}
// TODO chef: PubSession SubSession
// TODO chef: 没有进化成Pub Sub时的超时释放
type AVMessageObserver interface {
// @param t: 8 audio, 9 video, 18 meta
// after cb, PullSession will use <message>
ReadAVMessageCB(t int, timestampAbs int, message []byte)
type ServerSessionObserver interface {
NewRTMPPubSessionCB(session *PubSession) // 上层代码应该在这个事件回调中注册音视频数据的监听
NewRTMPSubSessionCB(session *SubSession)
}
type ServerSessionType int
@ -32,27 +31,29 @@ type ServerSession struct {
StreamName string
UniqueKey string
obs ServerSessionObserver
avObs AVMessageObserver
conn net.Conn
rb *bufio.Reader
wb *bufio.Writer
t ServerSessionType
hs HandshakeServer
composer *Composer
packer *MessagePacker
obs ServerSessionObserver
conn net.Conn
rb *bufio.Reader
wb *bufio.Writer
t ServerSessionType
hs HandshakeServer
chunkComposer *ChunkComposer
packer *MessagePacker
// for PubSession
avObs AVMessageObserver
}
func NewServerSession(obs ServerSessionObserver, conn net.Conn) *ServerSession {
return &ServerSession{
obs: obs,
conn: conn,
rb: bufio.NewReaderSize(conn, readBufSize),
wb: bufio.NewWriterSize(conn, writeBufSize),
t: ServerSessionTypeInit,
composer: NewComposer(),
packer: NewMessagePacker(),
UniqueKey: util.GenUniqueKey("RTMPSERVER"),
obs: obs,
conn: conn,
rb: bufio.NewReaderSize(conn, readBufSize),
wb: bufio.NewWriterSize(conn, writeBufSize),
t: ServerSessionTypeInit,
chunkComposer: NewChunkComposer(),
packer: NewMessagePacker(),
UniqueKey: util.GenUniqueKey("RTMPSERVER"),
}
}
@ -60,11 +61,11 @@ func (s *ServerSession) RunLoop() error {
if err := s.handshake(); err != nil {
return err
}
return s.composer.RunLoop(s.rb, s.doMsg)
return s.chunkComposer.RunLoop(s.rb, s.doMsg)
}
func (s *ServerSession) SetAVMessageObserver(obs AVMessageObserver) {
s.avObs = obs
func (s *ServerSession) WriteMessage() {
}
func (s *ServerSession) handshake() error {
@ -82,7 +83,7 @@ func (s *ServerSession) handshake() error {
func (s *ServerSession) doMsg(stream *Stream) error {
//log.Debugf("%d %d %v", stream.header.msgTypeID, stream.msgLen, stream.header)
switch stream.header.msgTypeID {
switch stream.header.MsgTypeID {
case typeidSetChunkSize:
// TODO chef:
case typeidCommandMessageAMF0:
@ -92,13 +93,22 @@ func (s *ServerSession) doMsg(stream *Stream) error {
case typeidAudio:
fallthrough
case typeidVideo:
s.avObs.ReadAVMessageCB(stream.header.msgTypeID, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
if s.t != ServerSessionTypePub {
log.Error("read audio/video message but server session not pub type.")
return rtmpErr
}
s.avObs.ReadAVMessageCB(stream.header, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
}
return nil
}
func (s *ServerSession) doDataMessageAMF0(stream *Stream) error {
if s.t != ServerSessionTypePub {
log.Error("read audio/video message but server session not pub type.")
return rtmpErr
}
val, err := stream.msg.peekStringWithType()
if err != nil {
return err
@ -127,7 +137,7 @@ func (s *ServerSession) doDataMessageAMF0(stream *Stream) error {
return nil
}
s.avObs.ReadAVMessageCB(stream.header.msgTypeID, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
s.avObs.ReadAVMessageCB(stream.header, stream.timestampAbs, stream.msg.buf[stream.msg.b:stream.msg.e])
return nil
}
@ -219,7 +229,10 @@ func (s *ServerSession) doPublish(tid int, stream *Stream) error {
return err
}
s.t = ServerSessionTypePub
s.obs.NewRTMPPubSessionCB(s)
newUniqueKey := strings.Replace(s.UniqueKey, "RTMPSERVER", "RTMPPUB", 1)
log.Infof("session unique key upgrade. %s -> %s", s.UniqueKey, newUniqueKey)
s.UniqueKey = newUniqueKey
s.obs.NewRTMPPubSessionCB(NewPubSession(s))
return nil
}
@ -238,6 +251,9 @@ func (s *ServerSession) doPlay(tid int, stream *Stream) error {
return err
}
s.t = ServerSessionTypeSub
s.obs.NewRTMPSubSessionCB(s)
newUniqueKey := strings.Replace(s.UniqueKey, "RTMPSERVER", "RTMPSUB", 1)
log.Infof("session unique key upgrade. %s -> %s", s.UniqueKey, newUniqueKey)
s.UniqueKey = newUniqueKey
s.obs.NewRTMPSubSessionCB(NewSubSession(s))
return nil
}

@ -0,0 +1,11 @@
package rtmp
type SubSession struct {
*ServerSession
}
func NewSubSession(ss *ServerSession) *SubSession {
return &SubSession{
ss,
}
}

@ -5,11 +5,12 @@ import "github.com/q191201771/lal/log"
var initMsgLen = 4096
type Header struct {
//csid int
//msgLen int
csid int
msgLen int
timestamp int
msgTypeID int
timestamp int // NOTICE 是header中的时间戳可能是绝对的也可能是相对的。
// 如果需要绝对时间戳应该使用Stream中的timestampAbs
MsgTypeID int // 8 audio 9 video 18 metadata
msgStreamID int
}

Loading…
Cancel
Save