1616from urllib .parse import urlparse , parse_qs
1717from typing import Any , TYPE_CHECKING
1818
19- # aiortc and its dependencies have lots of internal warnings :(
20- import warnings
21- warnings .filterwarnings ("ignore" , category = DeprecationWarning )
22- warnings .filterwarnings ("ignore" , category = RuntimeWarning ) # TODO: remove this when google-crc32c publish a python3.12 wheel
23-
2419import capnp
2520if TYPE_CHECKING :
26- from aiortc .rtcdatachannel import RTCDataChannel
27- import aioice .ice
21+ from teleoprtc .stream import RTCDataChannelAdapter
2822
2923from openpilot .system .webrtc .helpers import StreamRequestBody
3024from openpilot .system .webrtc .schema import generate_field
@@ -44,17 +38,6 @@ def _default_route_ip() -> str | None:
4438 finally :
4539 s .close ()
4640
47- # aioice patch: gather ICE candidates only on the default-route interface
48- _get_host_addresses = aioice .ice .get_host_addresses
49- def _primary_host_addresses (use_ipv4 : bool , use_ipv6 : bool ) -> list [str ]:
50- addresses = _get_host_addresses (use_ipv4 , use_ipv6 )
51- primary = _default_route_ip ()
52- if primary not in addresses :
53- return addresses
54- return [primary , ]
55- aioice .ice .get_host_addresses = _primary_host_addresses
56-
57-
5841class AsyncTaskRunner :
5942 def __init__ (self ):
6043 self .is_running = False
@@ -86,10 +69,10 @@ def __init__(self, services: list[str], enabled: bool = True):
8669 super ().__init__ ()
8770 self .services = list (services )
8871 self .sm = messaging .SubMaster (self .services )
89- self .channels : list [RTCDataChannel ] = []
72+ self .channels : list [RTCDataChannelAdapter ] = []
9073 self ._enabled = enabled
9174
92- def add_channel (self , channel : 'RTCDataChannel ' ):
75+ def add_channel (self , channel : 'RTCDataChannelAdapter ' ):
9376 self .channels .append (channel )
9477
9578 def enable (self , enable : bool ):
@@ -118,20 +101,17 @@ def update(self):
118101 outgoing_msg = {"type" : service , "logMonoTime" : mono_time , "valid" : valid , "data" : msg_dict }
119102 encoded_msg = json .dumps (outgoing_msg ).encode ()
120103 for channel in self .channels :
104+ if hasattr (channel , "is_open" ) and not channel .is_open ():
105+ continue
121106 channel .send (encoded_msg )
122107
123108 async def run (self ):
124- from aiortc .exceptions import InvalidStateError
125-
126109 while True :
127110 if not self ._enabled :
128111 await asyncio .sleep (0.01 )
129112 continue
130113 try :
131114 self .update ()
132- except InvalidStateError :
133- self .logger .warning ("Cereal outgoing proxy invalid state (connection closed)" )
134- break
135115 except Exception :
136116 self .logger .exception ("Cereal outgoing proxy failure" )
137117 await asyncio .sleep (0.01 )
@@ -217,6 +197,8 @@ async def run(self):
217197 self ._publish (self .bitrates [self .level ])
218198
219199 async def _sample (self ) -> float | None :
200+ if not hasattr (self .pc , "getStats" ):
201+ return None
220202 report = await self .pc .getStats ()
221203 packets_lost = packets_sent = 0
222204 for s in report .values ():
@@ -248,17 +230,15 @@ class StreamSession:
248230 shared_pub_master = DynamicPubMaster ([])
249231
250232 def __init__ (self , body : StreamRequestBody , debug_mode : bool = False ):
251- if debug_mode :
252- from aiortc .mediastreams import VideoStreamTrack
253233 from openpilot .system .webrtc .device .video import LiveStreamVideoStreamTrack
254234 from teleoprtc .builder import WebRTCAnswerBuilder
255235
256236 self .identifier = str (uuid .uuid4 ())
257237 self .params = Params ()
258- builder = WebRTCAnswerBuilder (body .sdp )
238+ builder = WebRTCAnswerBuilder (body .sdp , bind_address = _default_route_ip () )
259239
260240 self .enabled = body .enabled
261- self .video_track = LiveStreamVideoStreamTrack (body .init_camera , self .enabled ) if not debug_mode else VideoStreamTrack ()
241+ self .video_track = LiveStreamVideoStreamTrack (body .init_camera , self .enabled )
262242 builder .add_video_stream (body .init_camera , self .video_track )
263243 self .stream = builder .stream ()
264244
@@ -305,13 +285,16 @@ def message_handler(self, message: bytes):
305285 case "livestreamCameraSwitch" :
306286 self .video_track .switch_camera (payload ["data" ]["camera" ])
307287 case "livestreamSettings" :
308- self .bitrate_controller .set_quality (payload ["data" ]["quality" ])
288+ if self .bitrate_controller is not None :
289+ self .bitrate_controller .set_quality (payload ["data" ]["quality" ])
309290 case "livestreamVideoEnable" :
310291 enabled = payload ["data" ]["enabled" ]
311292 self .enabled = enabled
312293 self .video_track .enable (enabled )
313- self .outgoing_bridge .enable (enabled )
314- self .bitrate_controller .enable (enabled )
294+ if self .outgoing_bridge is not None :
295+ self .outgoing_bridge .enable (enabled )
296+ if self .bitrate_controller is not None :
297+ self .bitrate_controller .enable (enabled )
315298 if not enabled :
316299 self .params .put ("LivestreamRequestKeyframe" , True )
317300 case "clockSync" :
@@ -325,7 +308,8 @@ def message_handler(self, message: bytes):
325308 case _:
326309 if payload .get ("type" ) not in self .incoming_bridge_services :
327310 return
328- self .incoming_bridge .send (message )
311+ if self .incoming_bridge is not None :
312+ self .incoming_bridge .send (message )
329313 except Exception :
330314 self .logger .exception ("Cereal incoming proxy failure" )
331315
@@ -341,7 +325,8 @@ async def run(self):
341325 channel = self .stream .get_messaging_channel ()
342326 self .outgoing_bridge .add_channel (channel )
343327 self .outgoing_bridge .start ()
344- self .bitrate_controller .start ()
328+ if self .bitrate_controller is not None :
329+ self .bitrate_controller .start ()
345330
346331 self .logger .info ("Stream session (%s) connected" , self .identifier )
347332 await self .stream .wait_for_disconnection ()
@@ -357,7 +342,8 @@ async def post_run_cleanup(self):
357342 return
358343 self ._cleanup_done = True
359344 self .params .put ("LivestreamRequestKeyframe" , False )
360- await self .bitrate_controller .stop ()
345+ if self .bitrate_controller is not None :
346+ await self .bitrate_controller .stop ()
361347 if self .outgoing_bridge is not None :
362348 await self .outgoing_bridge .stop ()
363349 if self .video_track is not None :
@@ -558,9 +544,6 @@ async def _shutdown(server: WebrtcdHTTPServer, state: ServerState, loop: asyncio
558544
559545
560546def prewarm_stream_session_imports (debug_mode : bool = False ) -> None :
561- if debug_mode :
562- from aiortc .mediastreams import VideoStreamTrack
563- assert VideoStreamTrack
564547 from openpilot .system .webrtc .device .video import LiveStreamVideoStreamTrack
565548 from teleoprtc .builder import WebRTCAnswerBuilder
566549 assert LiveStreamVideoStreamTrack
0 commit comments