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.
lal/pkg/gb28181/pub_session.go

266 lines
6.6 KiB
Go

// Copyright 2022, 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 gb28181
import (
"fmt"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/nazanet"
"io"
"net"
"sync"
)
type OnReadPacket func(b []byte)
type PubSession struct {
unpacker *PsUnpacker
streamName string
hookOnReadPacket OnReadPacket
isTcpFlag bool
disposeOnce sync.Once
udpConn *nazanet.UdpConnection
listener net.Listener
tcpConn net.Conn
sessionStat base.BasicSessionStat
}
func NewPubSession() *PubSession {
return &PubSession{
unpacker: NewPsUnpacker(),
sessionStat: base.NewBasicSessionStat(base.SessionTypePsPub, ""),
}
}
// WithOnAvPacket 设置音视频的回调。
//
// @param onAvPacket: 见 PsUnpacker.WithOnAvPacket 的注释
func (session *PubSession) WithOnAvPacket(onAvPacket base.OnAvPacketFunc) *PubSession {
session.unpacker.WithOnAvPacket(onAvPacket)
return session
}
func (session *PubSession) WithStreamName(streamName string) *PubSession {
session.streamName = streamName
return session
}
// WithHookReadPacket
//
// 将接收的数据返回给上层。
// 注意,底层的解析逻辑依然走。
// 可以用这个方式来截取数据进行调试。
func (session *PubSession) WithHookReadPacket(fn OnReadPacket) *PubSession {
session.hookOnReadPacket = fn
return session
}
// Listen 非阻塞函数
//
// 注意,当`port`参数为0时内部会自动选择一个可用端口监听并通过返回值返回该端口
func (session *PubSession) Listen(port int, isTcpFlag bool) (int, error) {
session.isTcpFlag = isTcpFlag
if isTcpFlag {
return session.listenTcp(port)
}
return session.listenUdp(port)
}
// RunLoop 阻塞函数
func (session *PubSession) RunLoop() error {
if session.isTcpFlag {
return session.runLoopTcp()
}
return session.runLoopUdp()
}
// ----- IServerSessionLifecycle ---------------------------------------------------------------------------------------
func (session *PubSession) Dispose() error {
return session.dispose(nil)
}
// ----- ISessionUrlContext --------------------------------------------------------------------------------------------
func (session *PubSession) Url() string {
Log.Warnf("[%s] PubSession.Url() is not implemented", session.UniqueKey())
return "invalid"
}
func (session *PubSession) AppName() string {
Log.Warnf("[%s] PubSession.AppName() is not implemented", session.UniqueKey())
return "invalid"
}
func (session *PubSession) StreamName() string {
// 如果stream name没有设置则使用session的unique key作为stream name
if session.streamName == "" {
return session.UniqueKey()
}
return session.streamName
}
func (session *PubSession) RawQuery() string {
Log.Warnf("[%s] PubSession.RawQuery() is not implemented", session.UniqueKey())
return "invalid"
}
// ----- IObject -------------------------------------------------------------------------------------------------------
func (session *PubSession) UniqueKey() string {
return session.sessionStat.UniqueKey()
}
// ----- ISessionStat --------------------------------------------------------------------------------------------------
func (session *PubSession) UpdateStat(intervalSec uint32) {
session.sessionStat.UpdateStat(intervalSec)
}
func (session *PubSession) GetStat() base.StatSession {
return session.sessionStat.GetStat()
}
func (session *PubSession) IsAlive() (readAlive, writeAlive bool) {
return session.sessionStat.IsAlive()
}
// ---------------------------------------------------------------------------------------------------------------------
func (session *PubSession) listenUdp(port int) (int, error) {
var err error
var uconn *net.UDPConn
var addr string
if port == 0 {
uconn, _, err = defaultUdpConnPoll.Acquire()
if err != nil {
return -1, err
}
port = uconn.LocalAddr().(*net.UDPAddr).Port
} else {
addr = fmt.Sprintf(":%d", port)
}
session.udpConn, err = nazanet.NewUdpConnection(func(option *nazanet.UdpConnectionOption) {
option.LAddr = addr
option.Conn = uconn
})
return port, err
}
func (session *PubSession) listenTcp(port int) (int, error) {
var err error
// TODO(chef): [refactor] 考虑挪到naza中udp在naza中有类似的实现 202209
if port == 0 {
for i := defaultPubSessionPortMin; i < defaultPubSessionPortMax; i++ {
addr := fmt.Sprintf(":%d", i)
if session.listener, err = net.Listen("tcp", addr); err == nil {
return int(i), nil
}
}
return 0, err
}
addr := fmt.Sprintf(":%d", port)
session.listener, err = net.Listen("tcp", addr)
return port, err
}
func (session *PubSession) runLoopUdp() error {
err := session.udpConn.RunLoop(func(b []byte, raddr *net.UDPAddr, err error) bool {
if len(b) == 0 && err != nil {
return false
}
session.feedPacket(b)
return true
})
return err
}
func (session *PubSession) runLoopTcp() error {
for {
conn, err := session.listener.Accept()
if err != nil {
nazalog.Debugf("[%s] stop accept. err=%+v", session.UniqueKey(), err)
return err
}
if session.tcpConn != nil {
nazalog.Warnf("[%s] tcp conn already exist, close the prev. err=%+v", session.UniqueKey(), err)
session.tcpConn.Close()
// TODO(chef): [fix] reset unpack 202209
}
session.tcpConn = conn
go func() {
lb := make([]byte, 2)
buf := nazabytes.NewBuffer(1500)
for {
if _, rErr := io.ReadFull(conn, lb); rErr != nil {
nazalog.Debugf("[%s] read failed. err=%+v", session.UniqueKey(), rErr)
break
}
length := int(bele.BeUint16(lb))
b := buf.ReserveBytes(length)
if _, rErr := io.ReadFull(conn, b); rErr != nil {
nazalog.Debugf("[%s] read failed. err=%+v", session.UniqueKey(), rErr)
break
}
session.feedPacket(b)
}
}()
}
}
func (session *PubSession) feedPacket(b []byte) {
if session.hookOnReadPacket != nil {
session.hookOnReadPacket(b)
}
session.sessionStat.AddReadBytes(len(b))
session.unpacker.FeedRtpPacket(b)
}
func (session *PubSession) dispose(err error) error {
var retErr error
session.disposeOnce.Do(func() {
Log.Infof("[%s] lifecycle dispose gb28181 PubSession. err=%+v", session.UniqueKey(), err)
if session.isTcpFlag {
if session.tcpConn == nil {
retErr = base.ErrSessionNotStarted
return
}
retErr = session.tcpConn.Close()
} else {
if session.udpConn == nil {
retErr = base.ErrSessionNotStarted
return
}
retErr = session.udpConn.Dispose()
}
session.unpacker.Dispose()
})
return retErr
}