feat: point the fleet at wss LiveKit room uwh-telhai
Publish and subscribe over wss://livekit.uni-wh.de:7800 and refuse cleartext ws://. Conference room is uwh-telhai. Includes the uncommitted encoded H.264 publish path, Rally hairpin, and KMS wall overlay.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
"""Logitech Rally Camera V-R0010 + Dante room mic (speaker identity)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from discovery import Camera
|
||||
from usb_ids import (
|
||||
RALLY_IDENTITY,
|
||||
is_rally_pid,
|
||||
looks_like_rally_name,
|
||||
usb_vid_pid_of_video_node,
|
||||
)
|
||||
|
||||
log = logging.getLogger("cameras.rally")
|
||||
|
||||
_CARD_RE = re.compile(r"\s*(\d+)\s+\[([A-Za-z0-9_]+)\s*\]:\s*(.*)")
|
||||
|
||||
|
||||
def dante_capture_card() -> int | None:
|
||||
"""ALSA card index for the Audinate Dante USB I/O module, if present."""
|
||||
path = Path("/proc/asound/cards")
|
||||
if not path.is_file():
|
||||
return None
|
||||
for line in path.read_text().splitlines():
|
||||
m = _CARD_RE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
rest = (m.group(3) or "").lower()
|
||||
name = (m.group(2) or "").lower()
|
||||
if "dante" in rest or "dante" in name:
|
||||
return int(m.group(1))
|
||||
return None
|
||||
|
||||
|
||||
def rally_video_node() -> str | None:
|
||||
pinned = Path("/dev/rally")
|
||||
if pinned.exists():
|
||||
return str(pinned)
|
||||
sysfs = Path("/sys/class/video4linux")
|
||||
if not sysfs.is_dir():
|
||||
return None
|
||||
for p in sorted(sysfs.glob("video*"), key=lambda x: int(x.name[5:] or 0)):
|
||||
name = (p / "name").read_text().strip() if (p / "name").is_file() else ""
|
||||
idx = (p / "index").read_text().strip() if (p / "index").is_file() else ""
|
||||
node = f"/dev/{p.name}"
|
||||
if idx != "0":
|
||||
continue
|
||||
if looks_like_rally_name(name):
|
||||
return node
|
||||
ids = usb_vid_pid_of_video_node(node)
|
||||
if ids is not None and is_rally_pid(ids[1]):
|
||||
return node
|
||||
return None
|
||||
|
||||
|
||||
def _serial_of_video(node: str) -> str:
|
||||
link = Path(f"/sys/class/video4linux/{Path(node).name}/device")
|
||||
try:
|
||||
p = link.resolve()
|
||||
except OSError:
|
||||
return ""
|
||||
for _ in range(12):
|
||||
cand = p / "serial"
|
||||
if cand.is_file():
|
||||
return cand.read_text().strip()
|
||||
if str(p).startswith("/sys/devices"):
|
||||
p = p.parent
|
||||
else:
|
||||
break
|
||||
return ""
|
||||
|
||||
|
||||
def discover_rally(*, width: int = 1920, height: int = 1080) -> Camera | None:
|
||||
"""Speaker camera: Rally UVC + Dante capture. None if the head is missing."""
|
||||
video = rally_video_node()
|
||||
if not video:
|
||||
return None
|
||||
card = dante_capture_card()
|
||||
serial = _serial_of_video(video)
|
||||
cam = Camera(
|
||||
index=20,
|
||||
identity=RALLY_IDENTITY,
|
||||
video_device=video,
|
||||
audio_card=card,
|
||||
serial=serial,
|
||||
width=width,
|
||||
height=height,
|
||||
)
|
||||
log.info(
|
||||
"rally video=%s audio=%s serial=%s %dx%d",
|
||||
video,
|
||||
f"hw:{card},0" if card is not None else "none",
|
||||
serial or "-",
|
||||
width,
|
||||
height,
|
||||
)
|
||||
return cam
|
||||
Reference in New Issue
Block a user