streamlit-webrtc 0.51.2__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.
- streamlit_webrtc/__init__.py +0 -2
- streamlit_webrtc/component.py +21 -76
- streamlit_webrtc/config.py +46 -1
- streamlit_webrtc/credentials.py +29 -2
- streamlit_webrtc/frontend/dist/assets/{index-1ywg1u80.js → index-BQIqAyML.js} +28 -28
- streamlit_webrtc/frontend/dist/index.html +1 -1
- streamlit_webrtc/webrtc.py +3 -2
- {streamlit_webrtc-0.51.2.dist-info → streamlit_webrtc-0.52.0.dist-info}/METADATA +1 -1
- {streamlit_webrtc-0.51.2.dist-info → streamlit_webrtc-0.52.0.dist-info}/RECORD +11 -11
- {streamlit_webrtc-0.51.2.dist-info → streamlit_webrtc-0.52.0.dist-info}/WHEEL +0 -0
- {streamlit_webrtc-0.51.2.dist-info → streamlit_webrtc-0.52.0.dist-info}/licenses/LICENSE +0 -0
streamlit_webrtc/__init__.py
CHANGED
@@ -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",
|
streamlit_webrtc/component.py
CHANGED
@@ -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,11 +36,14 @@ 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
|
+
)
|
44
|
+
from .credentials import (
|
45
|
+
get_available_ice_servers,
|
43
46
|
)
|
44
|
-
from .credentials import get_hf_ice_servers, get_twilio_ice_servers
|
45
47
|
from .session_info import get_script_run_count, get_this_session_info
|
46
48
|
from .webrtc import (
|
47
49
|
AudioProcessorFactory,
|
@@ -72,11 +74,6 @@ else:
|
|
72
74
|
_component_func = components.declare_component("webrtc_streamer", path=build_dir)
|
73
75
|
|
74
76
|
|
75
|
-
class ClientSettings(TypedDict, total=False):
|
76
|
-
rtc_configuration: RTCConfiguration
|
77
|
-
media_stream_constraints: MediaStreamConstraints
|
78
|
-
|
79
|
-
|
80
77
|
class WebRtcStreamerState(NamedTuple):
|
81
78
|
playing: bool
|
82
79
|
signalling: bool
|
@@ -93,7 +90,6 @@ class WebRtcStreamerContext(Generic[VideoProcessorT, AudioProcessorT]):
|
|
93
90
|
_worker_ref: "Optional[weakref.ReferenceType[WebRtcWorker[VideoProcessorT, AudioProcessorT]]]" # noqa
|
94
91
|
|
95
92
|
_component_value_snapshot: Union[ComponentValueSnapshot, None]
|
96
|
-
_rtc_configuration: Optional[Union[Dict[str, Any], RTCConfiguration]]
|
97
93
|
|
98
94
|
def __init__(
|
99
95
|
self,
|
@@ -103,7 +99,6 @@ class WebRtcStreamerContext(Generic[VideoProcessorT, AudioProcessorT]):
|
|
103
99
|
self._set_worker(worker)
|
104
100
|
self._set_state(state)
|
105
101
|
self._component_value_snapshot = None
|
106
|
-
self._rtc_configuration = None
|
107
102
|
|
108
103
|
def _set_worker(
|
109
104
|
self, worker: Optional[WebRtcWorker[VideoProcessorT, AudioProcessorT]]
|
@@ -246,7 +241,6 @@ def webrtc_streamer(
|
|
246
241
|
translations: Optional[Translations] = None,
|
247
242
|
on_change: Optional[Callable] = None,
|
248
243
|
# Deprecated. Just for backward compatibility
|
249
|
-
client_settings: Optional[Union[ClientSettings, Dict]] = None,
|
250
244
|
video_transformer_factory: None = None,
|
251
245
|
async_transform: Optional[bool] = None,
|
252
246
|
) -> WebRtcStreamerContext:
|
@@ -287,7 +281,6 @@ def webrtc_streamer(
|
|
287
281
|
translations: Optional[Translations] = None,
|
288
282
|
on_change: Optional[Callable] = None,
|
289
283
|
# Deprecated. Just for backward compatibility
|
290
|
-
client_settings: Optional[Union[ClientSettings, Dict]] = None,
|
291
284
|
video_transformer_factory: None = None,
|
292
285
|
async_transform: Optional[bool] = None,
|
293
286
|
) -> WebRtcStreamerContext[VideoProcessorT, Any]:
|
@@ -324,7 +317,6 @@ def webrtc_streamer(
|
|
324
317
|
translations: Optional[Translations] = None,
|
325
318
|
on_change: Optional[Callable] = None,
|
326
319
|
# Deprecated. Just for backward compatibility
|
327
|
-
client_settings: Optional[Union[ClientSettings, Dict]] = None,
|
328
320
|
video_transformer_factory: None = None,
|
329
321
|
async_transform: Optional[bool] = None,
|
330
322
|
) -> WebRtcStreamerContext[Any, AudioProcessorT]:
|
@@ -361,7 +353,6 @@ def webrtc_streamer(
|
|
361
353
|
translations: Optional[Translations] = None,
|
362
354
|
on_change: Optional[Callable] = None,
|
363
355
|
# Deprecated. Just for backward compatibility
|
364
|
-
client_settings: Optional[Union[ClientSettings, Dict]] = None,
|
365
356
|
video_transformer_factory: None = None,
|
366
357
|
async_transform: Optional[bool] = None,
|
367
358
|
) -> WebRtcStreamerContext[VideoProcessorT, AudioProcessorT]:
|
@@ -397,7 +388,6 @@ def webrtc_streamer(
|
|
397
388
|
translations: Optional[Translations] = None,
|
398
389
|
on_change: Optional[Callable] = None,
|
399
390
|
# Deprecated. Just for backward compatibility
|
400
|
-
client_settings: Optional[Union[ClientSettings, Dict]] = None,
|
401
391
|
video_transformer_factory=None,
|
402
392
|
async_transform: Optional[bool] = None,
|
403
393
|
) -> WebRtcStreamerContext[VideoProcessorT, AudioProcessorT]:
|
@@ -420,19 +410,6 @@ def webrtc_streamer(
|
|
420
410
|
stacklevel=2,
|
421
411
|
)
|
422
412
|
async_processing = async_transform
|
423
|
-
if client_settings is not None:
|
424
|
-
warnings.warn(
|
425
|
-
"The argument client_settings is deprecated. "
|
426
|
-
"Use rtc_configuration and media_stream_constraints instead.",
|
427
|
-
DeprecationWarning,
|
428
|
-
stacklevel=2,
|
429
|
-
)
|
430
|
-
rtc_configuration = (
|
431
|
-
client_settings.get("rtc_configuration") if client_settings else None
|
432
|
-
)
|
433
|
-
media_stream_constraints = (
|
434
|
-
client_settings.get("media_stream_constraints") if client_settings else None
|
435
|
-
)
|
436
413
|
|
437
414
|
if media_stream_constraints is None:
|
438
415
|
media_stream_constraints = DEFAULT_MEDIA_STREAM_CONSTRAINTS
|
@@ -460,49 +437,6 @@ def webrtc_streamer(
|
|
460
437
|
)
|
461
438
|
st.session_state[key] = context
|
462
439
|
|
463
|
-
if context._rtc_configuration is None:
|
464
|
-
context._rtc_configuration = copy.deepcopy(rtc_configuration)
|
465
|
-
if context._rtc_configuration is None or (
|
466
|
-
isinstance(context._rtc_configuration, dict)
|
467
|
-
and context._rtc_configuration.get("iceServers") is None
|
468
|
-
):
|
469
|
-
LOGGER.info(
|
470
|
-
"rtc_configuration.iceServers is not set. Try to set it automatically."
|
471
|
-
)
|
472
|
-
if hf_token := os.getenv("HF_TOKEN"):
|
473
|
-
LOGGER.info("Try to use TURN server from Hugging Face.")
|
474
|
-
try:
|
475
|
-
ice_servers = get_hf_ice_servers(hf_token)
|
476
|
-
if context._rtc_configuration is None:
|
477
|
-
context._rtc_configuration = {}
|
478
|
-
LOGGER.info("Successfully got TURN credentials from Hugging Face.")
|
479
|
-
context._rtc_configuration["iceServers"] = ice_servers
|
480
|
-
except Exception as e:
|
481
|
-
LOGGER.error("Failed to get TURN credentials from Hugging Face: %s", e)
|
482
|
-
elif os.getenv("TWILIO_ACCOUNT_SID") and os.getenv("TWILIO_AUTH_TOKEN"):
|
483
|
-
LOGGER.info("Try to use TURN server from Twilio.")
|
484
|
-
twilio_sid = os.getenv("TWILIO_ACCOUNT_SID")
|
485
|
-
twilio_token = os.getenv("TWILIO_AUTH_TOKEN")
|
486
|
-
try:
|
487
|
-
ice_servers = get_twilio_ice_servers(twilio_sid, twilio_token)
|
488
|
-
if context._rtc_configuration is None:
|
489
|
-
context._rtc_configuration = {}
|
490
|
-
LOGGER.info("Successfully got TURN credentials from Twilio.")
|
491
|
-
context._rtc_configuration["iceServers"] = ice_servers
|
492
|
-
except Exception as e:
|
493
|
-
LOGGER.error("Failed to get TURN credentials from Twilio: %s", e)
|
494
|
-
else:
|
495
|
-
LOGGER.info("Use STUN server from Google.")
|
496
|
-
# TODO: Check network reachability and unset ice_servers if failed
|
497
|
-
ice_servers = [{"urls": "stun:stun.l.google.com:19302"}]
|
498
|
-
if ice_servers:
|
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,
|
streamlit_webrtc/config.py
CHANGED
@@ -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
|
|
streamlit_webrtc/credentials.py
CHANGED
@@ -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.
|
71
|
-
twilio_token = os.
|
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")]
|