streamlit-webrtc 0.61.1__py3-none-any.whl → 0.61.3__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.
@@ -2,6 +2,7 @@ import copy
2
2
  import json
3
3
  import logging
4
4
  import os
5
+ import threading
5
6
  import warnings
6
7
  import weakref
7
8
  from typing import (
@@ -92,6 +93,7 @@ class WebRtcStreamerContext(Generic[VideoProcessorT, AudioProcessorT]):
92
93
  _worker_ref: "Optional[weakref.ReferenceType[WebRtcWorker[VideoProcessorT, AudioProcessorT]]]" # noqa
93
94
 
94
95
  _component_value_snapshot: Union[ComponentValueSnapshot, None]
96
+ _worker_creation_lock: threading.Lock
95
97
  _frontend_rtc_configuration: Optional[Union[Dict[str, Any], RTCConfiguration]]
96
98
 
97
99
  def __init__(
@@ -102,6 +104,7 @@ class WebRtcStreamerContext(Generic[VideoProcessorT, AudioProcessorT]):
102
104
  self._set_worker(worker)
103
105
  self._set_state(state)
104
106
  self._component_value_snapshot = None
107
+ self._worker_creation_lock = threading.Lock()
105
108
  self._frontend_rtc_configuration = None
106
109
 
107
110
  def _set_worker(
@@ -594,65 +597,66 @@ def webrtc_streamer(
594
597
  on_ended=on_audio_ended,
595
598
  )
596
599
 
597
- if not webrtc_worker and sdp_offer:
598
- LOGGER.debug(
599
- "No worker exists though the offer SDP is set. "
600
- 'Create a new worker (key="%s").',
601
- key,
602
- )
600
+ worker_created_in_this_run = None
601
+ with context._worker_creation_lock: # This point can be reached in parallel so we need to use a lock to make the worker creation process atomic.
602
+ should_create_worker_in_this_run = not context._get_worker() and sdp_offer
603
603
 
604
- aiortc_rtc_configuration = (
605
- compile_rtc_configuration(server_rtc_configuration)
606
- if server_rtc_configuration and isinstance(server_rtc_configuration, dict)
607
- else AiortcRTCConfiguration()
608
- )
609
- if aiortc_rtc_configuration.iceServers is None:
610
- LOGGER.info(
611
- "rtc_configuration.iceServers is not set. Try to set it automatically."
604
+ if should_create_worker_in_this_run:
605
+ LOGGER.debug(
606
+ "No worker exists though the offer SDP is set. "
607
+ 'Create a new worker (key="%s").',
608
+ key,
612
609
  )
613
- ice_servers = get_available_ice_servers()
614
- aiortc_rtc_configuration.iceServers = compile_ice_servers(ice_servers)
615
-
616
- webrtc_worker = WebRtcWorker(
617
- mode=mode,
618
- rtc_configuration=aiortc_rtc_configuration,
619
- player_factory=player_factory,
620
- in_recorder_factory=in_recorder_factory,
621
- out_recorder_factory=out_recorder_factory,
622
- video_frame_callback=video_frame_callback,
623
- audio_frame_callback=audio_frame_callback,
624
- queued_video_frames_callback=queued_video_frames_callback,
625
- queued_audio_frames_callback=queued_audio_frames_callback,
626
- on_video_ended=on_video_ended,
627
- on_audio_ended=on_audio_ended,
628
- video_processor_factory=video_processor_factory,
629
- audio_processor_factory=audio_processor_factory,
630
- async_processing=async_processing,
631
- video_receiver_size=video_receiver_size,
632
- audio_receiver_size=audio_receiver_size,
633
- source_video_track=source_video_track,
634
- source_audio_track=source_audio_track,
635
- sendback_video=sendback_video,
636
- sendback_audio=sendback_audio,
637
- )
638
610
 
639
- # Setting the worker here before calling `webrtc_worker.process_offer()` is important.
640
- # `webrtc_worker.process_offer()` waits for processing the offer in another thread and
641
- # a new script run can be triggered during it.
642
- # so, if the worker is not set here, `context._get_worker()` will return None in the next run
643
- # and it leads to creating a worker in the next run again.
644
- context._set_worker(webrtc_worker)
611
+ aiortc_rtc_configuration = (
612
+ compile_rtc_configuration(server_rtc_configuration)
613
+ if server_rtc_configuration
614
+ and isinstance(server_rtc_configuration, dict)
615
+ else AiortcRTCConfiguration()
616
+ )
617
+ if aiortc_rtc_configuration.iceServers is None:
618
+ LOGGER.info(
619
+ "rtc_configuration.iceServers is not set. Try to set it automatically."
620
+ )
621
+ ice_servers = get_available_ice_servers() # NOTE: This may include a yield point where Streamlit's script runner interrupts the execution and may stop the current run.
622
+ aiortc_rtc_configuration.iceServers = compile_ice_servers(ice_servers)
623
+
624
+ worker_created_in_this_run = WebRtcWorker(
625
+ mode=mode,
626
+ rtc_configuration=aiortc_rtc_configuration,
627
+ player_factory=player_factory,
628
+ in_recorder_factory=in_recorder_factory,
629
+ out_recorder_factory=out_recorder_factory,
630
+ video_frame_callback=video_frame_callback,
631
+ audio_frame_callback=audio_frame_callback,
632
+ queued_video_frames_callback=queued_video_frames_callback,
633
+ queued_audio_frames_callback=queued_audio_frames_callback,
634
+ on_video_ended=on_video_ended,
635
+ on_audio_ended=on_audio_ended,
636
+ video_processor_factory=video_processor_factory,
637
+ audio_processor_factory=audio_processor_factory,
638
+ async_processing=async_processing,
639
+ video_receiver_size=video_receiver_size,
640
+ audio_receiver_size=audio_receiver_size,
641
+ source_video_track=source_video_track,
642
+ source_audio_track=source_audio_track,
643
+ sendback_video=sendback_video,
644
+ sendback_audio=sendback_audio,
645
+ )
645
646
 
646
- webrtc_worker.process_offer(sdp_offer["sdp"], sdp_offer["type"], timeout=None)
647
+ # Set the worker here within the lock.
648
+ context._set_worker(worker_created_in_this_run)
647
649
 
648
- if ice_candidates:
649
- webrtc_worker.set_ice_candidates_from_offerer(ice_candidates)
650
+ webrtc_worker = context._get_worker()
651
+ if webrtc_worker and ice_candidates:
652
+ webrtc_worker.set_ice_candidates_from_offerer(ice_candidates)
653
+
654
+ if worker_created_in_this_run:
655
+ worker_created_in_this_run.process_offer(
656
+ sdp_offer["sdp"], sdp_offer["type"], timeout=None
657
+ )
650
658
 
651
659
  # Rerun to send the SDP answer to frontend
652
660
  rerun()
653
661
 
654
- if webrtc_worker and ice_candidates:
655
- webrtc_worker.set_ice_candidates_from_offerer(ice_candidates)
656
-
657
- context._set_worker(webrtc_worker)
658
662
  return context