def test_has_voice(): from discord.voice_client import has_nacl from discord.opus import is_loaded, Encoder Encoder() # Ensure opus gets loaded assert has_nacl assert is_loaded()
def __init__(self, bot): super().__init__() self.bot = bot self.filename = None self.converter = None self.opus_data = [] self.encoder = OpusEncoder(48000, 2) self.delay = self.encoder.frame_length / 1000.0 self.encoder.frame_length = 0 self._connected = Event() self._connected.set() self.to_encode = asyncio.Queue() self.encode_task = bot.loop.create_task(self.queue_encoder())
class Encoder: def __init__(self, bot): super().__init__() self.bot = bot self.filename = None self.converter = None self.opus_data = [] self.encoder = OpusEncoder(48000, 2) self.delay = self.encoder.frame_length / 1000.0 self.encoder.frame_length = 0 self._connected = Event() self._connected.set() self.to_encode = asyncio.Queue() self.encode_task = bot.loop.create_task(self.queue_encoder()) def __unload(self): try: self.encode_task.cancel() except: pass def create_ffmpeg_player(self, filename, *, use_avconv=False, pipe=False, options=None, before_options=None, headers=None, after=None): """ Stolen from Rapptz/Danny, thanks! """ command = 'ffmpeg' if not use_avconv else 'avconv' input_name = '-' if pipe else shlex.quote(filename) before_args = "" if isinstance(headers, dict): for key, value in headers.items(): before_args += "{}: {}\r\n".format(key, value) before_args = ' -headers ' + shlex.quote(before_args) if isinstance(before_options, str): before_args += ' ' + before_options cmd = command + '{} -i {} -f s16le -ar {} -ac {} -loglevel warning' cmd = cmd.format(before_args, input_name, self.encoder.sampling_rate, self.encoder.channels) if isinstance(options, str): cmd = cmd + ' ' + options cmd += ' pipe:1' stdin = None if not pipe else filename args = shlex.split(cmd) try: p = subprocess.Popen(args, stdin=stdin, stdout=subprocess.PIPE) return ProcessPlayer(p, self, after) except FileNotFoundError as e: raise ClientException('ffmpeg/avconv was not found in your PATH' ' environment variable') from e except subprocess.SubprocessError as e: raise ClientException( 'Popen failed: {0.__name__} {1}'.format(type(e), str(e))) \ from e @commands.command() async def encode(self, filename: AudioCacheFileConverter): """Encodes a song to pickled pcm data""" if self.converter is not None: return self.converter = \ self.create_ffmpeg_player(filename, after=self._reset_converter) self.filename = filename self.converter.start() log.debug('encoding {}'.format(filename)) @commands.command(pass_context=True) async def fakeplay(self, ctx, filename: EncodedCacheFile): audio = self.bot.get_cog('Audio') with open(filename, 'rb') as f: esong = pickle.load(f) if not isinstance(esong, EncodedSong): await self.bot.say("Bad file") return vc = audio.voice_client(ctx.message.server) vc.audio_player = ShittyPlayer(esong, audio, vc._connected, vc.play_audio) vc.audio_player.start() def play_audio(self, data_frame): # opus_frame = self.encoder.encode(data_frame, # self.encoder.samples_per_frame) # self.opus_data.append(opus_frame) self.to_encode.put_nowait(data_frame) async def queue_encoder(self): while True: pcm = await self.to_encode.get() opus = self.encoder.encode(pcm, self.encoder.samples_per_frame) self.opus_data.append(opus) def _reset_converter(self): self.save_opus_data() self.converter = None self.filename = None def save_opus_data(self): enc = EncodedSong(self.filename + "-encoded", self.delay, self.opus_data) with open(enc.filename, 'wb') as f: pickle.dump(enc, f)
def test_encoding(): """ This generates some PCM, encodes it with opus, encrypts it, and then decrypts it. This serves as a test that the interface to opus and nacl work properly. """ from discord.opus import Decoder, Encoder encoder = Encoder() # We need to generate some PCM for testing pcm_data = b'' # Time that passes per PCM frame time_per_frame = encoder.FRAME_LENGTH / 1000 # Frames per second frames_per_second = int(1 / time_per_frame) # Time that passes per PCM sample time_per_sample = time_per_frame / encoder.SAMPLES_PER_FRAME # Maximum magnitude within PCM data type magnitude = (2**15) - 1 # Generate a 'Middle C' tone frequency = 261.625 # Generate 1 second of PCM data for sample in range(encoder.SAMPLES_PER_FRAME * frames_per_second): sample_time = sample * time_per_sample value = magnitude * math.sin(2 * math.pi * sample_time * frequency) # Duplicate PCM value per channel pcm_data += struct.pack('h', int(value)) * encoder.CHANNELS # Ensure data generated is of correct form assert len(pcm_data) == (encoder.FRAME_SIZE * frames_per_second) # Encode the data opus_packets = [] for index in range(0, len(pcm_data), encoder.FRAME_SIZE): encoded = encoder.encode(pcm_data[index:index + encoder.FRAME_SIZE], encoder.SAMPLES_PER_FRAME) opus_packets.append(encoded) # Prepare to encrypt the data import nacl.secret import nacl.utils # Generate a random secret key and create an encryption box secret_key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE) box = nacl.secret.SecretBox(secret_key) # Encrypt the data encrypted_packets = [] for packet in opus_packets: encrypted_data = box.encrypt(packet) # Check message length assert len( encrypted_data) == len(packet) + box.NONCE_SIZE + box.MACBYTES encrypted_packets.append(encrypted_data) # Decrypt the data decrypted_packets = [] for packet, original_packet in zip(encrypted_packets, opus_packets): decrypted_data = box.decrypt(packet) # Ensure data matches assert decrypted_data == original_packet decrypted_packets.append(decrypted_data) # Create decoder decoder = Decoder() decoder.set_volume(1.0) # Decode the data decoded_pcm = b'' for packet in decrypted_packets: decoded_pcm += decoder.decode(packet) assert len(decoded_pcm) == len(pcm_data)