|
|
@@ -12,6 +12,8 @@ ffmpeg into the same registries the RTSP path uses.
|
|
|
from __future__ import annotations
|
|
|
|
|
|
import asyncio
|
|
|
+import os
|
|
|
+import sys
|
|
|
import time
|
|
|
from contextlib import suppress
|
|
|
from unittest.mock import AsyncMock, mock_open, patch
|
|
|
@@ -196,6 +198,47 @@ def test_scan_matches_v4l2_ffmpeg(monkeypatch):
|
|
|
assert 52020 in camera._scan_bambu_ffmpeg_pids()
|
|
|
|
|
|
|
|
|
+def test_scan_matches_an_orphaned_rtsp_ffmpeg(monkeypatch):
|
|
|
+ cmdline = b"ffmpeg\x00-i\x00rtsp://bblp:code@127.0.0.1:4000/streaming/live/1\x00-\x00"
|
|
|
+ monkeypatch.setattr("os.listdir", lambda _p: ["52022"])
|
|
|
+ monkeypatch.setattr(camera, "_read_ppid", lambda _pid: 1)
|
|
|
+ with patch("builtins.open", mock_open(read_data=cmdline)):
|
|
|
+ assert camera._scan_bambu_ffmpeg_pids() == [52022]
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.skipif(sys.platform != "linux", reason="/proc")
|
|
|
+def test_scan_leaves_another_processes_camera_ffmpeg_alone(tmp_path):
|
|
|
+ """For real, through /proc: the same command run by another program (here
|
|
|
+ a shell standing in for another Bambuddy) is not found; ours is."""
|
|
|
+ import subprocess
|
|
|
+
|
|
|
+ fake = tmp_path / "ffmpeg"
|
|
|
+ fake.write_text("#!/bin/sh\nsleep 30\n")
|
|
|
+ fake.chmod(0o755)
|
|
|
+ url = "rtsp://bblp:code@127.0.0.1:4000/streaming/live/1"
|
|
|
+ theirs = subprocess.Popen(["sh", "-c", f"'{fake}' -i '{url}' -; true"])
|
|
|
+ ours = subprocess.Popen([str(fake), "-i", url, "-"])
|
|
|
+ try:
|
|
|
+ deadline = time.monotonic() + 5
|
|
|
+ other = None
|
|
|
+ while other is None and time.monotonic() < deadline:
|
|
|
+ for pid in os.listdir("/proc"):
|
|
|
+ if pid.isdigit() and camera._read_ppid(int(pid)) == theirs.pid:
|
|
|
+ other = int(pid)
|
|
|
+ time.sleep(0.05)
|
|
|
+ assert other is not None, "the other program's ffmpeg did not start"
|
|
|
+
|
|
|
+ found = camera._scan_bambu_ffmpeg_pids()
|
|
|
+
|
|
|
+ assert ours.pid in found
|
|
|
+ assert other not in found
|
|
|
+ finally:
|
|
|
+ for proc in (theirs, ours):
|
|
|
+ proc.kill()
|
|
|
+ proc.wait()
|
|
|
+ subprocess.run(["pkill", "-f", str(fake)], check=False)
|
|
|
+
|
|
|
+
|
|
|
def test_scan_ignores_unrelated_ffmpeg(monkeypatch):
|
|
|
# A transcode of a local file is not ours — must not be reaped.
|
|
|
cmdline = b"ffmpeg\x00-i\x00/home/user/movie.mp4\x00out.mkv\x00"
|
|
|
@@ -233,9 +276,19 @@ def test_a_different_v4l2_command_shape_is_not_ours():
|
|
|
assert not camera._is_bambuddy_ffmpeg(recording, OWN_PID, OWN_PID)
|
|
|
|
|
|
|
|
|
-def test_bambu_rtsp_is_ours_whatever_the_parent():
|
|
|
- rtsp = [b"ffmpeg", b"-rtsp_transport", b"tcp", b"-i", b"rtsps://bblp:code@10.0.0.5:322/streaming/live/1", b"-"]
|
|
|
- assert camera._is_bambuddy_ffmpeg(rtsp, 777, OWN_PID)
|
|
|
+RTSP = [b"ffmpeg", b"-rtsp_transport", b"tcp", b"-i", b"rtsps://bblp:code@10.0.0.5:322/streaming/live/1", b"-"]
|
|
|
+
|
|
|
+
|
|
|
+def test_our_bambu_rtsp_and_its_orphans_are_ours():
|
|
|
+ assert camera._is_bambuddy_ffmpeg(RTSP, OWN_PID, OWN_PID)
|
|
|
+ assert camera._is_bambuddy_ffmpeg(RTSP, 1, OWN_PID)
|
|
|
+
|
|
|
+
|
|
|
+def test_another_bambuddys_rtsp_is_not_ours():
|
|
|
+ """A second install or a test server on the same host runs the same
|
|
|
+ command: its cleanup killed this one's live camera every minute."""
|
|
|
+ assert not camera._is_bambuddy_ffmpeg(RTSP, 777, OWN_PID)
|
|
|
+ assert not camera._is_bambuddy_ffmpeg(RTSP, None, OWN_PID)
|
|
|
|
|
|
|
|
|
def test_unknown_parent_is_not_ours_for_usb():
|