aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/config.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/config.py43
1 files changed, 43 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/config.py b/packages/meshbay-node/src/meshbay_node/config.py
index e4444d3..3de2473 100644
--- a/packages/meshbay-node/src/meshbay_node/config.py
+++ b/packages/meshbay-node/src/meshbay_node/config.py
@@ -37,6 +37,13 @@ ui_port = 18000 # local admin UI (127.0.0.1 only)
invite_ttl_hours = 168 # 7 days
pair_ttl_hours = 24
+# How many people may watch a video at once. One ffmpeg runs per viewer for as
+# long as they watch — it remuxes rather than re-encodes, so it costs little CPU
+# and around 50 MB of memory, and spends most of the film idle. Past this, a
+# viewer is told the server is busy. Raise it on a machine with memory to spare;
+# lower it on a Pi.
+max_concurrent_streams = 8
+
# Browser and native clients reach this node over WebRTC DataChannel via hub
# signaling — no inbound port to open. QUIC is the optional direct path.
@@ -82,6 +89,11 @@ class NodeConfig:
# the SSH session that printed it.
invite_ttl_hours: int = 168 # 7 days
pair_ttl_hours: int = 24
+ # How many people may watch a video at the same time. One ffmpeg runs per
+ # viewer for as long as they watch, so this is the knob that decides when
+ # the node answers "server busy" — see MAX_CONCURRENT_TRANSCODES in
+ # transport/webrtc_server.py for what one costs.
+ max_concurrent_streams: int = 8
@dataclass
@@ -121,6 +133,30 @@ class Config:
return self.groups[0] if self.groups else GroupConfig()
+def _positive(value: object, default: int, name: str) -> int:
+ """A count that must be at least one, or the default with a word about it.
+
+ Zero is the dangerous one: `asyncio.Semaphore(0)` is not "no limit", it is
+ a node where no video ever plays and nothing in the log says why.
+ """
+ # bool before int: TOML `true` is a bool, and `int(True)` is 1 — a node
+ # where exactly one person may watch, arrived at by a typo and announced
+ # nowhere.
+ if isinstance(value, bool) or not isinstance(value, (int, str)):
+ log.warning("%s = %r is not a count — using %d", name, value, default)
+ return default
+ try:
+ n = int(value)
+ except (TypeError, ValueError):
+ log.warning("%s = %r is not a number — using %d", name, value, default)
+ return default
+ if n < 1:
+ log.warning("%s = %d would stop the feature entirely — using %d",
+ name, n, default)
+ return default
+ return n
+
+
def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config:
"""
Load config from TOML file. Supports both single [group] and
@@ -144,6 +180,9 @@ def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config:
nd.get("invite_ttl_hours", cfg.node.invite_ttl_hours))
cfg.node.pair_ttl_hours = int(
nd.get("pair_ttl_hours", cfg.node.pair_ttl_hours))
+ cfg.node.max_concurrent_streams = _positive(
+ nd.get("max_concurrent_streams", cfg.node.max_concurrent_streams),
+ cfg.node.max_concurrent_streams, "max_concurrent_streams")
# Multi-group: [[groups]] array
if "groups" in raw:
@@ -189,6 +228,10 @@ def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config:
cfg.hub.username = user
if port := os.environ.get("MESHBAY_QUIC_PORT"):
cfg.node.quic_port = int(port)
+ if streams := os.environ.get("MESHBAY_MAX_CONCURRENT_STREAMS"):
+ cfg.node.max_concurrent_streams = _positive(
+ streams, cfg.node.max_concurrent_streams,
+ "MESHBAY_MAX_CONCURRENT_STREAMS")
return cfg