
    qi                         d dl Z d dlZd dlZd dlmZ d dlmZmZ ddlm	Z	 ddl
mZ ddlmZ ddlmZ  ej                   e      Z G d	 d
      Z G d d      Zy)    N)deque)OptionalUnion   )
VideoFrame)
AudioFrame)AudioSource)VideoSourcec                       e Zd ZdZddddedededed	ef
d
Z	 ddee	e
f   dee   ddfdZddZddZddZddZddZedefd       Zedefd       Zedefd       Zy)AVSynchronizerah  Synchronize audio and video capture.

    Usage:
        av_sync = AVSynchronizer(
            audio_source=audio_source,
            video_source=video_source,
            video_fps=video_fps,
        )

        async for video_frame, audio_frame in video_generator:
            await av_sync.push(video_frame)
            await av_sync.push(audio_frame)
    d   ,  )video_queue_size_ms_max_delay_tolerance_msaudio_sourcevideo_source	video_fpsr   r   c                8   || _         || _        || _        || _        || _        d| _        d| _        d| _        t        | j                  | j                  z  dz        | _	        | j                  dkD  rt        d| j                        | _	        t        j                  t        t        t        t            f      | j                        | _        t%        | j                  | j                        | _        t        j(                  | j+                               | _        y )NFr     r   )maxsize)expected_fpsmax_delay_tolerance_ms)_audio_source_video_source
_video_fps_video_queue_size_msr   _stopped_last_video_time_last_audio_timeint_video_queue_max_sizemaxasyncioQueuetupler   r   float_video_queue_FPSController_fps_controllercreate_task_capture_video_capture_video_task)selfr   r   r   r   r   s         J/opt/pipecat/venv/lib/python3.12/site-packages/livekit/rtc/synchronizer.py__init__zAVSynchronizer.__init__   s     *)#$7!'>$'('(%(4;T;T)TW[)[%\"$$q(),Q0J0J)KD&#MM%
HUO0K*LM..
  .#'#?#? 
 $+#6#6t7J7J7L#M     Nframe	timestampreturnc                    K   t        |t              r-| j                  j                  |       d{    ||| _        y| j
                  j                  ||f       d{    y7 47 w)a  Push a frame to the synchronizer

        Args:
            frame: The video or audio frame to push.
            timestamp: (optional) The timestamp of the frame, for logging purposes for now.
                For AudioFrame, it should be the end time of the frame.
        N)
