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/rtsp/server_command_session.go

344 lines
11 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package rtsp
import (
"bufio"
"fmt"
"net"
"strings"
"github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/sdp"
"github.com/q191201771/naza/pkg/nazahttp"
"github.com/q191201771/naza/pkg/nazalog"
)
type ServerCommandSessionObserver interface {
// @brief Announce阶段回调
// @return 如果返回false则表示上层要强制关闭这个推流请求
OnNewRTSPPubSession(session *PubSession) bool
// @brief Describe阶段回调
// @return ok 如果返回false则表示上层要强制关闭这个拉流请求
// @return sdp
OnNewRTSPSubSessionDescribe(session *SubSession) (ok bool, sdp []byte)
// @brief Describe阶段回调
// @return ok 如果返回false则表示上层要强制关闭这个拉流请求
OnNewRTSPSubSessionPlay(session *SubSession) bool
}
type ServerCommandSession struct {
uniqueKey string // const after ctor
observer ServerCommandSessionObserver // const after ctor
conn connection.Connection
prevConnStat connection.Stat
staleStat *connection.Stat
stat base.StatSession
pubSession *PubSession
subSession *SubSession
}
func NewServerCommandSession(observer ServerCommandSessionObserver, conn net.Conn) *ServerCommandSession {
uk := base.GenUKRTSPServerCommandSession()
s := &ServerCommandSession{
uniqueKey: uk,
observer: observer,
conn: connection.New(conn, func(option *connection.Option) {
option.ReadBufSize = serverCommandSessionReadBufSize
}),
}
nazalog.Infof("[%s] lifecycle new rtsp ServerSession. session=%p, laddr=%s, raddr=%s", uk, s, conn.LocalAddr().String(), conn.RemoteAddr().String())
return s
}
func (session *ServerCommandSession) RunLoop() error {
return session.runCmdLoop()
}
func (session *ServerCommandSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose rtsp ServerCommandSession. session=%p", session.uniqueKey, session)
return session.conn.Close()
}
// 使用RTSP TCP命令连接向对端发送RTP数据
func (session *ServerCommandSession) WriteInterleavedPacket(packet []byte, channel int) error {
_, err := session.conn.Write(packInterleaved(channel, packet))
return err
}
func (session *ServerCommandSession) RemoteAddr() string {
return session.conn.RemoteAddr().String()
}
func (session *ServerCommandSession) UpdateStat(intervalSec uint32) {
currStat := session.conn.GetStat()
rDiff := currStat.ReadBytesSum - session.prevConnStat.ReadBytesSum
session.stat.Bitrate = int(rDiff * 8 / 1024 / uint64(intervalSec))
wDiff := currStat.WroteBytesSum - session.prevConnStat.WroteBytesSum
session.stat.Bitrate = int(wDiff * 8 / 1024 / uint64(intervalSec))
session.prevConnStat = currStat
}
func (session *ServerCommandSession) GetStat() base.StatSession {
connStat := session.conn.GetStat()
session.stat.ReadBytesSum = connStat.ReadBytesSum
session.stat.WroteBytesSum = connStat.WroteBytesSum
return session.stat
}
func (session *ServerCommandSession) IsAlive() (readAlive, writeAlive bool) {
currStat := session.conn.GetStat()
if session.staleStat == nil {
session.staleStat = new(connection.Stat)
*session.staleStat = currStat
return true, true
}
readAlive = !(currStat.ReadBytesSum-session.staleStat.ReadBytesSum == 0)
writeAlive = !(currStat.WroteBytesSum-session.staleStat.WroteBytesSum == 0)
*session.staleStat = currStat
return
}
func (session *ServerCommandSession) UniqueKey() string {
return session.uniqueKey
}
func (session *ServerCommandSession) runCmdLoop() error {
var r = bufio.NewReader(session.conn)
Loop:
for {
isInterleaved, packet, channel, err := readInterleaved(r)
if err != nil {
nazalog.Errorf("[%s] read interleaved error. err=%+v", session.uniqueKey, err)
break Loop
}
if isInterleaved {
if session.pubSession != nil {
session.pubSession.HandleInterleavedPacket(packet, int(channel))
} 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)
break Loop
}
continue
}
// 读取一个message
requestCtx, err := nazahttp.ReadHTTPRequestMessage(r)
if err != nil {
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))
var handleMsgErr error
switch requestCtx.Method {
case MethodOptions:
// pub, sub
handleMsgErr = session.handleOptions(requestCtx)
case MethodAnnounce:
// pub
handleMsgErr = session.handleAnnounce(requestCtx)
case MethodDescribe:
// sub
handleMsgErr = session.handleDescribe(requestCtx)
case MethodSetup:
// pub, sub
handleMsgErr = session.handleSetup(requestCtx)
case MethodRecord:
// pub
handleMsgErr = session.handleRecord(requestCtx)
case MethodPlay:
// sub
handleMsgErr = session.handlePlay(requestCtx)
case MethodTeardown:
// pub
handleMsgErr = session.handleTeardown(requestCtx)
break Loop
default:
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)
break
}
}
_ = session.conn.Close()
nazalog.Debugf("[%s] < handleTCPConnect.", session.uniqueKey)
return nil
}
func (session *ServerCommandSession) handleOptions(requestCtx nazahttp.HTTPReqMsgCtx) error {
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)
urlCtx, err := base.ParseRTSPURL(requestCtx.URI)
if err != nil {
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)
return err
}
session.pubSession = NewPubSession(urlCtx, session)
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)
return ErrRTSP
}
resp := PackResponseAnnounce(requestCtx.Headers[HeaderCSeq])
_, err = session.conn.Write([]byte(resp))
return err
}
func (session *ServerCommandSession) handleDescribe(requestCtx nazahttp.HTTPReqMsgCtx) error {
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)
return err
}
session.subSession = NewSubSession(urlCtx, session)
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)
return ErrRTSP
}
sdpLogicCtx, _ := sdp.ParseSDP2LogicContext(rawSDP)
session.subSession.InitWithSDP(rawSDP, sdpLogicCtx)
resp := PackResponseDescribe(requestCtx.Headers[HeaderCSeq], string(rawSDP))
_, err = session.conn.Write([]byte(resp))
return err
}
// 一次SETUP对应一路流音频或视频
func (session *ServerCommandSession) handleSetup(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R SETUP", session.uniqueKey)
remoteAddr := session.conn.RemoteAddr().String()
host, _, _ := net.SplitHostPort(remoteAddr)
// 是否为interleaved模式
htv := requestCtx.Headers[HeaderTransport]
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)
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)
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)
return err
}
} else {
nazalog.Errorf("[%s] setup but session not exist.", session.uniqueKey)
return ErrRTSP
}
resp := PackResponseSetup(requestCtx.Headers[HeaderCSeq], htv)
_, err = session.conn.Write([]byte(resp))
return err
}
rRTPPort, rRTCPPort, err := parseClientPort(requestCtx.Headers[HeaderTransport])
if err != nil {
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)
return err
}
nazalog.Debugf("[%s] init conn. lRTPPort=%d, lRTCPPort=%d, rRTPPort=%d, rRTCPPort=%d",
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)
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)
return err
}
htv = fmt.Sprintf(HeaderTransportServerPlayTmpl, rRTPPort, rRTCPPort, lRTPPort, lRTCPPort)
} else {
nazalog.Errorf("[%s] setup but session not exist.", session.uniqueKey)
return ErrRTSP
}
resp := PackResponseSetup(requestCtx.Headers[HeaderCSeq], htv)
_, err = session.conn.Write([]byte(resp))
return err
}
func (session *ServerCommandSession) handleRecord(requestCtx nazahttp.HTTPReqMsgCtx) error {
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)
if ok := session.observer.OnNewRTSPSubSessionPlay(session.subSession); !ok {
return ErrRTSP
}
resp := PackResponsePlay(requestCtx.Headers[HeaderCSeq])
_, err := session.conn.Write([]byte(resp))
return err
}
func (session *ServerCommandSession) handleTeardown(requestCtx nazahttp.HTTPReqMsgCtx) error {
nazalog.Infof("[%s] < R TEARDOWN", session.uniqueKey)
resp := PackResponseTeardown(requestCtx.Headers[HeaderCSeq])
_, err := session.conn.Write([]byte(resp))
return err
}