import pytest from iqpilot.system.webrtc.rtc.info import parse_info_from_offer from iqpilot.system.webrtc.rtc.stream import WebRTCBaseStream from iqpilot.system.webrtc.rtc.tracks import TiciTrackWrapper, TiciVideoStreamTrack, parse_video_track_id, video_track_id def sdp_with_media(media): mids = " ".join(str(i) for i in range(len(media))) sections = [] for index, (kind, direction) in enumerate(media): if kind == "video": payload, mapping = "96", "H264/90000" protocol = "UDP/TLS/RTP/SAVPF" elif kind == "audio": payload, mapping = "111", "opus/48000/2" protocol = "UDP/TLS/RTP/SAVPF" else: sections.extend([ "m=application 9 UDP/DTLS/SCTP webrtc-datachannel", "c=IN IP4 0.0.0.0", f"a=mid:{index}", "a=sctp-port:5000", ]) continue sections.extend([ f"m={kind} 9 {protocol} {payload}", "c=IN IP4 0.0.0.0", f"a=mid:{index}", f"a={direction}", f"a=rtpmap:{payload} {mapping}", "a=rtcp-mux", ]) lines = [ "v=0", "o=- 1 1 IN IP4 0.0.0.0", "s=-", "t=0 0", f"a=group:BUNDLE {mids}", *sections, ] return "\r\n".join(lines) + "\r\n" @pytest.mark.parametrize("direction,expected_outgoing,expected_incoming", [ ("recvonly", True, False), ("sendonly", False, True), ("sendrecv", True, True), ("inactive", False, False), ]) def test_audio_directions(direction, expected_outgoing, expected_incoming): info = parse_info_from_offer(sdp_with_media([("audio", direction)])) assert info.expected_audio_track == expected_outgoing assert info.incoming_audio_track == expected_incoming def test_video_and_data_channel_metadata(): info = parse_info_from_offer(sdp_with_media([ ("video", "recvonly"), ("video", "sendrecv"), ("application", "sendrecv"), ])) assert info.n_expected_camera_tracks == 2 assert info.incoming_datachannel def test_explicit_empty_ice_servers_disable_defaults(): assert WebRTCBaseStream._make_ice_servers([]) == [] def test_default_ice_server(): servers = WebRTCBaseStream._make_ice_servers(None) assert len(servers) == 1 assert servers[0].hostname == "stun.l.google.com" assert servers[0].port == 19302 def test_authenticated_ice_servers(): servers = WebRTCBaseStream._make_ice_servers([{ "urls": ["turn:relay.example.com:3478", "stun:stun.example.com:3478"], "username": "user", "credential": "secret", }]) assert [(server.hostname, server.port) for server in servers] == [ ("relay.example.com", 3478), ("stun.example.com", 3478), ] assert all(server.username == "user" and server.password == "secret" for server in servers) def test_track_id_roundtrip(): assert parse_video_track_id(video_track_id("driver", "track")) == ("driver", "track") def test_invalid_track_id(): with pytest.raises(ValueError): parse_video_track_id("driver") def test_track_wrapper_preserves_camera(): class Track: kind = "video" id = "source" async def recv(self): return b"frame" wrapper = TiciTrackWrapper("road", Track()) assert parse_video_track_id(wrapper.id)[0] == "road" wrapper.stop() assert wrapper.readyState == "ended" def test_track_stores_frame_period(): track = TiciVideoStreamTrack("wideRoad", 0.05) assert track._dt == 0.05