isinstancer   r   capture_framer   r'   putr-   r1   r2   s      r.   pushzAVSynchronizer.pushA   sg      eZ($$225999$(1%##UI$6777 :
 	8s!   /A*A&.A* A(!A*(A*c                 (  K   | j                   j                          | j                  j                         sX| j                  j	                          d {    | j                  j                          | j                  j                         sWy y 7 ;wN)r   clear_queuer'   emptyget	task_doner-   s    r.   r<   zAVSynchronizer.clear_queueS   sk     &&(##))+##'')))'') ##))+)s   ABB8BBc                    K   t        j                  | j                  j                         | j                  j                                d{    y7 w)z5Wait until all video and audio frames are played out.N)r#   gatherr   wait_for_playoutr'   joinr@   s    r.   rC   zAVSynchronizer.wait_for_playoutY   s@     nn//1""$
 	
 	
s   A
AAAc                 8    | j                   j                          y r;   )r)   resetr@   s    r.   rF   zAVSynchronizer.reset`   s    ""$r0   c                   K   | j                   s| j                  j                          d {   \  }}| j                  4 d {    | j                  j                  |       ||| _        d d d       d {    | j                  j                          | j                   sy y 7 y7 c7 1# 1 d {  7  sw Y   AxY wwr;   )r   r'   r>   r)   r   r6   r   r?   r8   s      r.   r+   zAVSynchronizer._capture_videoc   s     --%)%6%6%:%:%<<E9++ 6 6""007(,5D)6 6 '') --<6 6 6 6 6s]   *CB&CB(C%B,-C8B*9*C$C(C*C,B>2B53B>:Cc                 h   K   d| _         | j                  r| j                  j                          y y w)NT)r   r,   cancelr@   s    r.   aclosezAVSynchronizer.aclosel   s-     ##$$++- $s   02c                 .    | j                   j                  S r;   )r)   
actual_fpsr@   s    r.   rL   zAVSynchronizer.actual_fpsq   s    ##...r0   c                     | j                   S )z)The time of the last video frame captured)r   r@   s    r.   last_video_timezAVSynchronizer.last_video_timeu   s     $$$r0   c                 H    | j                   | j                  j                  z
  S )z+The time of the last audio frame played out)r   r   queued_durationr@   s    r.   last_audio_timezAVSynchronizer.last_audio_timez   s!     $$t'9'9'I'IIIr0   r;   r3   N)__name__
__module____qualname____doc__r	   r
   r&   r/   r   r   r   r   r9   r<   rC   rF   r+   rJ   propertyrL   rN   rQ    r0   r.   r   r      s    ( &)), N " N "	 N
  N # N "' NF RV8:z128?G8	8$*
%*.
 /E / / % % % J J Jr0   r   c                   z    e Zd ZdddededdfdZddZdd	Zdd
ZddZddZ	e
defd       Ze
defd       Zy)r(   r   )r   r   r   r3   Nc                    || _         d|z  | _        |dz  | _        d| _        t	        dt        d|z              | _        t        | j                        | _        y)aT  Controls frame rate by adjusting sleep time based on actual FPS.

        Usage:
            async with _FPSController(expected_fps=30):
                # process frame
                pass

        Args:
            expected_fps: Target frames per second
            max_delay_tolerance_ms: Maximum delay tolerance in milliseconds
        g      ?r   N   )maxlen)	_expected_fps_frame_interval_max_delay_tolerance_secs_next_frame_timer"   r    _fps_calc_winsizer   _send_timestamps)r-   r   r   s      r.   r/   z_FPSController.__init__   sZ     *"\1)?$)F&15!$QC,,>(?!@.34;Q;Q.Rr0   c                 @   K   | j                          d {    y 7 wr;   )wait_next_processr@   s    r.   
__aenter__z_FPSController.__aenter__   s     $$&&&s   c                 ,   K   | j                          y wr;   )after_process)r-   exc_typeexc_valexc_tbs       r.   	__aexit__z_FPSController.__aexit__   s     s   c                 F    d | _         | j                  j                          y r;   )r`   rb   clearr@   s    r.   rF   z_FPSController.reset   s     $##%r0   c                 R  K   t        j                         }| j                  || _        | j                  |z
  }|dkD  rt        j                  |       d{    y| | j
                  kD  r8t        j                  d| dz  dd       t        j                         | _        yy7 Nw)zzWait until it's time for the next frame.

        Adjusts sleep time based on actual FPS to maintain target rate.
        Nr   z&Frame capture was behind schedule for r   z.2fz ms)timeperf_counterr`   r#   sleepr_   loggerwarning)r-   current_time
sleep_times      r.   rd   z _FPSController.wait_next_process   s     
 ((*   ($0D! **\9
>--
+++ {T;;;!GVZHZ[^G__bcd(,(9(9(;% < ,s   AB'B%AB'c                     | j                   J d       | j                  j                  t        j                                | xj                   | j
                  z  c_         y)z3Update timing information after processing a frame.Nz&wait_next_process must be called first)r`   rb   appendro   rp   r^   r@   s    r.   rg   z_FPSController.after_process   sR    $$0Z2ZZ0 	$$T%6%6%89 	!5!55r0   c                     | j                   S r;   )r]   r@   s    r.   r   z_FPSController.expected_fps   s    !!!r0   c                     t        | j                        dk  ryt        | j                        dz
  | j                  d   | j                  d   z
  z  S )zGet current average FPS.r[   r   r   )lenrb   r@   s    r.   rL   z_FPSController.actual_fps   sU     t$$%)D))*Q.!!"%(=(=a(@@
 	
r0   rR   )rS   rT   rU   r&   r/   re   rk   rF   rd   rg   rW   r   rL   rX   r0   r.   r(   r(      ss    OR S Su SW[ S('&<*6 "e " " 
E 
 
r0   r(   )r#   loggingro   collectionsr   typingr   r   video_framer   audio_framer   r   r	   r   r
   	getLoggerrS   rr   r   r(   rX   r0   r.   <module>r      sO        " # # % % 
		8	$mJ mJ`J
 J
r0   