djs-selfbot-v13 3.7.32 → 3.7.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -1
- package/src/client/Client.js +68 -1
- package/src/client/voice/ClientVoiceManager.js +95 -17
- package/src/client/voice/StreamSession.js +193 -0
- package/src/client/voice/VoiceConnection.js +266 -19
- package/src/client/voice/WebRtcStreamSession.js +191 -0
- package/src/client/voice/dispatcher/AnnexBDispatcher.js +64 -11
- package/src/client/voice/dispatcher/BaseDispatcher.js +13 -9
- package/src/client/voice/dispatcher/VideoDispatcher.js +33 -0
- package/src/client/voice/networking/DAVESession.js +234 -0
- package/src/client/voice/networking/VoiceWebSocket.js +240 -24
- package/src/client/voice/player/MediaPlayer.js +3 -1
- package/src/client/voice/player/processing/AnnexBBitstreamReaderWriter.js +137 -0
- package/src/client/voice/player/processing/SPSVUIRewriter.js +203 -0
- package/src/client/voice/receiver/PacketHandler.js +11 -4
- package/src/errors/Messages.js +8 -1
- package/src/util/Constants.js +12 -0
- package/typings/index.d.ts +36 -0
|
@@ -20,7 +20,8 @@ Please use the @dank074/discord-video-stream library for the best support.
|
|
|
20
20
|
const { Buffer } = require('buffer');
|
|
21
21
|
const VideoDispatcher = require('./VideoDispatcher');
|
|
22
22
|
const Util = require('../../../util/Util');
|
|
23
|
-
const { H264Helpers, H265Helpers } = require('../player/processing/AnnexBNalSplitter');
|
|
23
|
+
const { H264Helpers, H265Helpers, H264NalUnitTypes, H265NalUnitTypes } = require('../player/processing/AnnexBNalSplitter');
|
|
24
|
+
const { rewriteSPSVUI } = require('../player/processing/SPSVUIRewriter');
|
|
24
25
|
|
|
25
26
|
class AnnexBDispatcher extends VideoDispatcher {
|
|
26
27
|
constructor(player, highWaterMark = 12, streams, fps, nalFunctions, payloadType) {
|
|
@@ -28,8 +29,65 @@ class AnnexBDispatcher extends VideoDispatcher {
|
|
|
28
29
|
this._nalFunctions = nalFunctions;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
_rewriteH264SPS(accessUnit) {
|
|
33
|
+
const parts = [];
|
|
34
|
+
let offset = 0;
|
|
35
|
+
while (offset < accessUnit.length) {
|
|
36
|
+
const naluSize = accessUnit.readUInt32BE(offset);
|
|
37
|
+
offset += 4;
|
|
38
|
+
let nalu = accessUnit.subarray(offset, offset + naluSize);
|
|
39
|
+
if (this._nalFunctions === H264Helpers && H264Helpers.getUnitType(nalu) === 7) {
|
|
40
|
+
try {
|
|
41
|
+
nalu = rewriteSPSVUI(nalu);
|
|
42
|
+
} catch {
|
|
43
|
+
// keep original SPS on rewrite failure
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const header = Buffer.alloc(4);
|
|
47
|
+
header.writeUInt32BE(nalu.length);
|
|
48
|
+
parts.push(header, nalu);
|
|
49
|
+
offset += naluSize;
|
|
50
|
+
}
|
|
51
|
+
return Buffer.concat(parts);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_accessUnitIsKeyframe(accessUnit) {
|
|
55
|
+
let offset = 0;
|
|
56
|
+
while (offset < accessUnit.length) {
|
|
57
|
+
const naluSize = accessUnit.readUInt32BE(offset);
|
|
58
|
+
offset += 4;
|
|
59
|
+
const nalu = accessUnit.subarray(offset, offset + naluSize);
|
|
60
|
+
const unitType = this._nalFunctions.getUnitType(nalu);
|
|
61
|
+
if (this._nalFunctions === H264Helpers) {
|
|
62
|
+
if (unitType === H264NalUnitTypes.CodedSliceIdr) return true;
|
|
63
|
+
} else if (
|
|
64
|
+
unitType === H265NalUnitTypes.IDR_W_RADL ||
|
|
65
|
+
unitType === H265NalUnitTypes.IDR_N_LP ||
|
|
66
|
+
unitType === H265NalUnitTypes.CRA_NUT
|
|
67
|
+
) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
offset += naluSize;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_sendVideoPacket(naluPayload, isLastPacket, isKeyframe) {
|
|
76
|
+
const includeContentType = isKeyframe && isLastPacket && this.player.isScreenSharing;
|
|
77
|
+
this._playChunk(
|
|
78
|
+
Buffer.concat([this.createPayloadExtension(includeContentType), naluPayload]),
|
|
79
|
+
isLastPacket,
|
|
80
|
+
{ isKeyframe },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
31
84
|
_codecCallback(frame) {
|
|
32
|
-
let accessUnit = frame;
|
|
85
|
+
let accessUnit = this._nalFunctions === H264Helpers ? this._rewriteH264SPS(frame) : frame;
|
|
86
|
+
const isKeyframe = this._accessUnitIsKeyframe(accessUnit);
|
|
87
|
+
const dave = this.player.voiceConnection.dave;
|
|
88
|
+
if (dave?.session?.ready) {
|
|
89
|
+
accessUnit = dave.encryptVideo(accessUnit, this.player.voiceConnection.videoCodec);
|
|
90
|
+
}
|
|
33
91
|
let offset = 0;
|
|
34
92
|
|
|
35
93
|
// Extract NALUs from the access unit
|
|
@@ -39,24 +97,19 @@ class AnnexBDispatcher extends VideoDispatcher {
|
|
|
39
97
|
const nalu = accessUnit.subarray(offset, offset + naluSize);
|
|
40
98
|
const isLastNal = offset + naluSize >= accessUnit.length;
|
|
41
99
|
if (nalu.length <= this.mtu) {
|
|
42
|
-
|
|
43
|
-
this._playChunk(Buffer.concat([this.createPayloadExtension(), nalu]), isLastNal);
|
|
100
|
+
this._sendVideoPacket(nalu, isLastNal, isKeyframe);
|
|
44
101
|
} else {
|
|
45
102
|
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
|
|
46
103
|
const dataFragments = this.partitionMtu(naluData);
|
|
47
|
-
// Send as Fragmentation Unit A (FU-A):
|
|
48
104
|
for (let fragmentIndex = 0; fragmentIndex < dataFragments.length; fragmentIndex++) {
|
|
49
105
|
const data = dataFragments[fragmentIndex];
|
|
50
106
|
const isFirstPacket = fragmentIndex === 0;
|
|
51
107
|
const isFinalPacket = fragmentIndex === dataFragments.length - 1;
|
|
52
108
|
|
|
53
|
-
this.
|
|
54
|
-
Buffer.concat([
|
|
55
|
-
this.createPayloadExtension(),
|
|
56
|
-
this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader),
|
|
57
|
-
data,
|
|
58
|
-
]),
|
|
109
|
+
this._sendVideoPacket(
|
|
110
|
+
Buffer.concat([this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader), data]),
|
|
59
111
|
isLastNal && isFinalPacket,
|
|
112
|
+
isKeyframe,
|
|
60
113
|
);
|
|
61
114
|
}
|
|
62
115
|
}
|
|
@@ -5,6 +5,7 @@ const crypto = require('node:crypto');
|
|
|
5
5
|
const { Writable } = require('node:stream');
|
|
6
6
|
const { setTimeout } = require('node:timers');
|
|
7
7
|
const secretbox = require('../util/Secretbox');
|
|
8
|
+
const Speaking = require('../../../util/Speaking');
|
|
8
9
|
|
|
9
10
|
const MAX_UINT_16 = 2 ** 16 - 1;
|
|
10
11
|
const MAX_UINT_32 = 2 ** 32 - 1;
|
|
@@ -232,14 +233,16 @@ class BaseDispatcher extends Writable {
|
|
|
232
233
|
callback();
|
|
233
234
|
}
|
|
234
235
|
|
|
235
|
-
_playChunk(chunk, isLastPacket = false) {
|
|
236
|
+
_playChunk(chunk, isLastPacket = false, packetOpts = {}) {
|
|
236
237
|
if (
|
|
237
238
|
(this.player.dispatcher !== this && this.player.videoDispatcher !== this) ||
|
|
238
239
|
!this.player.voiceConnection.authentication.secret_key
|
|
239
240
|
) {
|
|
240
241
|
return;
|
|
241
242
|
}
|
|
243
|
+
this._packetOpts = packetOpts;
|
|
242
244
|
const packet = this._createPacket(chunk, isLastPacket);
|
|
245
|
+
this._packetOpts = null;
|
|
243
246
|
if (packet) this._sendPacket(packet);
|
|
244
247
|
}
|
|
245
248
|
|
|
@@ -375,7 +378,9 @@ class BaseDispatcher extends Writable {
|
|
|
375
378
|
rtpHeader[0] = 0x80; // Version + Flags (1 byte)
|
|
376
379
|
rtpHeader[1] = this.payloadType; // Payload Type (1 byte)
|
|
377
380
|
if (this.extensionEnabled) {
|
|
378
|
-
|
|
381
|
+
const extensionWordCount =
|
|
382
|
+
typeof this.getExtensionWordCount === 'function' ? this.getExtensionWordCount(isLastPacket) : 1;
|
|
383
|
+
rtpHeader = Buffer.concat([rtpHeader, this.createHeaderExtension(extensionWordCount)]);
|
|
379
384
|
rtpHeader[0] |= 1 << 4; // 0x90
|
|
380
385
|
}
|
|
381
386
|
if (this.getTypeDispatcher() === 'video' && isLastPacket) {
|
|
@@ -384,12 +389,11 @@ class BaseDispatcher extends Writable {
|
|
|
384
389
|
|
|
385
390
|
rtpHeader.writeUIntBE(this.getNewSequence(), 2, 2);
|
|
386
391
|
rtpHeader.writeUIntBE(this.timestamp, 4, 4);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
);
|
|
392
|
-
return Buffer.concat([rtpHeader, ...this._encrypt(buffer, rtpHeader)]);
|
|
392
|
+
const { audioSsrc, videoSsrc } = this.player.voiceConnection.getStreamSsrcs();
|
|
393
|
+
rtpHeader.writeUIntBE(this.getTypeDispatcher() === 'video' ? videoSsrc : audioSsrc, 8, 4);
|
|
394
|
+
const dave = this.player.voiceConnection.dave;
|
|
395
|
+
const payload = this.getTypeDispatcher() === 'audio' && dave?.session?.ready ? dave.encrypt(buffer) : buffer;
|
|
396
|
+
return Buffer.concat([rtpHeader, ...this._encrypt(payload, rtpHeader)]);
|
|
393
397
|
}
|
|
394
398
|
|
|
395
399
|
_sendPacket(packet) {
|
|
@@ -399,7 +403,7 @@ class BaseDispatcher extends Writable {
|
|
|
399
403
|
* @param {string} info The debug info
|
|
400
404
|
*/
|
|
401
405
|
if (this.getTypeDispatcher() === 'audio') {
|
|
402
|
-
this._setSpeaking(this.player.isScreenSharing ?
|
|
406
|
+
this._setSpeaking(this.player.isScreenSharing ? Speaking.FLAGS.SOUNDSHARE : Speaking.FLAGS.SPEAKING);
|
|
403
407
|
} else if (this.getTypeDispatcher() === 'video') {
|
|
404
408
|
this._setVideoStatus(true);
|
|
405
409
|
this._setStreamStatus(false);
|
|
@@ -60,6 +60,39 @@ class VideoDispatcher extends BaseDispatcher {
|
|
|
60
60
|
this.fps = value;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
shouldIncludeContentType(isLastPacket) {
|
|
64
|
+
return Boolean(
|
|
65
|
+
this.player.isScreenSharing && this._packetOpts?.isKeyframe && isLastPacket,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getExtensionWordCount(isLastPacket) {
|
|
70
|
+
return this.shouldIncludeContentType(isLastPacket) ? 2 : 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
createHeaderExtension(extensionWordCount = 1) {
|
|
74
|
+
const profile = Buffer.alloc(4);
|
|
75
|
+
profile[0] = 0xbe;
|
|
76
|
+
profile[1] = 0xde;
|
|
77
|
+
profile.writeInt16BE(extensionWordCount, 2);
|
|
78
|
+
return profile;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
createPayloadExtension(includeContentType = false) {
|
|
82
|
+
if (includeContentType) {
|
|
83
|
+
const data = Buffer.alloc(8);
|
|
84
|
+
data[0] = (5 << 4) | 1;
|
|
85
|
+
data.writeUIntBE(10, 1, 2);
|
|
86
|
+
data[3] = 6 << 4;
|
|
87
|
+
data[4] = 0x01;
|
|
88
|
+
return data;
|
|
89
|
+
}
|
|
90
|
+
const data = Buffer.alloc(4);
|
|
91
|
+
data[0] = (5 << 4) | 1;
|
|
92
|
+
data.writeUIntBE(10, 1, 2);
|
|
93
|
+
return data;
|
|
94
|
+
}
|
|
95
|
+
|
|
63
96
|
_codecCallback() {
|
|
64
97
|
throw new Error('The _codecCallback method must be implemented');
|
|
65
98
|
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require('events');
|
|
4
|
+
const Davey = require('@snazzah/davey');
|
|
5
|
+
|
|
6
|
+
const TRANSITION_EXPIRY = 10;
|
|
7
|
+
const TRANSITION_EXPIRY_PENDING_DOWNGRADE = 24;
|
|
8
|
+
const DEFAULT_DECRYPTION_FAILURE_TOLERANCE = 36;
|
|
9
|
+
|
|
10
|
+
const SILENCE_FRAME = Buffer.from([0xf8, 0xff, 0xfe]);
|
|
11
|
+
|
|
12
|
+
function getMaxProtocolVersion() {
|
|
13
|
+
return Davey.DAVE_PROTOCOL_VERSION ?? 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class DAVESession extends EventEmitter {
|
|
17
|
+
constructor(protocolVersion, userId, channelId, options = {}) {
|
|
18
|
+
super();
|
|
19
|
+
this.protocolVersion = protocolVersion;
|
|
20
|
+
this.userId = userId;
|
|
21
|
+
this.channelId = channelId;
|
|
22
|
+
this.failureTolerance = options.decryptionFailureTolerance ?? DEFAULT_DECRYPTION_FAILURE_TOLERANCE;
|
|
23
|
+
this.pendingTransitions = new Map();
|
|
24
|
+
this.downgraded = false;
|
|
25
|
+
this.consecutiveFailures = 0;
|
|
26
|
+
this.reinitializing = false;
|
|
27
|
+
this.lastTransitionId = undefined;
|
|
28
|
+
this.session = undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get voicePrivacyCode() {
|
|
32
|
+
if (this.protocolVersion === 0 || !this.session?.voicePrivacyCode) return null;
|
|
33
|
+
return this.session.voicePrivacyCode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getVerificationCode(userId) {
|
|
37
|
+
if (!this.session) throw new Error('Session not available');
|
|
38
|
+
return this.session.getVerificationCode(userId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
reinit() {
|
|
42
|
+
if (this.protocolVersion > 0) {
|
|
43
|
+
if (this.session) {
|
|
44
|
+
this.session.reinit(this.protocolVersion, this.userId, this.channelId);
|
|
45
|
+
this.emit('debug', `Session reinitialized for protocol version ${this.protocolVersion}`);
|
|
46
|
+
} else {
|
|
47
|
+
this.session = new Davey.DAVESession(this.protocolVersion, this.userId, this.channelId);
|
|
48
|
+
this.emit('debug', `Session initialized for protocol version ${this.protocolVersion}`);
|
|
49
|
+
}
|
|
50
|
+
this.emit('keyPackage', this.session.getSerializedKeyPackage());
|
|
51
|
+
} else if (this.session) {
|
|
52
|
+
this.session.reset();
|
|
53
|
+
this.session.setPassthroughMode(true, TRANSITION_EXPIRY);
|
|
54
|
+
this.emit('debug', 'Session reset');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setExternalSender(externalSender) {
|
|
59
|
+
if (!this.session) throw new Error('No session available');
|
|
60
|
+
try {
|
|
61
|
+
this.session.setExternalSender(externalSender);
|
|
62
|
+
this.emit('debug', 'Set MLS external sender');
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (String(error).includes('AlreadyInGroup')) {
|
|
65
|
+
this.emit('debug', 'MLS external sender already set, skipping');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
prepareTransition(data) {
|
|
73
|
+
this.emit('debug', `Preparing for transition (${data.transition_id}, v${data.protocol_version})`);
|
|
74
|
+
this.pendingTransitions.set(data.transition_id, data.protocol_version);
|
|
75
|
+
|
|
76
|
+
if (data.transition_id === 0) {
|
|
77
|
+
this.executeTransition(data.transition_id);
|
|
78
|
+
} else {
|
|
79
|
+
if (data.protocol_version === 0) this.session?.setPassthroughMode(true, TRANSITION_EXPIRY_PENDING_DOWNGRADE);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
executeTransition(transitionId) {
|
|
87
|
+
this.emit('debug', `Executing transition (${transitionId})`);
|
|
88
|
+
if (!this.pendingTransitions.has(transitionId)) {
|
|
89
|
+
this.emit('debug', `Received execute transition, but we don't have a pending transition for ${transitionId}`);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const oldVersion = this.protocolVersion;
|
|
94
|
+
this.protocolVersion = this.pendingTransitions.get(transitionId);
|
|
95
|
+
|
|
96
|
+
if (oldVersion !== this.protocolVersion && this.protocolVersion === 0) {
|
|
97
|
+
this.downgraded = true;
|
|
98
|
+
this.emit('debug', 'Session downgraded');
|
|
99
|
+
} else if (transitionId > 0 && this.downgraded) {
|
|
100
|
+
this.downgraded = false;
|
|
101
|
+
this.session?.setPassthroughMode(true, TRANSITION_EXPIRY);
|
|
102
|
+
this.emit('debug', 'Session upgraded');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.reinitializing = false;
|
|
106
|
+
this.lastTransitionId = transitionId;
|
|
107
|
+
this.emit('debug', `Transition executed (v${oldVersion} -> v${this.protocolVersion}, id: ${transitionId})`);
|
|
108
|
+
this.pendingTransitions.delete(transitionId);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
prepareEpoch(data) {
|
|
113
|
+
this.emit('debug', `Preparing for epoch (${data.epoch})`);
|
|
114
|
+
if (data.epoch === 1) {
|
|
115
|
+
this.protocolVersion = data.protocol_version;
|
|
116
|
+
this.reinit();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
recoverFromInvalidTransition(transitionId) {
|
|
121
|
+
if (this.reinitializing) return;
|
|
122
|
+
this.emit('debug', `Invalidating transition ${transitionId}`);
|
|
123
|
+
this.reinitializing = true;
|
|
124
|
+
this.consecutiveFailures = 0;
|
|
125
|
+
this.emit('invalidateTransition', transitionId);
|
|
126
|
+
this.reinit();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
processProposals(payload, connectedClients) {
|
|
130
|
+
if (!this.session) throw new Error('No session available');
|
|
131
|
+
try {
|
|
132
|
+
const optype = payload.readUInt8(0);
|
|
133
|
+
const { commit, welcome } = this.session.processProposals(
|
|
134
|
+
optype,
|
|
135
|
+
payload.subarray(1),
|
|
136
|
+
Array.from(connectedClients),
|
|
137
|
+
);
|
|
138
|
+
this.emit('debug', 'MLS proposals processed');
|
|
139
|
+
if (!commit) return;
|
|
140
|
+
return welcome ? Buffer.concat([commit, welcome]) : commit;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
this.emit('debug', `MLS proposals errored: ${error}`);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
processCommit(payload) {
|
|
148
|
+
if (!this.session) throw new Error('No session available');
|
|
149
|
+
const transitionId = payload.readUInt16BE(0);
|
|
150
|
+
try {
|
|
151
|
+
this.session.processCommit(payload.subarray(2));
|
|
152
|
+
if (transitionId === 0) {
|
|
153
|
+
this.reinitializing = false;
|
|
154
|
+
this.lastTransitionId = transitionId;
|
|
155
|
+
} else {
|
|
156
|
+
this.pendingTransitions.set(transitionId, this.protocolVersion);
|
|
157
|
+
}
|
|
158
|
+
this.emit('debug', `MLS commit processed (transition id: ${transitionId})`);
|
|
159
|
+
return { transitionId, success: true };
|
|
160
|
+
} catch (error) {
|
|
161
|
+
this.emit('debug', `MLS commit errored from transition ${transitionId}: ${error}`);
|
|
162
|
+
this.recoverFromInvalidTransition(transitionId);
|
|
163
|
+
return { transitionId, success: false };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
processWelcome(payload) {
|
|
168
|
+
if (!this.session) throw new Error('No session available');
|
|
169
|
+
const transitionId = payload.readUInt16BE(0);
|
|
170
|
+
try {
|
|
171
|
+
this.session.processWelcome(payload.subarray(2));
|
|
172
|
+
if (transitionId === 0) {
|
|
173
|
+
this.reinitializing = false;
|
|
174
|
+
this.lastTransitionId = transitionId;
|
|
175
|
+
} else {
|
|
176
|
+
this.pendingTransitions.set(transitionId, this.protocolVersion);
|
|
177
|
+
}
|
|
178
|
+
this.emit('debug', `MLS welcome processed (transition id: ${transitionId})`);
|
|
179
|
+
return { transitionId, success: true };
|
|
180
|
+
} catch (error) {
|
|
181
|
+
this.emit('debug', `MLS welcome errored from transition ${transitionId}: ${error}`);
|
|
182
|
+
this.recoverFromInvalidTransition(transitionId);
|
|
183
|
+
return { transitionId, success: false };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
encrypt(packet) {
|
|
188
|
+
if (this.protocolVersion === 0 || !this.session?.ready || packet.equals(SILENCE_FRAME)) return packet;
|
|
189
|
+
return this.session.encryptOpus(packet);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
encryptVideo(packet, codec = 'H264') {
|
|
193
|
+
if (this.protocolVersion === 0 || !this.session?.ready) return packet;
|
|
194
|
+
const codecMap = {
|
|
195
|
+
H264: Davey.Codec.H264,
|
|
196
|
+
VP8: Davey.Codec.VP8,
|
|
197
|
+
H265: Davey.Codec.H265,
|
|
198
|
+
AV1: Davey.Codec.AV1,
|
|
199
|
+
};
|
|
200
|
+
return this.session.encrypt(Davey.MediaType.VIDEO, codecMap[codec] ?? Davey.Codec.UNKNOWN, packet);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
decrypt(packet, userId) {
|
|
204
|
+
const canDecrypt = this.session?.ready && (this.protocolVersion !== 0 || this.session?.canPassthrough(userId));
|
|
205
|
+
if (packet.equals(SILENCE_FRAME) || !canDecrypt || !this.session) return packet;
|
|
206
|
+
try {
|
|
207
|
+
const buffer = this.session.decrypt(userId, Davey.MediaType.AUDIO, packet);
|
|
208
|
+
this.consecutiveFailures = 0;
|
|
209
|
+
return buffer;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (!this.reinitializing && this.pendingTransitions.size === 0) {
|
|
212
|
+
this.consecutiveFailures++;
|
|
213
|
+
this.emit('debug', `Failed to decrypt a packet (${this.consecutiveFailures} consecutive fails)`);
|
|
214
|
+
if (this.consecutiveFailures > this.failureTolerance) {
|
|
215
|
+
if (this.lastTransitionId) this.recoverFromInvalidTransition(this.lastTransitionId);
|
|
216
|
+
else throw error;
|
|
217
|
+
}
|
|
218
|
+
} else if (this.reinitializing) {
|
|
219
|
+
this.emit('debug', 'Failed to decrypt a packet (reinitializing session)');
|
|
220
|
+
} else if (this.pendingTransitions.size > 0) {
|
|
221
|
+
this.emit('debug', `Failed to decrypt a packet (${this.pendingTransitions.size} pending transition[s])`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
destroy() {
|
|
228
|
+
try {
|
|
229
|
+
this.session?.reset();
|
|
230
|
+
} catch {}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
module.exports = { DAVESession, getMaxProtocolVersion };
|