diff --git a/README.md b/README.md index dbdc1df7..c50329c6 100644 --- a/README.md +++ b/README.md @@ -1051,9 +1051,7 @@ vlc --network-caching=50 rtsp://... ### General usage -RTMP is a protocol that allows to read and publish streams, but is less versatile and less efficient than RTSP (doesn't support UDP, encryption, doesn't support most RTSP codecs, doesn't support feedback mechanism). It is used when there's need of publishing or reading streams from a software that supports only RTMP (for instance, OBS Studio and DJI drones). - -At the moment, only the H264 and AAC codecs can be used with the RTMP protocol. +RTMP is a protocol that allows to read and publish streams, but is less versatile and less efficient than RTSP (doesn't support UDP, encryption, doesn't support most RTSP codecs, doesn't support feedback mechanism). It is used when there's need of publishing or reading streams from a software that supports RTMP only (for instance, OBS Studio and DJI drones). Streams can be published or read with the RTMP protocol, for instance with _FFmpeg_: @@ -1213,7 +1211,7 @@ http://localhost:8889/mystream ### WHIP and WHEP -WHIP and WHEP are two WebRTC extensions that allows to publish and read streams with WebRTC without passing through a web page. This allows to use WebRTC as a general purpose streaming protocol. +WHIP and WHEP are two WebRTC extensions that allow to publish and read streams with WebRTC without passing through a web page. This allows to use WebRTC as a general purpose streaming protocol. If you are using a software that supports WHIP, you can publish a stream to the server by using this WHIP URL: @@ -1270,15 +1268,21 @@ bluenviron/mediamtx Finally, if none of these methods work, you can force all WebRTC/ICE connections to pass through a TURN server, like [coturn](https://github.com/coturn/coturn), that must be configured externally. The server address and credentials must be set in the configuration file: ```yml -webrtcICEServers: [turn:user:pass:host:port] +webrtcICEServers2: +- url: turn:host:port + username: user + password: password ``` Where `user` and `pass` are the username and password of the server. Note that `port` is not optional. -If the server uses a secret-based authentication (for instance, coturn with the `use-auth-secret` option), it must be configured in this way: +If the server uses a secret-based authentication (for instance, coturn with the `use-auth-secret` option), it must be configured by using `AUTH_SECRET` as username, and the secret as password: ```yml -webrtcICEServers: [turn:AUTH_SECRET:secret:host:port] +webrtcICEServers2: +- url: turn:host:port + username: AUTH_SECRET + password: secret ``` where `secret` is the secret of the TURN server. _MediaMTX_ will generate a set of credentials by using the secret, and credentials will be sent to clients before the WebRTC/ICE connection is established. diff --git a/apidocs/openapi.yaml b/apidocs/openapi.yaml index 9d3ce631..18cbab01 100644 --- a/apidocs/openapi.yaml +++ b/apidocs/openapi.yaml @@ -149,10 +149,17 @@ components: type: array items: type: string - webrtcICEServers: + webrtcICEServers2: type: array items: - type: string + type: object + properties: + url: + type: string + username: + type: string + password: + type: string webrtcICEHostNAT1To1IPs: type: array items: diff --git a/internal/conf/authmethod.go b/internal/conf/auth_method.go similarity index 100% rename from internal/conf/authmethod.go rename to internal/conf/auth_method.go diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 7c489e22..75c9a6b6 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -137,17 +137,18 @@ type Conf struct { HLSDirectory string `json:"hlsDirectory"` // WebRTC - WebRTCDisable bool `json:"webrtcDisable"` - WebRTCAddress string `json:"webrtcAddress"` - WebRTCEncryption bool `json:"webrtcEncryption"` - WebRTCServerKey string `json:"webrtcServerKey"` - WebRTCServerCert string `json:"webrtcServerCert"` - WebRTCAllowOrigin string `json:"webrtcAllowOrigin"` - WebRTCTrustedProxies IPsOrCIDRs `json:"webrtcTrustedProxies"` - WebRTCICEServers []string `json:"webrtcICEServers"` - WebRTCICEHostNAT1To1IPs []string `json:"webrtcICEHostNAT1To1IPs"` - WebRTCICEUDPMuxAddress string `json:"webrtcICEUDPMuxAddress"` - WebRTCICETCPMuxAddress string `json:"webrtcICETCPMuxAddress"` + WebRTCDisable bool `json:"webrtcDisable"` + WebRTCAddress string `json:"webrtcAddress"` + WebRTCEncryption bool `json:"webrtcEncryption"` + WebRTCServerKey string `json:"webrtcServerKey"` + WebRTCServerCert string `json:"webrtcServerCert"` + WebRTCAllowOrigin string `json:"webrtcAllowOrigin"` + WebRTCTrustedProxies IPsOrCIDRs `json:"webrtcTrustedProxies"` + WebRTCICEServers []string `json:"webrtcICEServers"` // deprecated + WebRTCICEServers2 []WebRTCICEServer `json:"webrtcICEServers2"` + WebRTCICEHostNAT1To1IPs []string `json:"webrtcICEHostNAT1To1IPs"` + WebRTCICEUDPMuxAddress string `json:"webrtcICEUDPMuxAddress"` + WebRTCICETCPMuxAddress string `json:"webrtcICETCPMuxAddress"` // paths Paths map[string]*PathConf `json:"paths"` @@ -237,10 +238,25 @@ func (conf *Conf) Check() error { // WebRTC for _, server := range conf.WebRTCICEServers { - if !strings.HasPrefix(server, "stun:") && - !strings.HasPrefix(server, "turn:") && - !strings.HasPrefix(server, "turns:") { - return fmt.Errorf("invalid ICE server: '%s'", server) + parts := strings.Split(server, ":") + if len(parts) == 5 { + conf.WebRTCICEServers2 = append(conf.WebRTCICEServers2, WebRTCICEServer{ + URL: parts[0] + ":" + parts[3] + ":" + parts[4], + Username: parts[1], + Password: parts[2], + }) + } else { + conf.WebRTCICEServers2 = append(conf.WebRTCICEServers2, WebRTCICEServer{ + URL: server, + }) + } + } + conf.WebRTCICEServers = nil + for _, server := range conf.WebRTCICEServers2 { + if !strings.HasPrefix(server.URL, "stun:") && + !strings.HasPrefix(server.URL, "turn:") && + !strings.HasPrefix(server.URL, "turns:") { + return fmt.Errorf("invalid ICE server: '%s'", server.URL) } } @@ -324,7 +340,7 @@ func (conf *Conf) UnmarshalJSON(b []byte) error { conf.WebRTCServerKey = "server.key" conf.WebRTCServerCert = "server.crt" conf.WebRTCAllowOrigin = "*" - conf.WebRTCICEServers = []string{"stun:stun.l.google.com:19302"} + conf.WebRTCICEServers2 = []WebRTCICEServer{{URL: "stun:stun.l.google.com:19302"}} type alias Conf d := json.NewDecoder(bytes.NewReader(b)) diff --git a/internal/conf/env/env.go b/internal/conf/env/env.go index eb54ff8c..393370b2 100644 --- a/internal/conf/env/env.go +++ b/internal/conf/env/env.go @@ -14,6 +14,15 @@ type envUnmarshaler interface { UnmarshalEnv(string) error } +func envHasAtLeastAKeyWithPrefix(env map[string]string, prefix string) bool { + for key := range env { + if strings.HasPrefix(key, prefix) { + return true + } + } + return false +} + func loadEnvInternal(env map[string]string, prefix string, rv reflect.Value) error { rt := rv.Type() @@ -148,6 +157,24 @@ func loadEnvInternal(env map[string]string, prefix string, rv reflect.Value) err } return nil } + + if rt.Elem().Kind() == reflect.Struct { + for i := 0; ; i++ { + itemPrefix := prefix + "_" + strconv.FormatInt(int64(i), 10) + if !envHasAtLeastAKeyWithPrefix(env, itemPrefix) { + break + } + + elem := reflect.New(rt.Elem()) + err := loadEnvInternal(env, itemPrefix, elem.Elem()) + if err != nil { + return err + } + + rv.Set(reflect.Append(rv, elem.Elem())) + } + return nil + } } return fmt.Errorf("unsupported type: %v", rt) diff --git a/internal/conf/env/env_test.go b/internal/conf/env/env_test.go index b3ed489f..0f2338f7 100644 --- a/internal/conf/env/env_test.go +++ b/internal/conf/env/env_test.go @@ -40,15 +40,22 @@ func (d *myDuration) UnmarshalEnv(s string) error { return d.UnmarshalJSON([]byte(`"` + s + `"`)) } +type mySubStruct struct { + URL string + Username string + Password string +} + type testStruct struct { - MyString string - MyInt int - MyFloat float64 - MyBool bool - MyDuration myDuration - MyMap map[string]*mapEntry - MySlice []string - MySliceEmpty []string + MyString string + MyInt int + MyFloat float64 + MyBool bool + MyDuration myDuration + MyMap map[string]*mapEntry + MySlice []string + MySliceEmpty []string + MySliceSubStruct []mySubStruct } func TestLoad(t *testing.T) { @@ -82,6 +89,21 @@ func TestLoad(t *testing.T) { os.Setenv("MYPREFIX_MYSLICEEMPTY", "") defer os.Unsetenv("MYPREFIX_MYSLICEEMPTY") + os.Setenv("MYPREFIX_MYSLICESUBSTRUCT_0_URL", "url1") + defer os.Unsetenv("MYPREFIX_MYSLICESUBSTRUCT_0_URL") + + os.Setenv("MYPREFIX_MYSLICESUBSTRUCT_0_USERNAME", "user1") + defer os.Unsetenv("MYPREFIX_MYSLICESUBSTRUCT_0_USERNAME") + + os.Setenv("MYPREFIX_MYSLICESUBSTRUCT_0_PASSWORD", "pass1") + defer os.Unsetenv("MYPREFIX_MYSLICESUBSTRUCT_0_PASSWORD") + + os.Setenv("MYPREFIX_MYSLICESUBSTRUCT_1_URL", "url2") + defer os.Unsetenv("MYPREFIX_MYSLICESUBSTRUCT_1_URL") + + os.Setenv("MYPREFIX_MYSLICESUBSTRUCT_1_PASSWORD", "pass2") + defer os.Unsetenv("MYPREFIX_MYSLICESUBSTRUCT_1_PASSWORD") + var s testStruct err := Load("MYPREFIX", &s) require.NoError(t, err) @@ -102,4 +124,16 @@ func TestLoad(t *testing.T) { require.Equal(t, []string{"val1", "val2"}, s.MySlice) require.Equal(t, []string{}, s.MySliceEmpty) + + require.Equal(t, []mySubStruct{ + { + URL: "url1", + Username: "user1", + Password: "pass1", + }, + { + URL: "url2", + Password: "pass2", + }, + }, s.MySliceSubStruct) } diff --git a/internal/conf/hlsvariant.go b/internal/conf/hls_variant.go similarity index 100% rename from internal/conf/hlsvariant.go rename to internal/conf/hls_variant.go diff --git a/internal/conf/ipsorcidrs.go b/internal/conf/ips_or_cidrs.go similarity index 100% rename from internal/conf/ipsorcidrs.go rename to internal/conf/ips_or_cidrs.go diff --git a/internal/conf/logdestination.go b/internal/conf/log_destination.go similarity index 100% rename from internal/conf/logdestination.go rename to internal/conf/log_destination.go diff --git a/internal/conf/loglevel.go b/internal/conf/log_level.go similarity index 100% rename from internal/conf/loglevel.go rename to internal/conf/log_level.go diff --git a/internal/conf/rtsprangetype.go b/internal/conf/rtsp_range_type.go similarity index 100% rename from internal/conf/rtsprangetype.go rename to internal/conf/rtsp_range_type.go diff --git a/internal/conf/sourceprotocol.go b/internal/conf/source_protocol.go similarity index 100% rename from internal/conf/sourceprotocol.go rename to internal/conf/source_protocol.go diff --git a/internal/conf/stringduration.go b/internal/conf/string_duration.go similarity index 100% rename from internal/conf/stringduration.go rename to internal/conf/string_duration.go diff --git a/internal/conf/stringsize.go b/internal/conf/string_size.go similarity index 100% rename from internal/conf/stringsize.go rename to internal/conf/string_size.go diff --git a/internal/conf/webrtc_ice_server.go b/internal/conf/webrtc_ice_server.go new file mode 100644 index 00000000..f8acc197 --- /dev/null +++ b/internal/conf/webrtc_ice_server.go @@ -0,0 +1,8 @@ +package conf + +// WebRTCICEServer is a WebRTC ICE Server. +type WebRTCICEServer struct { + URL string `json:"url"` + Username string `json:"username"` + Password string `json:"password"` +} diff --git a/internal/core/core.go b/internal/core/core.go index af0dd97c..a794c53a 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -424,7 +424,7 @@ func (p *Core) createResources(initial bool) error { p.conf.WebRTCServerCert, p.conf.WebRTCAllowOrigin, p.conf.WebRTCTrustedProxies, - p.conf.WebRTCICEServers, + p.conf.WebRTCICEServers2, p.conf.ReadTimeout, p.conf.ReadBufferCount, p.pathManager, @@ -594,7 +594,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) { newConf.WebRTCServerCert != p.conf.WebRTCServerCert || newConf.WebRTCAllowOrigin != p.conf.WebRTCAllowOrigin || !reflect.DeepEqual(newConf.WebRTCTrustedProxies, p.conf.WebRTCTrustedProxies) || - !reflect.DeepEqual(newConf.WebRTCICEServers, p.conf.WebRTCICEServers) || + !reflect.DeepEqual(newConf.WebRTCICEServers2, p.conf.WebRTCICEServers2) || newConf.ReadTimeout != p.conf.ReadTimeout || newConf.ReadBufferCount != p.conf.ReadBufferCount || closeMetrics || diff --git a/internal/core/webrtc_http_server.go b/internal/core/webrtc_http_server.go index 02457517..83c65cbd 100644 --- a/internal/core/webrtc_http_server.go +++ b/internal/core/webrtc_http_server.go @@ -2,10 +2,12 @@ package core import ( _ "embed" + "encoding/json" "fmt" "io" "net" "net/http" + "regexp" "strconv" "strings" @@ -24,6 +26,59 @@ var webrtcPublishIndex []byte //go:embed webrtc_read_index.html var webrtcReadIndex []byte +func quoteCredential(v string) string { + b, _ := json.Marshal(v) + s := string(b) + return s[1 : len(s)-1] +} + +func unquoteCredential(v string) string { + var s string + json.Unmarshal([]byte("\""+v+"\""), &s) + return s +} + +func iceServersToLinkHeader(iceServers []webrtc.ICEServer) []string { + ret := make([]string, len(iceServers)) + + for i, server := range iceServers { + link := "<" + server.URLs[0] + ">; rel=\"ice-server\"" + if server.Username != "" { + link += "; username=\"" + quoteCredential(server.Username) + "\"" + + "; credential=\"" + quoteCredential(server.Credential.(string)) + "\"; credential-type=\"password\"" + } + ret[i] = link + } + + return ret +} + +var reLink = regexp.MustCompile(`^<(.+?)>; rel="ice-server"(; username="(.+?)"` + + `; credential="(.+?)"; credential-type="password")?`) + +func linkHeaderToIceServers(link []string) []webrtc.ICEServer { + var ret []webrtc.ICEServer + + for _, li := range link { + m := reLink.FindStringSubmatch(li) + if m != nil { + s := webrtc.ICEServer{ + URLs: []string{m[1]}, + } + + if m[3] != "" { + s.Username = unquoteCredential(m[3]) + s.Credential = unquoteCredential(m[4]) + s.CredentialType = webrtc.ICECredentialTypePassword + } + + ret = append(ret, s) + } + } + + return ret +} + func unmarshalICEFragment(buf []byte) ([]*webrtc.ICECandidateInit, error) { buf = append([]byte("v=0\r\no=- 0 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\n"), buf...) @@ -104,7 +159,7 @@ func marshalICEFragment(offer *webrtc.SessionDescription, candidates []*webrtc.I type webRTCHTTPServerParent interface { logger.Writer - genICEServers() []webrtc.ICEServer + generateICEServers() []webrtc.ICEServer sessionNew(req webRTCSessionNewReq) webRTCSessionNewRes sessionAddCandidates(req webRTCSessionAddCandidatesReq) webRTCSessionAddCandidatesRes } @@ -282,7 +337,7 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { case http.MethodOptions: ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PATCH") ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, If-Match") - ctx.Writer.Header()["Link"] = iceServersToLinkHeader(s.parent.genICEServers()) + ctx.Writer.Header()["Link"] = iceServersToLinkHeader(s.parent.generateICEServers()) ctx.Writer.WriteHeader(http.StatusOK) case http.MethodPost: @@ -314,7 +369,7 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { ctx.Writer.Header().Set("E-Tag", res.sx.secret.String()) ctx.Writer.Header().Set("ID", res.sx.uuid.String()) ctx.Writer.Header().Set("Accept-Patch", "application/trickle-ice-sdpfrag") - ctx.Writer.Header()["Link"] = iceServersToLinkHeader(s.parent.genICEServers()) + ctx.Writer.Header()["Link"] = iceServersToLinkHeader(s.parent.generateICEServers()) ctx.Writer.Header().Set("Location", ctx.Request.URL.String()) ctx.Writer.WriteHeader(http.StatusCreated) ctx.Writer.Write(res.answer) diff --git a/internal/core/webrtc_manager.go b/internal/core/webrtc_manager.go index 428dbdd0..bf699e7f 100644 --- a/internal/core/webrtc_manager.go +++ b/internal/core/webrtc_manager.go @@ -9,10 +9,8 @@ import ( "fmt" "net" "net/http" - "regexp" "sort" "strconv" - "strings" "sync" "time" @@ -24,46 +22,13 @@ import ( "github.com/bluenviron/mediamtx/internal/logger" ) -func iceServersToLinkHeader(iceServers []webrtc.ICEServer) []string { - ret := make([]string, len(iceServers)) - - for i, server := range iceServers { - link := "<" + server.URLs[0] + ">; rel=\"ice-server\"" - if server.Username != "" { - link += "; username=\"" + server.Username + "\"" + - "; credential=\"" + server.Credential.(string) + "\"; credential-type=\"password\"" - } - ret[i] = link - } - - return ret -} - -var reLink = regexp.MustCompile(`^<(.+?)>; rel="ice-server"(; username="(.+?)"` + - `; credential="(.+?)"; credential-type="password")?`) - -func linkHeaderToIceServers(link []string) []webrtc.ICEServer { - var ret []webrtc.ICEServer - - for _, li := range link { - m := reLink.FindStringSubmatch(li) - if m != nil { - s := webrtc.ICEServer{ - URLs: []string{m[1]}, - } - - if m[3] != "" { - s.Username = m[3] - s.Credential = m[4] - s.CredentialType = webrtc.ICECredentialTypePassword - } - - ret = append(ret, s) - } - } - - return ret -} +const ( + webrtcHandshakeTimeout = 10 * time.Second + webrtcTrackGatherTimeout = 2 * time.Second + webrtcPayloadMaxSize = 1188 // 1200 - 12 (RTP header) + webrtcStreamID = "mediamtx" + webrtcTurnSecretExpiration = 24 * 3600 * time.Second +) func randInt63() int64 { var b [8]byte @@ -155,7 +120,7 @@ type webRTCManagerParent interface { type webRTCManager struct { allowOrigin string trustedProxies conf.IPsOrCIDRs - iceServers []string + iceServers []conf.WebRTCICEServer readBufferCount int pathManager *pathManager metrics *metrics @@ -192,7 +157,7 @@ func newWebRTCManager( serverCert string, allowOrigin string, trustedProxies conf.IPsOrCIDRs, - iceServers []string, + iceServers []conf.WebRTCICEServer, readTimeout conf.StringDuration, readBufferCount int, pathManager *pathManager, @@ -395,35 +360,21 @@ func (m *webRTCManager) findSessionByUUID(uuid uuid.UUID) *webRTCSession { return nil } -func (m *webRTCManager) genICEServers() []webrtc.ICEServer { +func (m *webRTCManager) generateICEServers() []webrtc.ICEServer { ret := make([]webrtc.ICEServer, len(m.iceServers)) - for i, s := range m.iceServers { - parts := strings.Split(s, ":") - if len(parts) == 5 { - if parts[1] == "AUTH_SECRET" { - s := webrtc.ICEServer{ - URLs: []string{parts[0] + ":" + parts[3] + ":" + parts[4]}, - } + for i, server := range m.iceServers { + if server.Username == "AUTH_SECRET" { + expireDate := time.Now().Add(webrtcTurnSecretExpiration).Unix() + server.Username = strconv.FormatInt(expireDate, 10) + ":" + randomTurnUser() + h := hmac.New(sha1.New, []byte(server.Password)) + h.Write([]byte(server.Username)) + server.Password = base64.StdEncoding.EncodeToString(h.Sum(nil)) + } - expireDate := time.Now().Add(24 * 3600 * time.Second).Unix() - s.Username = strconv.FormatInt(expireDate, 10) + ":" + randomTurnUser() - - h := hmac.New(sha1.New, []byte(parts[2])) - h.Write([]byte(s.Username)) - s.Credential = base64.StdEncoding.EncodeToString(h.Sum(nil)) - - ret[i] = s - } else { - ret[i] = webrtc.ICEServer{ - URLs: []string{parts[0] + ":" + parts[3] + ":" + parts[4]}, - Username: parts[1], - Credential: parts[2], - } - } - } else { - ret[i] = webrtc.ICEServer{ - URLs: []string{s}, - } + ret[i] = webrtc.ICEServer{ + URLs: []string{server.URL}, + Username: server.Username, + Credential: server.Password, } } return ret diff --git a/internal/core/webrtc_publish_index.html b/internal/core/webrtc_publish_index.html index e5f678f7..7fd95cfe 100644 --- a/internal/core/webrtc_publish_index.html +++ b/internal/core/webrtc_publish_index.html @@ -118,6 +118,10 @@ const setState = (newState) => { const restartPause = 2000; +const unquoteCredential = (v) => ( + JSON.parse(`"${v}"`) +); + const linkToIceServers = (links) => ( (links !== null) ? links.split(', ').map((link) => { const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i); @@ -126,8 +130,8 @@ const linkToIceServers = (links) => ( }; if (m[3] !== undefined) { - ret.username = m[3]; - ret.credential = m[4]; + ret.username = unquoteCredential(m[3]); + ret.credential = unquoteCredential(m[4]); ret.credentialType = "password"; } diff --git a/internal/core/webrtc_read_index.html b/internal/core/webrtc_read_index.html index 0973da55..d68992f2 100644 --- a/internal/core/webrtc_read_index.html +++ b/internal/core/webrtc_read_index.html @@ -25,6 +25,10 @@ html, body { const restartPause = 2000; +const unquoteCredential = (v) => ( + JSON.parse(`"${v}"`) +); + const linkToIceServers = (links) => ( (links !== null) ? links.split(', ').map((link) => { const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i); @@ -33,8 +37,8 @@ const linkToIceServers = (links) => ( }; if (m[3] !== undefined) { - ret.username = m[3]; - ret.credential = m[4]; + ret.username = unquoteCredential(m[3]); + ret.credential = unquoteCredential(m[4]); ret.credentialType = "password"; } diff --git a/internal/core/webrtc_session.go b/internal/core/webrtc_session.go index b0eb5478..053a4fbb 100644 --- a/internal/core/webrtc_session.go +++ b/internal/core/webrtc_session.go @@ -18,13 +18,6 @@ import ( "github.com/bluenviron/mediamtx/internal/logger" ) -const ( - webrtcHandshakeTimeout = 10 * time.Second - webrtcTrackGatherTimeout = 2 * time.Second - webrtcPayloadMaxSize = 1188 // 1200 - 12 (RTP header) - webrtcStreamID = "mediamtx" -) - type trackRecvPair struct { track *webrtc.TrackRemote receiver *webrtc.RTPReceiver @@ -235,7 +228,7 @@ func (s *webRTCSession) runPublish() (int, error) { defer res.path.publisherRemove(pathPublisherRemoveReq{author: s}) pc, err := newPeerConnection( - s.parent.genICEServers(), + s.parent.generateICEServers(), s.iceHostNAT1To1IPs, s.iceUDPMux, s.iceTCPMux, @@ -357,7 +350,7 @@ func (s *webRTCSession) runRead() (int, error) { } pc, err := newPeerConnection( - s.parent.genICEServers(), + s.parent.generateICEServers(), s.iceHostNAT1To1IPs, s.iceUDPMux, s.iceTCPMux, diff --git a/mediamtx.yml b/mediamtx.yml index 635d807b..63c84baf 100644 --- a/mediamtx.yml +++ b/mediamtx.yml @@ -200,15 +200,18 @@ webrtcAllowOrigin: '*' # If the server receives a request from one of these entries, IP in logs # will be taken from the X-Forwarded-For header. webrtcTrustedProxies: [] -# List of ICE servers, in format type:user:password:host:port or type:host:port. -# type can be "stun", "turn" or "turns". -# STUN servers are used to obtain the public IP of server and clients. They are -# needed when server and clients are on different LANs. -# TURN servers are needed when a direct connection between server and clients -# is not possible. All traffic is routed through the chosen TURN server. -# if user is "AUTH_SECRET", then authentication is secret based. -# the secret must be inserted into the password field. -webrtcICEServers: [stun:stun.l.google.com:19302] +# List of ICE servers. +webrtcICEServers2: + # URL can point to a STUN, TURN or TURNS server. + # STUN servers are used to obtain the public IP of server and clients. They are + # needed when server and clients are on different LANs. + # TURN/TURNS servers are needed when a direct connection between server and + # clients is not possible. All traffic is routed through them. +- url: stun:stun.l.google.com:19302 + # if user is "AUTH_SECRET", then authentication is secret based. + # the secret must be inserted into the password field. + username: '' + password: '' # List of public IP addresses that are to be used as a host. # This is used typically for servers that are behind 1:1 D-NAT. webrtcICEHostNAT1To1IPs: []