werift 0.17.0 → 0.17.2
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/lib/ice/src/ice.d.ts +10 -1
- package/lib/ice/src/ice.js +15 -3
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/rtp/src/container/ebml/ebml.d.ts +32 -0
- package/lib/rtp/src/container/ebml/ebml.js +115 -0
- package/lib/rtp/src/container/ebml/ebml.js.map +1 -0
- package/lib/rtp/src/container/ebml/id.d.ts +221 -0
- package/lib/rtp/src/container/ebml/id.js +225 -0
- package/lib/rtp/src/container/ebml/id.js.map +1 -0
- package/lib/rtp/src/container/ebml/index.d.ts +3 -0
- package/lib/rtp/src/container/ebml/index.js +20 -0
- package/lib/rtp/src/container/ebml/index.js.map +1 -0
- package/lib/rtp/src/container/ebml/typedArrayUtils.d.ts +7 -0
- package/lib/rtp/src/container/ebml/typedArrayUtils.js +109 -0
- package/lib/rtp/src/container/ebml/typedArrayUtils.js.map +1 -0
- package/lib/rtp/src/container/webm.d.ts +10 -4
- package/lib/rtp/src/container/webm.js +56 -7
- package/lib/rtp/src/container/webm.js.map +1 -1
- package/lib/rtp/src/index.d.ts +1 -0
- package/lib/rtp/src/index.js +1 -0
- package/lib/rtp/src/index.js.map +1 -1
- package/lib/rtp/src/processor/avBuffer.d.ts +28 -0
- package/lib/rtp/src/processor/avBuffer.js +91 -0
- package/lib/rtp/src/processor/avBuffer.js.map +1 -0
- package/lib/rtp/src/processor/avBufferCallback.d.ts +10 -0
- package/lib/rtp/src/processor/avBufferCallback.js +27 -0
- package/lib/rtp/src/processor/avBufferCallback.js.map +1 -0
- package/lib/rtp/src/processor/depacketizer.d.ts +1 -1
- package/lib/rtp/src/processor/depacketizer.js.map +1 -1
- package/lib/rtp/src/processor/depacketizerCallback.d.ts +1 -3
- package/lib/rtp/src/processor/depacketizerCallback.js +2 -2
- package/lib/rtp/src/processor/depacketizerCallback.js.map +1 -1
- package/lib/rtp/src/processor/depacketizerTransformer.d.ts +1 -1
- package/lib/rtp/src/processor/index.d.ts +3 -0
- package/lib/rtp/src/processor/index.js +3 -0
- package/lib/rtp/src/processor/index.js.map +1 -1
- package/lib/rtp/src/processor/jitterBuffer.d.ts +1 -1
- package/lib/rtp/src/processor/jitterBuffer.js.map +1 -1
- package/lib/rtp/src/processor/jitterBufferCallback.d.ts +1 -3
- package/lib/rtp/src/processor/jitterBufferCallback.js +2 -2
- package/lib/rtp/src/processor/jitterBufferCallback.js.map +1 -1
- package/lib/rtp/src/processor/jitterBufferTransformer.d.ts +1 -1
- package/lib/rtp/src/processor/source/index.d.ts +2 -2
- package/lib/rtp/src/processor/source/index.js +16 -3
- package/lib/rtp/src/processor/source/index.js.map +1 -1
- package/lib/rtp/src/processor/source/rtpCallback.d.ts +17 -0
- package/lib/rtp/src/processor/source/rtpCallback.js +33 -0
- package/lib/rtp/src/processor/source/rtpCallback.js.map +1 -0
- package/lib/rtp/src/processor/source/{rtp.d.ts → rtpStream.d.ts} +2 -5
- package/lib/rtp/src/processor/source/{rtp.js → rtpStream.js} +12 -12
- package/lib/rtp/src/processor/source/rtpStream.js.map +1 -0
- package/lib/rtp/src/processor/webm.d.ts +17 -2
- package/lib/rtp/src/processor/webm.js +83 -33
- package/lib/rtp/src/processor/webm.js.map +1 -1
- package/lib/rtp/src/processor/webmCallback.d.ts +16 -0
- package/lib/rtp/src/processor/webmCallback.js +21 -0
- package/lib/rtp/src/processor/webmCallback.js.map +1 -0
- package/lib/rtp/src/processor/webmStream.d.ts +5 -16
- package/lib/rtp/src/processor/webmStream.js.map +1 -1
- package/lib/{webrtc/src/media/receiver/red.d.ts → rtp/src/rtp/red/handler.d.ts} +2 -2
- package/lib/{webrtc/src/media/receiver/red.js → rtp/src/rtp/red/handler.js} +10 -10
- package/lib/rtp/src/rtp/red/handler.js.map +1 -0
- package/lib/webrtc/src/const.d.ts +1 -1
- package/lib/webrtc/src/const.js +2 -2
- package/lib/webrtc/src/const.js.map +1 -1
- package/lib/webrtc/src/media/receiver/nack.d.ts +1 -0
- package/lib/webrtc/src/media/receiver/nack.js +29 -16
- package/lib/webrtc/src/media/receiver/nack.js.map +1 -1
- package/lib/webrtc/src/media/router.js +17 -13
- package/lib/webrtc/src/media/router.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +1 -1
- package/lib/webrtc/src/media/rtpReceiver.js +14 -9
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +10 -6
- package/lib/webrtc/src/media/rtpTransceiver.js +23 -14
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/media/track.js +3 -2
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/lib/webrtc/src/nonstandard/userMedia.d.ts +20 -3
- package/lib/webrtc/src/nonstandard/userMedia.js +79 -8
- package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +5 -2
- package/lib/webrtc/src/peerConnection.js +72 -46
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/sdp.js +2 -2
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/transport/ice.js +10 -3
- package/lib/webrtc/src/transport/ice.js.map +1 -1
- package/lib/webrtc/src/utils.d.ts +0 -1
- package/lib/webrtc/src/utils.js +1 -3
- package/lib/webrtc/src/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/const.ts +1 -1
- package/src/media/receiver/nack.ts +33 -17
- package/src/media/router.ts +16 -13
- package/src/media/rtpReceiver.ts +21 -9
- package/src/media/rtpTransceiver.ts +28 -15
- package/src/media/track.ts +3 -2
- package/src/nonstandard/recorder/writer/webm.ts +2 -2
- package/src/nonstandard/userMedia.ts +89 -7
- package/src/peerConnection.ts +88 -51
- package/src/sdp.ts +2 -2
- package/src/transport/ice.ts +13 -3
- package/src/utils.ts +0 -3
- package/lib/rtp/src/processor/source/rtp.js.map +0 -1
- package/lib/webrtc/src/media/receiver/red.js.map +0 -1
- package/src/media/receiver/red.ts +0 -70
|
@@ -13,23 +13,13 @@ import { RTCRtpSender } from "./rtpSender";
|
|
|
13
13
|
import { MediaStreamTrack } from "./track";
|
|
14
14
|
|
|
15
15
|
export class RTCRtpTransceiver {
|
|
16
|
-
readonly
|
|
16
|
+
readonly id = uuid.v4();
|
|
17
17
|
readonly onTrack = new Event<[MediaStreamTrack, RTCRtpTransceiver]>();
|
|
18
18
|
mid?: string;
|
|
19
19
|
mLineIndex?: number;
|
|
20
|
+
/**should not be reused because it has been used for sending before. */
|
|
20
21
|
usedForSender = false;
|
|
21
|
-
private _currentDirection?: Direction
|
|
22
|
-
set currentDirection(direction: Direction | "stopped" | undefined) {
|
|
23
|
-
this._currentDirection = direction;
|
|
24
|
-
if (SenderDirections.includes(this._currentDirection || "")) {
|
|
25
|
-
this.usedForSender = true;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/**RFC 8829 4.2.5. last negotiated direction */
|
|
29
|
-
get currentDirection(): Direction | "stopped" | undefined {
|
|
30
|
-
return this._currentDirection;
|
|
31
|
-
}
|
|
32
|
-
|
|
22
|
+
private _currentDirection?: Direction;
|
|
33
23
|
offerDirection!: Direction;
|
|
34
24
|
_codecs: RTCRtpCodecParameters[] = [];
|
|
35
25
|
set codecs(codecs: RTCRtpCodecParameters[]) {
|
|
@@ -49,7 +39,7 @@ export class RTCRtpTransceiver {
|
|
|
49
39
|
public receiver: RTCRtpReceiver,
|
|
50
40
|
public sender: RTCRtpSender,
|
|
51
41
|
/**RFC 8829 4.2.4. direction the transceiver was initialized with */
|
|
52
|
-
|
|
42
|
+
private _direction: Direction
|
|
53
43
|
) {
|
|
54
44
|
this.setDtlsTransport(dtlsTransport);
|
|
55
45
|
}
|
|
@@ -58,6 +48,27 @@ export class RTCRtpTransceiver {
|
|
|
58
48
|
return this.receiver.dtlsTransport;
|
|
59
49
|
}
|
|
60
50
|
|
|
51
|
+
/**RFC 8829 4.2.4. setDirectionに渡された最後の値を示します */
|
|
52
|
+
get direction() {
|
|
53
|
+
return this._direction;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setDirection(direction: Direction) {
|
|
57
|
+
this._direction = direction;
|
|
58
|
+
if (SenderDirections.includes(this._currentDirection ?? "")) {
|
|
59
|
+
this.usedForSender = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**RFC 8829 4.2.5. last negotiated direction */
|
|
64
|
+
get currentDirection(): Direction | undefined {
|
|
65
|
+
return this._currentDirection;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setCurrentDirection(direction: Direction | undefined) {
|
|
69
|
+
this._currentDirection = direction;
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
setDtlsTransport(dtls: RTCDtlsTransport) {
|
|
62
73
|
this.receiver.setDtlsTransport(dtls);
|
|
63
74
|
this.sender.setDtlsTransport(dtls);
|
|
@@ -77,7 +88,9 @@ export class RTCRtpTransceiver {
|
|
|
77
88
|
// todo impl
|
|
78
89
|
// https://www.w3.org/TR/webrtc/#methods-8
|
|
79
90
|
stop() {
|
|
80
|
-
if (this.stopping)
|
|
91
|
+
if (this.stopping) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
81
94
|
|
|
82
95
|
// todo Stop sending and receiving with transceiver.
|
|
83
96
|
|
package/src/media/track.ts
CHANGED
|
@@ -55,12 +55,13 @@ export class MediaStreamTrack extends EventTarget {
|
|
|
55
55
|
if (this.remote) {
|
|
56
56
|
throw new Error("this is remoteTrack");
|
|
57
57
|
}
|
|
58
|
-
if (
|
|
58
|
+
if (this.stopped) {
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const packet = Buffer.isBuffer(rtp) ? RtpPacket.deSerialize(rtp) : rtp;
|
|
63
|
-
packet.header.payloadType =
|
|
63
|
+
packet.header.payloadType =
|
|
64
|
+
this.codec?.payloadType ?? packet.header.payloadType;
|
|
64
65
|
this.onReceiveRtp.execute(packet);
|
|
65
66
|
};
|
|
66
67
|
}
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
jitterBufferTransformer,
|
|
8
8
|
MediaStreamTrack,
|
|
9
9
|
RtpSourceStream,
|
|
10
|
-
WebmLiveOutput,
|
|
11
10
|
WebmStream,
|
|
11
|
+
WebmStreamOutput,
|
|
12
12
|
WeriftError,
|
|
13
13
|
} from "../../..";
|
|
14
14
|
import { MediaWriter } from ".";
|
|
@@ -102,7 +102,7 @@ export class WebmFactory extends MediaWriter {
|
|
|
102
102
|
const readChunk = async ({
|
|
103
103
|
value,
|
|
104
104
|
done,
|
|
105
|
-
}: ReadableStreamDefaultReadResult<
|
|
105
|
+
}: ReadableStreamDefaultReadResult<WebmStreamOutput>) => {
|
|
106
106
|
if (done) return;
|
|
107
107
|
|
|
108
108
|
if (value.saveToFile) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { exec } from "child_process";
|
|
1
|
+
import { ChildProcess, exec } from "child_process";
|
|
2
2
|
import { createSocket } from "dgram";
|
|
3
3
|
import { setImmediate } from "timers/promises";
|
|
4
4
|
import { v4 } from "uuid";
|
|
@@ -7,17 +7,23 @@ import { randomPort } from "../../../common/src";
|
|
|
7
7
|
import { RtpPacket } from "../../../rtp/src";
|
|
8
8
|
import { MediaStreamTrack } from "../media/track";
|
|
9
9
|
|
|
10
|
-
export const
|
|
10
|
+
export const getUserMedia = async (path: string, loop?: boolean) => {
|
|
11
11
|
const audioPort = await randomPort();
|
|
12
12
|
const videoPort = await randomPort();
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
if (path.endsWith(".mp4")) {
|
|
15
|
+
return new MediaPlayerMp4(audioPort, videoPort, path, loop);
|
|
16
|
+
} else {
|
|
17
|
+
return new MediaPlayerWebm(audioPort, videoPort, path, loop);
|
|
18
|
+
}
|
|
15
19
|
};
|
|
16
20
|
|
|
17
|
-
class
|
|
21
|
+
export class MediaPlayerMp4 {
|
|
18
22
|
private streamId = v4();
|
|
19
23
|
audio = new MediaStreamTrack({ kind: "audio", streamId: this.streamId });
|
|
20
24
|
video = new MediaStreamTrack({ kind: "video", streamId: this.streamId });
|
|
25
|
+
private process!: ChildProcess;
|
|
26
|
+
stopped = false;
|
|
21
27
|
|
|
22
28
|
constructor(
|
|
23
29
|
private videoPort: number,
|
|
@@ -61,14 +67,90 @@ queue ! h264parse ! rtph264pay config-interval=10 pt=${payloadType++} ! \
|
|
|
61
67
|
udpsink host=127.0.0.1 port=${this.videoPort} d. ! \
|
|
62
68
|
queue ! aacparse ! avdec_aac ! audioresample ! audioconvert ! opusenc ! rtpopuspay pt=${payloadType++} ! \
|
|
63
69
|
udpsink host=127.0.0.1 port=${this.audioPort}`;
|
|
64
|
-
|
|
70
|
+
this.process = exec(cmd);
|
|
71
|
+
|
|
72
|
+
if (this.loop) {
|
|
73
|
+
await new Promise((r) => this.process.on("close", r));
|
|
74
|
+
if (!this.stopped) {
|
|
75
|
+
run();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
await setImmediate();
|
|
80
|
+
run();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
stop() {
|
|
84
|
+
this.stopped = true;
|
|
85
|
+
this.process.kill("SIGINT");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class MediaPlayerWebm {
|
|
90
|
+
private streamId = v4();
|
|
91
|
+
audio = new MediaStreamTrack({ kind: "audio", streamId: this.streamId });
|
|
92
|
+
video = new MediaStreamTrack({ kind: "video", streamId: this.streamId });
|
|
93
|
+
private process!: ChildProcess;
|
|
94
|
+
stopped = false;
|
|
95
|
+
|
|
96
|
+
constructor(
|
|
97
|
+
private videoPort: number,
|
|
98
|
+
private audioPort: number,
|
|
99
|
+
private path: string,
|
|
100
|
+
private loop?: boolean
|
|
101
|
+
) {
|
|
102
|
+
this.setupTrack(audioPort, this.audio);
|
|
103
|
+
this.setupTrack(videoPort, this.video);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private setupTrack = (port: number, track: MediaStreamTrack) => {
|
|
107
|
+
let payloadType = 0;
|
|
108
|
+
|
|
109
|
+
const socket = createSocket("udp4");
|
|
110
|
+
socket.bind(port);
|
|
111
|
+
socket.on("message", async (buf) => {
|
|
112
|
+
const rtp = RtpPacket.deSerialize(buf);
|
|
113
|
+
if (!payloadType) {
|
|
114
|
+
payloadType = rtp.header.payloadType;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// detect gStreamer restarted
|
|
118
|
+
if (payloadType !== rtp.header.payloadType) {
|
|
119
|
+
payloadType = rtp.header.payloadType;
|
|
120
|
+
track.onSourceChanged.execute(rtp.header);
|
|
121
|
+
}
|
|
122
|
+
track.writeRtp(buf);
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
async start() {
|
|
127
|
+
let payloadType = 96;
|
|
128
|
+
const run = async () => {
|
|
129
|
+
if (payloadType > 100) payloadType = 96;
|
|
130
|
+
|
|
131
|
+
const cmd = `gst-launch-1.0 filesrc location=${
|
|
132
|
+
this.path
|
|
133
|
+
} ! matroskademux name=d \
|
|
134
|
+
d.video_0 ! queue ! rtpvp8pay pt=${payloadType++} ! \
|
|
135
|
+
udpsink host=127.0.0.1 port=${this.videoPort} \
|
|
136
|
+
d.audio_0 ! queue ! rtpopuspay pt=${payloadType++} ! \
|
|
137
|
+
udpsink host=127.0.0.1 port=${this.audioPort}`;
|
|
138
|
+
this.process = exec(cmd);
|
|
139
|
+
console.log(cmd);
|
|
65
140
|
|
|
66
141
|
if (this.loop) {
|
|
67
|
-
await new Promise((r) => process.on("close", r));
|
|
68
|
-
|
|
142
|
+
await new Promise((r) => this.process.on("close", r));
|
|
143
|
+
if (!this.stopped) {
|
|
144
|
+
run();
|
|
145
|
+
}
|
|
69
146
|
}
|
|
70
147
|
};
|
|
71
148
|
await setImmediate();
|
|
72
149
|
run();
|
|
73
150
|
}
|
|
151
|
+
|
|
152
|
+
stop() {
|
|
153
|
+
this.stopped = true;
|
|
154
|
+
this.process.kill("SIGINT");
|
|
155
|
+
}
|
|
74
156
|
}
|
package/src/peerConnection.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
DISCARD_HOST,
|
|
27
27
|
DISCARD_PORT,
|
|
28
|
+
ReceiverDirection,
|
|
28
29
|
SenderDirections,
|
|
29
30
|
SRTP_PROFILE,
|
|
30
31
|
} from "./const";
|
|
@@ -79,14 +80,21 @@ const log = debug("werift:packages/webrtc/src/peerConnection.ts");
|
|
|
79
80
|
export class RTCPeerConnection extends EventTarget {
|
|
80
81
|
readonly cname = uuid.v4();
|
|
81
82
|
sctpTransport?: RTCSctpTransport;
|
|
82
|
-
|
|
83
|
+
transportEstablished = false;
|
|
83
84
|
config: Required<PeerConfig> = cloneDeep<PeerConfig>(defaultPeerConfig);
|
|
84
85
|
connectionState: ConnectionState = "new";
|
|
85
86
|
iceConnectionState: RTCIceConnectionState = "new";
|
|
86
87
|
iceGatheringState: IceGathererState = "new";
|
|
87
88
|
signalingState: RTCSignalingState = "stable";
|
|
88
89
|
negotiationneeded = false;
|
|
89
|
-
readonly transceivers: RTCRtpTransceiver[] = [];
|
|
90
|
+
private readonly transceivers: RTCRtpTransceiver[] = [];
|
|
91
|
+
private pushTransceiver(t: RTCRtpTransceiver) {
|
|
92
|
+
this.transceivers.push(t);
|
|
93
|
+
}
|
|
94
|
+
private replaceTransceiver(t: RTCRtpTransceiver, index: number) {
|
|
95
|
+
this.transceivers[index] = t;
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
candidatesSent = new Set<string>();
|
|
91
99
|
|
|
92
100
|
readonly iceGatheringStateChange = new Event<[IceGathererState]>();
|
|
@@ -240,11 +248,11 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
240
248
|
).filter((codecCandidate) => {
|
|
241
249
|
switch (codecCandidate.direction) {
|
|
242
250
|
case "recvonly": {
|
|
243
|
-
if (
|
|
251
|
+
if (ReceiverDirection.includes(transceiver.direction)) return true;
|
|
244
252
|
return false;
|
|
245
253
|
}
|
|
246
254
|
case "sendonly": {
|
|
247
|
-
if (
|
|
255
|
+
if (SenderDirections.includes(transceiver.direction)) return true;
|
|
248
256
|
return false;
|
|
249
257
|
}
|
|
250
258
|
case "sendrecv": {
|
|
@@ -403,8 +411,9 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
403
411
|
|
|
404
412
|
removeTrack(sender: RTCRtpSender) {
|
|
405
413
|
if (this.isClosed) throw new Error("peer closed");
|
|
406
|
-
if (!this.getSenders().find(({ ssrc }) => sender.ssrc === ssrc))
|
|
414
|
+
if (!this.getSenders().find(({ ssrc }) => sender.ssrc === ssrc)) {
|
|
407
415
|
throw new Error("unExist");
|
|
416
|
+
}
|
|
408
417
|
|
|
409
418
|
const transceiver = this.transceivers.find(
|
|
410
419
|
({ sender: { ssrc } }) => sender.ssrc === ssrc
|
|
@@ -419,15 +428,15 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
419
428
|
}
|
|
420
429
|
|
|
421
430
|
if (transceiver.stopping || transceiver.stopped) {
|
|
422
|
-
transceiver.
|
|
431
|
+
transceiver.setDirection("inactive");
|
|
423
432
|
} else {
|
|
424
433
|
if (transceiver.direction === "sendrecv") {
|
|
425
|
-
transceiver.
|
|
434
|
+
transceiver.setDirection("recvonly");
|
|
426
435
|
} else if (
|
|
427
436
|
transceiver.direction === "sendonly" ||
|
|
428
437
|
transceiver.direction === "recvonly"
|
|
429
438
|
) {
|
|
430
|
-
transceiver.
|
|
439
|
+
transceiver.setDirection("inactive");
|
|
431
440
|
}
|
|
432
441
|
}
|
|
433
442
|
this.needNegotiation();
|
|
@@ -614,19 +623,18 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
614
623
|
this.dtlsTransports.forEach((d) => setupRole(d));
|
|
615
624
|
|
|
616
625
|
// # configure direction
|
|
617
|
-
|
|
618
|
-
|
|
626
|
+
if (["answer", "pranswer"].includes(description.type)) {
|
|
627
|
+
this.transceivers.forEach((t) => {
|
|
619
628
|
const direction = andDirection(t.direction, t.offerDirection);
|
|
620
|
-
t.
|
|
621
|
-
}
|
|
622
|
-
}
|
|
629
|
+
t.setCurrentDirection(direction);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
623
632
|
|
|
624
633
|
// for trickle ice
|
|
625
634
|
this.setLocal(description);
|
|
626
635
|
|
|
627
636
|
// connect transports
|
|
628
637
|
if (description.type === "answer") {
|
|
629
|
-
log("callee start connect");
|
|
630
638
|
this.connect().catch((err) => {
|
|
631
639
|
log("connect failed", err);
|
|
632
640
|
this.setConnectionState("failed");
|
|
@@ -634,9 +642,19 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
634
642
|
}
|
|
635
643
|
|
|
636
644
|
// # gather candidates
|
|
637
|
-
|
|
638
|
-
|
|
645
|
+
const connected = this.iceTransports.find(
|
|
646
|
+
(transport) => transport.state === "connected"
|
|
639
647
|
);
|
|
648
|
+
if (this.remoteIsBundled && connected) {
|
|
649
|
+
// no need to gather ice candidates on an existing bundled connection
|
|
650
|
+
await connected.iceGather.gather();
|
|
651
|
+
} else {
|
|
652
|
+
await Promise.all(
|
|
653
|
+
this.iceTransports.map((iceTransport) =>
|
|
654
|
+
iceTransport.iceGather.gather()
|
|
655
|
+
)
|
|
656
|
+
);
|
|
657
|
+
}
|
|
640
658
|
|
|
641
659
|
description.media
|
|
642
660
|
.filter((m) => ["audio", "video"].includes(m.kind))
|
|
@@ -718,7 +736,10 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
718
736
|
}
|
|
719
737
|
|
|
720
738
|
private async connect() {
|
|
721
|
-
if (this.
|
|
739
|
+
if (this.transportEstablished) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
log("start connect");
|
|
722
743
|
|
|
723
744
|
this.setConnectionState("connecting");
|
|
724
745
|
|
|
@@ -745,7 +766,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
745
766
|
})
|
|
746
767
|
);
|
|
747
768
|
|
|
748
|
-
this.
|
|
769
|
+
this.transportEstablished = true;
|
|
749
770
|
this.setConnectionState("connected");
|
|
750
771
|
}
|
|
751
772
|
|
|
@@ -831,8 +852,8 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
831
852
|
transceiver.kind === media.kind &&
|
|
832
853
|
[undefined, media.rtp.muxId].includes(transceiver.mid);
|
|
833
854
|
|
|
834
|
-
let transports =
|
|
835
|
-
let dtlsTransport: RTCDtlsTransport
|
|
855
|
+
let transports = remoteSdp.media.map((remoteMedia, i) => {
|
|
856
|
+
let dtlsTransport: RTCDtlsTransport;
|
|
836
857
|
|
|
837
858
|
if (["audio", "video"].includes(remoteMedia.kind)) {
|
|
838
859
|
let transceiver = this.transceivers.find((t) =>
|
|
@@ -840,7 +861,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
840
861
|
);
|
|
841
862
|
if (!transceiver) {
|
|
842
863
|
// create remote transceiver
|
|
843
|
-
transceiver = this.
|
|
864
|
+
transceiver = this._addTransceiver(remoteMedia.kind, {
|
|
844
865
|
direction: "recvonly",
|
|
845
866
|
});
|
|
846
867
|
transceiver.mid = remoteMedia.rtp.muxId;
|
|
@@ -848,7 +869,10 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
848
869
|
} else {
|
|
849
870
|
if (transceiver.direction === "inactive" && transceiver.stopping) {
|
|
850
871
|
transceiver.stopped = true;
|
|
851
|
-
|
|
872
|
+
|
|
873
|
+
if (sessionDescription.type === "answer") {
|
|
874
|
+
transceiver.setCurrentDirection("inactive");
|
|
875
|
+
}
|
|
852
876
|
return;
|
|
853
877
|
}
|
|
854
878
|
}
|
|
@@ -946,11 +970,17 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
946
970
|
});
|
|
947
971
|
}
|
|
948
972
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
await iceTransport.iceGather.gather();
|
|
952
|
-
})
|
|
973
|
+
const connected = this.iceTransports.find(
|
|
974
|
+
(transport) => transport.state === "connected"
|
|
953
975
|
);
|
|
976
|
+
if (this.remoteIsBundled && connected) {
|
|
977
|
+
// no need to gather ice candidates on an existing bundled connection
|
|
978
|
+
await connected.iceGather.gather();
|
|
979
|
+
} else {
|
|
980
|
+
await Promise.all(
|
|
981
|
+
transports.map((iceTransport) => iceTransport.iceGather.gather())
|
|
982
|
+
);
|
|
983
|
+
}
|
|
954
984
|
|
|
955
985
|
this.negotiationneeded = false;
|
|
956
986
|
if (this.shouldNegotiationneeded) {
|
|
@@ -966,8 +996,8 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
966
996
|
) {
|
|
967
997
|
if (!transceiver.mid) {
|
|
968
998
|
transceiver.mid = remoteMedia.rtp.muxId;
|
|
969
|
-
transceiver.mLineIndex = mLineIndex;
|
|
970
999
|
}
|
|
1000
|
+
transceiver.mLineIndex = mLineIndex;
|
|
971
1001
|
|
|
972
1002
|
// # negotiate codecs
|
|
973
1003
|
transceiver.codecs = remoteMedia.rtp.codecs.filter((remoteCodec) => {
|
|
@@ -999,14 +1029,13 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
999
1029
|
);
|
|
1000
1030
|
|
|
1001
1031
|
// # configure direction
|
|
1002
|
-
const mediaDirection = remoteMedia.direction
|
|
1032
|
+
const mediaDirection = remoteMedia.direction ?? "inactive";
|
|
1003
1033
|
const direction = reverseDirection(mediaDirection);
|
|
1004
1034
|
if (["answer", "pranswer"].includes(type)) {
|
|
1005
|
-
transceiver.
|
|
1035
|
+
transceiver.setCurrentDirection(direction);
|
|
1006
1036
|
} else {
|
|
1007
1037
|
transceiver.offerDirection = direction;
|
|
1008
1038
|
}
|
|
1009
|
-
|
|
1010
1039
|
const localParams = this.getLocalRtpParams(transceiver);
|
|
1011
1040
|
transceiver.sender.prepareSend(localParams);
|
|
1012
1041
|
|
|
@@ -1137,6 +1166,13 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1137
1166
|
addTransceiver(
|
|
1138
1167
|
trackOrKind: Kind | MediaStreamTrack,
|
|
1139
1168
|
options: Partial<TransceiverOptions> = {}
|
|
1169
|
+
) {
|
|
1170
|
+
return this._addTransceiver(trackOrKind, options);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
private _addTransceiver(
|
|
1174
|
+
trackOrKind: Kind | MediaStreamTrack,
|
|
1175
|
+
options: Partial<TransceiverOptions> = {}
|
|
1140
1176
|
) {
|
|
1141
1177
|
const kind =
|
|
1142
1178
|
typeof trackOrKind === "string" ? trackOrKind : trackOrKind.kind;
|
|
@@ -1150,15 +1186,15 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1150
1186
|
|
|
1151
1187
|
const sender = new RTCRtpSender(trackOrKind);
|
|
1152
1188
|
const receiver = new RTCRtpReceiver(this.config, kind, sender.ssrc);
|
|
1153
|
-
const
|
|
1189
|
+
const newTransceiver = new RTCRtpTransceiver(
|
|
1154
1190
|
kind,
|
|
1155
1191
|
dtlsTransport,
|
|
1156
1192
|
receiver,
|
|
1157
1193
|
sender,
|
|
1158
1194
|
direction
|
|
1159
1195
|
);
|
|
1160
|
-
|
|
1161
|
-
this.router.registerRtpSender(
|
|
1196
|
+
newTransceiver.options = options;
|
|
1197
|
+
this.router.registerRtpSender(newTransceiver.sender);
|
|
1162
1198
|
|
|
1163
1199
|
// reuse inactive
|
|
1164
1200
|
const inactiveTransceiverIndex = this.transceivers.findIndex(
|
|
@@ -1166,20 +1202,20 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1166
1202
|
);
|
|
1167
1203
|
const inactiveTransceiver = this.transceivers.find(
|
|
1168
1204
|
(t) => t.currentDirection === "inactive"
|
|
1169
|
-
)
|
|
1170
|
-
if (inactiveTransceiverIndex > -1) {
|
|
1171
|
-
this.
|
|
1172
|
-
|
|
1173
|
-
inactiveTransceiver.
|
|
1205
|
+
);
|
|
1206
|
+
if (inactiveTransceiverIndex > -1 && inactiveTransceiver) {
|
|
1207
|
+
this.replaceTransceiver(newTransceiver, inactiveTransceiverIndex);
|
|
1208
|
+
newTransceiver.mLineIndex = inactiveTransceiver.mLineIndex;
|
|
1209
|
+
inactiveTransceiver.setCurrentDirection(undefined);
|
|
1174
1210
|
} else {
|
|
1175
|
-
this.
|
|
1211
|
+
this.pushTransceiver(newTransceiver);
|
|
1176
1212
|
}
|
|
1177
|
-
this.onTransceiverAdded.execute(
|
|
1213
|
+
this.onTransceiverAdded.execute(newTransceiver);
|
|
1178
1214
|
|
|
1179
1215
|
this.updateIceConnectionState();
|
|
1180
1216
|
this.needNegotiation();
|
|
1181
1217
|
|
|
1182
|
-
return
|
|
1218
|
+
return newTransceiver;
|
|
1183
1219
|
}
|
|
1184
1220
|
|
|
1185
1221
|
getTransceivers() {
|
|
@@ -1230,16 +1266,18 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1230
1266
|
sender.registerTrack(track);
|
|
1231
1267
|
switch (notSendTransceiver.direction) {
|
|
1232
1268
|
case "recvonly":
|
|
1233
|
-
notSendTransceiver.
|
|
1269
|
+
notSendTransceiver.setDirection("sendrecv");
|
|
1234
1270
|
break;
|
|
1235
1271
|
case "inactive":
|
|
1236
|
-
notSendTransceiver.
|
|
1272
|
+
notSendTransceiver.setDirection("sendonly");
|
|
1237
1273
|
break;
|
|
1238
1274
|
}
|
|
1239
1275
|
this.needNegotiation();
|
|
1240
1276
|
return sender;
|
|
1241
1277
|
} else {
|
|
1242
|
-
const transceiver = this.
|
|
1278
|
+
const transceiver = this._addTransceiver(track, {
|
|
1279
|
+
direction: "sendrecv",
|
|
1280
|
+
});
|
|
1243
1281
|
this.needNegotiation();
|
|
1244
1282
|
return transceiver.sender;
|
|
1245
1283
|
}
|
|
@@ -1306,13 +1344,12 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1306
1344
|
}
|
|
1307
1345
|
|
|
1308
1346
|
// # determine DTLS role, or preserve the currently configured role
|
|
1309
|
-
if (
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
media.dtlsParams.role = dtlsTransport.role;
|
|
1347
|
+
if (media.dtlsParams) {
|
|
1348
|
+
if (dtlsTransport.role === "auto") {
|
|
1349
|
+
media.dtlsParams.role = "client";
|
|
1350
|
+
} else {
|
|
1351
|
+
media.dtlsParams.role = dtlsTransport.role;
|
|
1352
|
+
}
|
|
1316
1353
|
}
|
|
1317
1354
|
|
|
1318
1355
|
media.simulcastParameters = remoteMedia.simulcastParameters.map((v) => ({
|
package/src/sdp.ts
CHANGED
|
@@ -279,7 +279,7 @@ export class SessionDescription {
|
|
|
279
279
|
if (!bundle.items.includes(i.toString())) continue;
|
|
280
280
|
const check = session.media[i];
|
|
281
281
|
if (
|
|
282
|
-
check
|
|
282
|
+
check?.iceParams &&
|
|
283
283
|
check.iceParams.usernameFragment &&
|
|
284
284
|
check.iceParams.password
|
|
285
285
|
) {
|
|
@@ -344,7 +344,7 @@ export class SessionDescription {
|
|
|
344
344
|
get string() {
|
|
345
345
|
const lines = [`v=${this.version}`, `o=${this.origin}`, `s=${this.name}`];
|
|
346
346
|
if (this.host) {
|
|
347
|
-
lines.push(`c=${
|
|
347
|
+
lines.push(`c=${ipAddressToSdp(this.host)}`);
|
|
348
348
|
}
|
|
349
349
|
lines.push(`t=${this.time}`);
|
|
350
350
|
this.group.forEach((group) => lines.push(`a=group:${group.str}`));
|
package/src/transport/ice.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import debug from "debug";
|
|
1
2
|
import Event from "rx.mini";
|
|
2
3
|
import { v4 } from "uuid";
|
|
3
4
|
|
|
4
5
|
import { Candidate, Connection, IceOptions } from "../../../ice/src";
|
|
5
6
|
import { candidateFromSdp, candidateToSdp } from "../sdp";
|
|
6
7
|
|
|
8
|
+
const log = debug("werift:packages/webrtc/src/transport/ice.ts");
|
|
9
|
+
|
|
7
10
|
export class RTCIceTransport {
|
|
8
11
|
readonly id = v4();
|
|
9
12
|
connection = this.gather.connection;
|
|
@@ -54,9 +57,16 @@ export class RTCIceTransport {
|
|
|
54
57
|
};
|
|
55
58
|
|
|
56
59
|
setRemoteParams(remoteParameters: RTCIceParameters) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
if (
|
|
61
|
+
this.connection.remoteUsername &&
|
|
62
|
+
this.connection.remotePassword &&
|
|
63
|
+
(this.connection.remoteUsername !== remoteParameters.usernameFragment ||
|
|
64
|
+
this.connection.remotePassword !== remoteParameters.password)
|
|
65
|
+
) {
|
|
66
|
+
log("restartIce", remoteParameters);
|
|
67
|
+
this.connection.resetNominatedPair();
|
|
68
|
+
}
|
|
69
|
+
this.connection.setRemoteParams(remoteParameters);
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
async start() {
|
package/src/utils.ts
CHANGED
|
@@ -51,9 +51,6 @@ export function reverseSimulcastDirection(dir: "recv" | "send") {
|
|
|
51
51
|
export const andDirection = (a: Direction, b: Direction) =>
|
|
52
52
|
Directions[Directions.indexOf(a) & Directions.indexOf(b)];
|
|
53
53
|
|
|
54
|
-
export const orDirection = (a: Direction, b: Direction) =>
|
|
55
|
-
Directions[Directions.indexOf(a) & Directions.indexOf(b)];
|
|
56
|
-
|
|
57
54
|
export function reverseDirection(dir: Direction): Direction {
|
|
58
55
|
if (dir === "sendonly") return "recvonly";
|
|
59
56
|
if (dir === "recvonly") return "sendonly";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rtp.js","sourceRoot":"","sources":["../../../../../../rtp/src/processor/source/rtp.ts"],"names":[],"mappings":";;;AAAA,oCAAsE;AAEtE,uCAA0C;AAO1C,MAAa,eAAe;IAK1B,YACU,UAGJ,EAAE;QAHE,YAAO,GAAP,OAAO,CAGT;QAEN,OAAO,CAAC,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC;QAEpE,IAAI,CAAC,QAAQ,GAAG,IAAI,oBAAc,CAAC;YACjC,KAAK,EAAE,CAAC,UAAU,EAAE,EAAE;gBACpB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;gBAC7B,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACpD,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,MAA0B;QAC7B,MAAM,GAAG,GACP,MAAM,YAAY,eAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,eAAS,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAEvE,IACE,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,SAAS;YACrC,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,GAAG,CAAC,MAAM,CAAC,WAAW,EACnD;YACA,IAAI,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE;gBACrC,GAAG,CAAC,KAAK,EAAE,CAAC;aACb;YACD,OAAO;SACR;QAED,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;CACF;AAzCD,0CAyCC","sourcesContent":["import { ReadableStream, ReadableStreamController } from \"stream/web\";\n\nimport { RtpPacket } from \"../../rtp/rtp\";\n\nexport interface RtpOutput {\n rtp?: RtpPacket;\n eol?: boolean;\n}\n\nexport class RtpSourceStream {\n readable: ReadableStream<RtpOutput>;\n write!: (chunk: RtpOutput) => void;\n protected controller!: ReadableStreamController<RtpOutput>;\n\n constructor(\n private options: {\n payloadType?: number;\n clearInvalidPTPacket?: boolean;\n } = {}\n ) {\n options.clearInvalidPTPacket = options.clearInvalidPTPacket ?? true;\n\n this.readable = new ReadableStream({\n start: (controller) => {\n this.controller = controller;\n this.write = (chunk) => controller.enqueue(chunk);\n },\n });\n }\n\n push(packet: Buffer | RtpPacket) {\n const rtp =\n packet instanceof RtpPacket ? packet : RtpPacket.deSerialize(packet);\n\n if (\n this.options.payloadType != undefined &&\n this.options.payloadType !== rtp.header.payloadType\n ) {\n if (this.options.clearInvalidPTPacket) {\n rtp.clear();\n }\n return;\n }\n\n this.write({ rtp });\n }\n\n stop() {\n this.controller.enqueue({ eol: true });\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"red.js","sourceRoot":"","sources":["../../../../../src/media/receiver/red.ts"],"names":[],"mappings":";;;AAAA,6BAAwE;AAExE,iEAAiE;AACjE,mEAAmE;AACnE,oEAAoE;AACpE,oEAAoE;AACpE,oEAAoE;AAEpE,kBAAkB;AAClB,oBAAoB;AACpB,oBAAoB;AACpB,oBAAoB;AAEpB,MAAa,eAAe;IAA5B;QACmB,SAAI,GAAG,GAAG,CAAC;QACpB,oBAAe,GAAa,EAAE,CAAC;IAsDzC,CAAC;IApDC,IAAI,CAAC,GAAQ,EAAE,GAAc;QAC3B,MAAM,OAAO,GAAgB,EAAE,CAAC;QAEhC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;YAC5D,MAAM,cAAc,GAAG,IAAA,aAAS,EAC9B,GAAG,CAAC,MAAM,CAAC,cAAc,EACzB,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAC/B,CAAC;YACF,IAAI,eAAe,EAAE;gBACnB,OAAO,CAAC,IAAI,CACV,IAAI,aAAS,CACX,IAAI,aAAS,CAAC;oBACZ,SAAS,EAAE,IAAA,aAAS,EAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,eAAe,CAAC;oBAC5D,WAAW,EAAE,OAAO;oBACpB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI;oBACrB,cAAc;oBACd,MAAM,EAAE,IAAI;iBACb,CAAC,EACF,KAAK,CACN,CACF,CAAC;aACH;iBAAM;gBACL,OAAO,CAAC,IAAI,CACV,IAAI,aAAS,CACX,IAAI,aAAS,CAAC;oBACZ,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS;oBAC/B,WAAW,EAAE,OAAO;oBACpB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI;oBACrB,cAAc;oBACd,MAAM,EAAE,IAAI;iBACb,CAAC,EACF,KAAK,CACN,CACF,CAAC;aACH;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACpC,YAAY;YACZ,IAAI,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE;gBAC1D,OAAO,KAAK,CAAC;aACd;iBAAM;gBACL,kBAAkB;gBAClB,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE;oBAC3C,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;iBAC9B;gBACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;gBACnD,OAAO,IAAI,CAAC;aACb;QACH,CAAC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAxDD,0CAwDC","sourcesContent":["import { Red, RtpHeader, RtpPacket, uint16Add, uint32Add } from \"../..\";\n\n// 0 1 2 3\n// 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\n// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n// |F| block PT | timestamp offset | block length |\n// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n// 0 1 2 3 4 5 6 7\n// +-+-+-+-+-+-+-+-+\n// |0| Block PT |\n// +-+-+-+-+-+-+-+-+\n\nexport class AudioRedHandler {\n private readonly size = 150;\n private sequenceNumbers: number[] = [];\n\n push(red: Red, rtp: RtpPacket) {\n const packets: RtpPacket[] = [];\n\n red.blocks.forEach(({ blockPT, timestampOffset, block }, i) => {\n const sequenceNumber = uint16Add(\n rtp.header.sequenceNumber,\n -(red.blocks.length - (i + 1))\n );\n if (timestampOffset) {\n packets.push(\n new RtpPacket(\n new RtpHeader({\n timestamp: uint32Add(rtp.header.timestamp, -timestampOffset),\n payloadType: blockPT,\n ssrc: rtp.header.ssrc,\n sequenceNumber,\n marker: true,\n }),\n block\n )\n );\n } else {\n packets.push(\n new RtpPacket(\n new RtpHeader({\n timestamp: rtp.header.timestamp,\n payloadType: blockPT,\n ssrc: rtp.header.ssrc,\n sequenceNumber,\n marker: true,\n }),\n block\n )\n );\n }\n });\n\n const filtered = packets.filter((p) => {\n // duplicate\n if (this.sequenceNumbers.includes(p.header.sequenceNumber)) {\n return false;\n } else {\n // buffer overflow\n if (this.sequenceNumbers.length > this.size) {\n this.sequenceNumbers.shift();\n }\n this.sequenceNumbers.push(p.header.sequenceNumber);\n return true;\n }\n });\n return filtered;\n }\n}\n"]}
|