mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-07-11 04:17:58 +00:00
Add encoder params to VoiceClient.play
This commit is contained in:
parent
b276f3f5a2
commit
8b8ce55378
@ -39,10 +39,17 @@ from .errors import DiscordException
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
APPLICATION_CTL = Literal['audio', 'voip', 'lowdelay']
|
||||||
BAND_CTL = Literal['narrow', 'medium', 'wide', 'superwide', 'full']
|
BAND_CTL = Literal['narrow', 'medium', 'wide', 'superwide', 'full']
|
||||||
SIGNAL_CTL = Literal['auto', 'voice', 'music']
|
SIGNAL_CTL = Literal['auto', 'voice', 'music']
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationCtl(TypedDict):
|
||||||
|
audio: int
|
||||||
|
voip: int
|
||||||
|
lowdelay: int
|
||||||
|
|
||||||
|
|
||||||
class BandCtl(TypedDict):
|
class BandCtl(TypedDict):
|
||||||
narrow: int
|
narrow: int
|
||||||
medium: int
|
medium: int
|
||||||
@ -90,9 +97,10 @@ OK = 0
|
|||||||
BAD_ARG = -1
|
BAD_ARG = -1
|
||||||
|
|
||||||
# Encoder CTLs
|
# Encoder CTLs
|
||||||
APPLICATION_AUDIO = 2049
|
APPLICATION_AUDIO = 'audio'
|
||||||
APPLICATION_VOIP = 2048
|
APPLICATION_VOIP = 'voip'
|
||||||
APPLICATION_LOWDELAY = 2051
|
APPLICATION_LOWDELAY = 'lowdelay'
|
||||||
|
# These remain as strings for backwards compat
|
||||||
|
|
||||||
CTL_SET_BITRATE = 4002
|
CTL_SET_BITRATE = 4002
|
||||||
CTL_SET_BANDWIDTH = 4008
|
CTL_SET_BANDWIDTH = 4008
|
||||||
@ -105,6 +113,12 @@ CTL_SET_GAIN = 4034
|
|||||||
CTL_LAST_PACKET_DURATION = 4039
|
CTL_LAST_PACKET_DURATION = 4039
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
application_ctl: ApplicationCtl = {
|
||||||
|
'audio': 2049,
|
||||||
|
'voip': 2048,
|
||||||
|
'lowdelay': 2051,
|
||||||
|
}
|
||||||
|
|
||||||
band_ctl: BandCtl = {
|
band_ctl: BandCtl = {
|
||||||
'narrow': 1101,
|
'narrow': 1101,
|
||||||
'medium': 1102,
|
'medium': 1102,
|
||||||
@ -319,16 +333,38 @@ class _OpusStruct:
|
|||||||
|
|
||||||
|
|
||||||
class Encoder(_OpusStruct):
|
class Encoder(_OpusStruct):
|
||||||
def __init__(self, application: int = APPLICATION_AUDIO):
|
def __init__(
|
||||||
_OpusStruct.get_opus_version()
|
self,
|
||||||
|
*,
|
||||||
|
application: APPLICATION_CTL = 'audio',
|
||||||
|
bitrate: int = 128,
|
||||||
|
fec: bool = True,
|
||||||
|
expected_packet_loss: float = 0.15,
|
||||||
|
bandwidth: BAND_CTL = 'full',
|
||||||
|
signal_type: SIGNAL_CTL = 'auto',
|
||||||
|
):
|
||||||
|
if application not in application_ctl:
|
||||||
|
raise ValueError(f'{application} is not a valid application setting. Try one of: {"".join(application_ctl)}')
|
||||||
|
|
||||||
self.application: int = application
|
if not 16 <= bitrate <= 512:
|
||||||
|
raise ValueError(f'bitrate must be between 16 and 512, not {bitrate}')
|
||||||
|
|
||||||
|
if not 0 < expected_packet_loss <= 1.0:
|
||||||
|
raise ValueError(
|
||||||
|
f'expected_packet_loss must be a positive number less than or equal to 1, not {expected_packet_loss}'
|
||||||
|
)
|
||||||
|
|
||||||
|
_OpusStruct.get_opus_version() # lazy loads the opus library
|
||||||
|
|
||||||
|
self.application: int = application_ctl[application]
|
||||||
self._state: EncoderStruct = self._create_state()
|
self._state: EncoderStruct = self._create_state()
|
||||||
self.set_bitrate(128)
|
|
||||||
self.set_fec(True)
|
self.set_bitrate(bitrate)
|
||||||
self.set_expected_packet_loss_percent(0.15)
|
self.set_fec(fec)
|
||||||
self.set_bandwidth('full')
|
if fec:
|
||||||
self.set_signal_type('auto')
|
self.set_expected_packet_loss_percent(expected_packet_loss)
|
||||||
|
self.set_bandwidth(bandwidth)
|
||||||
|
self.set_signal_type(signal_type)
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self) -> None:
|
||||||
if hasattr(self, '_state'):
|
if hasattr(self, '_state'):
|
||||||
@ -355,7 +391,7 @@ class Encoder(_OpusStruct):
|
|||||||
|
|
||||||
def set_signal_type(self, req: SIGNAL_CTL) -> None:
|
def set_signal_type(self, req: SIGNAL_CTL) -> None:
|
||||||
if req not in signal_ctl:
|
if req not in signal_ctl:
|
||||||
raise KeyError(f'{req!r} is not a valid bandwidth setting. Try one of: {",".join(signal_ctl)}')
|
raise KeyError(f'{req!r} is not a valid signal type setting. Try one of: {",".join(signal_ctl)}')
|
||||||
|
|
||||||
k = signal_ctl[req]
|
k = signal_ctl[req]
|
||||||
_lib.opus_encoder_ctl(self._state, CTL_SET_SIGNAL, k)
|
_lib.opus_encoder_ctl(self._state, CTL_SET_SIGNAL, k)
|
||||||
|
@ -58,7 +58,7 @@ if TYPE_CHECKING:
|
|||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .state import ConnectionState
|
from .state import ConnectionState
|
||||||
from .user import ClientUser
|
from .user import ClientUser
|
||||||
from .opus import Encoder
|
from .opus import Encoder, APPLICATION_CTL, BAND_CTL, SIGNAL_CTL
|
||||||
from .channel import StageChannel, VoiceChannel
|
from .channel import StageChannel, VoiceChannel
|
||||||
from . import abc
|
from . import abc
|
||||||
|
|
||||||
@ -569,7 +569,18 @@ class VoiceClient(VoiceProtocol):
|
|||||||
|
|
||||||
return header + box.encrypt(bytes(data), bytes(nonce)).ciphertext + nonce[:4]
|
return header + box.encrypt(bytes(data), bytes(nonce)).ciphertext + nonce[:4]
|
||||||
|
|
||||||
def play(self, source: AudioSource, *, after: Optional[Callable[[Optional[Exception]], Any]] = None) -> None:
|
def play(
|
||||||
|
self,
|
||||||
|
source: AudioSource,
|
||||||
|
*,
|
||||||
|
after: Optional[Callable[[Optional[Exception]], Any]] = None,
|
||||||
|
application: APPLICATION_CTL = 'audio',
|
||||||
|
bitrate: int = 128,
|
||||||
|
fec: bool = True,
|
||||||
|
expected_packet_loss: float = 0.15,
|
||||||
|
bandwidth: BAND_CTL = 'full',
|
||||||
|
signal_type: SIGNAL_CTL = 'auto',
|
||||||
|
) -> None:
|
||||||
"""Plays an :class:`AudioSource`.
|
"""Plays an :class:`AudioSource`.
|
||||||
|
|
||||||
The finalizer, ``after`` is called after the source has been exhausted
|
The finalizer, ``after`` is called after the source has been exhausted
|
||||||
@ -579,9 +590,15 @@ class VoiceClient(VoiceProtocol):
|
|||||||
caught and the audio player is then stopped. If no after callback is
|
caught and the audio player is then stopped. If no after callback is
|
||||||
passed, any caught exception will be logged using the library logger.
|
passed, any caught exception will be logged using the library logger.
|
||||||
|
|
||||||
|
Extra parameters may be passed to the internal opus encoder if a PCM based
|
||||||
|
source is used. Otherwise, they are ignored.
|
||||||
|
|
||||||
.. versionchanged:: 2.0
|
.. versionchanged:: 2.0
|
||||||
Instead of writing to ``sys.stderr``, the library's logger is used.
|
Instead of writing to ``sys.stderr``, the library's logger is used.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.4
|
||||||
|
Added encoder parameters as keyword arguments.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
-----------
|
-----------
|
||||||
source: :class:`AudioSource`
|
source: :class:`AudioSource`
|
||||||
@ -590,6 +607,27 @@ class VoiceClient(VoiceProtocol):
|
|||||||
The finalizer that is called after the stream is exhausted.
|
The finalizer that is called after the stream is exhausted.
|
||||||
This function must have a single parameter, ``error``, that
|
This function must have a single parameter, ``error``, that
|
||||||
denotes an optional exception that was raised during playing.
|
denotes an optional exception that was raised during playing.
|
||||||
|
application: :class:`str`
|
||||||
|
Configures the encoder's intended application. Can be one of:
|
||||||
|
``'audio'``, ``'voip'``, ``'lowdelay'``.
|
||||||
|
Defaults to ``'audio'``.
|
||||||
|
bitrate: :class:`int`
|
||||||
|
Configures the bitrate in the encoder. Can be between ``16`` and ``512``.
|
||||||
|
Defaults to ``128``.
|
||||||
|
fec: :class:`bool`
|
||||||
|
Configures the encoder's use of inband forward error correction.
|
||||||
|
Defaults to ``True``.
|
||||||
|
expected_packet_loss: :class:`float`
|
||||||
|
Configures the encoder's expected packet loss percentage. Requires FEC.
|
||||||
|
Defaults to ``0.15``.
|
||||||
|
bandwidth: :class:`str`
|
||||||
|
Configures the encoder's bandpass. Can be one of:
|
||||||
|
``'narrow'``, ``'medium'``, ``'wide'``, ``'superwide'``, ``'full'``.
|
||||||
|
Defaults to ``'full'``.
|
||||||
|
signal_type: :class:`str`
|
||||||
|
Configures the type of signal being encoded. Can be one of:
|
||||||
|
``'auto'``, ``'voice'``, ``'music'``.
|
||||||
|
Defaults to ``'auto'``.
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
-------
|
-------
|
||||||
@ -599,6 +637,8 @@ class VoiceClient(VoiceProtocol):
|
|||||||
Source is not a :class:`AudioSource` or after is not a callable.
|
Source is not a :class:`AudioSource` or after is not a callable.
|
||||||
OpusNotLoaded
|
OpusNotLoaded
|
||||||
Source is not opus encoded and opus is not loaded.
|
Source is not opus encoded and opus is not loaded.
|
||||||
|
ValueError
|
||||||
|
An improper value was passed as an encoder parameter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.is_connected():
|
if not self.is_connected():
|
||||||
@ -610,8 +650,15 @@ class VoiceClient(VoiceProtocol):
|
|||||||
if not isinstance(source, AudioSource):
|
if not isinstance(source, AudioSource):
|
||||||
raise TypeError(f'source must be an AudioSource not {source.__class__.__name__}')
|
raise TypeError(f'source must be an AudioSource not {source.__class__.__name__}')
|
||||||
|
|
||||||
if not self.encoder and not source.is_opus():
|
if not source.is_opus():
|
||||||
self.encoder = opus.Encoder()
|
self.encoder = opus.Encoder(
|
||||||
|
application=application,
|
||||||
|
bitrate=bitrate,
|
||||||
|
fec=fec,
|
||||||
|
expected_packet_loss=expected_packet_loss,
|
||||||
|
bandwidth=bandwidth,
|
||||||
|
signal_type=signal_type,
|
||||||
|
)
|
||||||
|
|
||||||
self._player = AudioPlayer(source, self, after=after)
|
self._player = AudioPlayer(source, self, after=after)
|
||||||
self._player.start()
|
self._player.start()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user