mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
199 lines
4.5 KiB
Go
199 lines
4.5 KiB
Go
package plugin_rtsp
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"m7s.live/v5/pkg/util"
|
|
. "m7s.live/v5/plugin/rtsp/pkg"
|
|
)
|
|
|
|
type RTSPServer struct {
|
|
*NetConnection
|
|
conf *RTSPPlugin
|
|
}
|
|
|
|
func (task *RTSPServer) Go() (err error) {
|
|
var receiver *Receiver
|
|
var sender *Sender
|
|
var req *util.Request
|
|
var sendMode bool
|
|
for {
|
|
req, err = task.ReadRequest()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if task.URL == nil {
|
|
task.URL = req.URL
|
|
task.Logger = task.With("url", task.URL.String())
|
|
task.UserAgent = req.Header.Get("User-Agent")
|
|
task.Info("connect", "userAgent", task.UserAgent)
|
|
}
|
|
|
|
//if !c.auth.Validate(req) {
|
|
// res := &tcp.Response{
|
|
// Status: "401 Unauthorized",
|
|
// Header: map[string][]string{"Www-Authenticate": {`Basic realm="go2rtc"`}},
|
|
// Request: req,
|
|
// }
|
|
// if err = c.WriteResponse(res); err != nil {
|
|
// return err
|
|
// }
|
|
// continue
|
|
//}
|
|
|
|
// Receiver: OPTIONS > DESCRIBE > SETUP... > PLAY > TEARDOWN
|
|
// Sender: OPTIONS > ANNOUNCE > SETUP... > RECORD > TEARDOWN
|
|
switch req.Method {
|
|
case MethodOptions:
|
|
res := &util.Response{
|
|
Header: map[string][]string{
|
|
"Public": {"OPTIONS, SETUP, TEARDOWN, DESCRIBE, PLAY, PAUSE, ANNOUNCE, RECORD"},
|
|
},
|
|
Request: req,
|
|
}
|
|
if err = task.WriteResponse(res); err != nil {
|
|
return
|
|
}
|
|
|
|
case MethodAnnounce:
|
|
if req.Header.Get("Content-Type") != "application/sdp" {
|
|
err = errors.New("wrong content type")
|
|
return
|
|
}
|
|
|
|
task.SDP = string(req.Body) // for info
|
|
var medias []*Media
|
|
if medias, err = UnmarshalSDP(req.Body); err != nil {
|
|
return
|
|
}
|
|
|
|
receiver = &Receiver{}
|
|
receiver.NetConnection = task.NetConnection
|
|
if receiver.Publisher, err = task.conf.Publish(task, strings.TrimPrefix(task.URL.Path, "/")); err != nil {
|
|
receiver = nil
|
|
err = task.WriteResponse(&util.Response{
|
|
StatusCode: 500, Status: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
receiver.Publisher.RemoteAddr = task.Conn.RemoteAddr().String()
|
|
if err = receiver.SetMedia(medias); err != nil {
|
|
return
|
|
}
|
|
res := &util.Response{Request: req}
|
|
if err = task.WriteResponse(res); err != nil {
|
|
return
|
|
}
|
|
task.Depend(receiver.Publisher)
|
|
case MethodDescribe:
|
|
sendMode = true
|
|
sender = &Sender{}
|
|
sender.NetConnection = task.NetConnection
|
|
sender.Subscriber, err = task.conf.Subscribe(task, strings.TrimPrefix(task.URL.Path, "/"))
|
|
if err != nil {
|
|
res := &util.Response{
|
|
StatusCode: http.StatusBadRequest,
|
|
Status: err.Error(),
|
|
Request: req,
|
|
}
|
|
_ = task.WriteResponse(res)
|
|
return
|
|
}
|
|
sender.Subscriber.RemoteAddr = task.Conn.RemoteAddr().String()
|
|
res := &util.Response{
|
|
Header: map[string][]string{
|
|
"Content-Type": {"application/sdp"},
|
|
},
|
|
Request: req,
|
|
}
|
|
// convert tracks to real output medias
|
|
var medias []*Media
|
|
if medias, err = sender.GetMedia(); err != nil {
|
|
return
|
|
}
|
|
if res.Body, err = MarshalSDP(task.SessionName, medias); err != nil {
|
|
return
|
|
}
|
|
|
|
task.SDP = string(res.Body) // for info
|
|
|
|
if err = task.WriteResponse(res); err != nil {
|
|
return
|
|
}
|
|
|
|
case MethodSetup:
|
|
tr := req.Header.Get("Transport")
|
|
|
|
res := &util.Response{
|
|
Header: map[string][]string{},
|
|
Request: req,
|
|
}
|
|
|
|
const transport = "RTP/AVP/TCP;unicast;interleaved="
|
|
if strings.HasPrefix(tr, transport) {
|
|
|
|
task.Session = util.RandomString(10)
|
|
|
|
if sendMode {
|
|
if i := reqTrackID(req); i >= 0 {
|
|
tr = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", i*2, i*2+1)
|
|
res.Header.Set("Transport", tr)
|
|
} else {
|
|
res.Status = "400 Bad Request"
|
|
}
|
|
} else {
|
|
res.Header.Set("Transport", tr[:len(transport)+3])
|
|
}
|
|
} else {
|
|
res.Status = "461 Unsupported transport"
|
|
}
|
|
|
|
if err = task.WriteResponse(res); err != nil {
|
|
return
|
|
}
|
|
|
|
case MethodRecord:
|
|
res := &util.Response{Request: req}
|
|
if err = task.WriteResponse(res); err != nil {
|
|
return
|
|
}
|
|
err = receiver.Receive()
|
|
return
|
|
case MethodPlay:
|
|
res := &util.Response{Request: req}
|
|
if err = task.WriteResponse(res); err != nil {
|
|
return
|
|
}
|
|
err = sender.Send()
|
|
return
|
|
case MethodTeardown:
|
|
res := &util.Response{Request: req}
|
|
_ = task.WriteResponse(res)
|
|
return
|
|
|
|
default:
|
|
task.Warn("unsupported method", "method", req.Method)
|
|
}
|
|
}
|
|
}
|
|
|
|
func reqTrackID(req *util.Request) int {
|
|
var s string
|
|
if req.URL.RawQuery != "" {
|
|
s = req.URL.RawQuery
|
|
} else {
|
|
s = req.URL.Path
|
|
}
|
|
if i := strings.LastIndexByte(s, '='); i > 0 {
|
|
if i, err := strconv.Atoi(s[i+1:]); err == nil {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|