
    qi?~                        d Z ddlZddlZddlZddlZddlmZmZmZm	Z	m
Z
 ddlmZ ddlmZmZ ddlmZ 	 ddlmZmZmZmZ ddlmZ dd	lmZ dZdZ dZ!dZ"dZ#dZ$ G d de      Z% G d de      Z& G d de      Z' G d d      Z( G d d      Z)eZ* G d de      Z+y# e$ r7Z ej:                  d
e         ej:                  d        ede       dZ[ww xY w)zSmall WebRTC connection implementation for Pipecat.

This module provides a WebRTC connection implementation using aiortc,
with support for audio/video tracks, data channels, and signaling
for real-time communication applications.
    N)AnyListLiteralOptionalUnion)logger)	BaseModelTypeAdapter)
BaseObject)RTCConfigurationRTCIceServerRTCPeerConnectionRTCSessionDescription)RemoteStreamTrack)FramezException: zNIn order to use the SmallWebRTC, you need to `pip install pipecat-ai[webrtc]`.zMissing module: 
signalling      2   
   c                   6    e Zd ZU dZed   ed<   eed<   eed<   y)TrackStatusMessagezMessage for updating track enabled/disabled status.

    Parameters:
        type: Message type identifier.
        receiver_index: Index of the track receiver to update.
        enabled: Whether the track should be enabled or disabled.
    trackStatustypereceiver_indexenabledN)__name__
__module____qualname____doc__r   __annotations__intbool     [/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/transports/smallwebrtc/connection.pyr   r   2   s     -
  Mr%   r   c                   &    e Zd ZU dZdZed   ed<   y)RenegotiateMessagezMessage requesting WebRTC renegotiation.

    Parameters:
        type: Message type identifier for renegotiation requests.
    renegotiater   Nr   r   r   r    r   r   r!   r$   r%   r&   r(   r(   @   s     $1D'-
 0r%   r(   c                   &    e Zd ZU dZdZed   ed<   y)PeerLeftMessagezMessage indicating a peer has left the connection.

    Parameters:
        type: Message type identifier for peer departure.
    peerLeftr   Nr*   r$   r%   r&   r,   r,   J   s     !+D'*
*r%   r,   c                   $    e Zd ZdZee   Zee   Zy)SignallingMessagezUnion types for signaling message handling.

    Parameters:
        Inbound: Types of messages that can be received from peers.
        outbound: Types of messages that can be sent to peers.
    N)	r   r   r   r    r   r   Inboundr(   outboundr$   r%   r&   r/   r/   T   s     &'G'(Hr%   r/   c                   \    e Zd ZdZd ZdeddfdZdefdZd Zde	e
   fd	Zd
 Zd Zd Zy)SmallWebRTCTrackzWrapper for WebRTC media tracks with enabled/disabled state management.

    Provides additional functionality on top of aiortc MediaStreamTrack including
    enable/disable control and frame discarding for audio and video streams.
    c                     || _         d| j                   _        |j                  | _        d| _        d| _        d| _        d| _        y)z{Initialize the WebRTC track wrapper.

        Args:
            receiver: The RemoteStreamTrack receiver instance.
        FTg        Ng       @)	_receiver_enabledtrack_track_last_recv_time
_idle_task_idle_timeout)selfreceivers     r&   __init__zSmallWebRTCTrack.__init__g   s@     ""'nn&)26$'r%   r   returnNc                     || _         y)zEnable or disable the track.

        Args:
            enabled: Whether the track should be enabled for receiving frames.
        Nr6   )r<   r   s     r&   set_enabledzSmallWebRTCTrack.set_enabledv   s      r%   c                     | j                   S )zCheck if the track is currently enabled.

        Returns:
            True if the track is enabled for receiving frames.
        rA   r<   s    r&   
