diff --git a/system/webrtc/device/video.py b/system/webrtc/device/video.py index 888f1739a..b8c90c9fa 100644 --- a/system/webrtc/device/video.py +++ b/system/webrtc/device/video.py @@ -64,7 +64,7 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack): self._seen_keyframe = False def request_keyframe(self) -> None: - self.params.put("LivestreamRequestKeyframe", True, block=False) + self.params.put_nonblocking("LivestreamRequestKeyframe", True) def _build_frame_data(self, msg) -> bytes: encode_data = getattr(msg, msg.which()) @@ -91,7 +91,7 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack): if msg is not None: if not self._seen_keyframe and (getattr(msg, msg.which()).idx.flags & V4L2_BUF_FLAG_KEYFRAME): self._seen_keyframe = True - self.params.put("LivestreamRequestKeyframe", False, block=False) + self.params.put_nonblocking("LivestreamRequestKeyframe", False) break await asyncio.sleep(0.005) diff --git a/system/webrtc/helpers.py b/system/webrtc/helpers.py index 1cc341a0e..bb47e3dea 100644 --- a/system/webrtc/helpers.py +++ b/system/webrtc/helpers.py @@ -9,11 +9,21 @@ WEBRTCD_PORT = 5001 @dataclass class StreamRequestBody: sdp: str - init_camera: str - enabled: bool + init_camera: str = "" + enabled: bool = True + cameras: list[str] = field(default_factory=list) bridge_services_in: list[str] = field(default_factory=list) bridge_services_out: list[str] = field(default_factory=list) + def __post_init__(self): + if not self.cameras: + if self.init_camera: + self.cameras = [self.init_camera] + else: + self.cameras = ["road"] + if not self.init_camera and self.cameras: + self.init_camera = self.cameras[0] + def post_stream_request(body: StreamRequestBody) -> dict: t_start = time.monotonic() diff --git a/system/webrtc/tests/test_helpers.py b/system/webrtc/tests/test_helpers.py new file mode 100644 index 000000000..022921786 --- /dev/null +++ b/system/webrtc/tests/test_helpers.py @@ -0,0 +1,29 @@ +from dataclasses import asdict + +from openpilot.system.webrtc.helpers import StreamRequestBody + + +def test_stream_request_body_defaults_legacy_clients_to_road(): + body = StreamRequestBody(sdp="offer") + + assert body.init_camera == "road" + assert body.enabled is True + assert body.cameras == ["road"] + assert body.bridge_services_in == [] + assert body.bridge_services_out == [] + + +def test_stream_request_body_maps_legacy_camera_to_new_camera_list(): + body = StreamRequestBody(sdp="offer", init_camera="driver", enabled=False) + + assert body.init_camera == "driver" + assert body.cameras == ["driver"] + assert body.enabled is False + + +def test_stream_request_body_preserves_explicit_multi_camera_request(): + body = StreamRequestBody(sdp="offer", cameras=["road", "driver"]) + + assert body.init_camera == "road" + assert body.cameras == ["road", "driver"] + assert asdict(body)["cameras"] == ["road", "driver"] diff --git a/system/webrtc/webrtcd.py b/system/webrtc/webrtcd.py index b3bacc81e..cdc4be387 100644 --- a/system/webrtc/webrtcd.py +++ b/system/webrtc/webrtcd.py @@ -397,7 +397,10 @@ def _text_response(text: str, status: int = 200) -> tuple[int, bytes, str]: async def handle_get_stream(state: ServerState, raw_body: bytes) -> tuple[int, bytes, str]: stream_dict = state.streams - body = StreamRequestBody(**json.loads(raw_body)) + parsed_dict = json.loads(raw_body) + valid_fields = {f.name for f in StreamRequestBody.__dataclass_fields__.values()} + filtered_dict = {k: v for k, v in parsed_dict.items() if k in valid_fields} + body = StreamRequestBody(**filtered_dict) async with state.stream_lock: # don't remove existing connection on prewarm request