probe: pause live upstream during synthetic rct runs
This commit is contained in:
parent
5667608707
commit
32b058973e
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.55"
|
version = "0.22.56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1686,7 +1686,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.55"
|
version = "0.22.56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1698,7 +1698,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.55"
|
version = "0.22.56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.55"
|
version = "0.22.56"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.55"
|
version = "0.22.56"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ DEFAULT_JPEG_QUALITY = 82
|
|||||||
HIGH_SPEED_ISOCHRONOUS_MICROFRAMES_PER_SEC = 8000
|
HIGH_SPEED_ISOCHRONOUS_MICROFRAMES_PER_SEC = 8000
|
||||||
DEFAULT_ISOCHRONOUS_LIMIT_PCT = 85
|
DEFAULT_ISOCHRONOUS_LIMIT_PCT = 85
|
||||||
DEFAULT_UVC_MAX_PACKET = 1024
|
DEFAULT_UVC_MAX_PACKET = 1024
|
||||||
|
DEFAULT_MEDIA_CONTROL_PATH = "/tmp/lesavka-media.control"
|
||||||
MARKER_BITS = 32
|
MARKER_BITS = 32
|
||||||
MARKER_COLUMNS = 16
|
MARKER_COLUMNS = 16
|
||||||
CADENCE_REASONS = {"frame_repeat", "frame_gap", "frame_backwards"}
|
CADENCE_REASONS = {"frame_repeat", "frame_gap", "frame_backwards"}
|
||||||
@ -53,6 +54,16 @@ def parse_args() -> argparse.Namespace:
|
|||||||
parser.add_argument("--artifact-dir", default="")
|
parser.add_argument("--artifact-dir", default="")
|
||||||
parser.add_argument("--remote-rct-dir", default="")
|
parser.add_argument("--remote-rct-dir", default="")
|
||||||
parser.add_argument("--remote-inject-dir", default="")
|
parser.add_argument("--remote-inject-dir", default="")
|
||||||
|
parser.add_argument(
|
||||||
|
"--pause-local-live-upstream",
|
||||||
|
action="store_true",
|
||||||
|
help="temporarily write camera=0 to the local Lesavka media control file so a live client does not preempt the synthetic injector",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--media-control-path",
|
||||||
|
default=os.environ.get("LESAVKA_MEDIA_CONTROL", DEFAULT_MEDIA_CONTROL_PATH),
|
||||||
|
help=f"local live-media control file used with --pause-local-live-upstream (default: {DEFAULT_MEDIA_CONTROL_PATH})",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--capture-before-inject",
|
"--capture-before-inject",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@ -155,6 +166,48 @@ def default_artifact_dir(mode: str) -> pathlib.Path:
|
|||||||
return pathlib.Path("artifacts/synthetic-rct") / f"{safe_mode}-{timestamp()}"
|
return pathlib.Path("artifacts/synthetic-rct") / f"{safe_mode}-{timestamp()}"
|
||||||
|
|
||||||
|
|
||||||
|
def media_control_with_camera(raw: str | None, enabled: bool) -> str:
|
||||||
|
tokens = raw.split() if raw else []
|
||||||
|
rendered: list[str] = []
|
||||||
|
saw_camera = False
|
||||||
|
saw_microphone = False
|
||||||
|
saw_audio = False
|
||||||
|
for token in tokens:
|
||||||
|
key, sep, _value = token.partition("=")
|
||||||
|
if sep and key == "camera":
|
||||||
|
rendered.append(f"camera={1 if enabled else 0}")
|
||||||
|
saw_camera = True
|
||||||
|
else:
|
||||||
|
rendered.append(token)
|
||||||
|
saw_microphone = saw_microphone or (sep and key in {"microphone", "mic"})
|
||||||
|
saw_audio = saw_audio or (sep and key in {"audio", "speaker"})
|
||||||
|
if not saw_camera:
|
||||||
|
rendered.insert(0, f"camera={1 if enabled else 0}")
|
||||||
|
if not saw_microphone:
|
||||||
|
rendered.append("microphone=1")
|
||||||
|
if not saw_audio:
|
||||||
|
rendered.append("audio=1")
|
||||||
|
return " ".join(rendered) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def pause_local_live_upstream(args: argparse.Namespace) -> tuple[pathlib.Path, bytes | None]:
|
||||||
|
path = pathlib.Path(args.media_control_path)
|
||||||
|
original = path.read_bytes() if path.exists() else None
|
||||||
|
raw = original.decode(errors="replace") if original is not None else None
|
||||||
|
path.write_text(media_control_with_camera(raw, False))
|
||||||
|
print(f"paused local live camera upstream via {path}", file=sys.stderr)
|
||||||
|
time.sleep(0.5)
|
||||||
|
return path, original
|
||||||
|
|
||||||
|
|
||||||
|
def restore_local_live_upstream(path: pathlib.Path, original: bytes | None) -> None:
|
||||||
|
if original is None:
|
||||||
|
path.unlink(missing_ok=True)
|
||||||
|
else:
|
||||||
|
path.write_bytes(original)
|
||||||
|
print(f"restored local live media control at {path}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def run_remote_orchestrated(args: argparse.Namespace) -> int:
|
def run_remote_orchestrated(args: argparse.Namespace) -> int:
|
||||||
if (not args.inject_host and not args.local_inject) or not args.rct_host:
|
if (not args.inject_host and not args.local_inject) or not args.rct_host:
|
||||||
raise SystemExit(
|
raise SystemExit(
|
||||||
@ -273,6 +326,8 @@ def run_remote_orchestrated(args: argparse.Namespace) -> int:
|
|||||||
"inject_host": args.inject_host,
|
"inject_host": args.inject_host,
|
||||||
"local_inject": args.local_inject,
|
"local_inject": args.local_inject,
|
||||||
"rct_host": args.rct_host,
|
"rct_host": args.rct_host,
|
||||||
|
"pause_local_live_upstream": args.pause_local_live_upstream,
|
||||||
|
"media_control_path": args.media_control_path,
|
||||||
},
|
},
|
||||||
indent=2,
|
indent=2,
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
@ -332,24 +387,31 @@ def run_remote_orchestrated(args: argparse.Namespace) -> int:
|
|||||||
|
|
||||||
capture: subprocess.Popen[Any] | None = None
|
capture: subprocess.Popen[Any] | None = None
|
||||||
diagnosis: list[str] = []
|
diagnosis: list[str] = []
|
||||||
if args.capture_before_inject:
|
paused_control: tuple[pathlib.Path, bytes | None] | None = None
|
||||||
capture = start_capture()
|
try:
|
||||||
time.sleep(1.0)
|
if args.pause_local_live_upstream:
|
||||||
inject = start_inject()
|
paused_control = pause_local_live_upstream(args)
|
||||||
capture_rc, inject_rc = wait_capture_or_inject_exit(capture, inject)
|
if args.capture_before_inject:
|
||||||
else:
|
|
||||||
inject = start_inject()
|
|
||||||
time.sleep(max(0.0, args.inject_warmup_s))
|
|
||||||
inject_rc = inject.poll()
|
|
||||||
if inject_rc is not None:
|
|
||||||
capture_rc = None
|
|
||||||
diagnosis.append(
|
|
||||||
"synthetic uplink exited before capture warmup completed; disconnect the live client or pause upstream webcam before running the isolated probe"
|
|
||||||
)
|
|
||||||
print(f"synthetic uplink exited before capture started rc={inject_rc}", file=sys.stderr)
|
|
||||||
else:
|
|
||||||
capture = start_capture()
|
capture = start_capture()
|
||||||
|
time.sleep(1.0)
|
||||||
|
inject = start_inject()
|
||||||
capture_rc, inject_rc = wait_capture_or_inject_exit(capture, inject)
|
capture_rc, inject_rc = wait_capture_or_inject_exit(capture, inject)
|
||||||
|
else:
|
||||||
|
inject = start_inject()
|
||||||
|
time.sleep(max(0.0, args.inject_warmup_s))
|
||||||
|
inject_rc = inject.poll()
|
||||||
|
if inject_rc is not None:
|
||||||
|
capture_rc = None
|
||||||
|
diagnosis.append(
|
||||||
|
"synthetic uplink exited before capture warmup completed; disconnect the live client or pause upstream webcam before running the isolated probe"
|
||||||
|
)
|
||||||
|
print(f"synthetic uplink exited before capture started rc={inject_rc}", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
capture = start_capture()
|
||||||
|
capture_rc, inject_rc = wait_capture_or_inject_exit(capture, inject)
|
||||||
|
finally:
|
||||||
|
if paused_control is not None:
|
||||||
|
restore_local_live_upstream(*paused_control)
|
||||||
local_capture = artifact_dir / "capture"
|
local_capture = artifact_dir / "capture"
|
||||||
local_inject = artifact_dir / "inject"
|
local_inject = artifact_dir / "inject"
|
||||||
if capture is not None:
|
if capture is not None:
|
||||||
|
|||||||
@ -16,7 +16,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.55"
|
version = "0.22.56"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,8 @@ fn synthetic_probe_keeps_bundled_network_ingress_and_rct_comparison_markers() {
|
|||||||
"--capture-before-inject",
|
"--capture-before-inject",
|
||||||
"--inject-warmup-s",
|
"--inject-warmup-s",
|
||||||
"--capture-finish-grace-s",
|
"--capture-finish-grace-s",
|
||||||
|
"--pause-local-live-upstream",
|
||||||
|
"--media-control-path",
|
||||||
"--jpeg-quality",
|
"--jpeg-quality",
|
||||||
"--inject-max-frame-bytes",
|
"--inject-max-frame-bytes",
|
||||||
"--stream-analyze",
|
"--stream-analyze",
|
||||||
@ -93,6 +95,9 @@ fn synthetic_probe_keeps_bundled_network_ingress_and_rct_comparison_markers() {
|
|||||||
"synthetic uplink completed but RCT capture did not finish",
|
"synthetic uplink completed but RCT capture did not finish",
|
||||||
"synthetic injector was preempted after sending",
|
"synthetic injector was preempted after sending",
|
||||||
"synthetic uplink exited before capture warmup completed",
|
"synthetic uplink exited before capture warmup completed",
|
||||||
|
"paused local live camera upstream",
|
||||||
|
"restored local live media control",
|
||||||
|
"media_control_with_camera",
|
||||||
"max_lower_mae",
|
"max_lower_mae",
|
||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
"v4l2",
|
"v4l2",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user