is_enabledzSmallWebRTCTrack.is_enabled~   s     }}r%   c                   K   | j                   }t        |t              rt        |d      r$t        |j                  t
        j                        st        d       yt        j                  d       |j                  j                         sP|j                  j                          |j                  j                          |j                  j                         sOyyyw)z:Discard old frames from the track queue to reduce latency._queuez8Warning: _queue does not exist or has changed in aiortc.NzDiscarding old frames)r8   
isinstancer   hasattrrG   asyncioQueueprintr   debugempty
get_nowait	task_done)r<   remote_tracks     r&   discard_old_framesz#SmallWebRTCTrack.discard_old_frames   s     {{l$56<2*##W]]; PQLL01"))//1##..0##--/ #))//1 7s   CCCc                   K   d| j                   _        t        j                         | _        | j                  r| j                  j                         r(t        j                  | j                               | _        | j                  s| j                  j                  dk(  ry| j                  j                          d{   S 7 w)a  Receive the next frame from the track.

        Enables the internal receiving state and starts idle watcher.

        Returns:
            The next frame, except for video tracks, where it returns the frame only if the track is enabled, otherwise, returns None.
        TvideoN)r5   r6   timer9   r:   donerJ   create_task_idle_watcherr8   kindrecvrD   s    r&   rZ   zSmallWebRTCTrack.recv   s      #'#yy{ $//"6"6"8%11$2D2D2FGDO}}!1!1W!<[[%%''''s   B<C>C?Cc                   K   | j                   j                  rt        j                  | j                         d{    t        j
                         | j                  z
  }|| j                  k\  rZt        j                  d| j                  j                   d|dd       | j                          d{    d| j                   _        | j                   j                  ryy7 7 0w)zMDisable receiving if idle for more than _idle_timeout and monitor queue size.NzDisabling receiver for z track after z.2fzs idleF)r5   r6   rJ   sleepr;   rU   r9   r   rM   r8   rY   rR   )r<   idle_durations     r&   rX   zSmallWebRTCTrack._idle_watcher   s     nn%%-- 2 2333 IIK$*>*>>M 2 22-dkk.>.>-?}][^L__ef --///*/' nn%%3 0s(   9C'C#A8C'4C%5+C'!C'%C'c                     d| j                   _        | j                  r!| j                  j                          d| _        | j                  r| j                  j                          yy)z%Stop receiving frames from the track.FN)r5   r6   r:   cancelr8   stoprD   s    r&   r`   zSmallWebRTCTrack.stop   sH    "'??OO""$"DO;;KK r%   c                 .    t        | j                  |      S )zForward attribute access to the underlying track.

        Args:
            name: The attribute name to access.

        Returns:
            The attribute value from the underlying track.
        )getattrr8   )r<   names     r&   __getattr__zSmallWebRTCTrack.__getattr__   s     t{{D))r%   )r   r   r   r    r>   r#   rB   rE   rR   r   r   rZ   rX   r`   rd   r$   r%   r&   r3   r3   `   sO    ( 4  D  D 0(HUO (&0
*r%   r3   c                   T    e Zd ZdZ	 	 d'deeee   ee   f      de	f fdZ
edefd       Zedefd       Zd Zd	 Zd
edefdZd
edefdZd Zd(d
ededefdZd Zd Zd Zd Zd Zd Zd Zd)dZd)dZd)dZd)dZ d Z!defdZ"d Z#d Z$d  Z%d!e&fd"Z'd# Z(d$ Z)d% Z*d& Z+ xZ,S )*SmallWebRTCConnectiona  WebRTC connection implementation using aiortc.

    Provides WebRTC peer connection functionality including ICE server configuration,
    track management, data channel communication, and connection state handling
    for real-time audio/video communication.
    ice_serversconnection_timeout_secsc                    t         |           |sg | _        nWt        d |D              r|| _        n=t        d |D              r |D cg c]  }t	        |       c}| _        nt        d      d| _        i | _        t        | j                  t        | j                  t        | j                  i| _        || _        | j!                          | j#                  d       | j#                  d       | j#                  d       | j#                  d	       | j#                  d
       | j#                  d       | j#                  d       | j#                  d       | j#                  d       yc c}w )a:  Initialize the WebRTC connection.

        Args:
            ice_servers: List of ICE servers as URLs or IceServer objects.
            connection_timeout_secs: Timeout in seconds for connecting to the peer.

        Raises:
            TypeError: If ice_servers contains mixed types or unsupported types.
        c              3   <   K   | ]  }t        |t                y wN)rH   	IceServer.0ss     r&   	<genexpr>z1SmallWebRTCConnection.__init__.<locals>.<genexpr>   s     ?aAy)?   c              3   <   K   | ]  }t        |t                y wrk   )rH   strrm   s     r&   rp   z1SmallWebRTCConnection.__init__.<locals>.<genexpr>   s     9As#9rq   )urlsz:ice_servers must be either List[str] or List[RTCIceServer]Fapp-messagetrack-startedtrack-ended
