diff --git a/console/src/App.vue b/console/src/App.vue
index 7c74b4c..0be0abb 100644
--- a/console/src/App.vue
+++ b/console/src/App.vue
@@ -17,7 +17,7 @@ import CommandsWidget from "./components/dashboard/CommandsWidget.vue";
diff --git a/console/src/components/dashboard/PropertiesWidget.vue b/console/src/components/dashboard/PropertiesWidget.vue
index 877c1fe..8714084 100644
--- a/console/src/components/dashboard/PropertiesWidget.vue
+++ b/console/src/components/dashboard/PropertiesWidget.vue
@@ -20,41 +20,49 @@ function buildNodes(obj, path='') {
return Object.entries(obj).map(([key, val]) => {
const fullKey = path ? `${path}/${key}` : key
- if (val && typeof val === 'object' && !('value' in val)) {
- return {
- key: fullKey,
- data: { name: key },
- children: buildNodes(val, fullKey)
- }
- }
+ const hasChildren = Object.keys(val).some(k => k !== 'value' && k !== '_value')
return {
key: fullKey,
data: {
name: key,
- value: val.value
- }
+ value: val.value ?? val._value
+ },
+ children: hasChildren ? buildNodes(
+ Object.fromEntries(
+ Object.entries(val).filter(([k]) => k !== 'value' && k !== '_value')
+ ),
+ fullKey
+ ) : undefined
}
})
}
function insertProperty(pathParts, value) {
let node = propertyTree
-
const fullKey = pathParts.join('/')
pathParts.forEach((part, i) => {
- if (i === pathParts.length - 1) {
- node[part] = { value }
+ const isLeaf = i === pathParts.length - 1
+
+ if (!node[part]) {
+ node[part] = {}
+ }
+
+ // If this node used to be a leaf, convert it into a branch
+ if (node[part].value !== undefined && !isLeaf) {
+ node[part] = { _value: node[part].value }
+ }
+
+ if (isLeaf) {
+ node[part].value = value
} else {
- if (!node[part]) node[part] = {}
node = node[part]
}
})
- // 🔥 always flash on update
+ // flash logic
changedKeys.value[fullKey] = true
-
setTimeout(() => {
delete changedKeys.value[fullKey]
}, 600)
@@ -62,10 +70,6 @@ function insertProperty(pathParts, value) {
nodes.value = buildNodes(propertyTree)
}
-function rowClass(node) {
- return changedKeys.value.has(node.key) ? 'flash-row' : ''
-}
-
watch(() => props.deviceId, () => {
propertyTree = {}
nodes.value = []
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..dd1b961
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,26 @@
+
+services:
+ emqx:
+ container_name: emqx
+ image: emqx/emqx:latest
+ ports:
+ - 1883:1883
+ - 8083:8083
+ - 8084:8084
+ - 8883:8883
+ - 18083:18083
+
+ mediamtx:
+ container_name: mediamtx
+ image: bluenviron/mediamtx:1.17.0-ffmpeg
+ volumes:
+ - ./mediamtx.yml:/mediamtx.yml:z
+ ports:
+ - 8554:8554
+ - 1935:1935
+ - 8888:8888
+ - 8889:8889
+ - 8890:8890/udp
+ - 8189:8189/udp
+ - 9998:9998
+
diff --git a/mediamtx.py b/mediamtx.py
new file mode 100755
index 0000000..2f302d1
--- /dev/null
+++ b/mediamtx.py
@@ -0,0 +1,68 @@
+#! /usr/bin/env python3
+
+import socket
+import signal
+import asyncio
+from dataclasses import dataclass
+import aiohttp
+
+from mqtthandler.command import command
+from mqtthandler.handler import MQTTConfig, MQTTHandler, task
+
+
+@dataclass
+class MediaMTXConfig:
+ host: str = "http://localhost"
+ metrics_path: str = ":9998/metrics"
+ username: str | None = "admin"
+ password: str | None = "admin"
+
+
+class MediaMTXHandler(MQTTHandler):
+ def __init__(
+ self,
+ mqtt_config: MQTTConfig,
+ handler_id: str,
+ mediamtx_config: MediaMTXConfig,
+ ):
+ super().__init__(mqtt_config, handler_id)
+ self.config = mediamtx_config
+
+ @task
+ async def retrieve_metrics(self):
+ cache = {}
+
+ while True:
+ auth = aiohttp.BasicAuth(login=self.config.username, password=self.config.password)
+ async with aiohttp.ClientSession(auth=auth) as session:
+ while True:
+ async with session.get(f"{self.config.host}{self.config.metrics_path}") as r:
+ metrics = await r.text()
+
+ for line in metrics.split("\n")[:-1]:
+ metric, value = line.split(" ")
+ topic = metric.replace("_", "/")
+ full_topic = f"{self.property_topic}/{topic}"
+
+ if cache.get(full_topic) != value:
+ cache[full_topic] = value
+ await self.mqtt_client.publish(full_topic, value, retain=True)
+
+
+ await asyncio.sleep(1)
+
+ print("Connection dropped!")
+ await asyncio.sleep(10)
+
+
+async def main():
+ handler_id = f"mediamtx-{socket.gethostname()}"
+ mqtt_config = MQTTConfig(host="127.0.0.1")
+ handler = MediaMTXHandler(mqtt_config, handler_id, MediaMTXConfig())
+
+ signal.signal(signal.SIGINT, lambda signum, frame: handler.stop())
+ await handler.run()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
diff --git a/mediamtx.yml b/mediamtx.yml
new file mode 100644
index 0000000..e64857a
--- /dev/null
+++ b/mediamtx.yml
@@ -0,0 +1,815 @@
+###############################################
+# Global settings
+
+# Settings in this section are applied anywhere.
+
+###############################################
+# Global settings -> General
+
+# Verbosity of the program; available values are "error", "warn", "info", "debug".
+logLevel: info
+# Destinations of log messages; available values are "stdout", "file" and "syslog".
+logDestinations: [stdout]
+# When destination is "stdout" or "file", emit logs in structured format (JSONL).
+logStructured: false
+# When "file" is in logDestinations, this is the file which will receive logs.
+logFile: mediamtx.log
+# When "syslog" is in logDestinations, use prefix for logs.
+sysLogPrefix: mediamtx
+# Dump packets to disk. This is useful for debugging.
+dumpPackets: false
+
+# Timeout of read operations.
+readTimeout: 10s
+# Timeout of write operations.
+writeTimeout: 10s
+# Size of the queue of outgoing packets.
+# A higher value allows to increase throughput, a lower value allows to save RAM.
+writeQueueSize: 512
+# Maximum size of outgoing UDP payloads.
+# It defaults to the maximum packet size on ethernet (1500) minus IPv6 and UDP headers (48).
+# This can be decreased to avoid fragmentation on networks with a low MTU.
+udpMaxPayloadSize: 1452
+# Size of the read buffer of every UDP socket.
+# This can be increased to decrease packet losses.
+# It defaults to the default value of the operating system.
+udpReadBufferSize: 0
+
+# Command to run when a client connects to the server.
+# This is terminated with SIGINT when a client disconnects from the server.
+# The following environment variables are available:
+# * MTX_CONN_TYPE: connection type
+# * MTX_CONN_ID: connection ID
+# * RTSP_PORT: RTSP server port
+runOnConnect:
+# Restart the command if it exits.
+runOnConnectRestart: false
+# Command to run when a client disconnects from the server.
+# Environment variables are the same of runOnConnect.
+runOnDisconnect:
+
+###############################################
+# Global settings -> Authentication
+
+# Authentication method. Available values are:
+# * internal: credentials are stored in the configuration file
+# * http: an external HTTP URL is contacted to perform authentication
+# * jwt: an external identity server provides authentication through JWTs
+authMethod: internal
+
+# Internal authentication.
+# Enabled users.
+authInternalUsers:
+ # Default unprivileged user.
+ # Username. 'any' means any user, including anonymous ones.
+- user: any
+ # Password. Not used in case of 'any' user.
+ pass:
+ # IPs or networks allowed to use this user. An empty list means any IP.
+ ips: []
+ # Permissions.
+ permissions:
+ # Available actions are: publish, read, playback, api, metrics, pprof.
+ - action: publish
+ # Paths can be set to further restrict access to a specific path.
+ # An empty path means any path.
+ # Regular expressions can be used by using a tilde as prefix.
+ path:
+ - action: read
+ path:
+ - action: playback
+ path:
+
+ # Default administrator.
+ # This allows to use API, metrics and PPROF without authentication,
+ # if the IP is localhost.
+- user: any
+ pass:
+ ips: ['127.0.0.1', '::1', '192.168.0.0/24']
+ permissions:
+ - action: api
+ - action: metrics
+ - action: pprof
+
+- user: admin
+ pass: admin
+ ips: ['0.0.0.0/0']
+ permissions:
+ - action: api
+ - action: metrics
+ - action: pprof
+
+
+# HTTP-based authentication.
+# URL called to perform authentication. Every time a user wants
+# to authenticate, the server calls this URL with the POST method
+# and a body containing:
+# {
+# "user": "user",
+# "password": "password",
+# "token": "token",
+# "ip": "ip",
+# "action": "publish|read|playback|api|metrics|pprof",
+# "path": "path",
+# "protocol": "rtsp|rtmp|hls|webrtc|srt",
+# "id": "id",
+# "query": "query"
+# }
+# If the response code is 20x, authentication is accepted, otherwise
+# it is discarded.
+authHTTPAddress:
+# If the HTTP authentication URL has a self-signed or invalid certificate,
+# you can provide the fingerprint of the certificate in order to
+# validate it anyway. It can be obtained by running:
+# openssl s_client -connect auth_http_domain:443 /dev/null | sed -n '/BEGIN/,/END/p' > server.crt
+# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
+authHTTPFingerprint:
+# Actions to exclude from HTTP-based authentication.
+# Format is the same as the one of user permissions.
+authHTTPExclude:
+- action: api
+- action: metrics
+- action: pprof
+
+# JWT-based authentication.
+# Users have to login through an external identity server and obtain a JWT.
+# This JWT must contain the claim "mediamtx_permissions" with permissions,
+# for instance:
+# {
+# "mediamtx_permissions": [
+# {
+# "action": "publish",
+# "path": "somepath"
+# }
+# ]
+# }
+# Users are expected to pass the JWT in the Authorization header or as password.
+# This is the JWKS URL that will be used to pull (once) the public key that allows
+# to validate JWTs.
+authJWTJWKS:
+# If the JWKS URL has a self-signed or invalid certificate,
+# you can provide the fingerprint of the certificate in order to
+# validate it anyway. It can be obtained by running:
+# openssl s_client -connect jwt_jwks_domain:443 /dev/null | sed -n '/BEGIN/,/END/p' > server.crt
+# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
+authJWTJWKSFingerprint:
+# name of the claim that contains permissions.
+authJWTClaimKey: mediamtx_permissions
+# Actions to exclude from JWT-based authentication.
+# Format is the same as the one of user permissions.
+authJWTExclude: []
+# Allow passing the JWT through query parameters of HTTP requests (i.e. ?jwt=JWT).
+# This is a security risk and will be disabled in the future.
+# RTSP and RTMP always allow JWT in query even if disabled, since there is no alternative.
+authJWTInHTTPQuery: true
+# Expected issuer (iss) claim in the JWT. Leave empty to skip validation.
+authJWTIssuer:
+# Expected audience (aud) claim in the JWT. Leave empty to skip validation.
+authJWTAudience:
+
+###############################################
+# Global settings -> Control API
+
+# Enable controlling the server through the Control API.
+api: false
+# Address of the Control API listener.
+apiAddress: :9997
+# Enable HTTPS on the Control API server.
+apiEncryption: false
+# Path to the server key. This is needed only when encryption is yes.
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+apiServerKey: server.key
+# Path to the server certificate.
+apiServerCert: server.crt
+# Allowed CORS origins.
+# Supports wildcards: ['http://*.example.com']
+apiAllowOrigins: ['*']
+# IPs or CIDRs of proxies placed before the HTTP server.
+# These proxies can use the X-Forwarded-For header to set the real IP of clients,
+# and the X-Forwarded-Proto header to set the original protocol.
+apiTrustedProxies: []
+
+###############################################
+# Global settings -> Metrics
+
+# Enable Prometheus-compatible metrics.
+metrics: true
+# Address of the metrics HTTP listener.
+metricsAddress: :9998
+# Enable HTTPS on the Metrics server.
+metricsEncryption: false
+# Path to the server key. This is needed only when encryption is yes.
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+metricsServerKey: server.key
+# Path to the server certificate.
+metricsServerCert: server.crt
+# Allowed CORS origins.
+# Supports wildcards: ['http://*.example.com']
+metricsAllowOrigins: ['*']
+# IPs or CIDRs of proxies placed before the HTTP server.
+# These proxies can use the X-Forwarded-For header to set the real IP of clients,
+# and the X-Forwarded-Proto header to set the original protocol.
+metricsTrustedProxies: []
+
+###############################################
+# Global settings -> PPROF
+
+# Enable pprof-compatible endpoint to monitor performances.
+pprof: false
+# Address of the pprof listener.
+pprofAddress: :9999
+# Enable HTTPS on the pprof server.
+pprofEncryption: false
+# Path to the server key. This is needed only when encryption is yes.
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+pprofServerKey: server.key
+# Path to the server certificate.
+pprofServerCert: server.crt
+# Allowed CORS origins.
+# Supports wildcards: ['http://*.example.com']
+pprofAllowOrigins: ['*']
+# IPs or CIDRs of proxies placed before the HTTP server.
+# These proxies can use the X-Forwarded-For header to set the real IP of clients,
+# and the X-Forwarded-Proto header to set the original protocol.
+pprofTrustedProxies: []
+
+###############################################
+# Global settings -> Playback server
+
+# Enable downloading recordings from the playback server.
+playback: false
+# Address of the playback server listener.
+playbackAddress: :9996
+# Enable HTTPS on the playback server.
+playbackEncryption: false
+# Path to the server key. This is needed only when encryption is yes.
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+playbackServerKey: server.key
+# Path to the server certificate.
+playbackServerCert: server.crt
+# Allowed CORS origins.
+# Supports wildcards: ['http://*.example.com']
+playbackAllowOrigins: ['*']
+# IPs or CIDRs of proxies placed before the HTTP server.
+# These proxies can use the X-Forwarded-For header to set the real IP of clients,
+# and the X-Forwarded-Proto header to set the original protocol.
+playbackTrustedProxies: []
+
+###############################################
+# Global settings -> RTSP server
+
+# Enable publishing and reading streams with the RTSP protocol.
+rtsp: true
+# Enabled RTSP transport protocols. The handshake is always performed with TCP.
+rtspTransports: [udp, multicast, tcp]
+# Use secure protocol variants (RTSPS, SRTP, SRTCP).
+# Available values are "no", "strict", "optional".
+rtspEncryption: "no"
+# Address of the TCP/RTSP listener. This is needed only when encryption is "no" or "optional".
+rtspAddress: :8554
+# Address of the TCP/RTSPS listener. This is needed only when encryption is "strict" or "optional".
+rtspsAddress: :8322
+# Address of the UDP/RTP listener. This is needed only when "udp" is in rtspTransports and encryption is "no" or "optional".
+rtpAddress: :8000
+# Address of the UDP/RTCP listener. This is needed only when "udp" is in rtspTransports and encryption is "no" or "optional".
+rtcpAddress: :8001
+# IP range of all UDP-multicast listeners. This is needed only when "multicast" is in rtspTransports and encryption is "no" or "optional".
+multicastIPRange: 224.1.0.0/16
+# Port of all UDP-multicast/RTP listeners. This is needed only when "multicast" is in rtspTransports and encryption is "no" or "optional".
+multicastRTPPort: 8002
+# Port of all UDP-multicast/RTCP listeners. This is needed only when "multicast" is in rtspTransports and encryption is "no" or "optional".
+multicastRTCPPort: 8003
+# Address of the UDP/SRTP listener. This is needed only when "udp" is in rtspTransports and encryption is "strict" or "optional".
+srtpAddress: :8004
+# Address of the UDP/SRTCP listener. This is needed only when "udp" is in rtspTransports and encryption is "strict" or "optional".
+srtcpAddress: :8005
+# Port of all UDP-multicast/SRTP listeners. This is needed only when "multicast" is in rtspTransports and encryption is "strict" or "optional".
+multicastSRTPPort: 8006
+# Port of all UDP-multicast/SRTCP listeners. This is needed only when "multicast" is in rtspTransports and encryption is "strict" or "optional".
+multicastSRTCPPort: 8007
+# Path to the server key. This is needed only when encryption is "strict" or "optional".
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+rtspServerKey: server.key
+# Path to the server certificate. This is needed only when encryption is "strict" or "optional".
+rtspServerCert: server.crt
+# Authentication methods. Available are "basic" and "digest".
+# "digest" doesn't provide any additional security and is available for compatibility only.
+rtspAuthMethods: [basic]
+
+###############################################
+# Global settings -> RTMP server
+
+# Enable publishing and reading streams with the RTMP protocol.
+rtmp: true
+# Use the secure protocol variant (RTMP).
+# Available values are "no", "strict", "optional".
+rtmpEncryption: "no"
+# Address of the RTMP listener. This is needed only when encryption is "no" or "optional".
+rtmpAddress: :1935
+# Address of the RTMPS listener. This is needed only when encryption is "strict" or "optional".
+rtmpsAddress: :1936
+# Path to the server key. This is needed only when encryption is "strict" or "optional".
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+rtmpServerKey: server.key
+# Path to the server certificate. This is needed only when encryption is "strict" or "optional".
+rtmpServerCert: server.crt
+
+###############################################
+# Global settings -> HLS server
+
+# Enable reading streams with the HLS protocol.
+hls: true
+# Address of the HLS listener.
+hlsAddress: :8888
+# Enable HTTPS on the HLS server.
+# This is required for Low-Latency HLS to function correctly on Apple devices.
+hlsEncryption: false
+# Path to the server key. This is needed only when encryption is yes.
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+hlsServerKey: server.key
+# Path to the server certificate.
+hlsServerCert: server.crt
+# Allowed CORS origins.
+# Supports wildcards: ['http://*.example.com']
+hlsAllowOrigins: ['*']
+# IPs or CIDRs of proxies placed before the HLS server.
+# If the server receives a request from one of these entries, IP in logs
+# will be taken from the X-Forwarded-For header.
+hlsTrustedProxies: []
+# By default, HLS is generated only when requested by a user.
+# This option allows to generate it always, avoiding the delay between request and generation.
+hlsAlwaysRemux: false
+# Variant of the HLS protocol to use. Available options are:
+# * mpegts - uses MPEG-TS segments, for maximum compatibility.
+# * fmp4 - uses fragmented MP4 segments, more efficient.
+# * lowLatency - uses Low-Latency HLS.
+hlsVariant: lowLatency
+# Number of HLS segments to keep on the server.
+# Segments allow to seek through the stream.
+# Their number doesn't influence latency.
+hlsSegmentCount: 7
+# Minimum duration of each segment.
+# A player usually puts 3 segments in a buffer before reproducing the stream.
+# The final segment duration is also influenced by the interval between IDR frames,
+# since the server changes the duration in order to include at least one IDR frame
+# in each segment.
+hlsSegmentDuration: 1s
+# Minimum duration of each part.
+# A player usually puts 3 parts in a buffer before reproducing the stream.
+# Parts are used in Low-Latency HLS in place of segments.
+# Part duration is influenced by the distance between video/audio samples
+# and is adjusted in order to produce segments with a similar duration.
+hlsPartDuration: 200ms
+# Maximum size of each segment.
+# This prevents RAM exhaustion.
+hlsSegmentMaxSize: 50M
+# Directory in which to save segments, instead of keeping them in the RAM.
+# This decreases performance, since reading from disk is less performant than
+# reading from RAM, but allows to save RAM.
+hlsDirectory: ''
+# The muxer will be closed when there are no
+# reader requests and this amount of time has passed.
+hlsMuxerCloseAfter: 60s
+
+###############################################
+# Global settings -> WebRTC server
+
+# Enable publishing and reading streams with the WebRTC protocol.
+webrtc: true
+# Address of the WebRTC HTTP listener.
+webrtcAddress: :8889
+# Enable HTTPS on the WebRTC server.
+# This covers only the WebRTC handshake and does not influence the encryption of WebRTC streams
+# which are always encrypted, with a key that is exchanged during the WebRTC handshake.
+webrtcEncryption: false
+# Path to the server key.
+# This can be generated with:
+# openssl genrsa -out server.key 2048
+# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
+webrtcServerKey: server.key
+# Path to the server certificate.
+webrtcServerCert: server.crt
+# Allowed CORS origins.
+# Supports wildcards: ['http://*.example.com']
+webrtcAllowOrigins: ['*']
+# IPs or CIDRs of proxies placed before the WebRTC server.
+# If the server receives a request from one of these entries, IP in logs
+# will be taken from the X-Forwarded-For header.
+webrtcTrustedProxies: []
+# Address of a local UDP listener that will receive connections.
+# Use a blank string to disable.
+webrtcLocalUDPAddress: :8189
+# Address of a local TCP listener that will receive connections.
+# This is disabled by default since TCP is less efficient than UDP and
+# introduces a progressive delay when network is congested.
+webrtcLocalTCPAddress: ''
+# WebRTC clients need to know the IP of the server.
+# Gather IPs from interfaces and send them to clients.
+webrtcIPsFromInterfaces: true
+# Interfaces whose IPs will be sent to clients.
+# An empty value means to use all available interfaces.
+webrtcIPsFromInterfacesList: []
+# Additional hosts or IPs to send to clients.
+webrtcAdditionalHosts: []
+# ICE servers. Needed only when local listeners can't be reached by clients.
+# STUN servers allow to obtain and share the public IP of the server.
+# TURN/TURNS servers force all traffic through them.
+webrtcICEServers2: []
+ # - 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: ''
+ # clientOnly: false
+# Maximum time to gather STUN candidates.
+webrtcSTUNGatherTimeout: 5s
+# Time to wait for the WebRTC handshake to complete.
+webrtcHandshakeTimeout: 10s
+# Maximum time to gather tracks.
+webrtcTrackGatherTimeout: 2s
+
+###############################################
+# Global settings -> SRT server
+
+# Enable publishing and reading streams with the SRT protocol.
+srt: true
+# Address of the SRT listener.
+srtAddress: :8890
+
+###############################################
+# Default path settings
+
+# Settings in "pathDefaults" are applied anywhere,
+# unless they are overridden in "paths".
+pathDefaults:
+
+ ###############################################
+ # Default path settings -> General
+
+ # Source of the stream. This can be:
+ # * publisher -> the stream is provided by a RTSP, RTMP, WebRTC or SRT client
+ # * rtsp://existing-url -> the stream is pulled from another RTSP server / camera
+ # * rtsps://existing-url -> the stream is pulled from another RTSP server / camera with RTSPS
+ # * rtsp+http://existing-url -> the stream is pulled from another RTSP server / camera, with HTTP tunneling
+ # * rtsps+http://existing-url -> the stream is pulled from another RTSP server / camera, with HTTPS tunneling
+ # * rtsp+ws://existing-url -> the stream is pulled from another RTSP server / camera, with WebSocket tunneling
+ # * rtsps+ws://existing-url -> the stream is pulled from another RTSP server / camera, with secure WebSocket tunneling
+ # * rtmp://existing-url -> the stream is pulled from another RTMP server / camera
+ # * rtmps://existing-url -> the stream is pulled from another RTMP server / camera with RTMPS
+ # * http://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera
+ # * https://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera with HTTPS
+ # * udp+mpegts://ip:port -> the stream is pulled from MPEG-TS over UDP, by listening on the specified address
+ # * unix+mpegts://socket -> the stream is pulled from MPEG-TS over Unix socket, by using the socket
+ # * udp+rtp://ip:port -> the stream is pulled from RTP over UDP, by listening on the specified address
+ # * srt://existing-url -> the stream is pulled from another SRT server / camera
+ # * whep://existing-url -> the stream is pulled from another WebRTC server / camera with HTTP+WHEP
+ # * wheps://existing-url -> the stream is pulled from another WebRTC server / camera with HTTPS+WHEP
+ # * redirect -> the stream is provided by another path or server
+ # * rpiCamera -> the stream is provided by a Raspberry Pi Camera
+ # The following variables can be used in the source string:
+ # * $MTX_QUERY: query parameters (passed by first reader)
+ # * $G1, $G2, ...: regular expression groups, if path name is
+ # a regular expression.
+ source: publisher
+ # If the source is a URL, 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 | sed -n '/BEGIN/,/END/p' > server.crt
+ # openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
+ sourceFingerprint:
+ # If the source is a URL, it will be pulled only when at least
+ # one reader is connected, saving bandwidth.
+ sourceOnDemand: false
+ # If sourceOnDemand is "yes", readers will be put on hold until the source is
+ # ready or until this amount of time has passed.
+ sourceOnDemandStartTimeout: 10s
+ # If sourceOnDemand is "yes", the source will be closed when there are no
+ # readers connected and this amount of time has passed.
+ sourceOnDemandCloseAfter: 10s
+ # Maximum number of readers. Zero means no limit.
+ maxReaders: 0
+ # SRT encryption passphrase required to read from this path.
+ srtReadPassphrase:
+ # Use absolute timestamp of frames, instead of replacing them with the current time.
+ useAbsoluteTimestamp: false
+
+ ###############################################
+ # Default path settings -> Always available
+
+ # Enable always-available mode, in which a offline segment is played on repeat when the stream is not available.
+ alwaysAvailable: false
+ # Tracks of the default offline segment.
+ alwaysAvailableTracks: []
+ # Available values are: AV1, VP9, H265, H264, Opus, MPEG4Audio, G711, LPCM
+ # - codec: H264
+ # # in case of MPEG4Audio, G711, LPCM, sampleRate and ChannelCount must be provided too.
+ # sampleRate: 48000
+ # channelCount: 2
+ # # in case of G711, muLaw must be provided too.
+ # muLaw: false
+ # A MP4 file can be used instead of the default offline segment.
+ alwaysAvailableFile: ''
+
+ ###############################################
+ # Default path settings -> Record
+
+ # Record streams to disk.
+ record: false
+ # Path of recording segments.
+ # Extension is added automatically.
+ # Available variables are %path (path name), %Y %m %d (year, month, day),
+ # %H %M %S (hours, minutes, seconds), %f (microseconds), %z (time zone), %s (unix epoch).
+ recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
+ # Format of recorded segments.
+ # Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS).
+ recordFormat: fmp4
+ # fMP4 segments are concatenation of small MP4 files (parts), each with this duration.
+ # MPEG-TS segments are concatenation of 188-bytes packets, flushed to disk with this period.
+ # When a system failure occurs, the last part gets lost.
+ # Therefore, the part duration is equal to the RPO (recovery point objective).
+ recordPartDuration: 1s
+ # This prevents RAM exhaustion.
+ recordMaxPartSize: 50M
+ # Minimum duration of each segment.
+ recordSegmentDuration: 1h
+ # Delete segments after this timespan.
+ # Set to 0s to disable automatic deletion.
+ recordDeleteAfter: 1d
+
+ ###############################################
+ # Default path settings -> Publisher source (when source is "publisher")
+
+ # Allow another client to disconnect the current publisher and publish in its place.
+ overridePublisher: true
+ # SRT encryption passphrase required to publish to this path.
+ srtPublishPassphrase:
+ # Demux MPEG-TS over RTSP into elementary streams.
+ # When enabled, RTSP publishers sending MP2T/90000 will be demultiplexed
+ # and their elementary streams (H.264, H.265, AAC, etc.) exposed as native tracks.
+ # This allows HLS, WebRTC, and other outputs to work transparently with MPEG-TS sources.
+ rtspDemuxMpegts: false
+
+ ###############################################
+ # Default path settings -> RTSP source (when source is a RTSP or a RTSPS URL)
+
+ # Transport protocol used to pull the stream. available values are "automatic", "udp", "multicast", "tcp".
+ rtspTransport: automatic
+ # Support sources that don't provide server ports or use random server ports. This is a security issue
+ # and must be used only when interacting with sources that require it.
+ rtspAnyPort: false
+ # Range header to send to the source, in order to start streaming from the specified offset.
+ # available values:
+ # * clock: Absolute time
+ # * npt: Normal Play Time
+ # * smpte: SMPTE timestamps relative to the start of the recording
+ rtspRangeType:
+ # Available values:
+ # * clock: UTC ISO 8601 combined date and time string, e.g. 20230812T120000Z
+ # * npt: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
+ # * smpte: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
+ rtspRangeStart:
+ # Range of ports used as source port in outgoing UDP packets.
+ rtspUDPSourcePortRange: [10000, 65535]
+
+ ###############################################
+ # Default path settings -> RTP source (when source is RTP)
+
+ # session description protocol (SDP) of the RTP stream.
+ rtpSDP:
+
+ ###############################################
+ # Default path settings -> WebRTC / WHEP source (when source is WHEP)
+
+ # Token to insert in the Authorization: Bearer header.
+ whepBearerToken: ''
+ # Maximum time to gather STUN candidates.
+ whepSTUNGatherTimeout: 5s
+ # Time to wait for the WebRTC handshake to complete.
+ whepHandshakeTimeout: 10s
+ # Maximum time to gather tracks.
+ whepTrackGatherTimeout: 2s
+
+ ###############################################
+ # Default path settings -> Redirect source (when source is "redirect")
+
+ # path which clients will be redirected to.
+ # It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
+ sourceRedirect:
+
+ ###############################################
+ # Default path settings -> Raspberry Pi Camera source (when source is "rpiCamera")
+
+ # ID of the camera.
+ rpiCameraCamID: 0
+ # Whether this is a secondary stream.
+ rpiCameraSecondary: false
+ # Width of frames.
+ rpiCameraWidth: 1920
+ # Height of frames.
+ rpiCameraHeight: 1080
+ # Flip horizontally.
+ rpiCameraHFlip: false
+ # Flip vertically.
+ rpiCameraVFlip: false
+ # Brightness [-1, 1].
+ rpiCameraBrightness: 0
+ # Contrast [0, 16].
+ rpiCameraContrast: 1
+ # Saturation [0, 16].
+ rpiCameraSaturation: 1
+ # Sharpness [0, 16].
+ rpiCameraSharpness: 1
+ # Exposure mode.
+ # values: normal, short, long, custom.
+ rpiCameraExposure: normal
+ # Auto-white-balance mode.
+ # (auto, incandescent, tungsten, fluorescent, indoor, daylight, cloudy or custom).
+ rpiCameraAWB: auto
+ # Auto-white-balance fixed gains. This can be used in place of rpiCameraAWB.
+ # format: [red,blue].
+ rpiCameraAWBGains: [0, 0]
+ # Denoise operating mode (off, cdn_off, cdn_fast, cdn_hq).
+ rpiCameraDenoise: "off"
+ # Fixed shutter speed, in microseconds.
+ rpiCameraShutter: 0
+ # Metering mode of the AEC/AGC algorithm (centre, spot, matrix or custom).
+ rpiCameraMetering: centre
+ # Fixed gain.
+ rpiCameraGain: 0
+ # EV compensation of the image in range [-10, 10].
+ rpiCameraEV: 0
+ # Region of interest, in format x,y,width,height (all normalized between 0 and 1).
+ rpiCameraROI:
+ # Whether to enable HDR on Raspberry Camera 3.
+ rpiCameraHDR: false
+ # Tuning file.
+ rpiCameraTuningFile:
+ # Sensor mode, in format [width]:[height]:[bit-depth]:[packing]
+ # bit-depth and packing are optional.
+ rpiCameraMode:
+ # frames per second.
+ rpiCameraFPS: 30
+ # Autofocus mode (auto, manual or continuous).
+ rpiCameraAfMode: continuous
+ # Autofocus range (normal, macro or full).
+ rpiCameraAfRange: normal
+ # Autofocus speed (normal or fast).
+ rpiCameraAfSpeed: normal
+ # Lens position (for manual autofocus only), will be set to focus to a specific distance
+ # calculated by the following formula: d = 1 / value
+ # Examples: 0 moves the lens to infinity.
+ # 0.5 moves the lens to focus on objects 2m away.
+ # 2 moves the lens to focus on objects 50cm away.
+ rpiCameraLensPosition: 0.0
+ # Autofocus window, in the form x,y,width,height where the coordinates
+ # are given as a proportion of the entire image.
+ rpiCameraAfWindow:
+ # Manual flicker correction period, in microseconds.
+ rpiCameraFlickerPeriod: 0
+ # Enables printing text on each frame.
+ rpiCameraTextOverlayEnable: false
+ # Text that is printed on each frame.
+ # format is the one of the strftime() function.
+ rpiCameraTextOverlay: '%Y-%m-%d %H:%M:%S - MediaMTX'
+ # Codec (auto, hardwareH264, softwareH264 or mjpeg).
+ # When is "auto" and stream is primary, it defaults to hardwareH264 (if available) or softwareH264.
+ # When is "auto" and stream is secondary, it defaults to mjpeg.
+ rpiCameraCodec: auto
+ # Period between IDR frames (when codec is hardwareH264 or softwareH264).
+ rpiCameraIDRPeriod: 60
+ # Bitrate (when codec is hardwareH264 or softwareH264).
+ rpiCameraBitrate: 5000000
+ # Hardware H264 profile (baseline, main or high) (when codec is hardwareH264).
+ rpiCameraHardwareH264Profile: main
+ # Hardware H264 level (4.0, 4.1 or 4.2) (when codec is hardwareH264).
+ rpiCameraHardwareH264Level: '4.1'
+ # Software H264 profile (baseline, main or high) (when codec is softwareH264).
+ rpiCameraSoftwareH264Profile: baseline
+ # Software H264 level (4.0, 4.1 or 4.2) (when codec is softwareH264).
+ rpiCameraSoftwareH264Level: '4.1'
+ # M-JPEG JPEG quality (when codec is mjpeg).
+ rpiCameraMJPEGQuality: 60
+
+ ###############################################
+ # Default path settings -> Hooks
+
+ # Command to run when this path is initialized.
+ # This can be used to publish a stream when the server is launched.
+ # This is terminated with SIGINT when the program closes.
+ # The following environment variables are available:
+ # * MTX_PATH: path name
+ # * RTSP_PORT: RTSP server port
+ # * G1, G2, ...: regular expression groups, if path name is
+ # a regular expression.
+ runOnInit:
+ # Restart the command if it exits.
+ runOnInitRestart: false
+
+ # Command to run when this path is requested by a reader
+ # and no one is publishing to this path yet.
+ # This can be used to publish a stream on demand.
+ # This is terminated with SIGINT when there are no readers anymore.
+ # The following environment variables are available:
+ # * MTX_PATH: path name
+ # * MTX_QUERY: query parameters (passed by first reader)
+ # * RTSP_PORT: RTSP server port
+ # * G1, G2, ...: regular expression groups, if path name is
+ # a regular expression.
+ runOnDemand:
+ # Restart the command if it exits.
+ runOnDemandRestart: false
+ # Readers will be put on hold until the runOnDemand command starts publishing
+ # or until this amount of time has passed.
+ runOnDemandStartTimeout: 10s
+ # The command will be closed when there are no
+ # readers connected and this amount of time has passed.
+ runOnDemandCloseAfter: 10s
+ # Command to run when there are no readers anymore.
+ # Environment variables are the same of runOnDemand.
+ runOnUnDemand:
+
+ # Command to run when the stream is ready to be read, whenever it is
+ # published by a client or pulled from a server / camera.
+ # This is terminated with SIGINT when the stream is not ready anymore.
+ # The following environment variables are available:
+ # * MTX_PATH: path name
+ # * MTX_QUERY: query parameters (passed by publisher)
+ # * MTX_SOURCE_TYPE: source type
+ # * MTX_SOURCE_ID: source ID
+ # * RTSP_PORT: RTSP server port
+ # * G1, G2, ...: regular expression groups, if path name is
+ # a regular expression.
+ runOnReady:
+ # Restart the command if it exits.
+ runOnReadyRestart: false
+ # Command to run when the stream is not available anymore.
+ # Environment variables are the same of runOnReady.
+ runOnNotReady:
+
+ # Command to run when a client starts reading.
+ # This is terminated with SIGINT when a client stops reading.
+ # The following environment variables are available:
+ # * MTX_PATH: path name
+ # * MTX_QUERY: query parameters (passed by reader)
+ # * MTX_READER_TYPE: reader type
+ # * MTX_READER_ID: reader ID
+ # * RTSP_PORT: RTSP server port
+ # * G1, G2, ...: regular expression groups, if path name is
+ # a regular expression.
+ runOnRead:
+ # Restart the command if it exits.
+ runOnReadRestart: false
+ # Command to run when a client stops reading.
+ # Environment variables are the same of runOnRead.
+ runOnUnread:
+
+ # Command to run when a recording segment is created.
+ # The following environment variables are available:
+ # * MTX_PATH: path name
+ # * MTX_SEGMENT_PATH: segment file path
+ # * RTSP_PORT: RTSP server port
+ # * G1, G2, ...: regular expression groups, if path name is
+ # a regular expression.
+ runOnRecordSegmentCreate:
+
+ # Command to run when a recording segment is complete.
+ # The following environment variables are available:
+ # * MTX_PATH: path name
+ # * MTX_SEGMENT_PATH: segment file path
+ # * MTX_SEGMENT_DURATION: segment duration
+ # * RTSP_PORT: RTSP server port
+ # * G1, G2, ...: regular expression groups, if path name is
+ # a regular expression.
+ runOnRecordSegmentComplete:
+
+###############################################
+# Path settings
+
+# Settings in "paths" are applied to specific paths, and the map key
+# is the name of the path.
+# Any setting in "pathDefaults" can be overridden here.
+# It's possible to use regular expressions by using a tilde as prefix,
+# for example "~^(test1|test2)$" will match both "test1" and "test2",
+# for example "~^prefix" will match all paths that start with "prefix".
+paths:
+ # example:
+ # my_camera:
+ # source: rtsp://my_camera
+
+ # Settings under path "all_others" are applied to all paths that
+ # do not match another entry.
+ all_others:
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 38b568f..a15df68 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,3 +4,5 @@ black
pyubx2
pyubxutils
jsonschema
+aiohttp
+aiodns