feat: new events (streamUpdate)

This commit is contained in:
Elysia
2024-10-29 15:49:10 +07:00
parent d410202709
commit f13e0e5a5a
6 changed files with 165 additions and 5 deletions

View File

@@ -686,6 +686,10 @@ class VoiceConnection extends EventEmitter {
this.streamConnection.disconnect();
break;
}
case 'STREAM_UPDATE': {
this.streamConnection.update(data);
break;
}
}
}
if (this.streamWatchConnection.has(StreamKey.userId) && this.channel.id == StreamKey.channelId) {
@@ -703,6 +707,11 @@ class VoiceConnection extends EventEmitter {
}
case 'STREAM_DELETE': {
streamConnection.disconnect();
streamConnection.receiver.packets.destroyAllStream();
break;
}
case 'STREAM_UPDATE': {
streamConnection.update(data);
break;
}
}
@@ -791,6 +800,10 @@ class VoiceConnection extends EventEmitter {
this.streamConnection.disconnect();
break;
}
case 'STREAM_UPDATE': {
this.streamConnection.update(data);
break;
}
}
}
if (this.streamWatchConnection.has(StreamKey.userId) && this.channel.id == StreamKey.channelId) {
@@ -808,6 +821,11 @@ class VoiceConnection extends EventEmitter {
}
case 'STREAM_DELETE': {
streamConnection.disconnect();
streamConnection.receiver.packets.destroyAllStream();
break;
}
case 'STREAM_UPDATE': {
streamConnection.update(data);
break;
}
}
@@ -844,6 +862,20 @@ class VoiceConnection extends EventEmitter {
}
});
}
/**
* @event VoiceConnection#streamUpdate
* @description Emitted when the StreamConnection or StreamConnectionReadonly
* state changes, providing the previous and current stream state.
*
* @param {StreamState} oldData - The previous state of the stream.
* @param {StreamState} newData - The current state of the stream.
*
* @typedef {Object} StreamState
* @property {boolean} isPaused - Indicates whether the stream is currently paused.
* @property {string|null} region - The region where the stream is hosted, or null if not specified.
* @property {Snowflake[]} viewerIds - An array of Snowflake IDs representing the viewers connected to the stream.
*/
}
/**
@@ -890,6 +922,18 @@ class StreamConnection extends VoiceConnection {
* @type {boolean}
*/
this.isPaused = false;
/**
* Viewer IDs
* @type {Snowflake[]}
*/
this.viewerIds = [];
/**
* Voice region name
* @type {string | null}
*/
this.region = null;
}
createStreamConnection() {
@@ -951,6 +995,19 @@ class StreamConnection extends VoiceConnection {
*/
sendScreenshareState(isPaused = false) {
if (isPaused == this.isPaused) return;
this.emit(
'streamUpdate',
{
isPaused: this.isPaused,
region: this.region,
viewerIds: this.viewerIds,
},
{
isPaused,
region: this.region,
viewerIds: this.viewerIds,
},
);
this.isPaused = isPaused;
this.channel.client.ws.broadcast({
op: Opcodes.STREAM_SET_PAUSED,
@@ -976,6 +1033,24 @@ class StreamConnection extends VoiceConnection {
});
}
update(data) {
this.emit(
'streamUpdate',
{
isPaused: this.isPaused,
region: this.region,
viewerIds: this.viewerIds.slice(),
},
{
isPaused: data.paused,
region: data.region,
viewerIds: data.viewer_ids,
},
);
this.viewerIds = data.viewer_ids;
this.region = data.region;
}
/**
* Current stream key
* @type {string}
@@ -1032,6 +1107,24 @@ class StreamConnectionReadonly extends VoiceConnection {
* @type {string | null}
*/
this.serverId = null;
/**
* Stream state
* @type {boolean}
*/
this.isPaused = false;
/**
* Viewer IDs
* @type {Snowflake[]}
*/
this.viewerIds = [];
/**
* Voice region name
* @type {string | null}
*/
this.region = null;
}
createStreamConnection() {
@@ -1097,6 +1190,25 @@ class StreamConnectionReadonly extends VoiceConnection {
});
}
update(data) {
this.emit(
'streamUpdate',
{
isPaused: this.isPaused,
region: this.region,
viewerIds: this.viewerIds.slice(),
},
{
isPaused: data.paused,
region: data.region,
viewerIds: data.viewer_ids,
},
);
this.isPaused = data.paused;
this.viewerIds = data.viewer_ids;
this.region = data.region;
}
/**
* Current stream key
* @type {string}

View File

@@ -15,9 +15,17 @@ const { StreamOutput } = require('../util/Socket');
* @extends {EventEmitter}
*/
class FFmpegHandler extends EventEmitter {
constructor(codec, portUdp, output, isEnableAudio) {
constructor(receiver, userId, codec, portUdp, output, isEnableAudio) {
super();
Object.defineProperty(this, 'receiver', { value: receiver });
/**
* The user ID
* @type {Snowflake}
*/
this.userId = userId;
/**
* If the audio is enabled
* @type {boolean}
@@ -130,9 +138,21 @@ class FFmpegHandler extends EventEmitter {
let process = list.find(o => o.pid === ffmpegPid || o.ppid === ffmpegPid || o.cmd.includes(args));
if (process) {
kill(process.pid);
this.receiver.videoStreams.delete(this.userId);
this.emit('closed');
}
});
}
/**
* Emitted when the FFmpegHandler becomes ready to start working.
* @event FFmpegHandler#ready
*/
/**
* Emitted when the FFmpegHandler is closed.
* @event FFmpegHandler#closed
*/
}
module.exports = FFmpegHandler;

View File

@@ -58,7 +58,7 @@ class PacketHandler extends EventEmitter {
makeVideoStream(user, portUdp, codec, output, isEnableAudio = false) {
if (this.videoStreams.has(user)) return this.videoStreams.get(user);
const stream = new FFmpegHandler(codec, portUdp, output, isEnableAudio);
const stream = new FFmpegHandler(this, user, codec, portUdp, output, isEnableAudio);
stream.on('ready', () => {
this.videoStreams.set(user, stream);
});
@@ -231,6 +231,18 @@ class PacketHandler extends EventEmitter {
this.videoReceiver(buffer);
this.audioReceiverForStream(buffer);
}
// When udp connection is closed (STREAM_DELETE), destroy all streams (Memory leak)
destroyAllStream() {
for (const stream of this.streams.values()) {
stream.stream.destroy();
}
this.streams.clear();
for (const stream of this.videoStreams.values()) {
stream.destroy();
}
this.videoStreams.clear();
}
}
module.exports = PacketHandler;

View File

@@ -42,7 +42,7 @@ const payloadTypes = [
payload_type: 103,
rtx_payload_type: 104,
encode: false,
decode: false,
decode: false, // Working but very glitchy
},
{
name: 'H264',