Codex: Refactor proxy server APIs.
This commit is contained in:
parent
b6dd97ddff
commit
cb90bf1fec
|
|
@ -99,25 +99,25 @@ func (b *proxyBootstrap) initializeLoadBalancer(ctx context.Context, environment
|
||||||
// startServers initializes and starts all protocol servers.
|
// startServers initializes and starts all protocol servers.
|
||||||
func (b *proxyBootstrap) startServers(ctx context.Context, environment env.ProxyEnvironment, gracefulQuitTimeout time.Duration) error {
|
func (b *proxyBootstrap) startServers(ctx context.Context, environment env.ProxyEnvironment, gracefulQuitTimeout time.Duration) error {
|
||||||
// Start the RTMP server.
|
// Start the RTMP server.
|
||||||
srsRTMPServer := server.NewSRSRTMPServer(environment)
|
rtmpServer := server.NewRTMPServer(environment)
|
||||||
if err := srsRTMPServer.Run(ctx); err != nil {
|
if err := rtmpServer.Run(ctx); err != nil {
|
||||||
return errors.Wrapf(err, "rtmp server")
|
return errors.Wrapf(err, "rtmp server")
|
||||||
}
|
}
|
||||||
defer srsRTMPServer.Close()
|
defer rtmpServer.Close()
|
||||||
|
|
||||||
// Start the WebRTC server.
|
// Start the WebRTC server.
|
||||||
srsWebRTCServer := server.NewSRSWebRTCServer(environment)
|
webRTCServer := server.NewWebRTCServer(environment)
|
||||||
if err := srsWebRTCServer.Run(ctx); err != nil {
|
if err := webRTCServer.Run(ctx); err != nil {
|
||||||
return errors.Wrapf(err, "rtc server")
|
return errors.Wrapf(err, "rtc server")
|
||||||
}
|
}
|
||||||
defer srsWebRTCServer.Close()
|
defer webRTCServer.Close()
|
||||||
|
|
||||||
// Start the HTTP API server.
|
// Start the HTTP API server.
|
||||||
srsHTTPAPIServer := server.NewSRSHTTPAPIServer(environment, gracefulQuitTimeout, srsWebRTCServer)
|
httpAPIServer := server.NewHTTPAPIServer(environment, gracefulQuitTimeout, webRTCServer)
|
||||||
if err := srsHTTPAPIServer.Run(ctx); err != nil {
|
if err := httpAPIServer.Run(ctx); err != nil {
|
||||||
return errors.Wrapf(err, "http api server")
|
return errors.Wrapf(err, "http api server")
|
||||||
}
|
}
|
||||||
defer srsHTTPAPIServer.Close()
|
defer httpAPIServer.Close()
|
||||||
|
|
||||||
// Start the SRT server.
|
// Start the SRT server.
|
||||||
srsSRTServer := server.NewSRSSRTServer(environment)
|
srsSRTServer := server.NewSRSSRTServer(environment)
|
||||||
|
|
@ -134,11 +134,11 @@ func (b *proxyBootstrap) startServers(ctx context.Context, environment env.Proxy
|
||||||
defer systemAPI.Close()
|
defer systemAPI.Close()
|
||||||
|
|
||||||
// Start the HTTP web server.
|
// Start the HTTP web server.
|
||||||
srsHTTPStreamServer := server.NewSRSHTTPStreamServer(environment, gracefulQuitTimeout)
|
httpStreamServer := server.NewHTTPStreamServer(environment, gracefulQuitTimeout)
|
||||||
if err := srsHTTPStreamServer.Run(ctx); err != nil {
|
if err := httpStreamServer.Run(ctx); err != nil {
|
||||||
return errors.Wrapf(err, "http server")
|
return errors.Wrapf(err, "http server")
|
||||||
}
|
}
|
||||||
defer srsHTTPStreamServer.Close()
|
defer httpStreamServer.Close()
|
||||||
|
|
||||||
// Wait for the main loop to quit.
|
// Wait for the main loop to quit.
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
|
||||||
|
|
@ -234,8 +234,6 @@ func (v *protocol) ExpectMessage(ctx context.Context, types ...MessageType) (m M
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *protocol) parseAMFObject(p []byte) (pkt Packet, err error) {
|
func (v *protocol) parseAMFObject(p []byte) (pkt Packet, err error) {
|
||||||
|
|
|
||||||
|
|
@ -20,23 +20,28 @@ import (
|
||||||
"srsx/internal/version"
|
"srsx/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// srsHTTPAPIServer is the proxy for SRS HTTP API, to proxy the WebRTC HTTP API like WHIP and WHEP,
|
// HTTPAPIServer is the proxy for SRS HTTP API, to proxy the WebRTC HTTP API like WHIP and WHEP,
|
||||||
// to proxy other HTTP API of SRS like the streams and clients, etc.
|
// to proxy other HTTP API of SRS like the streams and clients, etc.
|
||||||
type srsHTTPAPIServer struct {
|
type HTTPAPIServer interface {
|
||||||
|
Run(ctx context.Context) error
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpAPIServer struct {
|
||||||
// The environment interface.
|
// The environment interface.
|
||||||
environment env.ProxyEnvironment
|
environment env.ProxyEnvironment
|
||||||
// The underlayer HTTP server.
|
// The underlayer HTTP server.
|
||||||
server *http.Server
|
server *http.Server
|
||||||
// The WebRTC server.
|
// The WebRTC server.
|
||||||
rtc *srsWebRTCServer
|
rtc WebRTCServer
|
||||||
// The gracefully quit timeout, wait server to quit.
|
// The gracefully quit timeout, wait server to quit.
|
||||||
gracefulQuitTimeout time.Duration
|
gracefulQuitTimeout time.Duration
|
||||||
// The wait group for all goroutines.
|
// The wait group for all goroutines.
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSRSHTTPAPIServer(environment env.ProxyEnvironment, gracefulQuitTimeout time.Duration, rtc *srsWebRTCServer) *srsHTTPAPIServer {
|
func NewHTTPAPIServer(environment env.ProxyEnvironment, gracefulQuitTimeout time.Duration, rtc WebRTCServer) HTTPAPIServer {
|
||||||
v := &srsHTTPAPIServer{
|
v := &httpAPIServer{
|
||||||
environment: environment,
|
environment: environment,
|
||||||
gracefulQuitTimeout: gracefulQuitTimeout,
|
gracefulQuitTimeout: gracefulQuitTimeout,
|
||||||
rtc: rtc,
|
rtc: rtc,
|
||||||
|
|
@ -44,7 +49,7 @@ func NewSRSHTTPAPIServer(environment env.ProxyEnvironment, gracefulQuitTimeout t
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsHTTPAPIServer) Close() error {
|
func (v *httpAPIServer) Close() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), v.gracefulQuitTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), v.gracefulQuitTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
v.server.Shutdown(ctx)
|
v.server.Shutdown(ctx)
|
||||||
|
|
@ -53,7 +58,7 @@ func (v *srsHTTPAPIServer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsHTTPAPIServer) Run(ctx context.Context) error {
|
func (v *httpAPIServer) Run(ctx context.Context) error {
|
||||||
// Parse address to listen.
|
// Parse address to listen.
|
||||||
addr := v.environment.HttpAPI()
|
addr := v.environment.HttpAPI()
|
||||||
if !strings.Contains(addr, ":") {
|
if !strings.Contains(addr, ":") {
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,15 @@ import (
|
||||||
"srsx/internal/version"
|
"srsx/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// srsHTTPStreamServer is the proxy server for SRS HTTP stream server, for HTTP-FLV, HTTP-TS,
|
// HTTPStreamServer is the proxy server for SRS HTTP stream server, for HTTP-FLV, HTTP-TS,
|
||||||
// HLS, etc. The proxy server will figure out which SRS origin server to proxy to, then proxy
|
// HLS, etc. The proxy server will figure out which SRS origin server to proxy to, then proxy
|
||||||
// the request to the origin server.
|
// the request to the origin server.
|
||||||
type srsHTTPStreamServer struct {
|
type HTTPStreamServer interface {
|
||||||
|
Run(ctx context.Context) error
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpStreamServer struct {
|
||||||
// The environment interface.
|
// The environment interface.
|
||||||
environment env.ProxyEnvironment
|
environment env.ProxyEnvironment
|
||||||
// The underlayer HTTP server.
|
// The underlayer HTTP server.
|
||||||
|
|
@ -37,15 +42,15 @@ type srsHTTPStreamServer struct {
|
||||||
wg stdSync.WaitGroup
|
wg stdSync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSRSHTTPStreamServer(environment env.ProxyEnvironment, gracefulQuitTimeout time.Duration) *srsHTTPStreamServer {
|
func NewHTTPStreamServer(environment env.ProxyEnvironment, gracefulQuitTimeout time.Duration) HTTPStreamServer {
|
||||||
v := &srsHTTPStreamServer{
|
v := &httpStreamServer{
|
||||||
environment: environment,
|
environment: environment,
|
||||||
gracefulQuitTimeout: gracefulQuitTimeout,
|
gracefulQuitTimeout: gracefulQuitTimeout,
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsHTTPStreamServer) Close() error {
|
func (v *httpStreamServer) Close() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), v.gracefulQuitTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), v.gracefulQuitTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
v.server.Shutdown(ctx)
|
v.server.Shutdown(ctx)
|
||||||
|
|
@ -54,7 +59,7 @@ func (v *srsHTTPStreamServer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsHTTPStreamServer) Run(ctx context.Context) error {
|
func (v *httpStreamServer) Run(ctx context.Context) error {
|
||||||
// Parse address to listen.
|
// Parse address to listen.
|
||||||
addr := v.environment.HttpServer()
|
addr := v.environment.HttpServer()
|
||||||
if !strings.Contains(addr, ":") {
|
if !strings.Contains(addr, ":") {
|
||||||
|
|
@ -123,12 +128,12 @@ func (v *srsHTTPStreamServer) Run(ctx context.Context) error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stream, _ := lb.SrsLoadBalancer.LoadOrStoreHLS(ctx, streamURL, NewHLSPlayStream(func(s *HLSPlayStream) {
|
stream, _ := lb.SrsLoadBalancer.LoadOrStoreHLS(ctx, streamURL, newHLSPlayStream(func(s *hlsPlayStream) {
|
||||||
s.SRSProxyBackendHLSID = logger.GenerateContextID()
|
s.SRSProxyBackendHLSID = logger.GenerateContextID()
|
||||||
s.StreamURL, s.FullURL = streamURL, fullURL
|
s.StreamURL, s.FullURL = streamURL, fullURL
|
||||||
}))
|
}))
|
||||||
|
|
||||||
stream.Initialize(ctx).(*HLSPlayStream).ServeHTTP(w, r)
|
stream.Initialize(ctx).(*hlsPlayStream).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,13 +145,13 @@ func (v *srsHTTPStreamServer) Run(ctx context.Context) error {
|
||||||
if stream, err := lb.SrsLoadBalancer.LoadHLSBySPBHID(ctx, srsProxyBackendID); err != nil {
|
if stream, err := lb.SrsLoadBalancer.LoadHLSBySPBHID(ctx, srsProxyBackendID); err != nil {
|
||||||
http.Error(w, fmt.Sprintf("load stream by spbhid %v", srsProxyBackendID), http.StatusBadRequest)
|
http.Error(w, fmt.Sprintf("load stream by spbhid %v", srsProxyBackendID), http.StatusBadRequest)
|
||||||
} else {
|
} else {
|
||||||
stream.Initialize(ctx).(*HLSPlayStream).ServeHTTP(w, r)
|
stream.Initialize(ctx).(*hlsPlayStream).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use HTTP pseudo streaming to proxy the request.
|
// Use HTTP pseudo streaming to proxy the request.
|
||||||
NewHTTPFlvTsConnection(func(c *HTTPFlvTsConnection) {
|
newHTTPFlvTsConnection(func(c *httpFlvTsConnection) {
|
||||||
c.ctx = ctx
|
c.ctx = ctx
|
||||||
}).ServeHTTP(w, r)
|
}).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
|
|
@ -182,26 +187,26 @@ func (v *srsHTTPStreamServer) Run(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPFlvTsConnection is an HTTP pseudo streaming connection, such as an HTTP-FLV or HTTP-TS
|
// httpFlvTsConnection is an HTTP pseudo streaming connection, such as an HTTP-FLV or HTTP-TS
|
||||||
// connection. There is no state need to be sync between proxy servers.
|
// connection. There is no state need to be sync between proxy servers.
|
||||||
//
|
//
|
||||||
// When we got an HTTP FLV or TS request, we will parse the stream URL from the HTTP request,
|
// When we got an HTTP FLV or TS request, we will parse the stream URL from the HTTP request,
|
||||||
// then proxy to the corresponding backend server. All state is in the HTTP request, so this
|
// then proxy to the corresponding backend server. All state is in the HTTP request, so this
|
||||||
// connection is stateless.
|
// connection is stateless.
|
||||||
type HTTPFlvTsConnection struct {
|
type httpFlvTsConnection struct {
|
||||||
// The context for HTTP streaming.
|
// The context for HTTP streaming.
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPFlvTsConnection(opts ...func(*HTTPFlvTsConnection)) *HTTPFlvTsConnection {
|
func newHTTPFlvTsConnection(opts ...func(*httpFlvTsConnection)) *httpFlvTsConnection {
|
||||||
v := &HTTPFlvTsConnection{}
|
v := &httpFlvTsConnection{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HTTPFlvTsConnection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (v *httpFlvTsConnection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
ctx := logger.WithContext(v.ctx)
|
ctx := logger.WithContext(v.ctx)
|
||||||
|
|
||||||
|
|
@ -212,7 +217,7 @@ func (v *HTTPFlvTsConnection) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HTTPFlvTsConnection) serve(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
func (v *httpFlvTsConnection) serve(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
||||||
// Always allow CORS for all requests.
|
// Always allow CORS for all requests.
|
||||||
if ok := utils.ApiCORS(ctx, w, r); ok {
|
if ok := utils.ApiCORS(ctx, w, r); ok {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -240,7 +245,7 @@ func (v *HTTPFlvTsConnection) serve(ctx context.Context, w http.ResponseWriter,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HTTPFlvTsConnection) serveByBackend(ctx context.Context, w http.ResponseWriter, r *http.Request, backend *lb.SRSServer) error {
|
func (v *httpFlvTsConnection) serveByBackend(ctx context.Context, w http.ResponseWriter, r *http.Request, backend *lb.SRSServer) error {
|
||||||
// Parse HTTP port from backend.
|
// Parse HTTP port from backend.
|
||||||
if len(backend.HTTP) == 0 {
|
if len(backend.HTTP) == 0 {
|
||||||
return errors.Errorf("no http stream server")
|
return errors.Errorf("no http stream server")
|
||||||
|
|
@ -288,14 +293,14 @@ func (v *HTTPFlvTsConnection) serveByBackend(ctx context.Context, w http.Respons
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HLSPlayStream is an HLS stream proxy, which represents the stream level object. This means multiple HLS
|
// hlsPlayStream is an HLS stream proxy, which represents the stream level object. This means multiple HLS
|
||||||
// clients will share this object, and they do not use the same ctx among proxy servers.
|
// clients will share this object, and they do not use the same ctx among proxy servers.
|
||||||
//
|
//
|
||||||
// Unlike the HTTP FLV or TS connection, HLS client may request the m3u8 or ts via different HTTP connections.
|
// Unlike the HTTP FLV or TS connection, HLS client may request the m3u8 or ts via different HTTP connections.
|
||||||
// Especially for requesting ts, we need to identify the stream URl or backend server for it. So we create
|
// Especially for requesting ts, we need to identify the stream URl or backend server for it. So we create
|
||||||
// the spbhid which can be seen as the hash of stream URL or backend server. The spbhid enable us to convert
|
// the spbhid which can be seen as the hash of stream URL or backend server. The spbhid enable us to convert
|
||||||
// to the stream URL and then query the backend server to serve it.
|
// to the stream URL and then query the backend server to serve it.
|
||||||
type HLSPlayStream struct {
|
type hlsPlayStream struct {
|
||||||
// The context for HLS streaming.
|
// The context for HLS streaming.
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
|
|
@ -307,26 +312,26 @@ type HLSPlayStream struct {
|
||||||
FullURL string `json:"full_url"`
|
FullURL string `json:"full_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHLSPlayStream(opts ...func(*HLSPlayStream)) *HLSPlayStream {
|
func newHLSPlayStream(opts ...func(*hlsPlayStream)) *hlsPlayStream {
|
||||||
v := &HLSPlayStream{}
|
v := &hlsPlayStream{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HLSPlayStream) Initialize(ctx context.Context) lb.HLSPlayStream {
|
func (v *hlsPlayStream) Initialize(ctx context.Context) lb.HLSPlayStream {
|
||||||
if v.ctx == nil {
|
if v.ctx == nil {
|
||||||
v.ctx = logger.WithContext(ctx)
|
v.ctx = logger.WithContext(ctx)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HLSPlayStream) GetSPBHID() string {
|
func (v *hlsPlayStream) GetSPBHID() string {
|
||||||
return v.SRSProxyBackendHLSID
|
return v.SRSProxyBackendHLSID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HLSPlayStream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (v *hlsPlayStream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
|
|
||||||
if err := v.serve(v.ctx, w, r); err != nil {
|
if err := v.serve(v.ctx, w, r); err != nil {
|
||||||
|
|
@ -337,7 +342,7 @@ func (v *HLSPlayStream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HLSPlayStream) serve(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
func (v *hlsPlayStream) serve(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
||||||
ctx, streamURL, fullURL := v.ctx, v.StreamURL, v.FullURL
|
ctx, streamURL, fullURL := v.ctx, v.StreamURL, v.FullURL
|
||||||
|
|
||||||
// Always allow CORS for all requests.
|
// Always allow CORS for all requests.
|
||||||
|
|
@ -358,7 +363,7 @@ func (v *HLSPlayStream) serve(ctx context.Context, w http.ResponseWriter, r *htt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HLSPlayStream) serveByBackend(ctx context.Context, w http.ResponseWriter, r *http.Request, backend *lb.SRSServer) error {
|
func (v *hlsPlayStream) serveByBackend(ctx context.Context, w http.ResponseWriter, r *http.Request, backend *lb.SRSServer) error {
|
||||||
// Parse HTTP port from backend.
|
// Parse HTTP port from backend.
|
||||||
if len(backend.HTTP) == 0 {
|
if len(backend.HTTP) == 0 {
|
||||||
return errors.Errorf("no rtmp server")
|
return errors.Errorf("no rtmp server")
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,17 @@ import (
|
||||||
"srsx/internal/utils"
|
"srsx/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// srsWebRTCServer is the proxy for SRS WebRTC server via WHIP or WHEP protocol. It will figure out
|
// WebRTCServer is the proxy for SRS WebRTC server via WHIP or WHEP protocol. It will figure out
|
||||||
// which backend server to proxy to. It will also replace the UDP port to the proxy server's in the
|
// which backend server to proxy to. It will also replace the UDP port to the proxy server's in the
|
||||||
// SDP answer.
|
// SDP answer.
|
||||||
type srsWebRTCServer struct {
|
type WebRTCServer interface {
|
||||||
|
Run(ctx context.Context) error
|
||||||
|
Close() error
|
||||||
|
HandleApiForWHIP(ctx context.Context, w http.ResponseWriter, r *http.Request) error
|
||||||
|
HandleApiForWHEP(ctx context.Context, w http.ResponseWriter, r *http.Request) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type webRTCServer struct {
|
||||||
// The environment interface.
|
// The environment interface.
|
||||||
environment env.ProxyEnvironment
|
environment env.ProxyEnvironment
|
||||||
// The UDP listener for WebRTC server.
|
// The UDP listener for WebRTC server.
|
||||||
|
|
@ -34,21 +41,21 @@ type srsWebRTCServer struct {
|
||||||
|
|
||||||
// Fast cache for the username to identify the connection.
|
// Fast cache for the username to identify the connection.
|
||||||
// The key is username, the value is the UDP address.
|
// The key is username, the value is the UDP address.
|
||||||
usernames sync.Map[string, *RTCConnection]
|
usernames sync.Map[string, *rtcConnection]
|
||||||
// Fast cache for the udp address to identify the connection.
|
// Fast cache for the udp address to identify the connection.
|
||||||
// The key is UDP address, the value is the username.
|
// The key is UDP address, the value is the username.
|
||||||
// TODO: Support fast earch by uint64 address.
|
// TODO: Support fast earch by uint64 address.
|
||||||
addresses sync.Map[string, *RTCConnection]
|
addresses sync.Map[string, *rtcConnection]
|
||||||
|
|
||||||
// The wait group for server.
|
// The wait group for server.
|
||||||
wg stdSync.WaitGroup
|
wg stdSync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSRSWebRTCServer(environment env.ProxyEnvironment, opts ...func(*srsWebRTCServer)) *srsWebRTCServer {
|
func NewWebRTCServer(environment env.ProxyEnvironment, opts ...func(*webRTCServer)) WebRTCServer {
|
||||||
v := &srsWebRTCServer{
|
v := &webRTCServer{
|
||||||
environment: environment,
|
environment: environment,
|
||||||
usernames: sync.NewMap[string, *RTCConnection](),
|
usernames: sync.NewMap[string, *rtcConnection](),
|
||||||
addresses: sync.NewMap[string, *RTCConnection](),
|
addresses: sync.NewMap[string, *rtcConnection](),
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(v)
|
opt(v)
|
||||||
|
|
@ -56,7 +63,7 @@ func NewSRSWebRTCServer(environment env.ProxyEnvironment, opts ...func(*srsWebRT
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsWebRTCServer) Close() error {
|
func (v *webRTCServer) Close() error {
|
||||||
if v.listener != nil {
|
if v.listener != nil {
|
||||||
_ = v.listener.Close()
|
_ = v.listener.Close()
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +72,7 @@ func (v *srsWebRTCServer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsWebRTCServer) HandleApiForWHIP(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
func (v *webRTCServer) HandleApiForWHIP(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
ctx = logger.WithContext(ctx)
|
ctx = logger.WithContext(ctx)
|
||||||
|
|
||||||
|
|
@ -102,7 +109,7 @@ func (v *srsWebRTCServer) HandleApiForWHIP(ctx context.Context, w http.ResponseW
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsWebRTCServer) HandleApiForWHEP(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
func (v *webRTCServer) HandleApiForWHEP(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
ctx = logger.WithContext(ctx)
|
ctx = logger.WithContext(ctx)
|
||||||
|
|
||||||
|
|
@ -139,7 +146,7 @@ func (v *srsWebRTCServer) HandleApiForWHEP(ctx context.Context, w http.ResponseW
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsWebRTCServer) proxyApiToBackend(
|
func (v *webRTCServer) proxyApiToBackend(
|
||||||
ctx context.Context, w http.ResponseWriter, r *http.Request, backend *lb.SRSServer,
|
ctx context.Context, w http.ResponseWriter, r *http.Request, backend *lb.SRSServer,
|
||||||
remoteSDPOffer string, streamURL string,
|
remoteSDPOffer string, streamURL string,
|
||||||
) error {
|
) error {
|
||||||
|
|
@ -215,11 +222,11 @@ func (v *srsWebRTCServer) proxyApiToBackend(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the new WebRTC connection to LB.
|
// Save the new WebRTC connection to LB.
|
||||||
icePair := &RTCICEPair{
|
icePair := &rtcICEPair{
|
||||||
RemoteICEUfrag: remoteICEUfrag, RemoteICEPwd: remoteICEPwd,
|
RemoteICEUfrag: remoteICEUfrag, RemoteICEPwd: remoteICEPwd,
|
||||||
LocalICEUfrag: localICEUfrag, LocalICEPwd: localICEPwd,
|
LocalICEUfrag: localICEUfrag, LocalICEPwd: localICEPwd,
|
||||||
}
|
}
|
||||||
if err := lb.SrsLoadBalancer.StoreWebRTC(ctx, streamURL, NewRTCConnection(func(c *RTCConnection) {
|
if err := lb.SrsLoadBalancer.StoreWebRTC(ctx, streamURL, newRTCConnection(func(c *rtcConnection) {
|
||||||
c.StreamURL, c.Ufrag = streamURL, icePair.Ufrag()
|
c.StreamURL, c.Ufrag = streamURL, icePair.Ufrag()
|
||||||
c.Initialize(ctx, v.listener)
|
c.Initialize(ctx, v.listener)
|
||||||
|
|
||||||
|
|
@ -239,7 +246,7 @@ func (v *srsWebRTCServer) proxyApiToBackend(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsWebRTCServer) Run(ctx context.Context) error {
|
func (v *webRTCServer) Run(ctx context.Context) error {
|
||||||
// Parse address to listen.
|
// Parse address to listen.
|
||||||
endpoint := v.environment.WebRTCServer()
|
endpoint := v.environment.WebRTCServer()
|
||||||
if !strings.Contains(endpoint, ":") {
|
if !strings.Contains(endpoint, ":") {
|
||||||
|
|
@ -287,8 +294,8 @@ func (v *srsWebRTCServer) Run(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsWebRTCServer) handleClientUDP(ctx context.Context, addr *net.UDPAddr, data []byte) error {
|
func (v *webRTCServer) handleClientUDP(ctx context.Context, addr *net.UDPAddr, data []byte) error {
|
||||||
var connection *RTCConnection
|
var connection *rtcConnection
|
||||||
|
|
||||||
// If STUN binding request, parse the ufrag and identify the connection.
|
// If STUN binding request, parse the ufrag and identify the connection.
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
|
|
@ -296,7 +303,7 @@ func (v *srsWebRTCServer) handleClientUDP(ctx context.Context, addr *net.UDPAddr
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var pkt RTCStunPacket
|
var pkt rtcStunPacket
|
||||||
if err := pkt.UnmarshalBinary(data); err != nil {
|
if err := pkt.UnmarshalBinary(data); err != nil {
|
||||||
return errors.Wrapf(err, "unmarshal stun packet")
|
return errors.Wrapf(err, "unmarshal stun packet")
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +318,7 @@ func (v *srsWebRTCServer) handleClientUDP(ctx context.Context, addr *net.UDPAddr
|
||||||
if s, err := lb.SrsLoadBalancer.LoadWebRTCByUfrag(ctx, pkt.Username); err != nil {
|
if s, err := lb.SrsLoadBalancer.LoadWebRTCByUfrag(ctx, pkt.Username); err != nil {
|
||||||
return errors.Wrapf(err, "load webrtc by ufrag %v", pkt.Username)
|
return errors.Wrapf(err, "load webrtc by ufrag %v", pkt.Username)
|
||||||
} else {
|
} else {
|
||||||
connection = s.(*RTCConnection).Initialize(ctx, v.listener)
|
connection = s.(*rtcConnection).Initialize(ctx, v.listener)
|
||||||
logger.Debug(ctx, "Create WebRTC connection by ufrag=%v, stream=%v", pkt.Username, connection.StreamURL)
|
logger.Debug(ctx, "Create WebRTC connection by ufrag=%v, stream=%v", pkt.Username, connection.StreamURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -346,17 +353,17 @@ func (v *srsWebRTCServer) handleClientUDP(ctx context.Context, addr *net.UDPAddr
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTCConnection is a WebRTC connection proxy, for both WHIP and WHEP. It represents a WebRTC
|
// rtcConnection is a WebRTC connection proxy, for both WHIP and WHEP. It represents a WebRTC
|
||||||
// connection, identify by the ufrag in sdp offer/answer and ICE binding request.
|
// connection, identify by the ufrag in sdp offer/answer and ICE binding request.
|
||||||
//
|
//
|
||||||
// It's not like RTMP or HTTP FLV/TS proxy connection, which are stateless and all state is
|
// It's not like RTMP or HTTP FLV/TS proxy connection, which are stateless and all state is
|
||||||
// in the client request. The RTCConnection is stateful, and need to sync the ufrag between
|
// in the client request. The rtcConnection is stateful, and need to sync the ufrag between
|
||||||
// proxy servers.
|
// proxy servers.
|
||||||
//
|
//
|
||||||
// The media transport is UDP, which is also a special thing for WebRTC. So if the client switch
|
// The media transport is UDP, which is also a special thing for WebRTC. So if the client switch
|
||||||
// to another UDP address, it may connect to another WebRTC proxy, then we should discover the
|
// to another UDP address, it may connect to another WebRTC proxy, then we should discover the
|
||||||
// RTCConnection by the ufrag from the ICE binding request.
|
// rtcConnection by the ufrag from the ICE binding request.
|
||||||
type RTCConnection struct {
|
type rtcConnection struct {
|
||||||
// The stream context for WebRTC streaming.
|
// The stream context for WebRTC streaming.
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
|
|
@ -373,15 +380,15 @@ type RTCConnection struct {
|
||||||
listenerUDP *net.UDPConn
|
listenerUDP *net.UDPConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRTCConnection(opts ...func(*RTCConnection)) *RTCConnection {
|
func newRTCConnection(opts ...func(*rtcConnection)) *rtcConnection {
|
||||||
v := &RTCConnection{}
|
v := &rtcConnection{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCConnection) Initialize(ctx context.Context, listener *net.UDPConn) *RTCConnection {
|
func (v *rtcConnection) Initialize(ctx context.Context, listener *net.UDPConn) *rtcConnection {
|
||||||
if v.ctx == nil {
|
if v.ctx == nil {
|
||||||
v.ctx = logger.WithContext(ctx)
|
v.ctx = logger.WithContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
@ -391,11 +398,11 @@ func (v *RTCConnection) Initialize(ctx context.Context, listener *net.UDPConn) *
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCConnection) GetUfrag() string {
|
func (v *rtcConnection) GetUfrag() string {
|
||||||
return v.Ufrag
|
return v.Ufrag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCConnection) HandlePacket(addr *net.UDPAddr, data []byte) error {
|
func (v *rtcConnection) HandlePacket(addr *net.UDPAddr, data []byte) error {
|
||||||
ctx := v.ctx
|
ctx := v.ctx
|
||||||
|
|
||||||
// Update the current UDP address.
|
// Update the current UDP address.
|
||||||
|
|
@ -437,7 +444,7 @@ func (v *RTCConnection) HandlePacket(addr *net.UDPAddr, data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCConnection) connectBackend(ctx context.Context) error {
|
func (v *rtcConnection) connectBackend(ctx context.Context) error {
|
||||||
if v.backendUDP != nil {
|
if v.backendUDP != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -470,7 +477,7 @@ func (v *RTCConnection) connectBackend(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type RTCICEPair struct {
|
type rtcICEPair struct {
|
||||||
// The remote ufrag, used for ICE username and session id.
|
// The remote ufrag, used for ICE username and session id.
|
||||||
RemoteICEUfrag string `json:"remote_ufrag"`
|
RemoteICEUfrag string `json:"remote_ufrag"`
|
||||||
// The remote pwd, used for ICE password.
|
// The remote pwd, used for ICE password.
|
||||||
|
|
@ -482,18 +489,18 @@ type RTCICEPair struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the ICE ufrag for the WebRTC streaming, format is remote-ufrag:local-ufrag.
|
// Generate the ICE ufrag for the WebRTC streaming, format is remote-ufrag:local-ufrag.
|
||||||
func (v *RTCICEPair) Ufrag() string {
|
func (v *rtcICEPair) Ufrag() string {
|
||||||
return fmt.Sprintf("%v:%v", v.LocalICEUfrag, v.RemoteICEUfrag)
|
return fmt.Sprintf("%v:%v", v.LocalICEUfrag, v.RemoteICEUfrag)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RTCStunPacket struct {
|
type rtcStunPacket struct {
|
||||||
// The stun message type.
|
// The stun message type.
|
||||||
MessageType uint16
|
MessageType uint16
|
||||||
// The stun username, or ufrag.
|
// The stun username, or ufrag.
|
||||||
Username string
|
Username string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCStunPacket) UnmarshalBinary(data []byte) error {
|
func (v *rtcStunPacket) UnmarshalBinary(data []byte) error {
|
||||||
if len(data) < 20 {
|
if len(data) < 20 {
|
||||||
return errors.Errorf("stun packet too short %v", len(data))
|
return errors.Errorf("stun packet too short %v", len(data))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,15 @@ import (
|
||||||
"srsx/internal/version"
|
"srsx/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// srsRTMPServer is the proxy for SRS RTMP server, to proxy the RTMP stream to backend SRS
|
// RTMPServer is the proxy for SRS RTMP server, to proxy the RTMP stream to backend SRS
|
||||||
// server. It will figure out the backend server to proxy to. Unlike the edge server, it will
|
// server. It will figure out the backend server to proxy to. Unlike the edge server, it will
|
||||||
// not cache the stream, but just proxy the stream to backend.
|
// not cache the stream, but just proxy the stream to backend.
|
||||||
type srsRTMPServer struct {
|
type RTMPServer interface {
|
||||||
|
Run(ctx context.Context) error
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type rtmpServer struct {
|
||||||
// The environment interface.
|
// The environment interface.
|
||||||
environment env.ProxyEnvironment
|
environment env.ProxyEnvironment
|
||||||
// The TCP listener for RTMP server.
|
// The TCP listener for RTMP server.
|
||||||
|
|
@ -32,15 +37,15 @@ type srsRTMPServer struct {
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSRSRTMPServer(environment env.ProxyEnvironment, opts ...func(*srsRTMPServer)) *srsRTMPServer {
|
func NewRTMPServer(environment env.ProxyEnvironment, opts ...func(*rtmpServer)) RTMPServer {
|
||||||
v := &srsRTMPServer{environment: environment}
|
v := &rtmpServer{environment: environment}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsRTMPServer) Close() error {
|
func (v *rtmpServer) Close() error {
|
||||||
if v.listener != nil {
|
if v.listener != nil {
|
||||||
v.listener.Close()
|
v.listener.Close()
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +54,7 @@ func (v *srsRTMPServer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *srsRTMPServer) Run(ctx context.Context) error {
|
func (v *rtmpServer) Run(ctx context.Context) error {
|
||||||
endpoint := v.environment.RtmpServer()
|
endpoint := v.environment.RtmpServer()
|
||||||
if !strings.Contains(endpoint, ":") {
|
if !strings.Contains(endpoint, ":") {
|
||||||
endpoint = ":" + endpoint
|
endpoint = ":" + endpoint
|
||||||
|
|
@ -97,7 +102,7 @@ func (v *srsRTMPServer) Run(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rc := NewRTMPConnection()
|
rc := newRTMPConnection()
|
||||||
if err := rc.serve(ctx, conn); err != nil {
|
if err := rc.serve(ctx, conn); err != nil {
|
||||||
handleErr(err)
|
handleErr(err)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -110,24 +115,24 @@ func (v *srsRTMPServer) Run(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTMPConnection is an RTMP streaming connection. There is no state need to be sync between
|
// rtmpConnection is an RTMP streaming connection. There is no state need to be sync between
|
||||||
// proxy servers.
|
// proxy servers.
|
||||||
//
|
//
|
||||||
// When we got an RTMP request, we will parse the stream URL from the RTMP publish or play request,
|
// When we got an RTMP request, we will parse the stream URL from the RTMP publish or play request,
|
||||||
// then proxy to the corresponding backend server. All state is in the RTMP request, so this
|
// then proxy to the corresponding backend server. All state is in the RTMP request, so this
|
||||||
// connection is stateless.
|
// connection is stateless.
|
||||||
type RTMPConnection struct {
|
type rtmpConnection struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRTMPConnection(opts ...func(*RTMPConnection)) *RTMPConnection {
|
func newRTMPConnection(opts ...func(*rtmpConnection)) *rtmpConnection {
|
||||||
v := &RTMPConnection{}
|
v := &rtmpConnection{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTMPConnection) serve(ctx context.Context, conn *net.TCPConn) error {
|
func (v *rtmpConnection) serve(ctx context.Context, conn *net.TCPConn) error {
|
||||||
logger.Debug(ctx, "Got RTMP client from %v", conn.RemoteAddr())
|
logger.Debug(ctx, "Got RTMP client from %v", conn.RemoteAddr())
|
||||||
|
|
||||||
// If any goroutine quit, cancel another one.
|
// If any goroutine quit, cancel another one.
|
||||||
|
|
@ -135,7 +140,7 @@ func (v *RTMPConnection) serve(ctx context.Context, conn *net.TCPConn) error {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
var backend *RTMPClientToBackend
|
var backend *rtmpClientToBackend
|
||||||
if true {
|
if true {
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
@ -289,7 +294,7 @@ func (v *RTMPConnection) serve(ctx context.Context, conn *net.TCPConn) error {
|
||||||
tcUrl, streamName, currentStreamID, clientType)
|
tcUrl, streamName, currentStreamID, clientType)
|
||||||
|
|
||||||
// Find a backend SRS server to proxy the RTMP stream.
|
// Find a backend SRS server to proxy the RTMP stream.
|
||||||
backend = NewRTMPClientToBackend(func(client *RTMPClientToBackend) {
|
backend = newRTMPClientToBackend(func(client *rtmpClientToBackend) {
|
||||||
client.typ = clientType
|
client.typ = clientType
|
||||||
})
|
})
|
||||||
defer backend.Close()
|
defer backend.Close()
|
||||||
|
|
@ -416,8 +421,8 @@ const (
|
||||||
RTMPClientTypeViewer RTMPClientType = "viewer"
|
RTMPClientTypeViewer RTMPClientType = "viewer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RTMPClientToBackend is a RTMP client to proxy the RTMP stream to backend.
|
// rtmpClientToBackend is an RTMP client to proxy the RTMP stream to backend.
|
||||||
type RTMPClientToBackend struct {
|
type rtmpClientToBackend struct {
|
||||||
// The underlayer tcp client.
|
// The underlayer tcp client.
|
||||||
tcpConn *net.TCPConn
|
tcpConn *net.TCPConn
|
||||||
// The RTMP protocol client.
|
// The RTMP protocol client.
|
||||||
|
|
@ -426,22 +431,22 @@ type RTMPClientToBackend struct {
|
||||||
typ RTMPClientType
|
typ RTMPClientType
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRTMPClientToBackend(opts ...func(*RTMPClientToBackend)) *RTMPClientToBackend {
|
func newRTMPClientToBackend(opts ...func(*rtmpClientToBackend)) *rtmpClientToBackend {
|
||||||
v := &RTMPClientToBackend{}
|
v := &rtmpClientToBackend{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTMPClientToBackend) Close() error {
|
func (v *rtmpClientToBackend) Close() error {
|
||||||
if v.tcpConn != nil {
|
if v.tcpConn != nil {
|
||||||
v.tcpConn.Close()
|
v.tcpConn.Close()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTMPClientToBackend) Connect(ctx context.Context, tcUrl, streamName string) error {
|
func (v *rtmpClientToBackend) Connect(ctx context.Context, tcUrl, streamName string) error {
|
||||||
// Build the stream URL in vhost/app/stream schema.
|
// Build the stream URL in vhost/app/stream schema.
|
||||||
streamURL, err := utils.BuildStreamURL(fmt.Sprintf("%v/%v", tcUrl, streamName))
|
streamURL, err := utils.BuildStreamURL(fmt.Sprintf("%v/%v", tcUrl, streamName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -527,7 +532,7 @@ func (v *RTMPClientToBackend) Connect(ctx context.Context, tcUrl, streamName str
|
||||||
return v.publish(ctx, client, streamName)
|
return v.publish(ctx, client, streamName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTMPClientToBackend) publish(ctx context.Context, client rtmp.Protocol, streamName string) error {
|
func (v *rtmpClientToBackend) publish(ctx context.Context, client rtmp.Protocol, streamName string) error {
|
||||||
if true {
|
if true {
|
||||||
identifyReq := rtmp.NewCallPacket()
|
identifyReq := rtmp.NewCallPacket()
|
||||||
identifyReq.CommandName = "releaseStream"
|
identifyReq.CommandName = "releaseStream"
|
||||||
|
|
@ -620,7 +625,7 @@ func (v *RTMPClientToBackend) publish(ctx context.Context, client rtmp.Protocol,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTMPClientToBackend) play(ctx context.Context, client rtmp.Protocol, streamName string) error {
|
func (v *rtmpClientToBackend) play(ctx context.Context, client rtmp.Protocol, streamName string) error {
|
||||||
var currentStreamID int
|
var currentStreamID int
|
||||||
if true {
|
if true {
|
||||||
createStream := rtmp.NewCreateStreamPacket()
|
createStream := rtmp.NewCreateStreamPacket()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user