connecting	connecteddisconnectedclosedfailednewN)superr>   rg   allrl   	TypeError_connect_invoked
_track_mapAUDIO_TRANSCEIVER_INDEXaudio_input_trackVIDEO_TRANSCEIVER_INDEXvideo_input_trackSCREEN_VIDEO_TRANSCEIVER_INDEXscreen_video_input_track_track_gettersrh   _initialize_register_event_handler)r<   rg   rh   ro   	__class__s       r&   r>   zSmallWebRTCConnection.__init__   s9    	02D?;??*D9[99;FGa	q 1GDXYY %#T%;%;#T%;%;*D,I,I

 (?$ 	$$]3$$_5$$]3$$\2$$[1$$^4$$X.$$X.$$U+3  Hs   
E'r?   c                     | j                   S )zsGet the underlying RTCPeerConnection.

        Returns:
            The aiortc RTCPeerConnection instance.
        )_pcrD   s    r&   pczSmallWebRTCConnection.pc  s     xxr%   c                     | j                   S )zzGet the peer connection identifier.

        Returns:
            The unique identifier for this peer connection.
        )_pc_idrD   s    r&   pc_idzSmallWebRTCConnection.pc_id  s     {{r%   c                 |   t        j                  d       t        | j                        }d| _        t        |      | _        | j                   dt        j                         j                   | _        | j                          d| _        d| _        d| _        g | _        d| _        g | _        d| _        d| _        y)z9Initialize the peer connection and associated components.z Initializing new peer connection)
iceServersN-FT)r   rM   r   rg   _answerr   r   rc   uuiduuid4hexr   _setup_listeners_data_channel_renegotiation_in_progress_last_received_time_outgoing_messages_queue_data_channel_enabled_pending_app_messages_connecting_timeout_task_data_channel_timeout_task)r<   
rtc_configs     r&   r   z!SmallWebRTCConnection._initialize  s    78%1A1AB
8<$Z01TZZ\%5%5$67!*/'#' (*%%)"%'"(,%*.'r%   c                 d     j                   j                  d       fd       } j                   j                  d       fd       } j                   j                  d       fd       } j                   j                  d       fd       } j                   j                  d	       fd
       }y)z/Set up event listeners for the peer connection.datachannelc                 x    | _         | j                  d      fd       }| j                  d      fd       }y )Nopenc                  X   K   t        j                  d        j                          y w)NzData channel is open!)r   rM   _flush_message_queuerD   s   r&   on_openzOSmallWebRTCConnection._setup_listeners.<locals>.on_datachannel.<locals>.on_open1  s      45))+s   '*messagec                 0  K   	 t        | t              r+| j                  d      rt        j                         _        y t        j                  |       }|d   t        k(  r&|j                  d      rj                  |d          y j                         rj                  d|       d {    y t        j                  d       j                  j                  |       y 7 6# t         $ r%}t        j"                  d|  d|        Y d }~y d }~ww xY ww)Npingr   r   ru   z*Client not connected. Queuing app-message.zError parsing JSON message z, )rH   rs   
