2019-12-31 12:48:17 +00:00
package main
import (
2020-05-10 20:56:46 +00:00
"errors"
2019-12-31 12:48:17 +00:00
"fmt"
"io"
"net"
2020-02-15 17:25:29 +00:00
"os/exec"
2019-12-31 12:48:17 +00:00
"strings"
2020-06-21 09:30:30 +00:00
"time"
2019-12-31 12:48:17 +00:00
2020-01-20 09:21:05 +00:00
"github.com/aler9/gortsplib"
2020-07-12 15:24:12 +00:00
"github.com/pion/sdp"
2019-12-31 12:48:17 +00:00
)
2020-06-21 09:30:30 +00:00
const (
2020-07-13 07:45:45 +00:00
clientCheckStreamInterval = 5 * time . Second
clientReceiverReportInterval = 10 * time . Second
2020-06-21 09:30:30 +00:00
)
2020-07-12 10:34:35 +00:00
type serverClientEvent interface {
isServerClientEvent ( )
}
type serverClientEventFrameTcp struct {
frame * gortsplib . InterleavedFrame
}
func ( serverClientEventFrameTcp ) isServerClientEvent ( ) { }
2020-07-11 08:24:42 +00:00
type serverClientState int
2020-01-20 13:41:04 +00:00
const (
2020-07-13 07:45:45 +00:00
clientStateStarting serverClientState = iota
clientStateAnnounce
clientStatePrePlay
clientStatePlay
clientStatePreRecord
clientStateRecord
2020-01-20 13:41:04 +00:00
)
2020-07-11 08:24:42 +00:00
func ( cs serverClientState ) String ( ) string {
2020-05-21 19:46:22 +00:00
switch cs {
2020-07-13 07:45:45 +00:00
case clientStateStarting :
2020-05-21 19:46:22 +00:00
return "STARTING"
2020-07-13 07:45:45 +00:00
case clientStateAnnounce :
2020-05-21 19:46:22 +00:00
return "ANNOUNCE"
2020-07-13 07:45:45 +00:00
case clientStatePrePlay :
2020-05-21 19:46:22 +00:00
return "PRE_PLAY"
2020-07-13 07:45:45 +00:00
case clientStatePlay :
2020-05-21 19:46:22 +00:00
return "PLAY"
2020-07-13 07:45:45 +00:00
case clientStatePreRecord :
2020-05-21 19:46:22 +00:00
return "PRE_RECORD"
2020-07-13 07:45:45 +00:00
case clientStateRecord :
2020-05-21 19:46:22 +00:00
return "RECORD"
}
return "UNKNOWN"
}
2020-05-03 10:38:11 +00:00
type serverClient struct {
2020-07-12 10:34:35 +00:00
p * program
conn * gortsplib . ConnServer
state serverClientState
path string
authUser string
authPass string
authHelper * gortsplib . AuthServer
authFailures int
2020-07-12 15:24:12 +00:00
streamSdpText [ ] byte // only if publisher
streamSdpParsed * sdp . SessionDescription // only if publisher
2020-07-12 10:34:35 +00:00
streamProtocol streamProtocol
streamTracks [ ] * track
2020-07-13 07:45:45 +00:00
RtcpReceivers [ ] * gortsplib . RtcpReceiver
2020-07-12 10:34:35 +00:00
readBuf * doubleBuffer
writeBuf * doubleBuffer
2020-07-13 08:43:14 +00:00
events chan serverClientEvent // only if state = Play and streamProtocol = TCP
2020-07-12 10:34:35 +00:00
done chan struct { }
2019-12-31 12:48:17 +00:00
}
2020-05-03 10:38:11 +00:00
func newServerClient ( p * program , nconn net . Conn ) * serverClient {
c := & serverClient {
2020-05-07 20:30:03 +00:00
p : p ,
conn : gortsplib . NewConnServer ( gortsplib . ConnServerConf {
2020-07-12 20:53:22 +00:00
Conn : nconn ,
2020-06-27 15:23:43 +00:00
ReadTimeout : p . conf . ReadTimeout ,
WriteTimeout : p . conf . WriteTimeout ,
2020-05-07 20:30:03 +00:00
} ) ,
2020-07-13 07:45:45 +00:00
state : clientStateStarting ,
2020-07-11 14:40:19 +00:00
readBuf : newDoubleBuffer ( 512 * 1024 ) ,
done : make ( chan struct { } ) ,
2019-12-31 12:48:17 +00:00
}
2020-05-10 14:23:57 +00:00
go c . run ( )
2019-12-31 12:48:17 +00:00
return c
}
2020-05-03 10:38:11 +00:00
func ( c * serverClient ) log ( format string , args ... interface { } ) {
2020-06-27 12:18:16 +00:00
c . p . log ( "[client %s] " + format , append ( [ ] interface { } { c . conn . NetConn ( ) . RemoteAddr ( ) . String ( ) } , args ... ) ... )
2019-12-31 12:48:17 +00:00
}
2020-05-03 21:26:41 +00:00
func ( c * serverClient ) ip ( ) net . IP {
return c . conn . NetConn ( ) . RemoteAddr ( ) . ( * net . TCPAddr ) . IP
}
func ( c * serverClient ) zone ( ) string {
return c . conn . NetConn ( ) . RemoteAddr ( ) . ( * net . TCPAddr ) . Zone
}
2020-06-30 13:12:39 +00:00
func ( c * serverClient ) publisherIsReady ( ) bool {
2020-07-13 07:45:45 +00:00
return c . state == clientStateRecord
2020-06-30 13:12:39 +00:00
}
func ( c * serverClient ) publisherSdpText ( ) [ ] byte {
return c . streamSdpText
}
2020-07-12 15:24:12 +00:00
func ( c * serverClient ) publisherSdpParsed ( ) * sdp . SessionDescription {
2020-06-30 13:12:39 +00:00
return c . streamSdpParsed
}
2020-05-03 10:38:11 +00:00
func ( c * serverClient ) run ( ) {
2020-06-27 15:23:43 +00:00
if c . p . conf . PreScript != "" {
preScript := exec . Command ( c . p . conf . PreScript )
2020-02-16 12:04:43 +00:00
err := preScript . Run ( )
if err != nil {
c . log ( "ERR: %s" , err )
}
}
2020-02-15 17:25:29 +00:00
2020-07-11 14:40:19 +00:00
outer :
for {
switch c . state {
2020-07-13 07:45:45 +00:00
case clientStatePlay :
2020-07-11 14:40:19 +00:00
ok := c . runPlay ( )
if ! ok {
break outer
}
2020-07-13 07:45:45 +00:00
case clientStateRecord :
2020-07-11 14:40:19 +00:00
ok := c . runRecord ( )
if ! ok {
break outer
}
default :
ok := c . runNormal ( )
if ! ok {
break outer
}
}
}
c . conn . NetConn ( ) . Close ( ) // close socket in case it has not been closed yet
func ( ) {
if c . p . conf . PostScript != "" {
postScript := exec . Command ( c . p . conf . PostScript )
err := postScript . Run ( )
if err != nil {
c . log ( "ERR: %s" , err )
}
}
} ( )
close ( c . done ) // close() never blocks
}
var errClientChangeRunMode = errors . New ( "change run mode" )
var errClientTerminate = errors . New ( "terminate" )
func ( c * serverClient ) runNormal ( ) bool {
var ret bool
outer :
2019-12-31 12:48:17 +00:00
for {
2020-01-20 15:44:02 +00:00
req , err := c . conn . ReadRequest ( )
2019-12-31 12:48:17 +00:00
if err != nil {
if err != io . EOF {
c . log ( "ERR: %s" , err )
}
2020-07-11 14:40:19 +00:00
ret = false
break outer
2019-12-31 12:48:17 +00:00
}
2020-07-11 14:40:19 +00:00
err = c . handleRequest ( req )
switch err {
case errClientChangeRunMode :
ret = true
break outer
case errClientTerminate :
ret = false
break outer
2020-01-03 22:05:06 +00:00
}
}
2020-05-10 14:23:57 +00:00
2020-07-11 14:40:19 +00:00
if ! ret {
done := make ( chan struct { } )
c . p . events <- programEventClientClose { done , c }
<- done
2020-06-21 09:30:30 +00:00
}
2020-07-11 14:40:19 +00:00
return ret
}
func ( c * serverClient ) runPlay ( ) bool {
2020-07-13 07:45:45 +00:00
if c . streamProtocol == streamProtocolTcp {
2020-07-12 10:34:35 +00:00
readDone := make ( chan error )
2020-07-11 14:40:19 +00:00
go func ( ) {
2020-07-12 10:34:35 +00:00
buf := make ( [ ] byte , 2048 )
for {
_ , err := c . conn . NetConn ( ) . Read ( buf )
if err != nil {
readDone <- err
break
}
2020-07-11 14:40:19 +00:00
}
} ( )
2020-07-12 10:34:35 +00:00
outer :
2020-07-11 14:40:19 +00:00
for {
2020-07-12 10:34:35 +00:00
select {
case err := <- readDone :
2020-07-11 14:40:19 +00:00
if err != io . EOF {
c . log ( "ERR: %s" , err )
}
2020-07-12 10:34:35 +00:00
break outer
case rawEvt := <- c . events :
switch evt := rawEvt . ( type ) {
case serverClientEventFrameTcp :
2020-07-12 20:53:22 +00:00
c . conn . WriteFrame ( evt . frame )
2020-07-12 10:34:35 +00:00
}
2020-07-11 14:40:19 +00:00
}
2020-06-27 12:18:16 +00:00
}
2020-07-12 10:34:35 +00:00
go func ( ) {
for range c . events {
}
} ( )
2020-07-11 14:40:19 +00:00
done := make ( chan struct { } )
c . p . events <- programEventClientClose { done , c }
<- done
2020-07-12 10:34:35 +00:00
close ( c . events )
2020-07-11 14:40:19 +00:00
} else {
for {
req , err := c . conn . ReadRequest ( )
2020-05-10 14:23:57 +00:00
if err != nil {
2020-07-11 14:40:19 +00:00
if err != io . EOF {
c . log ( "ERR: %s" , err )
}
break
}
err = c . handleRequest ( req )
if err != nil {
break
2020-05-10 14:23:57 +00:00
}
}
2020-07-11 14:40:19 +00:00
done := make ( chan struct { } )
c . p . events <- programEventClientClose { done , c }
<- done
}
2020-06-27 11:38:35 +00:00
2020-07-11 14:40:19 +00:00
return false
}
2020-06-27 11:38:35 +00:00
2020-07-11 14:40:19 +00:00
func ( c * serverClient ) runRecord ( ) bool {
2020-07-13 07:45:45 +00:00
if c . streamProtocol == streamProtocolTcp {
2020-07-11 14:40:19 +00:00
frame := & gortsplib . InterleavedFrame { }
2020-07-12 10:34:35 +00:00
readDone := make ( chan error )
go func ( ) {
for {
frame . Content = c . readBuf . swap ( )
frame . Content = frame . Content [ : cap ( frame . Content ) ]
2020-07-12 20:53:22 +00:00
recv , err := c . conn . ReadFrameOrRequest ( frame )
2020-07-12 10:34:35 +00:00
if err != nil {
readDone <- err
break
}
switch recvt := recv . ( type ) {
case * gortsplib . InterleavedFrame :
2020-07-13 07:45:45 +00:00
if frame . TrackId >= len ( c . streamTracks ) {
c . log ( "ERR: invalid track id '%d'" , frame . TrackId )
2020-07-12 10:34:35 +00:00
readDone <- nil
break
}
2020-07-13 07:45:45 +00:00
c . RtcpReceivers [ frame . TrackId ] . OnFrame ( frame . StreamType , frame . Content )
2020-07-12 10:34:35 +00:00
c . p . events <- programEventClientFrameTcp {
c . path ,
2020-07-13 07:45:45 +00:00
frame . TrackId ,
frame . StreamType ,
2020-07-12 10:34:35 +00:00
frame . Content ,
}
case * gortsplib . Request :
err := c . handleRequest ( recvt )
if err != nil {
readDone <- nil
break
}
2020-07-11 14:40:19 +00:00
}
}
2020-07-12 10:34:35 +00:00
} ( )
2020-07-11 14:40:19 +00:00
2020-07-13 07:45:45 +00:00
checkStreamTicker := time . NewTicker ( clientCheckStreamInterval )
receiverReportTicker := time . NewTicker ( clientReceiverReportInterval )
2020-07-11 14:40:19 +00:00
2020-07-12 10:34:35 +00:00
outer1 :
for {
select {
case err := <- readDone :
if err != nil && err != io . EOF {
c . log ( "ERR: %s" , err )
2020-07-11 14:40:19 +00:00
}
2020-07-12 10:34:35 +00:00
break outer1
case <- checkStreamTicker . C :
for trackId := range c . streamTracks {
2020-07-13 07:45:45 +00:00
if time . Since ( c . RtcpReceivers [ trackId ] . LastFrameTime ( ) ) >= c . p . conf . StreamDeadAfter {
2020-07-12 10:34:35 +00:00
c . log ( "ERR: stream is dead" )
c . conn . NetConn ( ) . Close ( )
<- readDone
break outer1
}
2020-07-11 14:40:19 +00:00
}
2020-07-12 10:34:35 +00:00
case <- receiverReportTicker . C :
for trackId := range c . streamTracks {
2020-07-13 07:45:45 +00:00
frame := c . RtcpReceivers [ trackId ] . Report ( )
2020-07-12 20:53:22 +00:00
c . conn . WriteFrame ( & gortsplib . InterleavedFrame {
2020-07-13 07:45:45 +00:00
TrackId : trackId ,
StreamType : gortsplib . StreamTypeRtcp ,
Content : frame ,
2020-07-12 10:34:35 +00:00
} )
2020-07-11 14:40:19 +00:00
}
}
}
2020-07-12 10:34:35 +00:00
checkStreamTicker . Stop ( )
receiverReportTicker . Stop ( )
2020-07-11 14:40:19 +00:00
} else {
2020-07-12 10:34:35 +00:00
readDone := make ( chan error )
2020-07-11 14:40:19 +00:00
go func ( ) {
2020-07-12 10:34:35 +00:00
for {
req , err := c . conn . ReadRequest ( )
if err != nil {
readDone <- err
break
}
err = c . handleRequest ( req )
if err != nil {
readDone <- nil // err is not needed
2020-07-11 14:40:19 +00:00
break
}
}
} ( )
2020-07-13 07:45:45 +00:00
checkStreamTicker := time . NewTicker ( clientCheckStreamInterval )
receiverReportTicker := time . NewTicker ( clientReceiverReportInterval )
2020-07-12 10:34:35 +00:00
outer2 :
2020-07-11 14:40:19 +00:00
for {
2020-07-12 10:34:35 +00:00
select {
case err := <- readDone :
if err != nil && err != io . EOF {
2020-07-11 14:40:19 +00:00
c . log ( "ERR: %s" , err )
}
2020-07-12 10:34:35 +00:00
break outer2
case <- checkStreamTicker . C :
for trackId := range c . streamTracks {
2020-07-13 07:45:45 +00:00
if time . Since ( c . RtcpReceivers [ trackId ] . LastFrameTime ( ) ) >= c . p . conf . StreamDeadAfter {
2020-07-12 10:34:35 +00:00
c . log ( "ERR: stream is dead" )
c . conn . NetConn ( ) . Close ( )
<- readDone
break outer2
}
}
2020-07-11 14:40:19 +00:00
2020-07-12 10:34:35 +00:00
case <- receiverReportTicker . C :
for trackId := range c . streamTracks {
2020-07-13 07:45:45 +00:00
frame := c . RtcpReceivers [ trackId ] . Report ( )
2020-07-12 10:34:35 +00:00
c . p . rtcpl . writeChan <- & udpAddrBufPair {
addr : & net . UDPAddr {
IP : c . ip ( ) ,
Zone : c . zone ( ) ,
Port : c . streamTracks [ trackId ] . rtcpPort ,
} ,
buf : frame ,
}
}
2020-07-11 14:40:19 +00:00
}
}
2020-07-12 10:34:35 +00:00
checkStreamTicker . Stop ( )
receiverReportTicker . Stop ( )
}
2020-07-11 14:40:19 +00:00
2020-07-12 10:34:35 +00:00
done := make ( chan struct { } )
c . p . events <- programEventClientClose { done , c }
<- done
for trackId := range c . streamTracks {
2020-07-13 07:45:45 +00:00
c . RtcpReceivers [ trackId ] . Close ( )
2020-07-11 14:40:19 +00:00
}
return false
2020-01-03 22:05:06 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 14:19:02 +00:00
func ( c * serverClient ) close ( ) {
c . conn . NetConn ( ) . Close ( )
<- c . done
}
2020-05-03 12:40:06 +00:00
func ( c * serverClient ) writeResError ( req * gortsplib . Request , code gortsplib . StatusCode , err error ) {
2020-01-03 22:05:06 +00:00
c . log ( "ERR: %s" , err )
2019-12-31 12:48:17 +00:00
2020-05-03 12:40:06 +00:00
header := gortsplib . Header { }
2020-01-26 11:41:26 +00:00
if cseq , ok := req . Header [ "CSeq" ] ; ok && len ( cseq ) == 1 {
2020-06-27 11:38:35 +00:00
header [ "CSeq" ] = cseq
2019-12-31 12:48:17 +00:00
}
2020-05-03 12:40:06 +00:00
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : code ,
Header : header ,
} )
2019-12-31 12:48:17 +00:00
}
2020-07-10 09:24:57 +00:00
func ( c * serverClient ) findConfForPath ( path string ) * ConfPath {
if pconf , ok := c . p . conf . Paths [ path ] ; ok {
return pconf
}
if pconf , ok := c . p . conf . Paths [ "all" ] ; ok {
return pconf
}
return nil
}
2020-05-10 20:56:46 +00:00
var errAuthCritical = errors . New ( "auth critical" )
var errAuthNotCritical = errors . New ( "auth not critical" )
2020-07-10 09:24:57 +00:00
func ( c * serverClient ) authenticate ( ips [ ] interface { } , user string , pass string , req * gortsplib . Request ) error {
// validate ip
2020-06-15 20:41:14 +00:00
err := func ( ) error {
if ips == nil {
return nil
}
2020-05-10 20:56:46 +00:00
2020-07-10 09:24:57 +00:00
ip := c . ip ( )
2020-07-11 14:40:19 +00:00
if ! ipEqualOrInRange ( ip , ips ) {
c . log ( "ERR: ip '%s' not allowed" , ip )
return errAuthCritical
2020-05-10 20:56:46 +00:00
}
2020-07-11 14:40:19 +00:00
return nil
2020-06-15 20:41:14 +00:00
} ( )
if err != nil {
return err
}
2020-07-10 09:24:57 +00:00
// validate credentials
2020-06-15 20:41:14 +00:00
err = func ( ) error {
if user == "" {
return nil
}
2020-05-10 20:56:46 +00:00
2020-07-10 09:24:57 +00:00
// reset authHelper every time the credentials change
if c . authHelper == nil || c . authUser != user || c . authPass != pass {
c . authUser = user
c . authPass = pass
2020-07-12 11:16:33 +00:00
c . authHelper = gortsplib . NewAuthServer ( user , pass , c . p . conf . authMethodsParsed )
2020-05-10 20:56:46 +00:00
}
2020-07-10 09:24:57 +00:00
err := c . authHelper . ValidateHeader ( req . Header [ "Authorization" ] , req . Method , req . Url )
2020-06-15 20:41:14 +00:00
if err != nil {
2020-07-10 09:24:57 +00:00
c . authFailures += 1
// vlc with login prompt sends 4 requests:
// 1) without credentials
// 2) with password but without the username
// 3) without credentials
// 4) with password and username
// hence we must allow up to 3 failures
var retErr error
if c . authFailures > 3 {
2020-06-15 20:41:14 +00:00
c . log ( "ERR: unauthorized: %s" , err )
2020-07-10 09:24:57 +00:00
retErr = errAuthCritical
} else if c . authFailures > 1 {
c . log ( "WARN: unauthorized: %s" , err )
retErr = errAuthNotCritical
} else {
retErr = errAuthNotCritical
2020-06-15 20:41:14 +00:00
}
c . conn . WriteResponse ( & gortsplib . Response {
StatusCode : gortsplib . StatusUnauthorized ,
Header : gortsplib . Header {
2020-06-27 19:22:50 +00:00
"CSeq" : req . Header [ "CSeq" ] ,
2020-07-10 09:24:57 +00:00
"WWW-Authenticate" : c . authHelper . GenerateHeader ( ) ,
2020-06-15 20:41:14 +00:00
} ,
} )
2020-07-10 09:24:57 +00:00
return retErr
2020-06-15 20:41:14 +00:00
}
2020-06-21 09:30:30 +00:00
2020-07-10 09:24:57 +00:00
// reset authFailures after a successful login
c . authFailures = 0
2020-06-15 20:41:14 +00:00
return nil
} ( )
if err != nil {
return err
2020-05-10 20:56:46 +00:00
}
return nil
}
2020-07-11 14:40:19 +00:00
func ( c * serverClient ) handleRequest ( req * gortsplib . Request ) error {
2020-05-03 14:07:56 +00:00
c . log ( string ( req . Method ) )
2020-01-03 22:05:06 +00:00
2020-01-26 11:41:26 +00:00
cseq , ok := req . Header [ "CSeq" ]
if ! ok || len ( cseq ) != 1 {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "cseq missing" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-01-03 21:39:55 +00:00
path := func ( ) string {
2020-05-03 17:47:23 +00:00
ret := req . Url . Path
2019-12-31 13:55:46 +00:00
// remove leading slash
2020-01-03 21:39:55 +00:00
if len ( ret ) > 1 {
ret = ret [ 1 : ]
2019-12-31 13:55:46 +00:00
}
// strip any subpath
2020-01-03 21:39:55 +00:00
if n := strings . Index ( ret , "/" ) ; n >= 0 {
ret = ret [ : n ]
2019-12-31 13:55:46 +00:00
}
2020-01-03 21:39:55 +00:00
return ret
2019-12-31 13:55:46 +00:00
} ( )
2019-12-31 12:48:17 +00:00
switch req . Method {
2020-05-03 14:07:56 +00:00
case gortsplib . OPTIONS :
2019-12-31 12:48:17 +00:00
// do not check state, since OPTIONS can be requested
// in any state
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-01-26 11:41:26 +00:00
"Public" : [ ] string { strings . Join ( [ ] string {
2020-05-03 14:07:56 +00:00
string ( gortsplib . DESCRIBE ) ,
string ( gortsplib . ANNOUNCE ) ,
string ( gortsplib . SETUP ) ,
string ( gortsplib . PLAY ) ,
string ( gortsplib . RECORD ) ,
string ( gortsplib . TEARDOWN ) ,
2020-01-26 11:41:26 +00:00
} , ", " ) } ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-11 14:40:19 +00:00
return nil
2019-12-31 12:48:17 +00:00
2020-05-03 14:07:56 +00:00
case gortsplib . DESCRIBE :
2020-07-13 07:45:45 +00:00
if c . state != clientStateStarting {
2020-05-21 19:46:22 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest ,
2020-07-13 07:45:45 +00:00
fmt . Errorf ( "client is in state '%s' instead of '%s'" , c . state , clientStateStarting ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-06-27 19:22:50 +00:00
pconf := c . findConfForPath ( path )
if pconf == nil {
c . writeResError ( req , gortsplib . StatusBadRequest ,
fmt . Errorf ( "unable to find a valid configuration for path '%s'" , path ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 19:22:50 +00:00
}
2020-07-10 09:24:57 +00:00
err := c . authenticate ( pconf . readIpsParsed , pconf . ReadUser , pconf . ReadPass , req )
2020-05-10 20:56:46 +00:00
if err != nil {
if err == errAuthCritical {
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-05-10 20:56:46 +00:00
}
2020-07-11 14:40:19 +00:00
return nil
2020-05-10 20:56:46 +00:00
}
2020-06-27 11:38:35 +00:00
res := make ( chan [ ] byte )
2020-06-30 13:12:39 +00:00
c . p . events <- programEventClientDescribe { path , res }
2020-06-27 11:38:35 +00:00
sdp := <- res
if sdp == nil {
2020-07-12 10:34:35 +00:00
c . writeResError ( req , gortsplib . StatusNotFound , fmt . Errorf ( "no one is publishing on path '%s'" , path ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-06-28 15:43:56 +00:00
"Content-Base" : [ ] string { req . Url . String ( ) + "/" } ,
2020-01-26 11:41:26 +00:00
"Content-Type" : [ ] string { "application/sdp" } ,
2019-12-31 12:48:17 +00:00
} ,
Content : sdp ,
2020-01-03 22:05:06 +00:00
} )
2020-07-11 14:40:19 +00:00
return nil
2019-12-31 12:48:17 +00:00
2020-05-03 14:07:56 +00:00
case gortsplib . ANNOUNCE :
2020-07-13 07:45:45 +00:00
if c . state != clientStateStarting {
2020-05-21 19:46:22 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest ,
2020-07-13 07:45:45 +00:00
fmt . Errorf ( "client is in state '%s' instead of '%s'" , c . state , clientStateStarting ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-07-12 15:24:12 +00:00
if len ( path ) == 0 {
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "path can't be empty" ) )
return errClientTerminate
}
2020-06-27 19:22:50 +00:00
pconf := c . findConfForPath ( path )
if pconf == nil {
c . writeResError ( req , gortsplib . StatusBadRequest ,
fmt . Errorf ( "unable to find a valid configuration for path '%s'" , path ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 19:22:50 +00:00
}
2020-07-10 09:24:57 +00:00
err := c . authenticate ( pconf . publishIpsParsed , pconf . PublishUser , pconf . PublishPass , req )
2020-05-10 20:56:46 +00:00
if err != nil {
if err == errAuthCritical {
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-02-16 15:05:08 +00:00
}
2020-07-11 14:40:19 +00:00
return nil
2020-02-16 15:05:08 +00:00
}
2020-01-26 11:41:26 +00:00
ct , ok := req . Header [ "Content-Type" ]
if ! ok || len ( ct ) != 1 {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "Content-Type header missing" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-01-26 11:41:26 +00:00
if ct [ 0 ] != "application/sdp" {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "unsupported Content-Type '%s'" , ct ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-07-12 15:24:12 +00:00
sdpParsed := & sdp . SessionDescription { }
err = sdpParsed . Unmarshal ( string ( req . Content ) )
2019-12-31 12:48:17 +00:00
if err != nil {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "invalid SDP: %s" , err ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-07-12 20:53:22 +00:00
sdpParsed , req . Content = sdpForServer ( sdpParsed )
2020-01-21 09:08:53 +00:00
2020-07-12 15:24:12 +00:00
if len ( sdpParsed . MediaDescriptions ) == 0 {
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "no tracks defined" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 11:38:35 +00:00
res := make ( chan error )
2020-06-30 13:12:39 +00:00
c . p . events <- programEventClientAnnounce { res , c , path }
2020-06-27 11:38:35 +00:00
err = <- res
2019-12-31 12:48:17 +00:00
if err != nil {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , err )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-06-30 13:12:39 +00:00
c . streamSdpText = req . Content
c . streamSdpParsed = sdpParsed
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-11 14:40:19 +00:00
return nil
2019-12-31 12:48:17 +00:00
2020-05-03 14:07:56 +00:00
case gortsplib . SETUP :
2020-01-26 11:41:26 +00:00
tsRaw , ok := req . Header [ "Transport" ]
if ! ok || len ( tsRaw ) != 1 {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "transport header missing" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-01-26 11:41:26 +00:00
th := gortsplib . ReadHeaderTransport ( tsRaw [ 0 ] )
2020-06-14 15:46:27 +00:00
if _ , ok := th [ "multicast" ] ; ok {
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "multicast is not supported" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
switch c . state {
// play
2020-07-13 07:45:45 +00:00
case clientStateStarting , clientStatePrePlay :
2020-06-27 19:22:50 +00:00
pconf := c . findConfForPath ( path )
if pconf == nil {
c . writeResError ( req , gortsplib . StatusBadRequest ,
fmt . Errorf ( "unable to find a valid configuration for path '%s'" , path ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 19:22:50 +00:00
}
2020-07-10 09:24:57 +00:00
err := c . authenticate ( pconf . readIpsParsed , pconf . ReadUser , pconf . ReadPass , req )
2020-05-10 20:56:46 +00:00
if err != nil {
if err == errAuthCritical {
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-05-10 20:56:46 +00:00
}
2020-07-11 14:40:19 +00:00
return nil
2020-05-10 20:56:46 +00:00
}
2019-12-31 12:48:17 +00:00
// play via UDP
2020-01-14 20:19:25 +00:00
if func ( ) bool {
_ , ok := th [ "RTP/AVP" ]
if ok {
return true
}
_ , ok = th [ "RTP/AVP/UDP" ]
if ok {
return true
}
return false
} ( ) {
2020-07-13 07:45:45 +00:00
if _ , ok := c . p . conf . protocolsParsed [ streamProtocolUdp ] ; ! ok {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusUnsupportedTransport , fmt . Errorf ( "UDP streaming is disabled" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-01-15 21:48:51 +00:00
}
2020-01-20 11:53:06 +00:00
rtpPort , rtcpPort := th . GetPorts ( "client_port" )
2019-12-31 12:48:17 +00:00
if rtpPort == 0 || rtcpPort == 0 {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "transport header does not have valid client ports (%s)" , tsRaw [ 0 ] ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2019-12-31 13:55:46 +00:00
if c . path != "" && path != c . path {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "path has changed" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 13:55:46 +00:00
}
2020-07-13 07:45:45 +00:00
if len ( c . streamTracks ) > 0 && c . streamProtocol != streamProtocolUdp {
2020-06-27 11:38:35 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "can't receive tracks with different protocols" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 11:38:35 +00:00
res := make ( chan error )
2020-07-13 07:45:45 +00:00
c . p . events <- programEventClientSetupPlay { res , c , path , streamProtocolUdp , rtpPort , rtcpPort }
2020-06-27 11:38:35 +00:00
err = <- res
2019-12-31 12:48:17 +00:00
if err != nil {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , err )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-01-26 11:41:26 +00:00
"Transport" : [ ] string { strings . Join ( [ ] string {
2020-01-14 20:33:53 +00:00
"RTP/AVP/UDP" ,
2019-12-31 12:48:17 +00:00
"unicast" ,
fmt . Sprintf ( "client_port=%d-%d" , rtpPort , rtcpPort ) ,
2020-06-27 15:23:43 +00:00
fmt . Sprintf ( "server_port=%d-%d" , c . p . conf . RtpPort , c . p . conf . RtcpPort ) ,
2020-01-26 11:41:26 +00:00
} , ";" ) } ,
"Session" : [ ] string { "12345678" } ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-11 14:40:19 +00:00
return nil
2019-12-31 12:48:17 +00:00
// play via TCP
} else if _ , ok := th [ "RTP/AVP/TCP" ] ; ok {
2020-07-13 07:45:45 +00:00
if _ , ok := c . p . conf . protocolsParsed [ streamProtocolTcp ] ; ! ok {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusUnsupportedTransport , fmt . Errorf ( "TCP streaming is disabled" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-01-15 21:48:51 +00:00
}
2019-12-31 13:55:46 +00:00
if c . path != "" && path != c . path {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "path has changed" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 13:55:46 +00:00
}
2020-07-13 07:45:45 +00:00
if len ( c . streamTracks ) > 0 && c . streamProtocol != streamProtocolTcp {
2020-06-27 11:38:35 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "can't receive tracks with different protocols" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 11:38:35 +00:00
res := make ( chan error )
2020-07-13 07:45:45 +00:00
c . p . events <- programEventClientSetupPlay { res , c , path , streamProtocolTcp , 0 , 0 }
2020-06-27 11:38:35 +00:00
err = <- res
2019-12-31 12:48:17 +00:00
if err != nil {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , err )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
interleaved := fmt . Sprintf ( "%d-%d" , ( ( len ( c . streamTracks ) - 1 ) * 2 ) , ( ( len ( c . streamTracks ) - 1 ) * 2 ) + 1 )
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-01-26 11:41:26 +00:00
"Transport" : [ ] string { strings . Join ( [ ] string {
2019-12-31 12:48:17 +00:00
"RTP/AVP/TCP" ,
"unicast" ,
fmt . Sprintf ( "interleaved=%s" , interleaved ) ,
2020-01-26 11:41:26 +00:00
} , ";" ) } ,
"Session" : [ ] string { "12345678" } ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-11 14:40:19 +00:00
return nil
2019-12-31 12:48:17 +00:00
} else {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "transport header does not contain a valid protocol (RTP/AVP, RTP/AVP/UDP or RTP/AVP/TCP) (%s)" , tsRaw [ 0 ] ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
// record
2020-07-13 07:45:45 +00:00
case clientStateAnnounce , clientStatePreRecord :
2019-12-31 12:48:17 +00:00
if _ , ok := th [ "mode=record" ] ; ! ok {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "transport header does not contain mode=record" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-06-27 11:38:35 +00:00
// after ANNOUNCE, c.path is already set
2019-12-31 13:55:46 +00:00
if path != c . path {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "path has changed" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 13:55:46 +00:00
}
2019-12-31 12:48:17 +00:00
// record via UDP
2020-01-15 21:27:44 +00:00
if func ( ) bool {
_ , ok := th [ "RTP/AVP" ]
if ok {
return true
}
_ , ok = th [ "RTP/AVP/UDP" ]
if ok {
return true
}
return false
2020-01-15 21:48:51 +00:00
} ( ) {
2020-07-13 07:45:45 +00:00
if _ , ok := c . p . conf . protocolsParsed [ streamProtocolUdp ] ; ! ok {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusUnsupportedTransport , fmt . Errorf ( "UDP streaming is disabled" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-01-15 21:48:51 +00:00
}
2020-01-20 11:53:06 +00:00
rtpPort , rtcpPort := th . GetPorts ( "client_port" )
2019-12-31 12:48:17 +00:00
if rtpPort == 0 || rtcpPort == 0 {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "transport header does not have valid client ports (%s)" , tsRaw [ 0 ] ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-07-13 07:45:45 +00:00
if len ( c . streamTracks ) > 0 && c . streamProtocol != streamProtocolUdp {
2020-06-27 11:38:35 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "can't publish tracks with different protocols" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-07-12 15:24:12 +00:00
if len ( c . streamTracks ) >= len ( c . streamSdpParsed . MediaDescriptions ) {
2020-06-27 11:38:35 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "all the tracks have already been setup" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 11:38:35 +00:00
res := make ( chan error )
2020-07-13 07:45:45 +00:00
c . p . events <- programEventClientSetupRecord { res , c , streamProtocolUdp , rtpPort , rtcpPort }
2020-06-27 11:38:35 +00:00
err := <- res
2019-12-31 12:48:17 +00:00
if err != nil {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , err )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-01-26 11:41:26 +00:00
"Transport" : [ ] string { strings . Join ( [ ] string {
2020-01-14 20:33:53 +00:00
"RTP/AVP/UDP" ,
2019-12-31 12:48:17 +00:00
"unicast" ,
fmt . Sprintf ( "client_port=%d-%d" , rtpPort , rtcpPort ) ,
2020-06-27 15:23:43 +00:00
fmt . Sprintf ( "server_port=%d-%d" , c . p . conf . RtpPort , c . p . conf . RtcpPort ) ,
2020-01-26 11:41:26 +00:00
} , ";" ) } ,
"Session" : [ ] string { "12345678" } ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-11 14:40:19 +00:00
return nil
2019-12-31 12:48:17 +00:00
// record via TCP
} else if _ , ok := th [ "RTP/AVP/TCP" ] ; ok {
2020-07-13 07:45:45 +00:00
if _ , ok := c . p . conf . protocolsParsed [ streamProtocolTcp ] ; ! ok {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusUnsupportedTransport , fmt . Errorf ( "TCP streaming is disabled" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-01-15 21:48:51 +00:00
}
2020-07-13 07:45:45 +00:00
if len ( c . streamTracks ) > 0 && c . streamProtocol != streamProtocolTcp {
2020-06-27 11:38:35 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "can't publish tracks with different protocols" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 11:38:35 +00:00
interleaved := th . GetValue ( "interleaved" )
if interleaved == "" {
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "transport header does not contain the interleaved field" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 11:38:35 +00:00
expInterleaved := fmt . Sprintf ( "%d-%d" , 0 + len ( c . streamTracks ) * 2 , 1 + len ( c . streamTracks ) * 2 )
if interleaved != expInterleaved {
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "wrong interleaved value, expected '%s', got '%s'" , expInterleaved , interleaved ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-07-12 15:24:12 +00:00
if len ( c . streamTracks ) >= len ( c . streamSdpParsed . MediaDescriptions ) {
2020-06-27 11:38:35 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "all the tracks have already been setup" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2020-06-27 11:38:35 +00:00
}
2019-12-31 12:48:17 +00:00
2020-06-27 11:38:35 +00:00
res := make ( chan error )
2020-07-13 07:45:45 +00:00
c . p . events <- programEventClientSetupRecord { res , c , streamProtocolTcp , 0 , 0 }
2020-06-27 11:38:35 +00:00
err := <- res
2019-12-31 12:48:17 +00:00
if err != nil {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , err )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-01-26 11:41:26 +00:00
"Transport" : [ ] string { strings . Join ( [ ] string {
2019-12-31 12:48:17 +00:00
"RTP/AVP/TCP" ,
"unicast" ,
fmt . Sprintf ( "interleaved=%s" , interleaved ) ,
2020-01-26 11:41:26 +00:00
} , ";" ) } ,
"Session" : [ ] string { "12345678" } ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-11 14:40:19 +00:00
return nil
2019-12-31 12:48:17 +00:00
} else {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "transport header does not contain a valid protocol (RTP/AVP, RTP/AVP/UDP or RTP/AVP/TCP) (%s)" , tsRaw [ 0 ] ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
default :
2020-05-21 19:46:22 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "client is in state '%s'" , c . state ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-05-03 14:07:56 +00:00
case gortsplib . PLAY :
2020-07-13 07:45:45 +00:00
if c . state != clientStatePrePlay {
2020-05-21 19:46:22 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest ,
2020-07-13 07:45:45 +00:00
fmt . Errorf ( "client is in state '%s' instead of '%s'" , c . state , clientStatePrePlay ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2019-12-31 13:55:46 +00:00
if path != c . path {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "path has changed" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 13:55:46 +00:00
}
2020-06-27 11:38:35 +00:00
// check publisher existence
res := make ( chan error )
c . p . events <- programEventClientPlay1 { res , c }
err := <- res
2019-12-31 12:48:17 +00:00
if err != nil {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , err )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-06-27 11:38:35 +00:00
// write response before setting state
// otherwise, in case of TCP connections, RTP packets could be sent
2020-01-03 22:05:06 +00:00
// before the response
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-01-26 11:41:26 +00:00
"Session" : [ ] string { "12345678" } ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-13 07:45:45 +00:00
if c . streamProtocol == streamProtocolTcp {
2020-07-12 10:34:35 +00:00
c . writeBuf = newDoubleBuffer ( 2048 )
c . events = make ( chan serverClientEvent )
}
2020-07-11 14:40:19 +00:00
2020-06-27 12:18:16 +00:00
// set state
res = make ( chan error )
c . p . events <- programEventClientPlay2 { res , c }
<- res
2020-01-03 22:05:06 +00:00
c . log ( "is receiving on path '%s', %d %s via %s" , c . path , len ( c . streamTracks ) , func ( ) string {
if len ( c . streamTracks ) == 1 {
return "track"
}
return "tracks"
} ( ) , c . streamProtocol )
2020-07-11 14:40:19 +00:00
return errClientChangeRunMode
2019-12-31 12:48:17 +00:00
2020-05-03 14:07:56 +00:00
case gortsplib . RECORD :
2020-07-13 07:45:45 +00:00
if c . state != clientStatePreRecord {
2020-05-21 19:46:22 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest ,
2020-07-13 07:45:45 +00:00
fmt . Errorf ( "client is in state '%s' instead of '%s'" , c . state , clientStatePreRecord ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2019-12-31 13:55:46 +00:00
if path != c . path {
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "path has changed" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 13:55:46 +00:00
}
2020-07-12 15:24:12 +00:00
if len ( c . streamTracks ) != len ( c . streamSdpParsed . MediaDescriptions ) {
2020-06-27 11:38:35 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "not all tracks have been setup" ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
2020-05-03 13:22:41 +00:00
c . conn . WriteResponse ( & gortsplib . Response {
2020-05-03 12:40:06 +00:00
StatusCode : gortsplib . StatusOK ,
2020-01-26 11:41:26 +00:00
Header : gortsplib . Header {
2020-06-27 11:38:35 +00:00
"CSeq" : cseq ,
2020-01-26 11:41:26 +00:00
"Session" : [ ] string { "12345678" } ,
2019-12-31 12:48:17 +00:00
} ,
2020-01-03 22:05:06 +00:00
} )
2020-07-13 07:45:45 +00:00
c . RtcpReceivers = make ( [ ] * gortsplib . RtcpReceiver , len ( c . streamTracks ) )
2020-07-12 10:34:35 +00:00
for trackId := range c . streamTracks {
2020-07-13 07:45:45 +00:00
c . RtcpReceivers [ trackId ] = gortsplib . NewRtcpReceiver ( )
2020-07-12 10:34:35 +00:00
}
2020-06-27 12:18:16 +00:00
res := make ( chan error )
c . p . events <- programEventClientRecord { res , c }
<- res
2020-01-03 22:05:06 +00:00
c . log ( "is publishing on path '%s', %d %s via %s" , c . path , len ( c . streamTracks ) , func ( ) string {
if len ( c . streamTracks ) == 1 {
return "track"
}
return "tracks"
} ( ) , c . streamProtocol )
2020-07-11 14:40:19 +00:00
return errClientChangeRunMode
2019-12-31 12:48:17 +00:00
2020-05-03 14:07:56 +00:00
case gortsplib . TEARDOWN :
2020-01-03 22:05:06 +00:00
// close connection silently
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
default :
2020-05-03 12:40:06 +00:00
c . writeResError ( req , gortsplib . StatusBadRequest , fmt . Errorf ( "unhandled method '%s'" , req . Method ) )
2020-07-11 14:40:19 +00:00
return errClientTerminate
2019-12-31 12:48:17 +00:00
}
}