streamlit-webrtc 0.51.3__py3-none-any.whl → 0.52.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,6 @@ except ModuleNotFoundError:
7
7
  import importlib_metadata # type: ignore
8
8
 
9
9
  from .component import (
10
- ClientSettings,
11
10
  WebRtcStreamerContext,
12
11
  WebRtcStreamerState,
13
12
  webrtc_streamer,
@@ -58,7 +57,6 @@ VideoTransformerFactory = VideoProcessorFactory
58
57
 
59
58
  __all__ = [
60
59
  "webrtc_streamer",
61
- "ClientSettings",
62
60
  "AudioProcessorBase",
63
61
  "AudioProcessorFactory",
64
62
  "AudioReceiver",
@@ -1,4 +1,3 @@
1
- import copy
2
1
  import json
3
2
  import logging
4
3
  import os
@@ -11,7 +10,6 @@ from typing import (
11
10
  Generic,
12
11
  NamedTuple,
13
12
  Optional,
14
- TypedDict,
15
13
  Union,
16
14
  cast,
17
15
  overload,
@@ -19,6 +17,7 @@ from typing import (
19
17
 
20
18
  import streamlit as st
21
19
  import streamlit.components.v1 as components
20
+ from aiortc import RTCConfiguration
22
21
  from aiortc.mediastreams import MediaStreamTrack
23
22
 
24
23
  from streamlit_webrtc.models import (
@@ -37,13 +36,13 @@ from .config import (
37
36
  DEFAULT_VIDEO_HTML_ATTRS,
38
37
  AudioHTMLAttributes,
39
38
  MediaStreamConstraints,
40
- RTCConfiguration,
41
39
  Translations,
42
40
  VideoHTMLAttributes,
41
+ compile_ice_servers,
42
+ compile_rtc_configuration,
43
43
  )
44
44
  from .credentials import (
45
- get_hf_ice_servers,
46
- get_twilio_ice_servers,
45
+ get_available_ice_servers,
47
46
  )
48
47
  from .session_info import get_script_run_count, get_this_session_info
49
48
  from .webrtc import (
@@ -75,11 +74,6 @@ else:
75
74
  _component_func = components.declare_component("webrtc_streamer", path=build_dir)
76
75
 
77
76
 
78
- class ClientSettings(TypedDict, total=False):
79
- rtc_configuration: RTCConfiguration
80
- media_stream_constraints: MediaStreamConstraints
81
-
82
-
83
77
  class WebRtcStreamerState(NamedTuple):
84
78
  playing: bool
85
79
  signalling: bool
@@ -96,7 +90,6 @@ class WebRtcStreamerContext(Generic[VideoProcessorT, AudioProcessorT]):
96
90
  _worker_ref: "Optional[weakref.ReferenceType[WebRtcWorker[VideoProcessorT, AudioProcessorT]]]" # noqa
97
91
 
98
92
  _component_value_snapshot: Union[ComponentValueSnapshot, None]
99
- _rtc_configuration: Optional[Union[Dict[str, Any], RTCConfiguration]]
100
93
 
101
94
  def __init__(
102
95
  self,
@@ -106,7 +99,6 @@ class WebRtcStreamerContext(Generic[VideoProcessorT, AudioProcessorT]):
106
99
  self._set_worker(worker)
107
100
  self._set_state(state)
108
101
  self._component_value_snapshot = None
109
- self._rtc_configuration = None
110
102
 
111
103
  def _set_worker(
112
104
  self, worker: Optional[WebRtcWorker[VideoProcessorT, AudioProcessorT]]
@@ -249,7 +241,6 @@ def webrtc_streamer(
249
241
  translations: Optional[Translations] = None,
250
242
  on_change: Optional[Callable] = None,
251
243
  # Deprecated. Just for backward compatibility
252
- client_settings: Optional[Union[ClientSettings, Dict]] = None,
253
244
  video_transformer_factory: None = None,
254
245
  async_transform: Optional[bool] = None,
255
246
  ) -> WebRtcStreamerContext:
@@ -290,7 +281,6 @@ def webrtc_streamer(
290
281
  translations: Optional[Translations] = None,
291
282
  on_change: Optional[Callable] = None,
292
283
  # Deprecated. Just for backward compatibility
293
- client_settings: Optional[Union[ClientSettings, Dict]] = None,
294
284
  video_transformer_factory: None = None,
295
285
  async_transform: Optional[bool] = None,
296
286
  ) -> WebRtcStreamerContext[VideoProcessorT, Any]:
@@ -327,7 +317,6 @@ def webrtc_streamer(
327
317
  translations: Optional[Translations] = None,
328
318
  on_change: Optional[Callable] = None,
329
319
  # Deprecated. Just for backward compatibility
330
- client_settings: Optional[Union[ClientSettings, Dict]] = None,
331
320
  video_transformer_factory: None = None,
332
321
  async_transform: Optional[bool] = None,
333
322
  ) -> WebRtcStreamerContext[Any, AudioProcessorT]:
@@ -364,7 +353,6 @@ def webrtc_streamer(
364
353
  translations: Optional[Translations] = None,
365
354
  on_change: Optional[Callable] = None,
366
355
  # Deprecated. Just for backward compatibility
367
- client_settings: Optional[Union[ClientSettings, Dict]] = None,
368
356
  video_transformer_factory: None = None,
369
357
  async_transform: Optional[bool] = None,
370
358
  ) -> WebRtcStreamerContext[VideoProcessorT, AudioProcessorT]:
@@ -400,7 +388,6 @@ def webrtc_streamer(
400
388
  translations: Optional[Translations] = None,
401
389
  on_change: Optional[Callable] = None,
402
390
  # Deprecated. Just for backward compatibility
403
- client_settings: Optional[Union[ClientSettings, Dict]] = None,
404
391
  video_transformer_factory=None,
405
392
  async_transform: Optional[bool] = None,
406
393
  ) -> WebRtcStreamerContext[VideoProcessorT, AudioProcessorT]:
@@ -423,19 +410,6 @@ def webrtc_streamer(
423
410
  stacklevel=2,
424
411
  )
425
412
  async_processing = async_transform
426
- if client_settings is not None:
427
- warnings.warn(
428
- "The argument client_settings is deprecated. "
429
- "Use rtc_configuration and media_stream_constraints instead.",
430
- DeprecationWarning,
431
- stacklevel=2,
432
- )
433
- rtc_configuration = (
434
- client_settings.get("rtc_configuration") if client_settings else None
435
- )
436
- media_stream_constraints = (
437
- client_settings.get("media_stream_constraints") if client_settings else None
438
- )
439
413
 
440
414
  if media_stream_constraints is None:
441
415
  media_stream_constraints = DEFAULT_MEDIA_STREAM_CONSTRAINTS
@@ -463,46 +437,6 @@ def webrtc_streamer(
463
437
  )
464
438
  st.session_state[key] = context
465
439
 
466
- if context._rtc_configuration is None:
467
- context._rtc_configuration = copy.deepcopy(rtc_configuration)
468
- if context._rtc_configuration is None or (
469
- isinstance(context._rtc_configuration, dict)
470
- and context._rtc_configuration.get("iceServers") is None
471
- ):
472
- LOGGER.info(
473
- "rtc_configuration.iceServers is not set. Try to set it automatically."
474
- )
475
- if hf_token := os.getenv("HF_TOKEN"):
476
- LOGGER.info("Try to use TURN server from Hugging Face.")
477
- try:
478
- ice_servers = get_hf_ice_servers(hf_token)
479
- if context._rtc_configuration is None:
480
- context._rtc_configuration = {}
481
- LOGGER.info("Successfully got TURN credentials from Hugging Face.")
482
- context._rtc_configuration["iceServers"] = ice_servers
483
- except Exception as e:
484
- LOGGER.error("Failed to get TURN credentials from Hugging Face: %s", e)
485
- elif os.getenv("TWILIO_ACCOUNT_SID") and os.getenv("TWILIO_AUTH_TOKEN"):
486
- LOGGER.info("Try to use TURN server from Twilio.")
487
- twilio_sid = os.getenv("TWILIO_ACCOUNT_SID")
488
- twilio_token = os.getenv("TWILIO_AUTH_TOKEN")
489
- try:
490
- ice_servers = get_twilio_ice_servers(twilio_sid, twilio_token)
491
- if context._rtc_configuration is None:
492
- context._rtc_configuration = {}
493
- LOGGER.info("Successfully got TURN credentials from Twilio.")
494
- context._rtc_configuration["iceServers"] = ice_servers
495
- except Exception as e:
496
- LOGGER.error("Failed to get TURN credentials from Twilio: %s", e)
497
- else:
498
- LOGGER.info("Use STUN server from Google.")
499
- if context._rtc_configuration is None:
500
- context._rtc_configuration = {}
501
- LOGGER.info("Successfully got STUN server from Google.")
502
- context._rtc_configuration["iceServers"] = [
503
- {"urls": "stun:stun.l.google.com:19302"}
504
- ]
505
-
506
440
  webrtc_worker = context._get_worker()
507
441
 
508
442
  sdp_answer_json = None
@@ -539,8 +473,6 @@ def webrtc_streamer(
539
473
  key=frontend_key,
540
474
  sdp_answer_json=sdp_answer_json,
541
475
  mode=mode.name,
542
- settings=client_settings,
543
- rtc_configuration=context._rtc_configuration,
544
476
  media_stream_constraints=media_stream_constraints,
545
477
  video_html_attrs=video_html_attrs,
546
478
  audio_html_attrs=audio_html_attrs,
@@ -597,8 +529,6 @@ def webrtc_streamer(
597
529
  context._set_worker(None)
598
530
  webrtc_worker = None
599
531
 
600
- context._rtc_configuration = None
601
-
602
532
  # Rerun to unset the SDP answer from the frontend args
603
533
  rerun()
604
534
 
@@ -622,8 +552,23 @@ def webrtc_streamer(
622
552
  'Create a new worker (key="%s").',
623
553
  key,
624
554
  )
555
+
556
+ aiortc_rtc_configuration = (
557
+ compile_rtc_configuration(rtc_configuration)
558
+ if rtc_configuration and isinstance(rtc_configuration, dict)
559
+ else RTCConfiguration()
560
+ )
561
+
562
+ if aiortc_rtc_configuration.iceServers is None:
563
+ LOGGER.info(
564
+ "rtc_configuration.iceServers is not set. Try to set it automatically."
565
+ )
566
+ ice_servers = get_available_ice_servers()
567
+ aiortc_rtc_configuration.iceServers = compile_ice_servers(ice_servers)
568
+
625
569
  webrtc_worker = WebRtcWorker(
626
570
  mode=mode,
571
+ rtc_configuration=aiortc_rtc_configuration,
627
572
  player_factory=player_factory,
628
573
  in_recorder_factory=in_recorder_factory,
629
574
  out_recorder_factory=out_recorder_factory,
@@ -1,4 +1,11 @@
1
- from typing import Dict, List, Optional, TypedDict, Union
1
+ from typing import Any, Dict, List, Optional, TypedDict, Union
2
+
3
+ from aiortc import (
4
+ RTCConfiguration as AiortcRTCConfiguration,
5
+ )
6
+ from aiortc import (
7
+ RTCIceServer as AiortcRTCIceServer,
8
+ )
2
9
 
3
10
  RTCIceServer = TypedDict(
4
11
  "RTCIceServer",
@@ -15,6 +22,44 @@ class RTCConfiguration(TypedDict, total=False):
15
22
  iceServers: Optional[List[RTCIceServer]]
16
23
 
17
24
 
25
+ def compile_rtc_ice_server(
26
+ ice_server: Union[RTCIceServer, dict[str, Any]],
27
+ ) -> AiortcRTCIceServer:
28
+ if not isinstance(ice_server, dict):
29
+ raise ValueError("ice_server must be a dict")
30
+ if "urls" not in ice_server:
31
+ raise ValueError("ice_server must have a urls key")
32
+
33
+ return AiortcRTCIceServer(
34
+ urls=ice_server["urls"], # type: ignore # aiortc's type def is incorrect
35
+ username=ice_server.get("username"),
36
+ credential=ice_server.get("credential"),
37
+ )
38
+
39
+
40
+ def compile_ice_servers(
41
+ ice_servers: Union[List[RTCIceServer], List[dict[str, Any]]],
42
+ ) -> List[AiortcRTCIceServer]:
43
+ return [
44
+ compile_rtc_ice_server(server)
45
+ for server in ice_servers
46
+ if isinstance(server, dict) and "urls" in server
47
+ ]
48
+
49
+
50
+ def compile_rtc_configuration(
51
+ rtc_configuration: Union[RTCConfiguration, dict[str, Any]],
52
+ ) -> AiortcRTCConfiguration:
53
+ if not isinstance(rtc_configuration, dict):
54
+ raise ValueError("rtc_configuration must be a dict")
55
+ ice_servers = rtc_configuration.get("iceServers", [])
56
+ if not isinstance(ice_servers, list):
57
+ raise ValueError("iceServers must be a list")
58
+ return AiortcRTCConfiguration(
59
+ iceServers=compile_ice_servers(ice_servers),
60
+ )
61
+
62
+
18
63
  Number = Union[int, float]
19
64
 
20
65
 
@@ -24,6 +24,7 @@ SOFTWARE.
24
24
  # Original: https://github.com/freddyaboulton/fastrtc/blob/66f0a81b76684c5d58761464fb67642891066f93/LICENSE
25
25
 
26
26
  import json
27
+ import logging
27
28
  import os
28
29
  import urllib.error
29
30
  import urllib.request
@@ -31,6 +32,8 @@ from typing import List, Optional
31
32
 
32
33
  from .config import RTCIceServer
33
34
 
35
+ LOGGER = logging.getLogger(__name__)
36
+
34
37
 
35
38
  def get_hf_ice_servers(token: Optional[str] = None) -> List[RTCIceServer]:
36
39
  if token is None:
@@ -67,11 +70,35 @@ def get_twilio_ice_servers(
67
70
  raise ImportError("Please install twilio with `pip install twilio`")
68
71
 
69
72
  if not twilio_sid and not twilio_token:
70
- twilio_sid = os.environ.get("TWILIO_ACCOUNT_SID")
71
- twilio_token = os.environ.get("TWILIO_AUTH_TOKEN")
73
+ twilio_sid = os.getenv("TWILIO_ACCOUNT_SID")
74
+ twilio_token = os.getenv("TWILIO_AUTH_TOKEN")
75
+
76
+ if twilio_sid is None or twilio_token is None:
77
+ raise ValueError("TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN must be set")
72
78
 
73
79
  client = Client(twilio_sid, twilio_token)
74
80
 
75
81
  token = client.tokens.create()
76
82
 
77
83
  return token.ice_servers
84
+
85
+
86
+ def get_available_ice_servers() -> List[RTCIceServer]:
87
+ try:
88
+ LOGGER.info("Try to use TURN server from Hugging Face.")
89
+ ice_servers = get_hf_ice_servers()
90
+ LOGGER.info("Successfully got TURN credentials from Hugging Face.")
91
+ return ice_servers
92
+ except Exception as e:
93
+ LOGGER.info("Failed to get TURN credentials from Hugging Face: %s", e)
94
+
95
+ try:
96
+ LOGGER.info("Try to use TURN server from Twilio.")
97
+ ice_servers = get_twilio_ice_servers()
98
+ LOGGER.info("Successfully got TURN credentials from Twilio.")
99
+ return ice_servers
100
+ except Exception as e:
101
+ LOGGER.info("Failed to get TURN credentials from Twilio: %s", e)
102
+
103
+ LOGGER.info("Use STUN server from Google.")
104
+ return [RTCIceServer(urls="stun:stun.l.google.com:19302")]