3636import sys
3737from typing import TYPE_CHECKING , Any , Callable , Literal , TypedDict , TypeVar
3838
39+ from discord .voice .packets .rtp import FakePacket
40+ from discord .voice .utils .wrapped import gap_wrapped , add_wrapped
41+ from discord .voice .utils .buffer import JitterBuffer
42+
43+ import davey
44+
3945from .errors import DiscordException
4046from .sinks import RawData
4147
4248if TYPE_CHECKING :
49+ from discord .user import User
50+ from discord .member import Member
4351 from discord .voice .client import VoiceClient
52+ from discord .voice .receive .router import PacketRouter
53+ from discord .voice .packets .core import Packet
54+ from discord .voice .packets import VoiceData
55+ from discord .sinks .core import Sink
4456
4557 T = TypeVar ("T" )
4658 APPLICATION_CTL = Literal ["audio" , "voip" , "lowdelay" ]
@@ -102,6 +114,12 @@ class DecoderStruct(ctypes.Structure):
102114# Error codes
103115OK = 0
104116BAD_ARG = - 1
117+ BUFF_TOO_SMALL = - 2
118+ INTERNAL_ERROR = - 3
119+ INVALID_PACKET = - 4
120+ UNIMPLEMENTED = - 5
121+ INVALID_STATE = - 6
122+ ALLOC_FAIL = - 7
105123
106124# Encoder CTLs
107125application_ctl : ApplicationCtl = {
@@ -449,20 +467,23 @@ def set_fec(self, enabled: bool = True) -> None:
449467 def set_expected_packet_loss_percent (self , percentage : float ) -> None :
450468 _lib .opus_encoder_ctl (self ._state , CTL_SET_PLP , min (100 , max (0 , int (percentage * 100 )))) # type: ignore
451469
452- def encode (self , pcm : bytes , frame_size : int ) -> bytes :
470+ def encode (self , pcm : bytes , frame_size : int | None = None ) -> bytes :
453471 max_data_bytes = len (pcm )
454472 # bytes can be used to reference pointer
455473 pcm_ptr = ctypes .cast (pcm , c_int16_ptr ) # type: ignore
456474 data = (ctypes .c_char * max_data_bytes )()
457475
476+ if frame_size is None :
477+ frame_size = self .FRAME_SIZE
478+
458479 ret = _lib .opus_encode (self ._state , pcm_ptr , frame_size , data , max_data_bytes )
459480
460481 # array can be initialized with bytes but mypy doesn't know
461482 return array .array ("b" , data [:ret ]).tobytes () # type: ignore
462483
463484
464485class Decoder (_OpusStruct ):
465- def __init__ (self ):
486+ def __init__ (self ) -> None :
466487 _OpusStruct .get_opus_version ()
467488
468489 self ._state = self ._create_state ()
@@ -521,18 +542,18 @@ def _get_last_packet_duration(self):
521542 _lib .opus_decoder_ctl (self ._state , CTL_LAST_PACKET_DURATION , ctypes .byref (ret ))
522543 return ret .value
523544
524- def decode (self , data , * , fec = False ):
545+ def decode (self , data : bytes | None , * , fec : bool = True ):
525546 if data is None and fec :
526547 raise OpusError (
527548 message = "Invalid arguments: FEC cannot be used with null data"
528549 )
529550
551+ channel_count = self .CHANNELS
552+
530553 if data is None :
531554 frame_size = self ._get_last_packet_duration () or self .SAMPLES_PER_FRAME
532- channel_count = self .CHANNELS
533555 else :
534556 frames = self .packet_get_nb_frames (data )
535- channel_count = self .CHANNELS
536557 samples_per_frame = self .packet_get_samples_per_frame (data )
537558 frame_size = frames * samples_per_frame
538559
@@ -542,9 +563,150 @@ def decode(self, data, *, fec=False):
542563 # )()
543564 pcm = (ctypes .c_int16 * (frame_size * channel_count ))()
544565 pcm_ptr = ctypes .cast (pcm , c_int16_ptr )
566+ pcm_ptr = ctypes .cast (
567+ pcm ,
568+ c_int16_ptr ,
569+ )
545570
546571 ret = _lib .opus_decode (
547572 self ._state , data , len (data ) if data else 0 , pcm_ptr , frame_size , fec
548573 )
549574
550575 return array .array ("h" , pcm [: ret * channel_count ]).tobytes ()
576+
577+
578+ class PacketDecoder :
579+ def __init__ (self , router : PacketRouter , ssrc : int ) -> None :
580+ self .router : PacketRouter = router
581+ self .ssrc : int = ssrc
582+
583+ self ._decoder : Decoder | None = None if self .sink .is_opus () else Decoder ()
584+ self ._buffer : JitterBuffer = JitterBuffer ()
585+ self ._cached_id : int | None = None
586+
587+ self ._last_seq : int = - 1
588+ self ._last_ts : int = - 1
589+
590+ @property
591+ def sink (self ) -> Sink :
592+ return self .router .sink
593+
594+ def _get_user (self , user_id : int ) -> User | Member | None :
595+ vc : VoiceClient = self .sink .client # type: ignore
596+ return vc .guild .get_member (user_id ) or vc .client .get_user (user_id )
597+
598+ def _get_cached_member (self ) -> User | Member | None :
599+ return self ._get_user (self ._cached_id ) if self ._cached_id else None
600+
601+ def _flag_ready_state (self ) -> None :
602+ if self ._buffer .peek ():
603+ self .router .waiter .register (self )
604+ else :
605+ self .router .waiter .unregister (self )
606+
607+ def push_packet (self , packet : Packet ) -> None :
608+ self ._buffer .push (packet )
609+ self ._flag_ready_state ()
610+
611+ def pop_data (self , * , timeout : float = 0 ) -> VoiceData | None :
612+ packet = self ._get_next_packet (timeout )
613+ self ._flag_ready_state ()
614+
615+ if packet is None :
616+ return None
617+ return self ._process_packet (packet )
618+
619+ def set_user_id (self , user_id : int ) -> None :
620+ self ._cached_id = user_id
621+
622+ def reset (self ) -> None :
623+ self ._buffer .reset ()
624+ self ._decoder = None if self .sink .is_opus () else Decoder ()
625+ self ._last_seq = self ._last_ts = - 1
626+ self ._flag_ready_state ()
627+
628+ def destroy (self ) -> None :
629+ self ._buffer .reset ()
630+ self ._decoder = None
631+ self ._flag_ready_state ()
632+
633+ def _get_next_packet (self , timeout : float ) -> Packet | None :
634+ packet = self ._buffer .pop (timeout = timeout )
635+
636+ if packet is None :
637+ if self ._buffer :
638+ packets = self ._buffer .flush ()
639+ if any (packets [1 :]):
640+ _log .warning (
641+ "%s packets were lost being flushed in decoder-%s" ,
642+ len (packets ) - 1 ,
643+ self .ssrc ,
644+ )
645+ return packets [0 ]
646+ return
647+ elif not packet :
648+ packet = self ._make_fakepacket ()
649+ return packet
650+
651+ def _make_fakepacket (self ) -> FakePacket :
652+ seq = add_wrapped (self ._last_seq , 1 )
653+ ts = add_wrapped (self ._last_ts , Decoder .SAMPLES_PER_FRAME , wrap = 2 ** 32 )
654+ return FakePacket (self .ssrc , seq , ts )
655+
656+ def _process_packet (self , packet : Packet ) -> VoiceData :
657+ from discord .object import Object
658+
659+ pcm = None
660+
661+ if not self .sink .is_opus ():
662+ packet , pcm = self ._decode_packet (packet )
663+
664+ member = self ._get_cached_member ()
665+
666+ if member is None :
667+ self ._cached_id = self .sink .client ._connection ._get_id_from_ssrc (self .ssrc )
668+ member = self ._get_cached_member ()
669+
670+ # yet still none, use Object
671+ if member is None and self ._cached_id :
672+ member = Object (id = self ._cached_id )
673+
674+ data = VoiceData (packet , member , pcm = pcm )
675+ self ._last_seq = packet .sequence
676+ self ._last_ts = packet .timestamp
677+ return data
678+
679+ def _decode_packet (self , packet : Packet ) -> tuple [Packet , bytes ]:
680+ assert self ._decoder is not None
681+ assert self .sink .client
682+
683+ user_id : int | None = self ._cached_id
684+ dave : davey .DaveSession | None = self .sink .client ._connection .dave_session
685+ in_dave = dave is not None
686+
687+ # personally, the best variable
688+ other_code = True
689+
690+ if packet :
691+ other_code = False
692+ pcm = self ._decoder .decode (packet .decrypted_data , fec = False )
693+
694+ if other_code :
695+ next_packet = self ._buffer .peek_next ()
696+
697+ if next_packet is not None :
698+ nextdata : bytes = next_packet .decrypted_data # type: ignore
699+
700+ _log .debug (
701+ "Generating fec packet: fake=%s, fec=%s" ,
702+ packet .sequence ,
703+ next_packet .sequence ,
704+ )
705+ pcm = self ._decoder .decode (nextdata , fec = True )
706+ else :
707+ pcm = self ._decoder .decode (None , fec = False )
708+
709+ if user_id is not None and in_dave and dave .can_passthrough (user_id ):
710+ pcm = dave .decrypt (user_id , davey .MediaType .audio , pcm )
711+
712+ return packet , pcm
0 commit comments