@@ -712,17 +712,21 @@ protected function readOggOpus(Deferred $deferred, OggStream &$ogg, int &$loops)
712712 {
713713 $ this ->readOpusTimer = null ;
714714
715- $ loops += 1 ;
715+ // Record the target send time for THIS packet BEFORE the async fetch so
716+ // that the 20 ms window runs concurrently with getPacket(), not after it.
717+ $ targetTime = $ this ->startTime + (20.0 / 1000.0 ) * $ loops ;
718+ $ loops ++;
716719
717- // If the client is paused, delay by frame size and check again .
720+ // If the client is paused, insert silence and wait until the next slot .
718721 if ($ this ->paused ) {
719722 $ this ->udp ->insertSilence ();
720- $ this ->readOpusTimer = $ this ->discord ->getLoop ()->addTimer ($ this ->frameSize / 1000 , fn () => $ this ->readOggOpus ($ deferred , $ ogg , $ loops ));
723+ $ delay = max (0.0 , $ targetTime + (20.0 / 1000.0 ) - microtime (true ));
724+ $ this ->readOpusTimer = $ this ->discord ->getLoop ()->addTimer ($ delay , fn () => $ this ->readOggOpus ($ deferred , $ ogg , $ loops ));
721725
722726 return ;
723727 }
724728
725- $ ogg ->getPacket ()->then (function ($ packet ) use (&$ loops , &$ ogg , $ deferred ) {
729+ $ ogg ->getPacket ()->then (function ($ packet ) use (&$ loops , &$ ogg , $ deferred, $ targetTime ) {
726730 // EOF for Ogg stream.
727731 if (null === $ packet ) {
728732 $ this ->reset ();
@@ -731,12 +735,14 @@ protected function readOggOpus(Deferred $deferred, OggStream &$ogg, int &$loops)
731735 return ;
732736 }
733737
734- $ this ->udp ->sendBuffer ($ packet );
735-
736- $ nextTime = $ this ->startTime + (20.0 / 1000.0 ) * $ loops ;
737- $ delay = max (0.0 , $ nextTime - microtime (true ));
738+ $ delay = max (0.0 , $ targetTime - microtime (true ));
738739
739- $ this ->readOpusTimer = $ this ->discord ->getLoop ()->addTimer ($ delay , fn () => $ this ->readOggOpus ($ deferred , $ ogg , $ loops ));
740+ // Use addTimer(0) even when the deadline has already passed to avoid
741+ // unbounded recursion if several consecutive packets arrive late.
742+ $ this ->readOpusTimer = $ this ->discord ->getLoop ()->addTimer ($ delay , function () use ($ packet , $ deferred , &$ ogg , &$ loops ) {
743+ $ this ->udp ->sendBuffer ($ packet );
744+ $ this ->readOggOpus ($ deferred , $ ogg , $ loops );
745+ });
740746 }, function () use ($ deferred ) {
741747 $ this ->reset ();
742748 $ deferred ->resolve (null );
0 commit comments