-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Expand file tree
/
Copy pathAudioReceiveStream.ts
More file actions
119 lines (99 loc) · 2.65 KB
/
AudioReceiveStream.ts
File metadata and controls
119 lines (99 loc) · 2.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import type { Buffer } from 'node:buffer';
import process from 'node:process';
import { Readable, type ReadableOptions } from 'node:stream';
import { SILENCE_FRAME } from '../audio/AudioPlayer';
/**
* The different behaviors an audio receive stream can have for deciding when to end.
*/
export enum EndBehaviorType {
/**
* The stream will only end when manually destroyed.
*/
Manual,
/**
* The stream will end after a given time period of silence/no audio packets.
*/
AfterSilence,
/**
* The stream will end after a given time period of no audio packets.
*/
AfterInactivity,
}
export type EndBehavior =
| {
behavior: EndBehaviorType.AfterInactivity | EndBehaviorType.AfterSilence;
duration: number;
}
| {
behavior: EndBehaviorType.Manual;
};
export interface AudioReceiveStreamOptions extends ReadableOptions {
end: EndBehavior;
}
/**
* A Buffer containing encoded Opus packet data and key RTP Header metadata.
*/
export interface AudioPacket extends Buffer {
/**
* The RTP sequence number of this packet (16-bit, wraps at 65535).
*/
readonly sequence: number;
/**
* The synchronization source identifier for this packet (32-bit).
* A change in SSRC indicates a new RTP stream, so any associated
* decoder should be reset.
*/
readonly ssrc: number;
/**
* The RTP timestamp of this packet (32-bit, wraps at 2^32 - 1).
*/
readonly timestamp: number;
}
export function createDefaultAudioReceiveStreamOptions(): AudioReceiveStreamOptions {
return {
end: {
behavior: EndBehaviorType.Manual,
},
};
}
/**
* A readable stream of Opus packets received from a specific entity
* in a Discord voice connection.
*/
export class AudioReceiveStream extends Readable {
/**
* The end behavior of the receive stream.
*/
public readonly end: EndBehavior;
private endTimeout?: NodeJS.Timeout;
public constructor(options: AudioReceiveStreamOptions) {
const { end, ...rest } = options;
super({
...rest,
objectMode: true,
});
this.end = end;
}
public override push(buffer: Buffer | null) {
if (
buffer &&
(this.end.behavior === EndBehaviorType.AfterInactivity ||
(this.end.behavior === EndBehaviorType.AfterSilence &&
(buffer.compare(SILENCE_FRAME) !== 0 || this.endTimeout === undefined)))
) {
this.renewEndTimeout(this.end);
}
if (buffer === null) {
// null marks EOF for stream
process.nextTick(() => this.destroy());
}
return super.push(buffer);
}
private renewEndTimeout(end: EndBehavior & { duration: number }) {
if (this.endTimeout) {
clearTimeout(this.endTimeout);
}
this.endTimeout = setTimeout(() => this.push(null), end.duration);
}
public override _read() {}
}