1+ #!/usr/bin/env python3
2+
3+ import unittest
4+
5+ import aiortc
6+ from aiortc .mediastreams import AudioStreamTrack , VideoStreamTrack
7+ from parameterized import parameterized
8+
9+ from teleoprtc .builder import WebRTCOfferBuilder , WebRTCAnswerBuilder
10+ from teleoprtc .stream import StreamingOffer
11+ from teleoprtc .info import parse_info_from_offer
12+ from teleoprtc .tracks import TiciVideoStreamTrack
13+
14+
15+ class OfferCapture :
16+ def __init__ (self ):
17+ self .offer = None
18+
19+ async def __call__ (self , offer ):
20+ self .offer = offer
21+ raise Exception ("Offer captured" )
22+
23+
24+ class DummyH264VideoStreamTrack (TiciVideoStreamTrack ):
25+ kind = "video"
26+
27+ async def recv (self ):
28+ raise NotImplementedError ()
29+
30+ def codec_preference (self ):
31+ return "H264"
32+
33+
34+ class TestOfferStream (unittest .IsolatedAsyncioTestCase ):
35+ async def test_offer_stream_sdp_recvonly_audio (self ):
36+ capture = OfferCapture ()
37+ builder = WebRTCOfferBuilder (capture )
38+ builder .offer_to_receive_audio_stream ()
39+ stream = builder .stream ()
40+
41+ try :
42+ _ = await stream .start ()
43+ except Exception as e :
44+ pass
45+
46+ info = parse_info_from_offer (capture .offer .sdp )
47+ self .assertTrue (info .expected_audio_track )
48+ self .assertFalse (info .incoming_audio_track )
49+
50+ async def test_offer_stream_sdp_sendonly_audio (self ):
51+ capture = OfferCapture ()
52+ builder = WebRTCOfferBuilder (capture )
53+ builder .add_audio_stream (AudioStreamTrack ())
54+ stream = builder .stream ()
55+
56+ try :
57+ _ = await stream .start ()
58+ except Exception as e :
59+ pass
60+
61+ info = parse_info_from_offer (capture .offer .sdp )
62+ self .assertFalse (info .expected_audio_track )
63+ self .assertTrue (info .incoming_audio_track )
64+
65+ async def test_offer_stream_sdp_channel (self ):
66+ capture = OfferCapture ()
67+ builder = WebRTCOfferBuilder (capture )
68+ builder .add_messaging ()
69+ stream = builder .stream ()
70+
71+ try :
72+ _ = await stream .start ()
73+ except Exception as e :
74+ pass
75+
76+ info = parse_info_from_offer (capture .offer .sdp )
77+ self .assertTrue (info .incoming_datachannel )
78+
79+
80+ class TestAnswerStream (unittest .IsolatedAsyncioTestCase ):
81+ async def test_codec_preference (self ):
82+ offer_sdp = """v=0
83+ o=- 3910274679 3910274679 IN IP4 0.0.0.0
84+ s=-
85+ t=0 0
86+ a=group:BUNDLE 0
87+ a=msid-semantic:WMS *
88+ m=video 1337 UDP/TLS/RTP/SAVPF 97 98 99 100 101 102
89+ c=IN IP4 0.0.0.0
90+ a=recvonly
91+ a=mid:0
92+ a=msid:34803878-98f8-4245-b45c-f773e5f926df 881dbc20-356a-499c-b4e8-695303bb901d
93+ a=rtcp:9 IN IP4 0.0.0.0
94+ a=rtcp-mux
95+ a=ssrc-group:FID 1303546896 3784011659
96+ a=ssrc:1303546896 cname:a59185ac-c115-48d3-b39b-db7d615a6966
97+ a=ssrc:3784011659 cname:a59185ac-c115-48d3-b39b-db7d615a6966
98+ a=rtpmap:97 VP8/90000
99+ a=rtcp-fb:97 nack
100+ a=rtcp-fb:97 nack pli
101+ a=rtcp-fb:97 goog-remb
102+ a=rtpmap:99 H264/90000
103+ a=rtcp-fb:99 nack
104+ a=rtcp-fb:99 nack pli
105+ a=rtcp-fb:99 goog-remb
106+ a=fmtp:99 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
107+ a=ice-ufrag:1234
108+ a=ice-pwd:1234
109+ 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
110+ a=setup:actpass"""
111+
112+ builder = WebRTCAnswerBuilder (offer_sdp )
113+ builder .add_video_stream ("road" , DummyH264VideoStreamTrack ("road" , 0.05 ))
114+ stream = builder .stream ()
115+ answer = await stream .start ()
116+
117+ sdp_desc = aiortc .sdp .SessionDescription .parse (answer .sdp )
118+ video_desc = [m for m in sdp_desc .media if m .kind == "video" ][0 ]
119+ codecs = video_desc .rtp .codecs
120+ self .assertEqual (codecs [0 ].mimeType , "video/H264" )
121+
122+ async def test_fail_if_preferred_codec_not_in_offer (self ):
123+ offer_sdp = """v=0
124+ o=- 3910274679 3910274679 IN IP4 0.0.0.0
125+ s=-
126+ t=0 0
127+ a=group:BUNDLE 0
128+ a=msid-semantic:WMS *
129+ m=video 1337 UDP/TLS/RTP/SAVPF 97 98 99 100 101 102
130+ c=IN IP4 0.0.0.0
131+ a=recvonly
132+ a=mid:0
133+ a=msid:34803878-98f8-4245-b45c-f773e5f926df 881dbc20-356a-499c-b4e8-695303bb901d
134+ a=rtcp:9 IN IP4 0.0.0.0
135+ a=rtcp-mux
136+ a=ssrc-group:FID 1303546896 3784011659
137+ a=ssrc:1303546896 cname:a59185ac-c115-48d3-b39b-db7d615a6966
138+ a=ssrc:3784011659 cname:a59185ac-c115-48d3-b39b-db7d615a6966
139+ a=rtpmap:97 VP8/90000
140+ a=rtcp-fb:97 nack
141+ a=rtcp-fb:97 nack pli
142+ a=rtcp-fb:97 goog-remb
143+ a=ice-ufrag:1234
144+ a=ice-pwd:1234
145+ 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
146+ a=setup:actpass"""
147+
148+ builder = WebRTCAnswerBuilder (offer_sdp )
149+ builder .add_video_stream ("road" , DummyH264VideoStreamTrack ("road" , 0.05 ))
150+ stream = builder .stream ()
151+
152+ with self .assertRaises (Exception ):
153+ _ = await stream .start ()
154+
155+
156+ if __name__ == "__main__" :
157+ unittest .main ()
0 commit comments