streamlit-webrtc 0.51.3__tar.gz → 0.52.0__tar.gz
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-0.51.3 → streamlit_webrtc-0.52.0}/PKG-INFO +1 -1
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/pyproject.toml +2 -2
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/__init__.py +0 -2
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/component.py +19 -74
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/config.py +46 -1
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/credentials.py +29 -2
- streamlit_webrtc-0.51.3/streamlit_webrtc/frontend/dist/assets/index-1ywg1u80.js → streamlit_webrtc-0.52.0/streamlit_webrtc/frontend/dist/assets/index-BQIqAyML.js +28 -28
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/frontend/dist/index.html +1 -1
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/webrtc.py +3 -2
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/.gitignore +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/LICENSE +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/README.md +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/_compat.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/components_callbacks.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/eventloop.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/factory.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/mix.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/models.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/process.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/py.typed +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/receive.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/relay.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/server.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/session_info.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/shutdown.py +0 -0
- {streamlit_webrtc-0.51.3 → streamlit_webrtc-0.52.0}/streamlit_webrtc/source.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: streamlit-webrtc
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.52.0
|
4
4
|
Summary: Real-time video and audio processing on Streamlit
|
5
5
|
Project-URL: Repository, https://github.com/whitphx/streamlit-webrtc
|
6
6
|
Author-email: "Yuichiro Tachibana (Tsuchiya)" <t.yic.yt@gmail.com>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "streamlit-webrtc"
|
3
|
-
version = "0.
|
3
|
+
version = "0.52.0"
|
4
4
|
description = "Real-time video and audio processing on Streamlit"
|
5
5
|
authors = [{ name = "Yuichiro Tachibana (Tsuchiya)", email = "t.yic.yt@gmail.com" }]
|
6
6
|
requires-python = ">=3.9,!=3.9.7" # 3.9.7 is excluded due to https://github.com/streamlit/streamlit/pull/5168
|
@@ -51,7 +51,7 @@ build-backend = "hatchling.build"
|
|
51
51
|
extend-select = ["I"]
|
52
52
|
|
53
53
|
[tool.bumpversion]
|
54
|
-
version = "0.
|
54
|
+
version = "0.52.0"
|
55
55
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
56
56
|
serialize = ["{major}.{minor}.{patch}"]
|
57
57
|
search = "{current_version}"
|
@@ -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
|
-
|
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.
|
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")]
|