From 0a47d0e1d63835387fb54a8525d4303d7427c5f3 Mon Sep 17 00:00:00 2001 From: Amish Shah Date: Wed, 28 Dec 2016 18:16:26 +0000 Subject: [PATCH] Remove old stuff --- src/client/voice/VoiceConnection.js | 1 - .../voice/dispatcher/StreamDispatcher.old.js | 304 ------------------ src/client/voice/player/AudioPlayer.js | 25 +- src/client/voice/player/AudioPlayer.old.js | 100 ------ src/client/voice/player/BasePlayer.old.js | 121 ------- src/client/voice/player/DefaultPlayer.old.js | 19 -- 6 files changed, 18 insertions(+), 552 deletions(-) delete mode 100644 src/client/voice/dispatcher/StreamDispatcher.old.js delete mode 100644 src/client/voice/player/AudioPlayer.old.js delete mode 100644 src/client/voice/player/BasePlayer.old.js delete mode 100644 src/client/voice/player/DefaultPlayer.old.js diff --git a/src/client/voice/VoiceConnection.js b/src/client/voice/VoiceConnection.js index 08cc1f924..5c8d72771 100644 --- a/src/client/voice/VoiceConnection.js +++ b/src/client/voice/VoiceConnection.js @@ -79,7 +79,6 @@ class VoiceConnection extends EventEmitter { * @param {string|Error} warning the warning */ this.emit('warn', e); - this.player.cleanup(); }); /** diff --git a/src/client/voice/dispatcher/StreamDispatcher.old.js b/src/client/voice/dispatcher/StreamDispatcher.old.js deleted file mode 100644 index a56a4a98e..000000000 --- a/src/client/voice/dispatcher/StreamDispatcher.old.js +++ /dev/null @@ -1,304 +0,0 @@ -const EventEmitter = require('events').EventEmitter; -const NaCl = require('tweetnacl'); - -const nonce = new Buffer(24); -nonce.fill(0); - -/** - * The class that sends voice packet data to the voice connection. - * ```js - * // obtained using: - * voiceChannel.join().then(connection => { - * // you can play a file or a stream here: - * const dispatcher = connection.playFile('./file.mp3'); - * }); - * ``` - * @extends {EventEmitter} - */ -class StreamDispatcher extends EventEmitter { - constructor(player, stream, streamOptions) { - super(); - this.player = player; - this.stream = stream; - this._startStreaming(); - this._triggered = false; - this._volume = streamOptions.volume; - - /** - * How many passes the dispatcher should take when sending packets to reduce packet loss. Values over 5 - * aren't recommended, as it means you are using 5x more bandwidth. You _can_ edit this at runtime. - * @type {number} - */ - this.passes = streamOptions.passes || 1; - - /** - * Whether playing is paused - * @type {boolean} - */ - this.paused = false; - - this.setVolume(streamOptions.volume || 1); - } - - get streamingData() { - return this.player.streamingData; - } - - /** - * How long the stream dispatcher has been "speaking" for - * @type {number} - * @readonly - */ - get time() { - return this.streamingData.count * (this.streamingData.length || 0); - } - - /** - * The total time, taking into account pauses and skips, that the dispatcher has been streaming for - * @type {number} - * @readonly - */ - get totalStreamTime() { - return this.time + this.streamingData.pausedTime; - } - - /** - * The volume of the stream, relative to the stream's input volume - * @type {number} - * @readonly - */ - get volume() { - return this._volume; - } - - /** - * Sets the volume relative to the input stream - i.e. 1 is normal, 0.5 is half, 2 is double. - * @param {number} volume The volume that you want to set - */ - setVolume(volume) { - this._volume = volume; - } - - /** - * Set the volume in decibels - * @param {number} db The decibels - */ - setVolumeDecibels(db) { - this._volume = Math.pow(10, db / 20); - } - - /** - * Set the volume so that a perceived value of 0.5 is half the perceived volume etc. - * @param {number} value The value for the volume - */ - setVolumeLogarithmic(value) { - this._volume = Math.pow(value, 1.660964); - } - - /** - * Stops sending voice packets to the voice connection (stream may still progress however) - */ - pause() { - this._setPaused(true); - } - - /** - * Resumes sending voice packets to the voice connection (may be further on in the stream than when paused) - */ - resume() { - this._setPaused(false); - } - - /** - * Stops the current stream permanently and emits an `end` event. - * @param {string} [reason='user'] An optional reason for stopping the dispatcher. - */ - end(reason = 'user') { - this._triggerTerminalState('end', reason); - } - - _setSpeaking(value) { - this.speaking = value; - /** - * Emitted when the dispatcher starts/stops speaking - * @event StreamDispatcher#speaking - * @param {boolean} value Whether or not the dispatcher is speaking - */ - this.emit('speaking', value); - } - - _sendBuffer(buffer, sequence, timestamp) { - let repeats = this.passes; - const packet = this._createPacket(sequence, timestamp, this.player.opusEncoder.encode(buffer)); - while (repeats--) { - this.player.voiceConnection.sockets.udp.send(packet) - .catch(e => this.emit('debug', `Failed to send a packet ${e}`)); - } - } - - _createPacket(sequence, timestamp, buffer) { - const packetBuffer = new Buffer(buffer.length + 28); - packetBuffer.fill(0); - packetBuffer[0] = 0x80; - packetBuffer[1] = 0x78; - - packetBuffer.writeUIntBE(sequence, 2, 2); - packetBuffer.writeUIntBE(timestamp, 4, 4); - packetBuffer.writeUIntBE(this.player.voiceConnection.authentication.ssrc, 8, 4); - - packetBuffer.copy(nonce, 0, 0, 12); - buffer = NaCl.secretbox(buffer, nonce, this.player.voiceConnection.authentication.secretKey.key); - - for (let i = 0; i < buffer.length; i++) packetBuffer[i + 12] = buffer[i]; - - return packetBuffer; - } - - _applyVolume(buffer) { - if (this._volume === 1) return buffer; - - const out = new Buffer(buffer.length); - for (let i = 0; i < buffer.length; i += 2) { - if (i >= buffer.length - 1) break; - const uint = Math.min(32767, Math.max(-32767, Math.floor(this._volume * buffer.readInt16LE(i)))); - out.writeInt16LE(uint, i); - } - - return out; - } - - _send() { - try { - if (this._triggered) { - this._setSpeaking(false); - return; - } - - const data = this.streamingData; - - if (data.missed >= 5) { - this._triggerTerminalState('end', 'Stream is not generating quickly enough.'); - return; - } - - if (this.paused) { - // data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0; - data.pausedTime += data.length * 10; - this.player.voiceConnection.voiceManager.client.setTimeout(() => this._send(), data.length * 10); - return; - } - - this._setSpeaking(true); - - if (!data.startTime) { - /** - * Emitted once the dispatcher starts streaming - * @event StreamDispatcher#start - */ - this.emit('start'); - data.startTime = Date.now(); - } - - const bufferLength = 1920 * data.channels; - let buffer = this.stream.read(bufferLength); - if (!buffer) { - data.missed++; - data.pausedTime += data.length * 10; - this.player.voiceConnection.voiceManager.client.setTimeout(() => this._send(), data.length * 10); - return; - } - - data.missed = 0; - - if (buffer.length !== bufferLength) { - const newBuffer = new Buffer(bufferLength).fill(0); - buffer.copy(newBuffer); - buffer = newBuffer; - } - - buffer = this._applyVolume(buffer); - - data.count++; - data.sequence = (data.sequence + 1) < 65536 ? data.sequence + 1 : 0; - data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0; - - this._sendBuffer(buffer, data.sequence, data.timestamp); - - const nextTime = data.length + (data.startTime + data.pausedTime + (data.count * data.length) - Date.now()); - this.player.voiceConnection.voiceManager.client.setTimeout(() => this._send(), nextTime); - } catch (e) { - this._triggerTerminalState('error', e); - } - } - - _triggerEnd(reason) { - /** - * Emitted once the stream has ended. Attach a `once` listener to this. - * @event StreamDispatcher#end - * @param {string} reason The reason for the end of the dispatcher. If it ended because it reached the end of the - * stream, this would be `stream`. If you invoke `.end()` without specifying a reason, this would be `user`. - */ - this.emit('end', reason); - } - - _triggerError(err) { - this.emit('end'); - /** - * Emitted once the stream has encountered an error. Attach a `once` listener to this. Also emits `end`. - * @event StreamDispatcher#error - * @param {Error} err The encountered error - */ - this.emit('error', err); - } - - _triggerTerminalState(state, err) { - if (this._triggered) return; - /** - * Emitted when the stream wants to give debug information. - * @event StreamDispatcher#debug - * @param {string} information The debug information - */ - this.emit('debug', `Triggered terminal state ${state} - stream is now dead`); - this._triggered = true; - this._setSpeaking(false); - switch (state) { - case 'end': - this._triggerEnd(err); - break; - case 'error': - this._triggerError(err); - break; - default: - this.emit('error', 'Unknown trigger state'); - break; - } - } - - _startStreaming() { - if (!this.stream) { - this.emit('error', 'No stream'); - return; - } - - this.stream.on('end', err => this._triggerTerminalState('end', err || 'stream')); - this.stream.on('error', err => this._triggerTerminalState('error', err)); - - const data = this.streamingData; - data.length = 20; - data.missed = 0; - - this.stream.once('readable', () => this._send()); - } - - _setPaused(paused) { - if (paused) { - this.paused = true; - this._setSpeaking(false); - } else { - this.paused = false; - this._setSpeaking(true); - } - } -} - -module.exports = StreamDispatcher; diff --git a/src/client/voice/player/AudioPlayer.js b/src/client/voice/player/AudioPlayer.js index fce6a69b7..fec034184 100644 --- a/src/client/voice/player/AudioPlayer.js +++ b/src/client/voice/player/AudioPlayer.js @@ -32,14 +32,20 @@ class AudioPlayer extends EventEmitter { return this.streams.last().transcoder; } - destroyAllStreams(exceptLatest) { + destroyStream(stream) { + const data = this.streams.get(stream); + if (!data) return; + const transcoder = data.transcoder; + const dispatcher = data.dispatcher; + if (transcoder) transcoder.kill(); + if (dispatcher) dispatcher.destroy('end'); + } + + destroyAllStreams(except) { for (const stream of this.streams.keys()) { - const data = this.streams.get(stream); - const transcoder = data.transcoder; - const dispatcher = data.dispatcher; - if (exceptLatest && transcoder === this.currentTranscoder) continue; - if (transcoder) transcoder.kill(); - if (dispatcher) dispatcher.destroy('end'); + if (except === stream) continue; + if (except === true && this.streams.get(stream) === this.streams.last()) continue; + this.destroyStream(stream); } } @@ -51,6 +57,11 @@ class AudioPlayer extends EventEmitter { ffmpegArguments: ffmpegArguments.concat(['-ss', String(seek)]), }); this.streams.set(stream, { transcoder }); + transcoder.on('error', e => { + this.destroyStream(stream); + if (this.listenerCount('error') > 0) this.emit('error', e); + else this.emit('warn', e); + }); this.playPCMStream(transcoder.output, options); } diff --git a/src/client/voice/player/AudioPlayer.old.js b/src/client/voice/player/AudioPlayer.old.js deleted file mode 100644 index 7e9ce616b..000000000 --- a/src/client/voice/player/AudioPlayer.old.js +++ /dev/null @@ -1,100 +0,0 @@ -const OpusEncoders = require('../opus/OpusEngineList'); -const EventEmitter = require('events').EventEmitter; -const StreamDispatcher = require('../dispatcher/StreamDispatcher'); - -const ffmpegArguments = [ - '-analyzeduration', '0', - '-loglevel', '0', - '-f', 's16le', - '-ar', '48000', - '-ac', '2', -]; - -/** - * Represents the Audio Player of a Voice Connection - * @extends {EventEmitter} - * @private - */ -class AudioPlayer extends EventEmitter { - constructor(voiceConnection) { - super(); - /** - * The voice connection the player belongs to - * @type {VoiceConnection} - */ - this.voiceConnection = voiceConnection; - this.opusEncoder = OpusEncoders.fetch(); - this.currentConverter = null; - /** - * The current stream dispatcher, if a stream is being played - * @type {StreamDispatcher} - */ - this.dispatcher = null; - // this.prism.on('error', e => this.emit('error', e)); - this.streamingData = { - channels: 2, - count: 0, - sequence: 0, - timestamp: 0, - pausedTime: 0, - }; - this.voiceConnection.on('closing', () => this.cleanup(null, 'voice connection closing')); - } - - get prism() { - return this.voiceConnection.prism; - } - - playUnknownStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) { - const options = { seek, volume, passes }; - const transcoder = this.prism.transcode({ - type: 'ffmpeg', - media: stream, - ffmpegArguments, - }); - this.playPCMStream(transcoder.output, options); - } - - /*playUnknownStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) { - const options = { seek, volume, passes }; - stream.on('end', () => { - this.emit('debug', 'Input stream to converter has ended'); - }); - stream.on('error', e => this.emit('error', e)); - const conversionProcess = this.audioToPCM.createConvertStream(options.seek); - conversionProcess.on('error', e => this.emit('error', e)); - conversionProcess.setInput(stream); - return this.playPCMStream(conversionProcess.process.stdout, conversionProcess, options); -}*/ - - cleanup(checkStream, reason) { - // cleanup is a lot less aggressive than v9 because it doesn't try to kill every single stream it is aware of - this.emit('debug', `Clean up triggered due to ${reason}`); - const filter = checkStream && this.dispatcher && this.dispatcher.stream === checkStream; - if (this.currentConverter && (checkStream ? filter : true)) { - this.currentConverter.destroy(); - this.currentConverter = null; - } - } - - playPCMStream(stream, converter, { seek = 0, volume = 1, passes = 1 } = {}) { - const options = { seek, volume, passes }; - stream.on('end', () => this.emit('debug', 'PCM input stream ended')); - this.cleanup(null, 'outstanding play stream'); - this.currentConverter = converter; - if (this.dispatcher) { - this.streamingData = this.dispatcher.streamingData; - } - stream.on('error', e => this.emit('error', e)); - const dispatcher = new StreamDispatcher(this, stream, this.streamingData, options); - dispatcher.on('error', e => this.emit('error', e)); - dispatcher.on('end', () => this.cleanup(dispatcher.stream, 'dispatcher ended')); - dispatcher.on('speaking', value => this.voiceConnection.setSpeaking(value)); - this.dispatcher = dispatcher; - dispatcher.on('debug', m => this.emit('debug', `Stream dispatch - ${m}`)); - return dispatcher; - } - -} - -module.exports = AudioPlayer; diff --git a/src/client/voice/player/BasePlayer.old.js b/src/client/voice/player/BasePlayer.old.js deleted file mode 100644 index d5285cd34..000000000 --- a/src/client/voice/player/BasePlayer.old.js +++ /dev/null @@ -1,121 +0,0 @@ -const OpusEngines = require('../opus/OpusEngineList'); -const ConverterEngines = require('../pcm/ConverterEngineList'); -const Constants = require('../../../util/Constants'); -const StreamDispatcher = require('../dispatcher/StreamDispatcher'); -const EventEmitter = require('events').EventEmitter; - -class VoiceConnectionPlayer extends EventEmitter { - constructor(connection) { - super(); - this.connection = connection; - this.opusEncoder = OpusEngines.fetch(); - const Engine = ConverterEngines.fetch(); - this.converterEngine = new Engine(this); - this.converterEngine.on('error', err => { - this._shutdown(); - this.emit('error', err); - }); - this.speaking = false; - this.processMap = new Map(); - this.dispatcher = null; - this._streamingData = { - sequence: 0, - timestamp: 0, - }; - } - - convertStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) { - const options = { seek, volume, passes }; - const encoder = this.converterEngine.createConvertStream(options.seek); - const pipe = stream.pipe(encoder.stdin, { end: false }); - pipe.on('unpipe', () => { - this.killStream(encoder.stdout); - pipe.destroy(); - }); - this.processMap.set(encoder.stdout, { - pcmConverter: encoder, - inputStream: stream, - }); - return encoder.stdout; - } - - _shutdown() { - this.speaking = false; - if (this.dispatcher) this.dispatcher._triggerTerminalState('end', 'ended by parent player shutdown'); - for (const stream of this.processMap.keys()) this.killStream(stream); - } - - killStream(stream) { - const streams = this.processMap.get(stream); - this._streamingData = this.dispatcher.streamingData; - this.emit(Constants.Events.DEBUG, 'Cleaning up player after audio stream ended or encountered an error'); - - const dummyHandler = () => null; - - if (streams) { - this.processMap.delete(stream); - if (streams.inputStream && streams.pcmConverter) { - try { - streams.inputStream.once('error', dummyHandler); - streams.pcmConverter.once('error', dummyHandler); - streams.pcmConverter.stdin.once('error', dummyHandler); - streams.pcmConverter.stdout.once('error', dummyHandler); - if (streams.inputStream.unpipe) { - streams.inputStream.unpipe(streams.pcmConverter.stdin); - this.emit(Constants.Events.DEBUG, '- Unpiped input stream'); - } else if (streams.inputStream.destroy) { - streams.inputStream.destroy(); - this.emit(Constants.Events.DEBUG, '- Couldn\'t unpipe input stream, so destroyed input stream'); - } - if (streams.pcmConverter.stdin) { - streams.pcmConverter.stdin.end(); - this.emit(Constants.Events.DEBUG, '- Ended input stream to PCM converter'); - } - if (streams.pcmConverter && streams.pcmConverter.kill) { - streams.pcmConverter.kill('SIGINT'); - this.emit(Constants.Events.DEBUG, '- Killed the PCM converter'); - } - } catch (err) { - // if an error happened make sure the pcm converter is killed anyway - try { - if (streams.pcmConverter && streams.pcmConverter.kill) { - streams.pcmConverter.kill('SIGINT'); - this.emit(Constants.Events.DEBUG, '- Killed the PCM converter after previous error (abnormal)'); - } - } catch (e) { - return e; - } - return err; - } - } - } - return null; - } - - setSpeaking(value) { - if (this.speaking === value) return; - this.speaking = value; - this.connection.websocket.send({ - op: Constants.VoiceOPCodes.SPEAKING, - d: { - speaking: true, - delay: 0, - }, - }).catch(e => { - this.emit('debug', e); - }); - } - - playPCMStream(pcmStream, { seek = 0, volume = 1, passes = 1 } = {}) { - const options = { seek, volume, passes }; - const dispatcher = new StreamDispatcher(this, pcmStream, this._streamingData, options); - dispatcher.on('speaking', value => this.setSpeaking(value)); - dispatcher.on('end', () => this.killStream(pcmStream)); - dispatcher.on('error', () => this.killStream(pcmStream)); - dispatcher.setVolume(options.volume); - this.dispatcher = dispatcher; - return dispatcher; - } -} - -module.exports = VoiceConnectionPlayer; diff --git a/src/client/voice/player/DefaultPlayer.old.js b/src/client/voice/player/DefaultPlayer.old.js deleted file mode 100644 index b465e8cd6..000000000 --- a/src/client/voice/player/DefaultPlayer.old.js +++ /dev/null @@ -1,19 +0,0 @@ -const BasePlayer = require('./BasePlayer'); -const fs = require('fs'); - -class DefaultPlayer extends BasePlayer { - playFile(file, { seek = 0, volume = 1 } = {}) { - const options = { seek: seek, volume: volume }; - return this.playStream(fs.createReadStream(file), options); - } - - playStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) { - this._shutdown(); - const options = { seek, volume, passes }; - const pcmStream = this.convertStream(stream, options); - const dispatcher = this.playPCMStream(pcmStream, options); - return dispatcher; - } -} - -module.exports = DefaultPlayer;