add destFingerprint parameter (#6106)
this allows to validate self-signed certificates of forward destinations.
This commit is contained in:
@@ -1269,6 +1269,8 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
dest:
|
dest:
|
||||||
type: string
|
type: string
|
||||||
|
destFingerprint:
|
||||||
|
type: string
|
||||||
whipBearerToken:
|
whipBearerToken:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,16 @@ We support forwarding streams by using the WebRTC protocol and the WHIP extensio
|
|||||||
paths:
|
paths:
|
||||||
mypath:
|
mypath:
|
||||||
forward:
|
forward:
|
||||||
|
# use whip:// for HTTP and whips:// for HTTPS.
|
||||||
- dest: whip://host:port/mystream/whip
|
- dest: whip://host:port/mystream/whip
|
||||||
whipBearerToken: mytoken
|
# If the destination is HTTPS and the destination TLS certificate is self-signed
|
||||||
|
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# validate it anyway. It can be obtained by running:
|
||||||
|
# openssl s_client -connect dest_ip:dest_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
destFingerprint:
|
||||||
|
# Token to insert in the Authorization: Bearer header.
|
||||||
|
whipBearerToken: ""
|
||||||
```
|
```
|
||||||
|
|
||||||
If the remote server is a _MediaMTX_ instance, remember to add a `/whip` suffix after the stream name, since in _MediaMTX_ [it's part of the WHIP URL](../3-publish/05-webrtc-clients.md).
|
If the remote server is a _MediaMTX_ instance, remember to add a `/whip` suffix after the stream name, since in _MediaMTX_ [it's part of the WHIP URL](../3-publish/05-webrtc-clients.md).
|
||||||
@@ -42,7 +50,14 @@ Add the target URL inside `dest` of a `forward` entry:
|
|||||||
paths:
|
paths:
|
||||||
mypath:
|
mypath:
|
||||||
forward:
|
forward:
|
||||||
|
# Use rtsp:// for plain RTSP and rtsps:// for encrypted RTSP.
|
||||||
- dest: rtsp://user:pass@host:port/path
|
- dest: rtsp://user:pass@host:port/path
|
||||||
|
# If the destination is RTSPS and the destination TLS certificate is self-signed
|
||||||
|
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# validate it anyway. It can be obtained by running:
|
||||||
|
# openssl s_client -connect dest_ip:dest_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
destFingerprint:
|
||||||
```
|
```
|
||||||
|
|
||||||
## RTMP
|
## RTMP
|
||||||
@@ -53,7 +68,14 @@ Add the target URL inside `dest` of a `forward` entry:
|
|||||||
paths:
|
paths:
|
||||||
mypath:
|
mypath:
|
||||||
forward:
|
forward:
|
||||||
|
# Use rtmp:// for plain RTMP and rtmps:// for encrypted RTMP.
|
||||||
- dest: rtmp://user:pass@host:port/path#streamKey
|
- dest: rtmp://user:pass@host:port/path#streamKey
|
||||||
|
# If the destination is RTMPS and the destination TLS certificate is self-signed
|
||||||
|
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# validate it anyway. It can be obtained by running:
|
||||||
|
# openssl s_client -connect dest_ip:dest_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
destFingerprint:
|
||||||
```
|
```
|
||||||
|
|
||||||
## FFmpeg
|
## FFmpeg
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ paths:
|
|||||||
source: rtsp+http://standard-rtsp-url
|
source: rtsp+http://standard-rtsp-url
|
||||||
```
|
```
|
||||||
|
|
||||||
There are also the `rtsps+http`, `rtsp+ws`, `rtsps+ws` schemas to handle any variant.
|
There are also the `rtsps+http`, `rtsp+ws`, `rtsps+ws` schemes to handle any variant.
|
||||||
|
|
||||||
## MPEG-TS inside RTSP
|
## MPEG-TS inside RTSP
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,14 @@ In order to ingest a WebRTC stream from a remote server, add the corresponding W
|
|||||||
```yml
|
```yml
|
||||||
paths:
|
paths:
|
||||||
proxied:
|
proxied:
|
||||||
# url of the source stream. Use whep:// for HTTP and wheps:// for HTTPS
|
# Use whep:// for HTTP and wheps:// for HTTPS.
|
||||||
source: whep://host:port/path
|
source: whep://host:port/path
|
||||||
|
# If the source is HTTPS and the source TLS certificate is self-signed
|
||||||
|
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# validate it anyway. It can be obtained by running:
|
||||||
|
# openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
sourceFingerprint:
|
||||||
# Token to insert in the Authorization: Bearer header.
|
# Token to insert in the Authorization: Bearer header.
|
||||||
whepBearerToken: ""
|
whepBearerToken: ""
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ Most IP cameras expose their video stream by using a RTSP server that is embedde
|
|||||||
```yml
|
```yml
|
||||||
paths:
|
paths:
|
||||||
proxied:
|
proxied:
|
||||||
|
# Use rtsp:// for plain RTSP and rtsps:// for encrypted RTSP.
|
||||||
source: rtsp://user:pass@host:port/path
|
source: rtsp://user:pass@host:port/path
|
||||||
|
# If the source is RTSPS and the source TLS certificate is self-signed
|
||||||
|
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# validate it anyway. It can be obtained by running:
|
||||||
|
# openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
sourceFingerprint:
|
||||||
```
|
```
|
||||||
|
|
||||||
If username or password contain special characters (like ?, :, etc), they need to be [url-encoded](https://www.urlencoder.org/).
|
If username or password contain special characters (like ?, :, etc), they need to be [url-encoded](https://www.urlencoder.org/).
|
||||||
|
|||||||
@@ -10,7 +10,14 @@ You can use _MediaMTX_ to connect to one or several existing RTMP servers and re
|
|||||||
```yml
|
```yml
|
||||||
paths:
|
paths:
|
||||||
proxied:
|
proxied:
|
||||||
|
# Use rtmp:// for plain RTMP and rtmps:// for encrypted RTMP.
|
||||||
source: rtmp://user:pass@host:port/path#streamKey
|
source: rtmp://user:pass@host:port/path#streamKey
|
||||||
|
# If the source is RTMPS and the source TLS certificate is self-signed
|
||||||
|
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# validate it anyway. It can be obtained by running:
|
||||||
|
# openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
sourceFingerprint:
|
||||||
```
|
```
|
||||||
|
|
||||||
If username or password contain special characters (like ?, :, etc), they need to be [url-encoded](https://www.urlencoder.org/).
|
If username or password contain special characters (like ?, :, etc), they need to be [url-encoded](https://www.urlencoder.org/).
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ HLS is a streaming protocol that works by splitting streams into segments, and b
|
|||||||
```yml
|
```yml
|
||||||
paths:
|
paths:
|
||||||
proxied:
|
proxied:
|
||||||
|
# Use http:// for plain HTTP and https:// for HTTPS.
|
||||||
source: http://user:pass@host:port/path
|
source: http://user:pass@host:port/path
|
||||||
|
# If the source is HTTPS and the source TLS certificate is self-signed
|
||||||
|
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# validate it anyway. It can be obtained by running:
|
||||||
|
# openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
sourceFingerprint:
|
||||||
```
|
```
|
||||||
|
|
||||||
If username or password contain special characters (like ?, :, etc), they need to be [url-encoded](https://www.urlencoder.org/).
|
If username or password contain special characters (like ?, :, etc), they need to be [url-encoded](https://www.urlencoder.org/).
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func TestForward(t *testing.T) {
|
|||||||
ID: whipID,
|
ID: whipID,
|
||||||
Pos: 2,
|
Pos: 2,
|
||||||
Created: time.Date(2026, 6, 18, 9, 1, 0, 0, time.UTC),
|
Created: time.Date(2026, 6, 18, 9, 1, 0, 0, time.UTC),
|
||||||
Conf: conf.ForwardDest{Dest: "whip://localhost/live/stream/whip", WhipBearerToken: "mytoken"},
|
Conf: conf.ForwardDest{Dest: "whip://localhost/live/stream/whip", WHIPBearerToken: "mytoken"},
|
||||||
Protocol: defs.APIForwardDestProtocolWHIP,
|
Protocol: defs.APIForwardDestProtocolWHIP,
|
||||||
State: defs.APIForwardDestStateForwarding,
|
State: defs.APIForwardDestStateForwarding,
|
||||||
OutboundBytes: 456,
|
OutboundBytes: 456,
|
||||||
@@ -117,7 +117,7 @@ func TestForward(t *testing.T) {
|
|||||||
ID: whipID,
|
ID: whipID,
|
||||||
Pos: 2,
|
Pos: 2,
|
||||||
Created: time.Date(2026, 6, 18, 9, 1, 0, 0, time.UTC),
|
Created: time.Date(2026, 6, 18, 9, 1, 0, 0, time.UTC),
|
||||||
Conf: conf.ForwardDest{Dest: "whip://localhost/live/stream/whip", WhipBearerToken: "mytoken"},
|
Conf: conf.ForwardDest{Dest: "whip://localhost/live/stream/whip", WHIPBearerToken: "mytoken"},
|
||||||
Protocol: defs.APIForwardDestProtocolWHIP,
|
Protocol: defs.APIForwardDestProtocolWHIP,
|
||||||
State: defs.APIForwardDestStateForwarding,
|
State: defs.APIForwardDestStateForwarding,
|
||||||
OutboundBytes: 456,
|
OutboundBytes: 456,
|
||||||
@@ -128,7 +128,7 @@ func TestForward(t *testing.T) {
|
|||||||
httpRequest(t, hc, http.MethodGet,
|
httpRequest(t, hc, http.MethodGet,
|
||||||
"http://localhost:9997/v3/paths/forward/get?path=my%2Fnested%2Fstream&id="+whipID.String(), nil, &item)
|
"http://localhost:9997/v3/paths/forward/get?path=my%2Fnested%2Fstream&id="+whipID.String(), nil, &item)
|
||||||
require.Equal(t, "whip://localhost/live/stream/whip", item.Conf.Dest)
|
require.Equal(t, "whip://localhost/live/stream/whip", item.Conf.Dest)
|
||||||
require.Equal(t, "mytoken", item.Conf.WhipBearerToken)
|
require.Equal(t, "mytoken", item.Conf.WHIPBearerToken)
|
||||||
require.Equal(t, defs.APIForwardDestProtocolWHIP, item.Protocol)
|
require.Equal(t, defs.APIForwardDestProtocolWHIP, item.Protocol)
|
||||||
require.Equal(t, defs.APIForwardDestStateForwarding, item.State)
|
require.Equal(t, defs.APIForwardDestStateForwarding, item.State)
|
||||||
require.Equal(t, uint64(456), item.OutboundBytes)
|
require.Equal(t, uint64(456), item.OutboundBytes)
|
||||||
|
|||||||
@@ -890,6 +890,7 @@ func TestConfErrors(t *testing.T) {
|
|||||||
" mypath:\n" +
|
" mypath:\n" +
|
||||||
" forward:\n" +
|
" forward:\n" +
|
||||||
" - dest: whip://localhost/stream/whip\n" +
|
" - dest: whip://localhost/stream/whip\n" +
|
||||||
|
" destFingerprint: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n" +
|
||||||
" whipBearerToken: mytoken\n",
|
" whipBearerToken: mytoken\n",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import (
|
|||||||
// ForwardDest is a destination to which a path is forwarded.
|
// ForwardDest is a destination to which a path is forwarded.
|
||||||
type ForwardDest struct {
|
type ForwardDest struct {
|
||||||
Dest string `json:"dest"`
|
Dest string `json:"dest"`
|
||||||
WhipBearerToken string `json:"whipBearerToken"`
|
DestFingerprint string `json:"destFingerprint"`
|
||||||
|
WHIPBearerToken string `json:"whipBearerToken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateForwardDest(dest string) (*url.URL, error) {
|
func validateForwardDest(dest string) (*url.URL, error) {
|
||||||
|
|||||||
@@ -538,7 +538,7 @@ func TestPathForwardWHIP(t *testing.T) {
|
|||||||
require.Len(t, list.Items, 1)
|
require.Len(t, list.Items, 1)
|
||||||
added := list.Items[0]
|
added := list.Items[0]
|
||||||
require.Equal(t, dest, added.Conf.Dest)
|
require.Equal(t, dest, added.Conf.Dest)
|
||||||
require.Equal(t, bearerToken, added.Conf.WhipBearerToken)
|
require.Equal(t, bearerToken, added.Conf.WHIPBearerToken)
|
||||||
require.Equal(t, defs.APIForwardDestProtocolWHIP, added.Protocol)
|
require.Equal(t, defs.APIForwardDestProtocolWHIP, added.Protocol)
|
||||||
require.Equal(t, 1, added.Pos)
|
require.Equal(t, 1, added.Pos)
|
||||||
|
|
||||||
@@ -550,7 +550,7 @@ func TestPathForwardWHIP(t *testing.T) {
|
|||||||
"http://localhost:9997/v3/paths/forward/get?path=source&id="+added.ID.String(), nil, &item)
|
"http://localhost:9997/v3/paths/forward/get?path=source&id="+added.ID.String(), nil, &item)
|
||||||
return item.State == defs.APIForwardDestStateForwarding &&
|
return item.State == defs.APIForwardDestStateForwarding &&
|
||||||
item.Protocol == defs.APIForwardDestProtocolWHIP &&
|
item.Protocol == defs.APIForwardDestProtocolWHIP &&
|
||||||
item.Conf.WhipBearerToken == bearerToken &&
|
item.Conf.WHIPBearerToken == bearerToken &&
|
||||||
item.OutboundBytes > 0
|
item.OutboundBytes > 0
|
||||||
}, 5*time.Second, 100*time.Millisecond)
|
}, 5*time.Second, 100*time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,19 +185,21 @@ func (h *DestHandler) runOnce(strm *stream.Stream) error {
|
|||||||
switch h.protocol {
|
switch h.protocol {
|
||||||
case defs.APIForwardDestProtocolRTMP, defs.APIForwardDestProtocolRTMPS:
|
case defs.APIForwardDestProtocolRTMP, defs.APIForwardDestProtocolRTMPS:
|
||||||
dest = &forwardrtmp.Dest{
|
dest = &forwardrtmp.Dest{
|
||||||
Stream: strm,
|
Stream: strm,
|
||||||
Dest: resolvedDest,
|
Dest: resolvedDest,
|
||||||
WriteTimeout: h.WriteTimeout,
|
DestFingerprint: h.Conf.DestFingerprint,
|
||||||
Parent: h,
|
WriteTimeout: h.WriteTimeout,
|
||||||
|
Parent: h,
|
||||||
}
|
}
|
||||||
|
|
||||||
case defs.APIForwardDestProtocolRTSP, defs.APIForwardDestProtocolRTSPS:
|
case defs.APIForwardDestProtocolRTSP, defs.APIForwardDestProtocolRTSPS:
|
||||||
dest = &forwardrtsp.Dest{
|
dest = &forwardrtsp.Dest{
|
||||||
Stream: strm,
|
Stream: strm,
|
||||||
Dest: resolvedDest,
|
Dest: resolvedDest,
|
||||||
ReadTimeout: h.ReadTimeout,
|
DestFingerprint: h.Conf.DestFingerprint,
|
||||||
WriteTimeout: h.WriteTimeout,
|
ReadTimeout: h.ReadTimeout,
|
||||||
Parent: h,
|
WriteTimeout: h.WriteTimeout,
|
||||||
|
Parent: h,
|
||||||
}
|
}
|
||||||
|
|
||||||
case defs.APIForwardDestProtocolSRT:
|
case defs.APIForwardDestProtocolSRT:
|
||||||
@@ -213,8 +215,9 @@ func (h *DestHandler) runOnce(strm *stream.Stream) error {
|
|||||||
dest = &forwardwebrtc.Dest{
|
dest = &forwardwebrtc.Dest{
|
||||||
Stream: strm,
|
Stream: strm,
|
||||||
Dest: resolvedDest,
|
Dest: resolvedDest,
|
||||||
|
DestFingerprint: h.Conf.DestFingerprint,
|
||||||
ReadTimeout: h.ReadTimeout,
|
ReadTimeout: h.ReadTimeout,
|
||||||
WhipBearerToken: h.Conf.WhipBearerToken,
|
BearerToken: h.Conf.WHIPBearerToken,
|
||||||
Parent: h,
|
Parent: h,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func (m *Manager) ReloadConf(forward conf.Forward) {
|
|||||||
toClose := make([]*DestHandler, 0)
|
toClose := make([]*DestHandler, 0)
|
||||||
|
|
||||||
for i, dest := range forward {
|
for i, dest := range forward {
|
||||||
if i < len(m.destHandlers) && m.destHandlers[i].Conf.Dest == dest.Dest {
|
if i < len(m.destHandlers) && m.destHandlers[i].Conf == dest {
|
||||||
newHandlers[i] = m.destHandlers[i]
|
newHandlers[i] = m.destHandlers[i]
|
||||||
} else {
|
} else {
|
||||||
if i < len(m.destHandlers) {
|
if i < len(m.destHandlers) {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ func TestManagerReloadConf(t *testing.T) {
|
|||||||
Forward: conf.Forward{
|
Forward: conf.Forward{
|
||||||
{Dest: "rtmp://localhost:5788/app/stream"},
|
{Dest: "rtmp://localhost:5788/app/stream"},
|
||||||
{Dest: "rtsp://localhost:5789/stream"},
|
{Dest: "rtsp://localhost:5789/stream"},
|
||||||
|
{Dest: "whip://localhost:5790/teststream/whip", WHIPBearerToken: "mytoken"},
|
||||||
},
|
},
|
||||||
Parent: test.NilLogger,
|
Parent: test.NilLogger,
|
||||||
}
|
}
|
||||||
@@ -126,13 +127,25 @@ func TestManagerReloadConf(t *testing.T) {
|
|||||||
Protocol: "rtsp",
|
Protocol: "rtsp",
|
||||||
State: list1.Items[1].State,
|
State: list1.Items[1].State,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: list1.Items[2].ID,
|
||||||
|
Pos: 3,
|
||||||
|
Created: list1.Items[2].Created,
|
||||||
|
Conf: conf.ForwardDest{
|
||||||
|
Dest: "whip://localhost:5790/teststream/whip",
|
||||||
|
WHIPBearerToken: "mytoken",
|
||||||
|
},
|
||||||
|
Protocol: "whip",
|
||||||
|
State: list1.Items[2].State,
|
||||||
|
LastError: list1.Items[2].LastError,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, list1)
|
}, list1)
|
||||||
|
|
||||||
m.ReloadConf(conf.Forward{
|
m.ReloadConf(conf.Forward{
|
||||||
{Dest: "rtmp://localhost:5788/app/stream"}, // unchanged
|
{Dest: "rtmp://localhost:5788/app/stream"}, // unchanged
|
||||||
{Dest: "whip://localhost:5790/teststream/whip", WhipBearerToken: "mytoken"},
|
{Dest: "rtsp://localhost:5789/stream", DestFingerprint: "fingerprint"}, // changed params
|
||||||
{Dest: "rtsp://localhost:5789/stream"},
|
{Dest: "whip://localhost:5790/teststream/whip", WHIPBearerToken: "othertoken"},
|
||||||
})
|
})
|
||||||
|
|
||||||
list2 := m.APIList()
|
list2 := m.APIList()
|
||||||
@@ -152,23 +165,31 @@ func TestManagerReloadConf(t *testing.T) {
|
|||||||
Pos: 2,
|
Pos: 2,
|
||||||
Created: list2.Items[1].Created,
|
Created: list2.Items[1].Created,
|
||||||
Conf: conf.ForwardDest{
|
Conf: conf.ForwardDest{
|
||||||
Dest: "whip://localhost:5790/teststream/whip",
|
Dest: "rtsp://localhost:5789/stream",
|
||||||
WhipBearerToken: "mytoken",
|
DestFingerprint: "fingerprint",
|
||||||
},
|
},
|
||||||
Protocol: "whip",
|
Protocol: "rtsp",
|
||||||
State: list2.Items[1].State,
|
State: list2.Items[1].State,
|
||||||
LastError: list2.Items[1].LastError,
|
LastError: list2.Items[1].LastError,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: list2.Items[2].ID,
|
ID: list2.Items[2].ID,
|
||||||
Pos: 3,
|
Pos: 3,
|
||||||
Created: list2.Items[2].Created,
|
Created: list2.Items[2].Created,
|
||||||
Conf: conf.ForwardDest{Dest: "rtsp://localhost:5789/stream"},
|
Conf: conf.ForwardDest{
|
||||||
Protocol: "rtsp",
|
Dest: "whip://localhost:5790/teststream/whip",
|
||||||
State: list2.Items[2].State,
|
WHIPBearerToken: "othertoken",
|
||||||
|
},
|
||||||
|
Protocol: "whip",
|
||||||
|
State: list2.Items[2].State,
|
||||||
|
LastError: list2.Items[2].LastError,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, list2)
|
}, list2)
|
||||||
|
|
||||||
|
require.Equal(t, list1.Items[0].ID, list2.Items[0].ID)
|
||||||
|
require.NotEqual(t, list1.Items[1].ID, list2.Items[1].ID)
|
||||||
|
require.NotEqual(t, list1.Items[2].ID, list2.Items[2].ID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/conf"
|
"github.com/bluenviron/mediamtx/internal/conf"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
rtmpprotocol "github.com/bluenviron/mediamtx/internal/protocols/rtmp"
|
rtmpprotocol "github.com/bluenviron/mediamtx/internal/protocols/rtmp"
|
||||||
|
ptls "github.com/bluenviron/mediamtx/internal/protocols/tls"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,10 +66,11 @@ func fourCCList(desc *description.Session) amf0.StrictArray {
|
|||||||
|
|
||||||
// Dest is a RTMP forward destination.
|
// Dest is a RTMP forward destination.
|
||||||
type Dest struct {
|
type Dest struct {
|
||||||
Stream *stream.Stream
|
Stream *stream.Stream
|
||||||
Dest string
|
Dest string
|
||||||
WriteTimeout conf.Duration
|
DestFingerprint string
|
||||||
Parent logger.Writer
|
WriteTimeout conf.Duration
|
||||||
|
Parent logger.Writer
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
outboundBytesFunc func() uint64
|
outboundBytesFunc func() uint64
|
||||||
@@ -98,8 +100,9 @@ func (d *Dest) Run(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn := &gortmplib.Client{
|
conn := &gortmplib.Client{
|
||||||
URL: u,
|
URL: u,
|
||||||
Publish: true,
|
Publish: true,
|
||||||
|
TLSConfig: ptls.MakeConfig(d.DestFingerprint),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = conn.Initialize(ctx)
|
err = conn.Initialize(ctx)
|
||||||
|
|||||||
@@ -13,17 +13,19 @@ import (
|
|||||||
|
|
||||||
"github.com/bluenviron/mediamtx/internal/conf"
|
"github.com/bluenviron/mediamtx/internal/conf"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
ptls "github.com/bluenviron/mediamtx/internal/protocols/tls"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
"github.com/bluenviron/mediamtx/internal/unit"
|
"github.com/bluenviron/mediamtx/internal/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dest is a RTSP forward destination.
|
// Dest is a RTSP forward destination.
|
||||||
type Dest struct {
|
type Dest struct {
|
||||||
Stream *stream.Stream
|
Stream *stream.Stream
|
||||||
Dest string
|
Dest string
|
||||||
ReadTimeout conf.Duration
|
DestFingerprint string
|
||||||
WriteTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
Parent logger.Writer
|
WriteTimeout conf.Duration
|
||||||
|
Parent logger.Writer
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
outboundBytesFunc func() uint64
|
outboundBytesFunc func() uint64
|
||||||
@@ -59,6 +61,7 @@ func (d *Dest) Run(ctx context.Context) error {
|
|||||||
Host: u.Host,
|
Host: u.Host,
|
||||||
ReadTimeout: time.Duration(d.ReadTimeout),
|
ReadTimeout: time.Duration(d.ReadTimeout),
|
||||||
WriteTimeout: time.Duration(d.WriteTimeout),
|
WriteTimeout: time.Duration(d.WriteTimeout),
|
||||||
|
TLSConfig: ptls.MakeConfig(d.DestFingerprint),
|
||||||
OnRequest: func(req *base.Request) {
|
OnRequest: func(req *base.Request) {
|
||||||
d.Log(logger.Debug, "[c->s] %v", req)
|
d.Log(logger.Debug, "[c->s] %v", req)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/bluenviron/mediamtx/internal/conf"
|
"github.com/bluenviron/mediamtx/internal/conf"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
ptls "github.com/bluenviron/mediamtx/internal/protocols/tls"
|
||||||
pwebrtc "github.com/bluenviron/mediamtx/internal/protocols/webrtc"
|
pwebrtc "github.com/bluenviron/mediamtx/internal/protocols/webrtc"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/whip"
|
"github.com/bluenviron/mediamtx/internal/protocols/whip"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
@@ -21,8 +22,9 @@ import (
|
|||||||
type Dest struct {
|
type Dest struct {
|
||||||
Stream *stream.Stream
|
Stream *stream.Stream
|
||||||
Dest string
|
Dest string
|
||||||
|
DestFingerprint string
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
WhipBearerToken string
|
BearerToken string
|
||||||
Parent logger.Writer
|
Parent logger.Writer
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
@@ -56,8 +58,6 @@ func (d *Dest) Run(ctx context.Context) error {
|
|||||||
|
|
||||||
u.Scheme = strings.Replace(u.Scheme, "whip", "http", 1)
|
u.Scheme = strings.Replace(u.Scheme, "whip", "http", 1)
|
||||||
|
|
||||||
hc := &http.Client{Timeout: time.Duration(d.ReadTimeout)}
|
|
||||||
|
|
||||||
r := &stream.Reader{Parent: d}
|
r := &stream.Reader{Parent: d}
|
||||||
pc := &pwebrtc.PeerConnection{}
|
pc := &pwebrtc.PeerConnection{}
|
||||||
|
|
||||||
@@ -66,14 +66,22 @@ func (d *Dest) Run(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr := &http.Transport{
|
||||||
|
TLSClientConfig: ptls.MakeConfig(d.DestFingerprint),
|
||||||
|
}
|
||||||
|
defer tr.CloseIdleConnections()
|
||||||
|
|
||||||
client := &whip.Client{
|
client := &whip.Client{
|
||||||
URL: u,
|
URL: u,
|
||||||
Publish: true,
|
Publish: true,
|
||||||
OutboundTracks: pc.OutboundTracks,
|
OutboundTracks: pc.OutboundTracks,
|
||||||
OutboundDataChannels: pc.OutboundDataChannels,
|
OutboundDataChannels: pc.OutboundDataChannels,
|
||||||
HTTPClient: hc,
|
HTTPClient: &http.Client{
|
||||||
BearerToken: d.WhipBearerToken,
|
Timeout: time.Duration(d.ReadTimeout),
|
||||||
Log: d,
|
Transport: tr,
|
||||||
|
},
|
||||||
|
BearerToken: d.BearerToken,
|
||||||
|
Log: d,
|
||||||
}
|
}
|
||||||
if err = client.Initialize(ctx); err != nil {
|
if err = client.Initialize(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -150,11 +150,11 @@ func TestDest(t *testing.T) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
dest := &forwardwebrtc.Dest{
|
dest := &forwardwebrtc.Dest{
|
||||||
Stream: strm,
|
Stream: strm,
|
||||||
Dest: destURL,
|
Dest: destURL,
|
||||||
ReadTimeout: conf.Duration(10 * time.Second),
|
ReadTimeout: conf.Duration(10 * time.Second),
|
||||||
WhipBearerToken: bearerToken,
|
BearerToken: bearerToken,
|
||||||
Parent: test.NilLogger,
|
Parent: test.NilLogger,
|
||||||
}
|
}
|
||||||
|
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
|
|||||||
@@ -550,6 +550,12 @@ pathDefaults:
|
|||||||
# * $MTX_PATH: path name
|
# * $MTX_PATH: path name
|
||||||
# * $G1, $G2, ...: regular expression groups, if path name is a regular expression.
|
# * $G1, $G2, ...: regular expression groups, if path name is a regular expression.
|
||||||
# - dest:
|
# - dest:
|
||||||
|
# # If the destination TLS certificate is self-signed
|
||||||
|
# # or invalid, you can provide the fingerprint of the certificate in order to
|
||||||
|
# # validate it anyway. It can be obtained by running:
|
||||||
|
# # openssl s_client -connect dest_ip:dest_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||||
|
# # openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||||
|
# destFingerprint:
|
||||||
# # Token to insert in the Authorization: Bearer header when using WHIP.
|
# # Token to insert in the Authorization: Bearer header when using WHIP.
|
||||||
# whipBearerToken: ""
|
# whipBearerToken: ""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user