@m7mdxzx1/discord-video-strem 0.0.1
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/README.md +294 -0
- package/dist/client/GatewayEvents.d.ts +27 -0
- package/dist/client/GatewayEvents.js +1 -0
- package/dist/client/GatewayOpCodes.d.ts +40 -0
- package/dist/client/GatewayOpCodes.js +41 -0
- package/dist/client/Streamer.d.ts +41 -0
- package/dist/client/Streamer.js +189 -0
- package/dist/client/encryptor/TransportEncryptor.d.ts +16 -0
- package/dist/client/encryptor/TransportEncryptor.js +38 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +4 -0
- package/dist/client/packet/AudioPacketizer.d.ts +8 -0
- package/dist/client/packet/AudioPacketizer.js +22 -0
- package/dist/client/packet/BaseMediaPacketizer.d.ts +109 -0
- package/dist/client/packet/BaseMediaPacketizer.js +243 -0
- package/dist/client/packet/VideoPacketizerAnnexB.d.ts +132 -0
- package/dist/client/packet/VideoPacketizerAnnexB.js +231 -0
- package/dist/client/packet/VideoPacketizerVP8.d.ts +15 -0
- package/dist/client/packet/VideoPacketizerVP8.js +56 -0
- package/dist/client/packet/index.d.ts +4 -0
- package/dist/client/packet/index.js +4 -0
- package/dist/client/processing/AnnexBHelper.d.ts +93 -0
- package/dist/client/processing/AnnexBHelper.js +132 -0
- package/dist/client/voice/BaseMediaConnection.d.ts +118 -0
- package/dist/client/voice/BaseMediaConnection.js +319 -0
- package/dist/client/voice/MediaUdp.d.ts +26 -0
- package/dist/client/voice/MediaUdp.js +140 -0
- package/dist/client/voice/StreamConnection.d.ts +10 -0
- package/dist/client/voice/StreamConnection.js +30 -0
- package/dist/client/voice/VoiceConnection.d.ts +7 -0
- package/dist/client/voice/VoiceConnection.js +10 -0
- package/dist/client/voice/VoiceMessageTypes.d.ts +136 -0
- package/dist/client/voice/VoiceMessageTypes.js +1 -0
- package/dist/client/voice/VoiceOpCodes.d.ts +21 -0
- package/dist/client/voice/VoiceOpCodes.js +22 -0
- package/dist/client/voice/index.d.ts +5 -0
- package/dist/client/voice/index.js +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/media/AudioStream.d.ts +25 -0
- package/dist/media/AudioStream.js +63 -0
- package/dist/media/BaseMediaStream.d.ts +31 -0
- package/dist/media/BaseMediaStream.js +145 -0
- package/dist/media/LibavCodecId.d.ts +541 -0
- package/dist/media/LibavCodecId.js +552 -0
- package/dist/media/LibavDecoder.d.ts +5 -0
- package/dist/media/LibavDecoder.js +63 -0
- package/dist/media/LibavDemuxer.d.ts +23 -0
- package/dist/media/LibavDemuxer.js +295 -0
- package/dist/media/VideoStream.d.ts +7 -0
- package/dist/media/VideoStream.js +10 -0
- package/dist/media/index.d.ts +1 -0
- package/dist/media/index.js +1 -0
- package/dist/media/newApi.d.ts +126 -0
- package/dist/media/newApi.js +387 -0
- package/dist/media/utils.d.ts +1 -0
- package/dist/media/utils.js +4 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.js +54 -0
- package/package.json +69 -0
- package/src/client/GatewayEvents.ts +41 -0
- package/src/client/GatewayOpCodes.ts +40 -0
- package/src/client/Streamer.ts +279 -0
- package/src/client/encryptor/TransportEncryptor.ts +62 -0
- package/src/client/index.ts +4 -0
- package/src/client/packet/AudioPacketizer.ts +28 -0
- package/src/client/packet/BaseMediaPacketizer.ts +307 -0
- package/src/client/packet/VideoPacketizerAnnexB.ts +263 -0
- package/src/client/packet/VideoPacketizerVP8.ts +73 -0
- package/src/client/packet/index.ts +4 -0
- package/src/client/processing/AnnexBHelper.ts +142 -0
- package/src/client/voice/BaseMediaConnection.ts +407 -0
- package/src/client/voice/MediaUdp.ts +171 -0
- package/src/client/voice/StreamConnection.ts +33 -0
- package/src/client/voice/VoiceConnection.ts +15 -0
- package/src/client/voice/VoiceMessageTypes.ts +164 -0
- package/src/client/voice/VoiceOpCodes.ts +21 -0
- package/src/client/voice/index.ts +5 -0
- package/src/index.ts +5 -0
- package/src/media/AudioStream.ts +81 -0
- package/src/media/BaseMediaStream.ts +173 -0
- package/src/media/LibavCodecId.ts +566 -0
- package/src/media/LibavDecoder.ts +82 -0
- package/src/media/LibavDemuxer.ts +348 -0
- package/src/media/VideoStream.ts +15 -0
- package/src/media/index.ts +1 -0
- package/src/media/newApi.ts +618 -0
- package/src/media/utils.ts +6 -0
- package/src/utils.ts +77 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare enum VoiceOpCodes {
|
|
2
|
+
IDENTIFY = 0,
|
|
3
|
+
SELECT_PROTOCOL = 1,
|
|
4
|
+
READY = 2,
|
|
5
|
+
HEARTBEAT = 3,
|
|
6
|
+
SELECT_PROTOCOL_ACK = 4,
|
|
7
|
+
SPEAKING = 5,
|
|
8
|
+
HEARTBEAT_ACK = 6,
|
|
9
|
+
RESUME = 7,
|
|
10
|
+
HELLO = 8,
|
|
11
|
+
RESUMED = 9,
|
|
12
|
+
VIDEO = 12,
|
|
13
|
+
CLIENT_DISCONNECT = 13,
|
|
14
|
+
SESSION_UPDATE = 14,
|
|
15
|
+
MEDIA_SINK_WANTS = 15,
|
|
16
|
+
VOICE_BACKEND_VERSION = 16,
|
|
17
|
+
CHANNEL_OPTIONS_UPDATE = 17,
|
|
18
|
+
FLAGS = 18,
|
|
19
|
+
SPEED_TEST = 19,
|
|
20
|
+
PLATFORM = 20
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export var VoiceOpCodes;
|
|
2
|
+
(function (VoiceOpCodes) {
|
|
3
|
+
VoiceOpCodes[VoiceOpCodes["IDENTIFY"] = 0] = "IDENTIFY";
|
|
4
|
+
VoiceOpCodes[VoiceOpCodes["SELECT_PROTOCOL"] = 1] = "SELECT_PROTOCOL";
|
|
5
|
+
VoiceOpCodes[VoiceOpCodes["READY"] = 2] = "READY";
|
|
6
|
+
VoiceOpCodes[VoiceOpCodes["HEARTBEAT"] = 3] = "HEARTBEAT";
|
|
7
|
+
VoiceOpCodes[VoiceOpCodes["SELECT_PROTOCOL_ACK"] = 4] = "SELECT_PROTOCOL_ACK";
|
|
8
|
+
VoiceOpCodes[VoiceOpCodes["SPEAKING"] = 5] = "SPEAKING";
|
|
9
|
+
VoiceOpCodes[VoiceOpCodes["HEARTBEAT_ACK"] = 6] = "HEARTBEAT_ACK";
|
|
10
|
+
VoiceOpCodes[VoiceOpCodes["RESUME"] = 7] = "RESUME";
|
|
11
|
+
VoiceOpCodes[VoiceOpCodes["HELLO"] = 8] = "HELLO";
|
|
12
|
+
VoiceOpCodes[VoiceOpCodes["RESUMED"] = 9] = "RESUMED";
|
|
13
|
+
VoiceOpCodes[VoiceOpCodes["VIDEO"] = 12] = "VIDEO";
|
|
14
|
+
VoiceOpCodes[VoiceOpCodes["CLIENT_DISCONNECT"] = 13] = "CLIENT_DISCONNECT";
|
|
15
|
+
VoiceOpCodes[VoiceOpCodes["SESSION_UPDATE"] = 14] = "SESSION_UPDATE";
|
|
16
|
+
VoiceOpCodes[VoiceOpCodes["MEDIA_SINK_WANTS"] = 15] = "MEDIA_SINK_WANTS";
|
|
17
|
+
VoiceOpCodes[VoiceOpCodes["VOICE_BACKEND_VERSION"] = 16] = "VOICE_BACKEND_VERSION";
|
|
18
|
+
VoiceOpCodes[VoiceOpCodes["CHANNEL_OPTIONS_UPDATE"] = 17] = "CHANNEL_OPTIONS_UPDATE";
|
|
19
|
+
VoiceOpCodes[VoiceOpCodes["FLAGS"] = 18] = "FLAGS";
|
|
20
|
+
VoiceOpCodes[VoiceOpCodes["SPEED_TEST"] = 19] = "SPEED_TEST";
|
|
21
|
+
VoiceOpCodes[VoiceOpCodes["PLATFORM"] = 20] = "PLATFORM";
|
|
22
|
+
})(VoiceOpCodes || (VoiceOpCodes = {}));
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MediaUdp } from "../client/voice/MediaUdp.js";
|
|
2
|
+
import { BaseMediaStream } from "./BaseMediaStream.js";
|
|
3
|
+
export declare class AudioStream extends BaseMediaStream {
|
|
4
|
+
udp: MediaUdp;
|
|
5
|
+
private _isMuted;
|
|
6
|
+
private _volume;
|
|
7
|
+
private _opusEncoder;
|
|
8
|
+
constructor(udp: MediaUdp, noSleep?: boolean, initialMuted?: boolean);
|
|
9
|
+
/**
|
|
10
|
+
* Mutes the audio stream. No audio frames will be sent.
|
|
11
|
+
*/
|
|
12
|
+
mute(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Unmutes the audio stream. Audio frames will resume sending.
|
|
15
|
+
*/
|
|
16
|
+
unmute(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the audio stream is currently muted.
|
|
19
|
+
* @returns True if muted, false otherwise.
|
|
20
|
+
*/
|
|
21
|
+
isMuted(): boolean;
|
|
22
|
+
setVolume(volume: number): void;
|
|
23
|
+
getVolume(): number;
|
|
24
|
+
protected _sendFrame(frame: Buffer, frametime: number): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import OpusScript from "opusscript";
|
|
2
|
+
import { BaseMediaStream } from "./BaseMediaStream.js";
|
|
3
|
+
export class AudioStream extends BaseMediaStream {
|
|
4
|
+
constructor(udp, noSleep = false, initialMuted = false) {
|
|
5
|
+
super("audio", noSleep);
|
|
6
|
+
this._volume = 1.0;
|
|
7
|
+
this.udp = udp;
|
|
8
|
+
this._isMuted = initialMuted; // Initialize mute state
|
|
9
|
+
this._opusEncoder = new OpusScript(48000, 2, 2048);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Mutes the audio stream. No audio frames will be sent.
|
|
13
|
+
*/
|
|
14
|
+
mute() {
|
|
15
|
+
this._isMuted = true;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Unmutes the audio stream. Audio frames will resume sending.
|
|
19
|
+
*/
|
|
20
|
+
unmute() {
|
|
21
|
+
this._isMuted = false;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Checks if the audio stream is currently muted.
|
|
25
|
+
* @returns True if muted, false otherwise.
|
|
26
|
+
*/
|
|
27
|
+
isMuted() {
|
|
28
|
+
return this._isMuted;
|
|
29
|
+
}
|
|
30
|
+
setVolume(volume) {
|
|
31
|
+
if (volume < 0) {
|
|
32
|
+
this._volume = 0;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
this._volume = Math.min(volume, 2.0);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
getVolume() {
|
|
39
|
+
return this._volume;
|
|
40
|
+
}
|
|
41
|
+
async _sendFrame(frame, frametime) {
|
|
42
|
+
if (this._isMuted) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let processedFrame = frame;
|
|
46
|
+
if (this._volume !== 1.0) {
|
|
47
|
+
try {
|
|
48
|
+
const pcmData = this._opusEncoder.decode(frame);
|
|
49
|
+
for (let i = 0; i < pcmData.length; i += 2) {
|
|
50
|
+
const sample = pcmData.readInt16LE(i);
|
|
51
|
+
const newSample = Math.max(-32768, Math.min(32767, Math.floor(sample * this._volume)));
|
|
52
|
+
pcmData.writeInt16LE(newSample, i);
|
|
53
|
+
}
|
|
54
|
+
processedFrame = this._opusEncoder.encode(pcmData, 960);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error("[AudioStream] Error processing audio frame:", error);
|
|
58
|
+
processedFrame = frame;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
await this.udp.sendAudioFrame(processedFrame, frametime);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Writable } from "node:stream";
|
|
2
|
+
import type { Packet } from "@lng2004/libav.js-variant-webcodecs-avf-with-decoders";
|
|
3
|
+
export declare class BaseMediaStream extends Writable {
|
|
4
|
+
private _pts?;
|
|
5
|
+
private _syncTolerance;
|
|
6
|
+
private _loggerSend;
|
|
7
|
+
private _loggerSync;
|
|
8
|
+
private _loggerSleep;
|
|
9
|
+
private _noSleep;
|
|
10
|
+
private _startTime?;
|
|
11
|
+
private _startPts?;
|
|
12
|
+
private _sync;
|
|
13
|
+
private _syncStream?;
|
|
14
|
+
constructor(type: string, noSleep?: boolean);
|
|
15
|
+
get sync(): boolean;
|
|
16
|
+
set sync(val: boolean);
|
|
17
|
+
get syncStream(): BaseMediaStream | undefined;
|
|
18
|
+
set syncStream(stream: BaseMediaStream | undefined);
|
|
19
|
+
get noSleep(): boolean;
|
|
20
|
+
set noSleep(val: boolean);
|
|
21
|
+
get pts(): number | undefined;
|
|
22
|
+
get syncTolerance(): number;
|
|
23
|
+
set syncTolerance(n: number);
|
|
24
|
+
protected _sendFrame(frame: Buffer, frametime: number): Promise<void>;
|
|
25
|
+
private ptsDelta;
|
|
26
|
+
private isAhead;
|
|
27
|
+
private isBehind;
|
|
28
|
+
private resetTimingCompensation;
|
|
29
|
+
_write(frame: Packet, _: BufferEncoding, callback: (error?: Error | null) => void): Promise<void>;
|
|
30
|
+
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Log } from "debug-level";
|
|
2
|
+
import { setTimeout } from "node:timers/promises";
|
|
3
|
+
import { Writable } from "node:stream";
|
|
4
|
+
import { combineLoHi } from "./utils.js";
|
|
5
|
+
export class BaseMediaStream extends Writable {
|
|
6
|
+
constructor(type, noSleep = false) {
|
|
7
|
+
super({ objectMode: true, highWaterMark: 0 });
|
|
8
|
+
this._syncTolerance = 20;
|
|
9
|
+
this._sync = true;
|
|
10
|
+
this._loggerSend = new Log(`stream:${type}:send`);
|
|
11
|
+
this._loggerSync = new Log(`stream:${type}:sync`);
|
|
12
|
+
this._loggerSleep = new Log(`stream:${type}:sleep`);
|
|
13
|
+
this._noSleep = noSleep;
|
|
14
|
+
}
|
|
15
|
+
get sync() {
|
|
16
|
+
return this._sync;
|
|
17
|
+
}
|
|
18
|
+
set sync(val) {
|
|
19
|
+
this._sync = val;
|
|
20
|
+
if (val)
|
|
21
|
+
this._loggerSync.debug("Sync enabled");
|
|
22
|
+
else
|
|
23
|
+
this._loggerSync.debug("Sync disabled");
|
|
24
|
+
}
|
|
25
|
+
get syncStream() {
|
|
26
|
+
return this._syncStream;
|
|
27
|
+
}
|
|
28
|
+
set syncStream(stream) {
|
|
29
|
+
if (stream === this)
|
|
30
|
+
throw new Error("A stream cannot sync with itself");
|
|
31
|
+
this._syncStream = stream;
|
|
32
|
+
}
|
|
33
|
+
get noSleep() {
|
|
34
|
+
return this._noSleep;
|
|
35
|
+
}
|
|
36
|
+
set noSleep(val) {
|
|
37
|
+
this._noSleep = val;
|
|
38
|
+
if (!val)
|
|
39
|
+
this.resetTimingCompensation();
|
|
40
|
+
}
|
|
41
|
+
get pts() {
|
|
42
|
+
return this._pts;
|
|
43
|
+
}
|
|
44
|
+
get syncTolerance() {
|
|
45
|
+
return this._syncTolerance;
|
|
46
|
+
}
|
|
47
|
+
set syncTolerance(n) {
|
|
48
|
+
if (n < 0)
|
|
49
|
+
return;
|
|
50
|
+
this._syncTolerance = n;
|
|
51
|
+
}
|
|
52
|
+
async _sendFrame(frame, frametime) {
|
|
53
|
+
throw new Error("Not implemented");
|
|
54
|
+
}
|
|
55
|
+
ptsDelta() {
|
|
56
|
+
if (this.pts !== undefined && this.syncStream?.pts !== undefined)
|
|
57
|
+
return this.pts - this.syncStream.pts;
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
isAhead() {
|
|
61
|
+
const delta = this.ptsDelta();
|
|
62
|
+
return this.syncStream?.writableEnded === false && delta !== undefined && delta > this.syncTolerance;
|
|
63
|
+
}
|
|
64
|
+
isBehind() {
|
|
65
|
+
const delta = this.ptsDelta();
|
|
66
|
+
return this.syncStream?.writableEnded === false && delta !== undefined && delta < -this.syncTolerance;
|
|
67
|
+
}
|
|
68
|
+
resetTimingCompensation() {
|
|
69
|
+
this._startTime = this._startPts = undefined;
|
|
70
|
+
}
|
|
71
|
+
async _write(frame, _, callback) {
|
|
72
|
+
const { data, ptshi, pts, durationhi, duration, time_base_num, time_base_den } = frame;
|
|
73
|
+
// biome-ignore lint/style/noNonNullAssertion: this will never happen with our media stream
|
|
74
|
+
const frametime = combineLoHi(durationhi, duration) / time_base_den * time_base_num * 1000;
|
|
75
|
+
const start_sendFrame = performance.now();
|
|
76
|
+
await this._sendFrame(Buffer.from(data), frametime);
|
|
77
|
+
const end_sendFrame = performance.now();
|
|
78
|
+
// biome-ignore lint/style/noNonNullAssertion: this will never happen with our media stream
|
|
79
|
+
this._pts = combineLoHi(ptshi, pts) / time_base_den * time_base_num * 1000;
|
|
80
|
+
this.emit("pts", this._pts);
|
|
81
|
+
const sendTime = end_sendFrame - start_sendFrame;
|
|
82
|
+
const ratio = sendTime / frametime;
|
|
83
|
+
this._loggerSend.debug({
|
|
84
|
+
stats: {
|
|
85
|
+
pts: this._pts,
|
|
86
|
+
frame_size: data.length,
|
|
87
|
+
duration: sendTime,
|
|
88
|
+
frametime
|
|
89
|
+
}
|
|
90
|
+
}, `Frame sent in ${sendTime.toFixed(2)}ms (${(ratio * 100).toFixed(2)}% frametime)`);
|
|
91
|
+
if (ratio > 1) {
|
|
92
|
+
this._loggerSend.warn({
|
|
93
|
+
frame_size: data.length,
|
|
94
|
+
duration: sendTime,
|
|
95
|
+
frametime
|
|
96
|
+
}, `Frame takes too long to send (${(ratio * 100).toFixed(2)}% frametime)`);
|
|
97
|
+
}
|
|
98
|
+
this._startTime ??= start_sendFrame;
|
|
99
|
+
this._startPts ??= this._pts;
|
|
100
|
+
const sleep = Math.max(0, this._pts - this._startPts + frametime - (end_sendFrame - this._startTime));
|
|
101
|
+
if (this._noSleep || sleep === 0) {
|
|
102
|
+
callback(null);
|
|
103
|
+
}
|
|
104
|
+
else if (this.sync && this.isBehind()) {
|
|
105
|
+
this._loggerSync.debug({
|
|
106
|
+
stats: {
|
|
107
|
+
pts: this.pts,
|
|
108
|
+
pts_other: this.syncStream?.pts
|
|
109
|
+
}
|
|
110
|
+
}, "Stream is behind. Not sleeping for this frame");
|
|
111
|
+
this.resetTimingCompensation();
|
|
112
|
+
callback(null);
|
|
113
|
+
}
|
|
114
|
+
else if (this.sync && this.isAhead()) {
|
|
115
|
+
do {
|
|
116
|
+
this._loggerSync.debug({
|
|
117
|
+
stats: {
|
|
118
|
+
pts: this.pts,
|
|
119
|
+
pts_other: this.syncStream?.pts,
|
|
120
|
+
frametime
|
|
121
|
+
}
|
|
122
|
+
}, `Stream is ahead. Waiting for ${frametime}ms`);
|
|
123
|
+
await setTimeout(frametime);
|
|
124
|
+
} while (this.sync && this.isAhead());
|
|
125
|
+
this.resetTimingCompensation();
|
|
126
|
+
callback(null);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this._loggerSleep.debug({
|
|
130
|
+
stats: {
|
|
131
|
+
pts: this._pts,
|
|
132
|
+
startPts: this._startPts,
|
|
133
|
+
time: end_sendFrame,
|
|
134
|
+
startTime: this._startTime,
|
|
135
|
+
frametime
|
|
136
|
+
}
|
|
137
|
+
}, `Sleeping for ${sleep}ms`);
|
|
138
|
+
setTimeout(sleep).then(() => callback(null));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
_destroy(error, callback) {
|
|
142
|
+
super._destroy(error, callback);
|
|
143
|
+
this.syncStream = undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|