Files
StarPilot/teleoprtc_repo/tests/test_stream.py
T
firestar5683 22a8575199 Vendor dependencies and resources for standalone builds
Keep official openpilot ancestry and dependency licenses. Store resources directly in Git, remove LFS setup requirements, and leave the downloadable big model outside the source tree.
2026-10-03 21:01:18 +00:00

234 lines
6.5 KiB
Python
Executable File

#!/usr/bin/env python3
import contextlib
import pytest
from libdatachannel import Description
from teleoprtc.builder import WebRTCOfferBuilder, WebRTCAnswerBuilder
from teleoprtc.info import parse_info_from_offer
from teleoprtc.tracks import TiciVideoStreamTrack
class OfferCapture:
def __init__(self):
self.offer = None
async def __call__(self, offer):
self.offer = offer
raise Exception("Offer captured")
class DummyH264VideoStreamTrack(TiciVideoStreamTrack):
kind = "video"
async def recv(self):
raise NotImplementedError()
@pytest.mark.asyncio
class TestOfferStream:
async def test_offer_stream_sdp_recvonly_audio(self):
capture = OfferCapture()
builder = WebRTCOfferBuilder(capture)
builder.offer_to_receive_audio_stream()
stream = builder.stream()
try:
_ = await stream.start()
except Exception:
pass
finally:
await stream.stop()
info = parse_info_from_offer(capture.offer.sdp)
assert info.expected_audio_track
assert not info.incoming_audio_track
async def test_offer_stream_sdp_channel(self):
capture = OfferCapture()
builder = WebRTCOfferBuilder(capture)
builder.add_messaging()
stream = builder.stream()
try:
_ = await stream.start()
except Exception:
pass
finally:
await stream.stop()
info = parse_info_from_offer(capture.offer.sdp)
assert info.incoming_datachannel
async def test_answer_side_messaging_channel_wrapper_retained_after_stop(self):
stream = WebRTCOfferBuilder(OfferCapture()).stream()
stream._retain_messaging_channel_on_close = True
channel = stream.peer_connection.create_data_channel("data")
stream.messaging_channel = channel
stream.messaging_channel_ready_event.set()
try:
await stream.stop()
assert stream.messaging_channel is None
assert stream._retained_messaging_channels[-1] is channel
finally:
stream._retained_messaging_channels.pop()
@pytest.mark.asyncio
class TestAnswerStream:
async def test_codec_preference(self):
offer_sdp = """v=0
o=- 3910274679 3910274679 IN IP4 0.0.0.0
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic:WMS *
m=video 1337 UDP/TLS/RTP/SAVPF 97 98 99 100 101 102
c=IN IP4 0.0.0.0
a=recvonly
a=mid:0
a=msid:34803878-98f8-4245-b45c-f773e5f926df 881dbc20-356a-499c-b4e8-695303bb901d
a=rtcp:9 IN IP4 0.0.0.0
a=rtcp-mux
a=ssrc-group:FID 1303546896 3784011659
a=ssrc:1303546896 cname:a59185ac-c115-48d3-b39b-db7d615a6966
a=ssrc:3784011659 cname:a59185ac-c115-48d3-b39b-db7d615a6966
a=rtpmap:97 VP8/90000
a=rtcp-fb:97 nack
a=rtcp-fb:97 nack pli
a=rtcp-fb:97 goog-remb
a=rtpmap:99 H264/90000
a=rtcp-fb:99 nack
a=rtcp-fb:99 nack pli
a=rtcp-fb:99 goog-remb
a=fmtp:99 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=ice-ufrag:1234
a=ice-pwd:1234
a=fingerprint:sha-256 15:F3:F0:23:67:44:EE:2C:AA:8C:D9:50:95:26:42:7C:67:EA:1F:D2:92:C5:97:01:7B:2E:57:C9:A3:13:00:4A
a=setup:actpass"""
builder = WebRTCAnswerBuilder(offer_sdp)
builder.add_video_stream("road", DummyH264VideoStreamTrack("road", 0.05))
stream = builder.stream()
try:
answer = await stream.start()
sdp_desc = Description(answer.sdp, Description.Type.Answer)
video_desc = next(sdp_desc.media(i) for i in range(sdp_desc.media_count()) if sdp_desc.media(i).type() == "video")
codecs = []
for pt in video_desc.payload_types():
with contextlib.suppress(ValueError):
codecs.append(video_desc.rtp_map(pt).format)
assert codecs[0] == "H264"
finally:
await stream.stop()
async def test_fail_if_preferred_codec_not_in_offer(self):
offer_sdp = """v=0
o=- 3910274679 3910274679 IN IP4 0.0.0.0
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic:WMS *
m=video 1337 UDP/TLS/RTP/SAVPF 97 98 99 100 101 102
c=IN IP4 0.0.0.0
a=recvonly
a=mid:0
a=msid:34803878-98f8-4245-b45c-f773e5f926df 881dbc20-356a-499c-b4e8-695303bb901d
a=rtcp:9 IN IP4 0.0.0.0
a=rtcp-mux
a=ssrc-group:FID 1303546896 3784011659
a=ssrc:1303546896 cname:a59185ac-c115-48d3-b39b-db7d615a6966
a=ssrc:3784011659 cname:a59185ac-c115-48d3-b39b-db7d615a6966
a=rtpmap:97 VP8/90000
a=rtcp-fb:97 nack
a=rtcp-fb:97 nack pli
a=rtcp-fb:97 goog-remb
a=ice-ufrag:1234
a=ice-pwd:1234
a=fingerprint:sha-256 15:F3:F0:23:67:44:EE:2C:AA:8C:D9:50:95:26:42:7C:67:EA:1F:D2:92:C5:97:01:7B:2E:57:C9:A3:13:00:4A
a=setup:actpass"""
builder = WebRTCAnswerBuilder(offer_sdp)
builder.add_video_stream("road", DummyH264VideoStreamTrack("road", 0.05))
stream = builder.stream()
try:
with pytest.raises(ValueError):
_ = await stream.start()
finally:
await stream.stop()
async def test_multi_track(self):
offer_sdp = """v=0
o=- 1 1 IN IP4 0.0.0.0
s=-
t=0 0
a=group:BUNDLE 0 1
m=video 9 UDP/TLS/RTP/SAVPF 99
c=IN IP4 0.0.0.0
a=recvonly
a=mid:0
a=rtpmap:99 H264/90000
a=ice-ufrag:1234
a=ice-pwd:1234
a=fingerprint:sha-256 15:F3:F0:23:67:44:EE:2C:AA:8C:D9:50:95:26:42:7C:67:EA:1F:D2:92:C5:97:01:7B:2E:57:C9:A3:13:00:4A
a=setup:actpass
m=video 9 UDP/TLS/RTP/SAVPF 99
c=IN IP4 0.0.0.0
a=recvonly
a=mid:1
a=rtpmap:99 H264/90000
a=ice-ufrag:1234
a=ice-pwd:1234
a=fingerprint:sha-256 15:F3:F0:23:67:44:EE:2C:AA:8C:D9:50:95:26:42:7C:67:EA:1F:D2:92:C5:97:01:7B:2E:57:C9:A3:13:00:4A
a=setup:actpass"""
builder = WebRTCAnswerBuilder(offer_sdp)
builder.add_video_stream("road", DummyH264VideoStreamTrack("road", 0.05))
builder.add_video_stream("wideRoad", DummyH264VideoStreamTrack("wideRoad", 0.05))
stream = builder.stream()
try:
answer = await stream.start()
sdp_desc = Description(answer.sdp, Description.Type.Answer)
video_ssrcs = []
for i in range(sdp_desc.media_count()):
media = sdp_desc.media(i)
if media and media.type() == "video":
video_ssrcs.extend(media.get_ssrcs())
assert len(video_ssrcs) == 2
assert len(set(video_ssrcs)) == 2
finally:
await stream.stop()
async def test_parse_incoming_streams_datachannel_counting(self):
offer_sdp = """v=0
o=- 3910274679 3910274679 IN IP4 0.0.0.0
s=-
t=0 0
a=group:BUNDLE 0 1
m=video 9 UDP/TLS/RTP/SAVPF 97
c=IN IP4 0.0.0.0
a=recvonly
a=mid:0
a=ice-ufrag:1234
a=ice-pwd:1234
a=fingerprint:sha-256 15:F3:F0:23:67:44:EE:2C:AA:8C:D9:50:95:26:42:7C:67:EA:1F:D2:92:C5:97:01:7B:2E:57:C9:A3:13:00:4A
a=setup:actpass
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=sendrecv
a=mid:1
a=sctp-port:5000"""
builder = WebRTCAnswerBuilder(offer_sdp)
stream = builder.stream()
stream._parse_incoming_streams(builder.offer_sdp)
assert stream.expected_number_of_incoming_media == 1
await stream.stop()