startswithrU   r   jsonloadsSIGNALLING_TYPEget_handle_signalling_messageis_connected_call_event_handlerr   rM   r   append	Exceptionerror)r   json_messageer<   s      r&   
on_messagezRSmallWebRTCConnection._setup_listeners.<locals>.on_datachannel.<locals>.on_message6  s     O "'3/G4F4Fv4N3799;0'+zz'':'/?B|GWGWXaGb ;;L<ST#002&*&>&>}l&[ [ [ &-Y Z $ : : A A, O !\ ! OLL#>wir!!MNNOsd   D:C% D AC% D%C% ,C#-C% 1D20C% "D#C% %	D.D	DDD)r   on)channelr   r   r<   s      r&   on_datachannelz>SmallWebRTCConnection._setup_listeners.<locals>.on_datachannel,  sG    !(D ZZ,  , ZZ	"O #Or%   connectionstatechangec                  B   K    j                          d {    y 7 wrk   )_handle_new_connection_staterD   s   r&   on_connectionstatechangezHSmallWebRTCConnection._setup_listeners.<locals>.on_connectionstatechangeL  s     33555s   iceconnectionstatechangec                     K   t        j                  d j                  j                   d j                  j                          y w)NzICE connection state is z, connection is )r   rM   r   iceConnectionStateconnectionStaterD   s   r&   on_iceconnectionstatechangezKSmallWebRTCConnection._setup_listeners.<locals>.on_iceconnectionstatechangeR  s=     LL*488+F+F*GGWX\X`X`XpXpWqrs   AAicegatheringstatechangec                  f   K   t        j                  d j                  j                          y w)NzICE gathering state is )r   rM   r   iceGatheringStaterD   s   r&   on_icegatheringstatechangezJSmallWebRTCConnection._setup_listeners.<locals>.on_icegatheringstatechangeX  s&     LL24883M3M2NOPs   .1r7   c                     K   t        j                  d j                   d       j                  d        d {     j	                  d       fd       }y 7 w)NTrack z	 receivedrv   endedc                     K   t        j                  dj                   d        j                  d       d {    y 7 w)Nr   z endedrw   )r   rM   rY   r   r<   r7   s   r&   on_endedzJSmallWebRTCConnection._setup_listeners.<locals>.on_track.<locals>.on_endeda  s6     vejj\89..}eDDDs   9AAA)r   rM   rY   r   r   )r7   r   r<   s   ` r&   on_trackz8SmallWebRTCConnection._setup_listeners.<locals>.on_track\  sY     LL6%**Y78**?EBBBXXgE E Cs   9AA AN)r   r   )r<   r   r   r   r   r   s   `     r&   r   z&SmallWebRTCConnection._setup_listeners)  s     
]	#	O 
$	O> 
,	-	6 
.	6
 
/	0	 
1	
 
.	/	Q 
0	Q 
W		E 
	Er%   sdpr   c                   K   t        ||      }| j                  j                  |       d{    | j                          t	        j
                  d       | j                  j                          d{   }| j                  j                  |       d{    t	        j
                  d       | j                  j                  | _	        y7 7 Z7 9w)z)Create an SDP answer for the given offer.)r   r   NzCreating answerz9Setting the answer after the local description is created)
r   r   setRemoteDescriptionforce_transceivers_to_send_recvr   rM   createAnswersetLocalDescriptionlocalDescriptionr   )r<   r   r   offerlocal_answers        r&   _create_answerz$SmallWebRTCConnection._create_answerf  s     %#D9hh++E222 	,,. 	(!XX2244hh**<888PRxx00 	3 58s4   ,CCAC5C6"CC6CCCc                 D   K   | j                  ||       d{    y7 w)zInitialize the connection with an SDP offer.

        Args:
            sdp: The SDP offer string.
            type: The SDP type (usually "offer").
        N)r   )r<   r   r   s      r&   
