feat: removing old voice encryption modes
- VP8 working (ok) - H264 working (bad) by #101 (dank074/Discord-video-stream)
This commit is contained in:
@@ -22,15 +22,7 @@ class SingleSilence extends Silence {
|
||||
}
|
||||
}
|
||||
|
||||
const SUPPORTED_MODES = [
|
||||
// Avalible (but only voice working)
|
||||
// 'aead_aes256_gcm_rtpsize',
|
||||
// 'aead_xchacha20_poly1305_rtpsize',
|
||||
// Deprecated
|
||||
'xsalsa20_poly1305_lite',
|
||||
'xsalsa20_poly1305_suffix',
|
||||
'xsalsa20_poly1305',
|
||||
];
|
||||
const SUPPORTED_MODES = ['aead_aes256_gcm_rtpsize', 'aead_xchacha20_poly1305_rtpsize'];
|
||||
const SUPPORTED_CODECS = ['VP8', 'H264'];
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,13 +38,12 @@ class AnnexBDispatcher extends VideoDispatcher {
|
||||
const nalu = accessUnit.subarray(offset, offset + naluSize);
|
||||
const isLastNal = offset + naluSize >= accessUnit.length;
|
||||
if (nalu.length <= this.mtu) {
|
||||
// If NALU size is within MTU, send it directly
|
||||
this._playChunk(Buffer.concat([this.createHeaderExtension(), nalu]), isLastNal);
|
||||
// Send as Single NAL Unit Packet.
|
||||
this._playChunk(Buffer.concat([this.createPayloadExtension(), nalu]), isLastNal);
|
||||
} else {
|
||||
// If NALU size exceeds MTU, fragment it
|
||||
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
|
||||
const dataFragments = this.partitionVideoData(naluData);
|
||||
|
||||
// Send as Fragmentation Unit A (FU-A):
|
||||
for (let fragmentIndex = 0; fragmentIndex < dataFragments.length; fragmentIndex++) {
|
||||
const data = dataFragments[fragmentIndex];
|
||||
const isFirstPacket = fragmentIndex === 0;
|
||||
@@ -52,7 +51,7 @@ class AnnexBDispatcher extends VideoDispatcher {
|
||||
|
||||
this._playChunk(
|
||||
Buffer.concat([
|
||||
this.createHeaderExtension(),
|
||||
this.createPayloadExtension(),
|
||||
this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader),
|
||||
data,
|
||||
]),
|
||||
|
||||
@@ -11,6 +11,8 @@ const secretbox = require('../util/Secretbox');
|
||||
const CHANNELS = 2;
|
||||
const MAX_NONCE_SIZE = 2 ** 32 - 1;
|
||||
|
||||
const extensions = [{ id: 5, len: 2, val: 0 }];
|
||||
|
||||
/**
|
||||
* @external WritableStream
|
||||
* @see {@link https://nodejs.org/api/stream.html#stream_class_stream_writable}
|
||||
@@ -257,15 +259,11 @@ class BaseDispatcher extends Writable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single extension of type playout-delay
|
||||
* Discord seems to send this extension on every video packet
|
||||
* @see https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/playout-delay
|
||||
* @returns {Buffer} playout-delay extension
|
||||
* Buffer <be de 00 01 51 00 00 00>
|
||||
* @private
|
||||
* Creates a one-byte extension header
|
||||
* https://www.rfc-editor.org/rfc/rfc5285#section-4.2
|
||||
* @returns {Buffer} extension header
|
||||
*/
|
||||
createHeaderExtension() {
|
||||
const extensions = [{ id: 5, len: 2, val: 0 }];
|
||||
/**
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
@@ -276,6 +274,17 @@ class BaseDispatcher extends Writable {
|
||||
profile[0] = 0xbe;
|
||||
profile[1] = 0xde;
|
||||
profile.writeInt16BE(extensions.length, 2); // Extension count
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single extension of type playout-delay
|
||||
* Discord seems to send this extension on every video packet
|
||||
* @see https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/playout-delay
|
||||
* @returns {Buffer} playout-delay extension
|
||||
*/
|
||||
createPayloadExtension() {
|
||||
const extensionsData = [];
|
||||
for (let ext of extensions) {
|
||||
/**
|
||||
@@ -301,7 +310,7 @@ class BaseDispatcher extends Writable {
|
||||
data.writeUIntBE(ext.val, 1, 2); // Not quite but its 0 anyway
|
||||
extensionsData.push(data);
|
||||
}
|
||||
return Buffer.concat([profile, ...extensionsData]);
|
||||
return Buffer.concat(extensionsData);
|
||||
}
|
||||
|
||||
_encrypt(buffer, additionalData) {
|
||||
@@ -338,16 +347,6 @@ class BaseDispatcher extends Writable {
|
||||
|
||||
return [encrypted, noncePadding];
|
||||
}
|
||||
case 'xsalsa20_poly1305_lite': {
|
||||
return [secretbox.methods.close(buffer, this._nonceBuffer, secret_key), noncePadding];
|
||||
}
|
||||
case 'xsalsa20_poly1305_suffix': {
|
||||
const random = secretbox.methods.random(24);
|
||||
return [secretbox.methods.close(buffer, random, secret_key), random];
|
||||
}
|
||||
case 'xsalsa20_poly1305': {
|
||||
return [secretbox.methods.close(buffer, Buffer.concat([additionalData, Buffer.alloc(12)]), secret_key)];
|
||||
}
|
||||
default: {
|
||||
// This should never happen. Our encryption mode is chosen from a list given to us by the gateway and checked with the ones we support.
|
||||
throw new RangeError(`Unsupported encryption method: ${mode}`);
|
||||
@@ -357,7 +356,9 @@ class BaseDispatcher extends Writable {
|
||||
|
||||
_createPacket(buffer, isLastPacket = false) {
|
||||
// Header
|
||||
const rtpHeader = Buffer.alloc(12); // RTP_HEADER_SIZE
|
||||
const rtpHeader = this.extensionEnabled
|
||||
? Buffer.concat([Buffer.alloc(12), this.createHeaderExtension()])
|
||||
: Buffer.alloc(12); // RTP_HEADER_SIZE
|
||||
rtpHeader[0] = this.extensionEnabled ? 0x90 : 0x80; // Version + Flags (1 byte)
|
||||
rtpHeader[1] = this.payloadType; // Payload Type (1 byte)
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ class VP8Dispatcher extends VideoDispatcher {
|
||||
}
|
||||
|
||||
makeChunk(buffer, isFirstFrame) {
|
||||
// Make frame
|
||||
const headerExtensionBuf = this.createHeaderExtension();
|
||||
// Vp8 payload descriptor
|
||||
const payloadDescriptorBuf = Buffer.alloc(2);
|
||||
payloadDescriptorBuf[0] = isFirstFrame ? 0x90 : 0x80; // Mark S bit, indicates start of frame: payloadDescriptorBuf[0] |= 0b00010000;
|
||||
@@ -36,13 +34,16 @@ class VP8Dispatcher extends VideoDispatcher {
|
||||
const pictureIdBuf = Buffer.alloc(2);
|
||||
pictureIdBuf.writeUintBE(this.count, 0, 2);
|
||||
pictureIdBuf[0] |= 0x80;
|
||||
return Buffer.concat([headerExtensionBuf, payloadDescriptorBuf, pictureIdBuf, buffer]);
|
||||
return Buffer.concat([payloadDescriptorBuf, pictureIdBuf, buffer]);
|
||||
}
|
||||
|
||||
codecCallback(chunk) {
|
||||
const chunkSplit = this.partitionVideoData(chunk);
|
||||
for (let i = 0; i < chunkSplit.length; i++) {
|
||||
this._playChunk(this.makeChunk(chunkSplit[i], i == 0), i + 1 === chunkSplit.length);
|
||||
this._playChunk(
|
||||
Buffer.concat([this.createPayloadExtension(), this.makeChunk(chunkSplit[i], i == 0)]),
|
||||
i + 1 === chunkSplit.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,39 +2,36 @@
|
||||
|
||||
const libs = {
|
||||
sodium: sodium => ({
|
||||
/** @deprecated */
|
||||
open: sodium.api.crypto_secretbox_open_easy,
|
||||
/** @deprecated */
|
||||
close: sodium.api.crypto_secretbox_easy,
|
||||
random: n => sodium.randombytes_buf(n),
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt: (plaintext, additionalData, nonce, key) =>
|
||||
sodium.api.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key),
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt: (plaintext, additionalData, nonce, key) =>
|
||||
sodium.api.crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, null, nonce, key),
|
||||
}),
|
||||
'libsodium-wrappers': sodium => ({
|
||||
/** @deprecated */
|
||||
open: sodium.crypto_secretbox_open_easy,
|
||||
/** @deprecated */
|
||||
close: sodium.crypto_secretbox_easy,
|
||||
random: n => sodium.randombytes_buf(n),
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt: (plaintext, additionalData, nonce, key) =>
|
||||
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key),
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt: (plaintext, additionalData, nonce, key) =>
|
||||
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, plaintext, additionalData, nonce, key),
|
||||
}),
|
||||
'@stablelib/xchacha20poly1305': stablelib => ({
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, additionalData, nonce, key) {
|
||||
const crypto = new stablelib.XChaCha20Poly1305(key);
|
||||
return crypto.seal(nonce, cipherText, additionalData);
|
||||
},
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, nonce, key) {
|
||||
const crypto = new stablelib.XChaCha20Poly1305(key);
|
||||
return crypto.open(nonce, plaintext, additionalData);
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
function NoLib() {
|
||||
throw new Error(
|
||||
'Cannot play audio as no valid encryption package is installed.\n- Install sodium or libsodium-wrappers.',
|
||||
'Cannot play audio as no valid encryption package is installed.\n- Install sodium, libsodium-wrappers, or @stablelib/xchacha20poly1305.',
|
||||
);
|
||||
}
|
||||
|
||||
exports.methods = {
|
||||
open: NoLib,
|
||||
close: NoLib,
|
||||
random: NoLib,
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt: NoLib,
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt: NoLib,
|
||||
};
|
||||
@@ -42,7 +39,7 @@ exports.methods = {
|
||||
(async () => {
|
||||
for (const libName of Object.keys(libs)) {
|
||||
try {
|
||||
const lib = require(libName);
|
||||
const lib = await import(libName);
|
||||
if (libName === 'libsodium-wrappers' && lib.ready) await lib.ready; // eslint-disable-line no-await-in-loop
|
||||
exports.methods = libs[libName](lib);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user