From 794be74847bdbf6f4a137756baf8d735aa97ad6e Mon Sep 17 00:00:00 2001 From: Garifullin Ruslan Date: Fri, 8 Jan 2021 17:27:11 +0300 Subject: [PATCH] fix(Voice): crash on dc, member connect Solves crashes when disconnecting. Fixes members not connecting and disconnecting from audio mixer. Also adds a voice settings button in the voice panel. --- assets/icons/microphone-settings-outline.png | Bin 0 -> 640 bytes assets/icons/microphone-settings.png | Bin 0 -> 643 bytes src/components/VoicePanel/VoicePanel.ts | 69 ++++++++++++++----- src/utilities/voice/VoiceProvider.ts | 21 +++--- 4 files changed, 62 insertions(+), 28 deletions(-) create mode 100644 assets/icons/microphone-settings-outline.png create mode 100644 assets/icons/microphone-settings.png diff --git a/assets/icons/microphone-settings-outline.png b/assets/icons/microphone-settings-outline.png new file mode 100644 index 0000000000000000000000000000000000000000..1793ab2aa9f492816139edeab10a39069e53bd1c GIT binary patch literal 640 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG?d}4kf#9d}?s_1_ zS>O>_45Sml_(QhSc?Jf?CQlc~5Rc<;r&#N-6bc-x-z?V}lcK+TK^RwSi(C1euP@er z?r>V`wvU&4FTGhJ@7IwMPS#SwkAnNZrvr zlC;se;px|}RgdG^TqPLSaR=)tg!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG?d}4kf#9d}?s_1_ zS>O>_45Sml_(QhSc?Jf?R!tHca;L2>H^Y z^y*li1Z^tHmp~_Sf>Tt-Z@oSzM~>c#kiN z`-9&4Yd^MXa!cM>#O{IcUb7-IkUqnI5%BN+ this.connection?.disconnect()); discntBtn.setFixedSize(32, 32); + voiceBtn.setFixedSize(32, 32); + voiceBtn.addEventListener('clicked', () => { + app.emit(AppEvents.SWITCH_VIEW, 'settings'); + app.emit(AppEvents.OPEN_SETTINGS_PAGE, __('VOICE')); + }); + this.setLayout(layout); } - private handleDisconnectButton() { + private handleDisconnect = () => { this.statusLabel.setText("Disconnecting"); - this.connection?.disconnect(); this.recordStream?.end(); this.playbackStream?.end(); this.mixer?.close(); @@ -165,7 +177,7 @@ export class VoicePanel extends QWidget { this.hide(); this.channel = undefined; this.connection = undefined; - } + }; private static openVoiceNotSupportedDialog(channel: VoiceChannel) { const msgBox = new QMessageBox(); @@ -210,6 +222,7 @@ export class VoicePanel extends QWidget { this.streams.set(member, input); this.mixer.addInput(input); stream.pipe(input); + debug(`Connected member ${member}.`); } else { error(`Couldn't connect member ${member} to the voice channel ${this.channel?.name}.`); } @@ -230,7 +243,7 @@ export class VoicePanel extends QWidget { } } - private initPlayback() { + private initPlayback = () => { if (!this.connection || !this.channel || !vp) { return; } @@ -253,9 +266,9 @@ export class VoicePanel extends QWidget { for (const member of channel.members.filter((m) => m.user !== app.client.user).values()) { this.connectToMixer(member); } - } + }; - private initRecord() { + private initRecord = () => { if (!this.connection || !vp) { return; } @@ -273,10 +286,17 @@ export class VoicePanel extends QWidget { ); this.connection.play(recorder, { bitrate: 256, type: 'converted', highWaterMark: 0 }); - } + }; private async joinChannel(channel: VoiceChannel) { - const { infoLabel, statusLabel, createConnection, initPlayback, initRecord } = this; + const { + infoLabel, + statusLabel, + handleDisconnect, + createConnection, + initPlayback, + initRecord, + } = this; if (!vp) { VoicePanel.openVoiceNotSupportedDialog(channel); @@ -284,19 +304,30 @@ export class VoicePanel extends QWidget { return; } - this.handleDisconnectButton(); + handleDisconnect(); statusLabel.setText("Joining Voice Channel"); this.show(); this.channel = channel; infoLabel.setText(channel.name); try { - this.connection = await createConnection.call(this, channel); + this.connection = await createConnection(channel); + this.connection.on('disconnect', handleDisconnect); + this.connection.on('error', error); + this.connection.on('warn', warn); + this.connection.on('reconnecting', () => { + statusLabel.setText("Reconnecting..."); + }); + + this.connection.on('ready', () => { + statusLabel.setText("Voice Connected"); + }); + statusLabel.setText("Connecting Devices"); this.connection.play(new Silence(), { type: 'opus' }); // To receive audio we need to send something. - initPlayback.call(this); - initRecord.call(this); + initPlayback(); + initRecord(); this.handleConfigUpdate(app.config); statusLabel.setText("Voice Connected"); @@ -311,11 +342,11 @@ export class VoicePanel extends QWidget { return; } - this.connection.setSpeaking(value ? 1 : 4); + this.connection.setSpeaking(value ? 1 : 0); this.connection.emit('speaking', app.client.user, this.connection.speaking); } - private async createConnection(channel: VoiceChannel) { + private createConnection = async (channel: VoiceChannel) => { const connection = await channel.join(); connection.on('warn', warn.bind(this, '[djs]')); @@ -323,5 +354,5 @@ export class VoicePanel extends QWidget { connection.on('failed', error.bind(this, '[djs]')); return connection; - } + }; } diff --git a/src/utilities/voice/VoiceProvider.ts b/src/utilities/voice/VoiceProvider.ts index 4c34174..d0ca1c4 100644 --- a/src/utilities/voice/VoiceProvider.ts +++ b/src/utilities/voice/VoiceProvider.ts @@ -29,11 +29,12 @@ export abstract class VoiceProvider { const { args, device } = processArgs(this, options, PLAYBACK_OPTIONS); debug(`Starting the playback stream, args: ${args.join(' ')}`); - const { kill, stdin: stream, stderr } = spawn(FFplay, args); + const proc = spawn(FFplay, args); - stderr.on('data', (chunk) => debug(chunk.toString())); + proc.on('exit', (code) => debug(`Playback stream closed with code ${code}.`)); + proc.stderr.on('data', (chunk) => debug(chunk.toString())); - return { device, stream, end: () => kill('SIGKILL') }; + return { device, stream: proc.stdin, end: () => proc.kill() }; }; protected createFFmpegPlaybackStream = (options: VoiceOptions = {}) => { @@ -41,11 +42,12 @@ export abstract class VoiceProvider { const { args, device } = processArgs(this, options, PLAYBACK_OPTIONS); debug(`Starting the playback stream, args: ${args.join(' ')}`); - const { kill, stdin: stream, stderr } = spawn(FFmpeg, args); + const proc = spawn(FFmpeg, args); - stderr.on('data', (chunk) => debug(chunk.toString())); + proc.on('exit', (code) => debug(`Playback stream closed with code ${code}.`)); + proc.stderr.on('data', (chunk) => debug(chunk.toString())); - return { device, stream, end: () => kill('SIGKILL') }; + return { device, stream: proc.stdin, end: () => proc.kill() }; }; protected createFFmpegRecordStream = (options: VoiceOptions = {}) => { @@ -53,11 +55,12 @@ export abstract class VoiceProvider { const { args, device } = processArgs(this, options, RECORD_OPTIONS); debug(`Starting the record stream, args: ${args.join(' ')}`); - const { kill, stdout: stream, stderr } = spawn(FFmpeg, args); + const proc = spawn(FFmpeg, args); - stderr.on('data', (chunk) => debug(chunk.toString())); + proc.on('exit', (code) => debug(`Record stream closed with code ${code}.`)); + proc.stderr.on('data', (chunk) => debug(chunk.toString())); - return { device, stream, end: () => kill('SIGKILL') }; + return { device, stream: proc.stdout, end: () => proc.kill() }; }; /**