initializez SmallWebRTCConnection.initializev  s      !!#t,,,s     c                   K   d| _         | j                         r| j                  d       d{    t        j                  d       | j
                  D ]  }| j                  d|       d{     | j                         }|r&| j                         j                          d{    | j                         }|r&| j                         j                          d{    |s|r| j                          yyy7 7 7 W7 !w)z<Connect the WebRTC peer connection and handle initial setup.Try   NzFlushing pending app-messagesru   )
r   r   r   r   rM   r   r   rR   r   ask_to_renegotiate)r<   r   r   r   s       r&   connectzSmallWebRTCConnection.connect  s      $**;777LL8955 G..}gFFFG !% 6 6 8 ,,.AACCC'+'D'D'F$'335HHJJJ $< '')	 %= 7 G
 D KsE   ,DC:=D,C<-9D&C>'7DD D<D>D D
restart_pcc                    K   t        j                  d j                          |rp j                  d       d{    t        j                  d        j                  j                           j                          d{     j                           j                  ||       d{     fd}t        j                   |              y7 7 P7 (w)zRenegotiate the WebRTC connection with new parameters.

        Args:
            sdp: The new SDP offer string.
            type: The SDP type (usually "offer").
            restart_pc: Whether to restart the peer connection entirely.
        zRenegotiating rz   NzClosing old peer connectionc                  Z   K   t        j                  d       d {    d _        y 7 w)Nr   F)rJ   r\   r   rD   s   r&   delayed_taskz7SmallWebRTCConnection.renegotiate.<locals>.delayed_task  s&     --""".3D+ #s   +)+)r   rM   r   r   r   remove_all_listeners_closer   r   rJ   rW   )r<   r   r   r   r   s   `    r&   r)   z!SmallWebRTCConnection.renegotiate  s      	~dkk]34**>:::LL67HH))+++-!!#t,,,
	4 	LN+# ;   	-s4   9CCACC)C-C.%CCCc                     | j                   j                         }t        |      D ]  \  }}|dk  rd|_        d|_         y)z:Force all transceivers to bidirectional send/receive mode.r   sendrecvrecvonlyN)r   getTransceivers	enumerate	direction)r<   transceiversitransceivers       r&   r   z5SmallWebRTCConnection.force_transceivers_to_send_recv  sC    xx//1'5 	3NA{1u(2%(2%		3r%   c                    t        j                  d|j                          | j                  j	                         }t        |      dkD  r.|d   j                  r|d   j                  j                  |       yt        j                  d       y)zReplace the audio track in the first transceiver.

        Args:
            track: The new audio track to use for sending.
        zReplacing audio track r   z8Audio transceiver not found. Cannot replace audio track.N	r   rM   rY   r   r   lensenderreplaceTrackwarningr<   r7   r   s      r&   replace_audio_trackz)SmallWebRTCConnection.replace_audio_track  n     	-ejj\:; xx//1|q \!_%;%;O""//6NNUVr%   c                    t        j                  d|j                          | j                  j	                         }t        |      dkD  r.|d   j                  r|d   j                  j                  |       yt        j                  d       y)zReplace the video track in the second transceiver.

        Args:
            track: The new video track to use for sending.
        zReplacing video track r   z8Video transceiver not found. Cannot replace video track.Nr   r   s      r&   replace_video_trackz)SmallWebRTCConnection.replace_video_track  r   r%   c                    t        j                  d|j                          | j                  j	                         }t        |      dkD  r.|d   j                  r|d   j                  j                  |       yt        j                  d       y)zReplace the screen video track in the second transceiver.

        Args:
            track: The new screen video track to use for sending.
        zReplacing screen video track r   zFScreen video transceiver not found. Cannot replace screen video track.Nr   r   s      r&   replace_screen_video_trackz0SmallWebRTCConnection.replace_screen_video_track  sn     	4UZZLAB xx//1|q \!_%;%;O""//6NNcdr%   c                    K   | j                  t        t               j                         d       | j	                          d{    y7 w)z+Disconnect from the WebRTC peer connection.r   r   N)send_app_messager   r,   
model_dumpr   rD   s    r&   
disconnectz SmallWebRTCConnection.disconnect  s4     ?CTC_C_Cabckkms   AAA
Ac                   K   | j                   j                         D ]  }|s|j                           | j                   j                          | j                  r"| j                  j                          d{    | j                  j                          d| _        | j                  j                          i | _         | j                          | j                          y7 gw)z0Close the peer connection and cleanup resources.NT)r   valuesr`   clearr   closer   r   r   #_cancel_monitoring_connecting_state_cancel_data_channel_timeoutr   s     r&   r   zSmallWebRTCConnection._close  s     __++- 	E

	 	88((.."""%%++-%)"""((*002))+ #s    C"AC"8C 9A(C"c                     | j                   sy| j                   j                  | j                   j                  | j                  dS )zGet the SDP answer for the current connection.

        Returns:
            Dictionary containing SDP answer, type, and peer connection ID,
            or None if no answer is available.
        N)r   r   r   )r   r   r   r   rD   s    r&   
get_answerz SmallWebRTCConnection.get_answer  s;     || <<##LL%%[[
 	
r%   c                 x     t        j                  d        fd}t        j                   |              _        y)a!  Start monitoring the peer connection while it is in the *connecting* state.

        This method schedules a timeout task that will automatically close the
        connection if it remains in the connecting state for more than the specified
        timeout, default to 60 seconds.
        zMonitoring connecting statec                     K   t        j                   j                         d {    t        j                  d        j                          d {    y 7 27 w)Nz@Timeout establishing the connection to the remote peer. Closing.)rJ   r\   rh   r   r   r   rD   s   r&   timeout_handlerzKSmallWebRTCConnection._monitoring_connecting_state.<locals>.timeout_handler  sF     -- < <===NN]^++- >  s!   #AA,AAAAN)r   rM   rJ   rW   r   r<   r  s   ` r&   _monitoring_connecting_statez2SmallWebRTCConnection._monitoring_connecting_state  s/     	23	  )0(;(;O<M(N%r%   c                     | j                   rI| j                   j                         s/t        j                  d       | j                   j	                          d| _         y)a#  Cancel the ongoing connecting-state timeout task, if any.

        This method should be called once the connection has either succeeded or
        transitioned out of the connecting state. If the timeout task is still
        pending, it will be canceled and the reference cleared.
        z&Cancelling the connecting timeout taskN)r   rV   r   rM   r_   rD   s    r&   r  z9SmallWebRTCConnection._cancel_monitoring_connecting_state)  sE     ((1N1N1S1S1ULLAB))002(,%r%   c                 N      fd}t        j                   |              _        y)a  Start a timeout to detect if the data channel fails to open after connection.

        Schedules a background task that fires ``DATA_CHANNEL_TIMEOUT_SECS`` seconds after
        the peer connection reaches the *connected* state.  If the data channel has not
        opened by then, the queued messages are discarded, a warning is logged, and future
        calls to :meth:`send_app_message` will silently drop messages instead of queuing
        them (fall-back to "discard" mode).

        The task is automatically cancelled when the data channel opens successfully (see
        :meth:`_flush_message_queue`) or when the connection is closed (see
        :meth:`_close`).
        c                    K   t        j                  t               d {     j                  r j                  j                  dk7  r?t        j                  dt         d        j                  j                          d _	        y y 7 iw)Nr   z$Data channel not established within zIs after connection. Clearing message queue and disabling future queueing.F)
rJ   r\   DATA_CHANNEL_TIMEOUT_SECSr   
readyStater   r   r   r  r   rD   s   r&   r  zJSmallWebRTCConnection._start_data_channel_timeout.<locals>.timeout_handlerC  s     -- 9:::%%););)F)F&)P:;T:U VX X --335-2* *Q ;s   BB
A*BN)rJ   rW   r   r  s   ` r&   _start_data_channel_timeoutz1SmallWebRTCConnection._start_data_channel_timeout5  s     	3 +2*=*=o>O*P'r%   c                     | j                   rI| j                   j                         s/t        j                  d       | j                   j	                          d| _         y)a)  Cancel the data-channel open timeout task, if any.

        Should be called when the data channel opens successfully (the timeout is no longer
        needed) or when the connection is being torn down.  If the task is still pending it
        will be cancelled and the reference cleared.
        z(Cancelling the data channel timeout taskN)r   rV   r   rM   r_   rD   s    r&   r  z2SmallWebRTCConnection._cancel_data_channel_timeoutO  sE     **43R3R3W3W3YLLCD++224*.'r%   c                   K   | j                   j                  }|dk(  r| j                          n| j                          |dk(  r| j                  s| j                          |dk(  r| j                  syt        j                  d|        | j                  |       d{    |dk(  r.t        j                  d       | j                          d{    yy7 87 w)z,Handle changes in the peer connection state.rx   ry   NzConnection state changed to: r|   z+Connection failed, closing peer connection.)r   r   r  r  r   r  r   r   rM   r   r   r   )r<   states     r&   r   z2SmallWebRTCConnection._handle_new_connection_state[  s     ((L --/446K(G(G,,.K(=(=4UG<=&&u---HNNHI++-  	.  s$   BCC1CCCCc                     | j                   sy| j                  | j                  j                  dk(  S t	        j                         | j                  z
  dk  S )zCheck if the WebRTC connection is currently active.

        Returns:
            True if the connection is active and receiving data.
        Fry      )r   r   r   r   rU   rD   s    r&   r   z"SmallWebRTCConnection.is_connectedr  sO     $$##+ 88++{::		d666!;;r%   c                 v   | j                   j                  t              r| j                   t           S | j                  j	                         }t        |      dk(  s|t           j                  st        j                  d       y|t           j                  }|rt        |      nd}|| j                   t        <   |S )zGet the audio input track wrapper.

        Returns:
            SmallWebRTCTrack wrapper for the audio track, or None if unavailable.
        r   z!No audio transceiver is availableN)
r   r   r   r   r   r   r=   r   r   r3   )r<   r   r=   audio_tracks       r&   r   z'SmallWebRTCConnection.audio_input_track       ??67??#:;;
 xx//1|!6M)N)W)WNN>? 78AA4<&x0$3>/0r%   c                 v   | j                   j                  t              r| j                   t           S | j                  j	                         }t        |      dk  s|t           j                  st        j                  d       y|t           j                  }|rt        |      nd}|| j                   t        <   |S )zGet the video input track wrapper.

        Returns:
            SmallWebRTCTrack wrapper for the video track, or None if unavailable.
        r   z!No video transceiver is availableN)
r   r   r   r   r   r   r=   r   r   r3   r<   r   r=   video_tracks       r&   r   z'SmallWebRTCConnection.video_input_track  r  r%   c                 v   | j                   j                  t              r| j                   t           S | j                  j	                         }t        |      dk  s|t           j                  st        j                  d       y|t           j                  }|rt        |      nd}|| j                   t        <   |S )zGet the screen video input track wrapper.

        Returns:
            SmallWebRTCTrack wrapper for the screen video track, or None if unavailable.
        r   z(No screen video transceiver is availableN)
r   r   r   r   r   r   r=   r   r   r3   r  s       r&   r   z.SmallWebRTCConnection.screen_video_input_track  s     ??=>??#ABB
 xx//1|!6T)U)^)^NNEF >?HH4<&x0$:E67r%   r   c                    t        j                  |      }| j                  r5| j                  j                  dk(  r| j                  j	                  |       y| j
                  rkt        | j                        t        k  r1t        j                  d       | j                  j                  |       yt        j                  dt         d       yt        j                  d       y)a  Send an application message through the data channel.

        If the data channel is open the message is sent immediately.  Otherwise,
        the message is placed in an in-memory queue so it can be flushed once the
        channel opens, subject to the following constraints:

        * Queueing is only attempted when ``_data_channel_enabled`` is ``True``.  It is
          set to ``False`` when the data-channel open timeout fires (see
          :meth:`_start_data_channel_timeout`), after which messages are silently
          discarded.
        * The queue will not grow beyond ``MAX_MESSAGE_QUEUE_SIZE`` entries.
          Messages that arrive when the queue is full are discarded with a warning.

        Args:
            message: The message to send (will be JSON serialized).
        r   z'Data channel not ready, queuing messagezMessage queue is full (z messages). Discarding message.zCData channel unavailable and queueing disabled. Discarding message.N)r   dumpsr   r  sendr   r   r   MAX_MESSAGE_QUEUE_SIZEr   rM   r   r   trace)r<   r   r   s      r&   r   z&SmallWebRTCConnection.send_app_message  s    " zz'*$"4"4"?"?6"I##L1''40014JJFG--44\B-.D-EEde
 LL^_r%   c                     | j                          t        j                  d       | j                  rD| j                  j	                  d      }| j
                  j                  |       | j                  rCyy)a)  Flush all queued messages through the now-open data channel.

        Called when the data channel transitions to the *open* state.  Cancels
        the data-channel open timeout (it is no longer needed) and sends every
        message that was buffered while the channel was unavailable.
        z.Data channel is open, flushing queued messagesr   N)r  r   rM   r   popr   r$  )r<   r   s     r&   r   z*SmallWebRTCConnection._flush_message_queue  s[     	))+EF++3377:G##G, ++r%   c                     | j                   ryd| _         | j                  t        t               j	                         d       y)z/Request renegotiation of the WebRTC connection.NTr   )r   r   r   r(   r  rD   s    r&   r   z(SmallWebRTCConnection.ask_to_renegotiate  s9    ***.'$1C1E1P1P1RS	
r%   c                 >   t        j                  d|        t        t        j                        }|j                  |      }|t        d xP\    | j                  j                  |j                        xs d        }|r|j                  |j                         yy y)z#Handle incoming signaling messages.zSignalling message received: r$   Nc                       y rk   r$   r$   r%   r&   <lambda>zBSmallWebRTCConnection._handle_signalling_message.<locals>.<lambda>  s    r%   )r   rM   r
   r/   r0   validate_pythonr   r   r   r   rB   r   )r<   r   inbound_adaptersignalling_messager7   s        r&   r   z0SmallWebRTCConnection._handle_signalling_message  s    4WI>?%&7&?&?@,<<WE #%`D''++,>,M,MN`S_ %%&8&@&@A 	 &r%   c                    K   t        j                  d|        | j                  j                  |       d{    y7 w)zHandle incoming ICE candidates.zAdding remote candidate: N)r   rM   r   addIceCandidate)r<   	candidates     r&   add_ice_candidatez'SmallWebRTCConnection.add_ice_candidate
  s2     0<=gg%%i000s   7A?A)N<   )F)r?   N)-r   r   r   r    r   r   r   rs   rl   r"   r>   propertyr   r   r   r   r   r   r   r   r#   r)   r   r   r   r   r  r   r
  r  r  r  r  r   r   r   r   r   r   r   r   r   r   r3  __classcell__)r   s   @r&   rf   rf      sI    DH')-,eDItI$>?@-, "%-,^ %   s  /$;Ez1 13 1 -C -s -*.,S , , ,<3W W e 
,
 O&
-Q4
/ .<d <$,,,` `@-
B1r%   rf   ),r    rJ   r   rU   r   typingr   r   r   r   r   logurur   pydanticr	   r
   pipecat.utils.base_objectr   aiortcr   r   r   r   aiortc.rtcrtpreceiverr   av.framer   ModuleNotFoundErrorr   r   r   r   r   r   r   r%  r  r   r(   r,   r/   r3   rl   rf   r$   r%   r&   <module>r?     s        6 6  + 0,  8   !"     1 1+i +	) 	)g* g*V 	1J 1Y  ,FLL;qc"#FLLab
&qc*
++,s   B C"2CC