werift 0.12.8 → 0.13.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/common/src/binary.d.ts +9 -0
- package/lib/common/src/binary.js +37 -1
- package/lib/common/src/binary.js.map +1 -1
- package/lib/dtls/src/context/srtp.d.ts +6 -2
- package/lib/dtls/src/context/srtp.js +9 -3
- package/lib/dtls/src/context/srtp.js.map +1 -1
- package/lib/dtls/src/flight/client/flight5.js.map +1 -1
- package/lib/dtls/src/flight/server/flight2.js.map +1 -1
- package/lib/dtls/src/socket.d.ts +3 -3
- package/lib/dtls/src/socket.js +6 -8
- package/lib/dtls/src/socket.js.map +1 -1
- package/lib/ice/src/utils.d.ts +1 -0
- package/lib/ice/src/utils.js +5 -1
- package/lib/ice/src/utils.js.map +1 -1
- package/lib/rtp/src/codec/base.d.ts +8 -0
- package/lib/rtp/src/codec/base.js +16 -0
- package/lib/rtp/src/codec/base.js.map +1 -0
- package/lib/rtp/src/codec/h264.d.ts +23 -2
- package/lib/rtp/src/codec/h264.js +66 -3
- package/lib/rtp/src/codec/h264.js.map +1 -1
- package/lib/rtp/src/codec/opus.d.ts +9 -0
- package/lib/rtp/src/codec/opus.js +18 -0
- package/lib/rtp/src/codec/opus.js.map +1 -0
- package/lib/rtp/src/codec/vp8.d.ts +18 -11
- package/lib/rtp/src/codec/vp8.js +51 -29
- package/lib/rtp/src/codec/vp8.js.map +1 -1
- package/lib/rtp/src/codec/vp9.d.ts +38 -10
- package/lib/rtp/src/codec/vp9.js +115 -26
- package/lib/rtp/src/codec/vp9.js.map +1 -1
- package/lib/rtp/src/index.d.ts +2 -0
- package/lib/rtp/src/index.js +2 -0
- package/lib/rtp/src/index.js.map +1 -1
- package/lib/rtp/src/rtcp/psfb/index.d.ts +2 -2
- package/lib/rtp/src/rtcp/psfb/index.js.map +1 -1
- package/lib/rtp/src/rtcp/rr.d.ts +2 -2
- package/lib/rtp/src/rtcp/rr.js.map +1 -1
- package/lib/rtp/src/rtcp/rtpfb/index.d.ts +2 -2
- package/lib/rtp/src/rtcp/rtpfb/index.js.map +1 -1
- package/lib/rtp/src/rtcp/sdes.d.ts +2 -2
- package/lib/rtp/src/rtcp/sdes.js.map +1 -1
- package/lib/rtp/src/rtcp/sr.d.ts +2 -2
- package/lib/rtp/src/rtcp/sr.js +36 -0
- package/lib/rtp/src/rtcp/sr.js.map +1 -1
- package/lib/rtp/src/rtp/rtp.d.ts +2 -0
- package/lib/rtp/src/rtp/rtp.js +2 -0
- package/lib/rtp/src/rtp/rtp.js.map +1 -1
- package/lib/rtp/src/srtp/cipher/ctr.d.ts +17 -0
- package/lib/rtp/src/srtp/cipher/ctr.js +103 -0
- package/lib/rtp/src/srtp/cipher/ctr.js.map +1 -0
- package/lib/rtp/src/srtp/cipher/gcm.d.ts +15 -0
- package/lib/rtp/src/srtp/cipher/gcm.js +106 -0
- package/lib/rtp/src/srtp/cipher/gcm.js.map +1 -0
- package/lib/rtp/src/srtp/cipher/index.d.ts +14 -0
- package/lib/rtp/src/srtp/cipher/index.js +25 -0
- package/lib/rtp/src/srtp/cipher/index.js.map +1 -0
- package/lib/rtp/src/srtp/const.d.ts +5 -0
- package/lib/rtp/src/srtp/const.js +23 -0
- package/lib/rtp/src/srtp/const.js.map +1 -0
- package/lib/rtp/src/srtp/context/context.d.ts +13 -11
- package/lib/rtp/src/srtp/context/context.js +23 -23
- package/lib/rtp/src/srtp/context/context.js.map +1 -1
- package/lib/rtp/src/srtp/context/srtcp.d.ts +3 -2
- package/lib/rtp/src/srtp/context/srtcp.js +10 -34
- package/lib/rtp/src/srtp/context/srtcp.js.map +1 -1
- package/lib/rtp/src/srtp/context/srtp.d.ts +4 -3
- package/lib/rtp/src/srtp/context/srtp.js +9 -28
- package/lib/rtp/src/srtp/context/srtp.js.map +1 -1
- package/lib/rtp/src/srtp/srtp.js +2 -2
- package/lib/rtp/src/srtp/srtp.js.map +1 -1
- package/lib/webrtc/src/const.d.ts +4 -3
- package/lib/webrtc/src/const.js +5 -4
- package/lib/webrtc/src/const.js.map +1 -1
- package/lib/webrtc/src/helper.d.ts +2 -2
- package/lib/webrtc/src/helper.js +2 -2
- package/lib/webrtc/src/helper.js.map +1 -1
- package/lib/webrtc/src/index.d.ts +6 -2
- package/lib/webrtc/src/index.js +6 -2
- package/lib/webrtc/src/index.js.map +1 -1
- package/lib/webrtc/src/{extension → media/extension}/rtcpFeedback.d.ts +1 -1
- package/lib/webrtc/src/{extension → media/extension}/rtcpFeedback.js +0 -0
- package/lib/webrtc/src/media/extension/rtcpFeedback.js.map +1 -0
- package/lib/webrtc/src/{extension → media/extension}/rtpExtension.d.ts +1 -1
- package/lib/webrtc/src/{extension → media/extension}/rtpExtension.js +1 -1
- package/lib/webrtc/src/media/extension/rtpExtension.js.map +1 -0
- package/lib/webrtc/src/media/{nack.d.ts → receiver/nack.d.ts} +2 -2
- package/lib/webrtc/src/media/{nack.js → receiver/nack.js} +2 -2
- package/lib/webrtc/src/media/receiver/nack.js.map +1 -0
- package/lib/webrtc/src/media/{statistics.d.ts → receiver/statistics.d.ts} +1 -1
- package/lib/webrtc/src/media/{statistics.js → receiver/statistics.js} +1 -1
- package/lib/webrtc/src/media/receiver/statistics.js.map +1 -0
- package/lib/webrtc/src/media/router.js +1 -1
- package/lib/webrtc/src/media/router.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +4 -2
- package/lib/webrtc/src/media/rtpReceiver.js +13 -6
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +4 -4
- package/lib/webrtc/src/media/rtpSender.js +12 -6
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/{senderBWE → sender}/cumulativeResult.d.ts +0 -0
- package/lib/webrtc/src/media/{senderBWE → sender}/cumulativeResult.js +0 -0
- package/lib/webrtc/src/media/sender/cumulativeResult.js.map +1 -0
- package/lib/webrtc/src/media/{senderBWE → sender}/senderBWE.d.ts +0 -0
- package/lib/webrtc/src/media/{senderBWE → sender}/senderBWE.js +0 -0
- package/lib/webrtc/src/media/sender/senderBWE.js.map +1 -0
- package/lib/webrtc/src/media/track.d.ts +3 -0
- package/lib/webrtc/src/media/track.js +2 -0
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/jitterBuffer.d.ts +12 -0
- package/lib/webrtc/src/nonstandard/jitterBuffer.js +48 -0
- package/lib/webrtc/src/nonstandard/jitterBuffer.js.map +1 -0
- package/lib/webrtc/src/nonstandard/lipsync.d.ts +20 -0
- package/lib/webrtc/src/nonstandard/lipsync.js +39 -0
- package/lib/webrtc/src/nonstandard/lipsync.js.map +1 -0
- package/lib/webrtc/src/nonstandard/recorder.d.ts +18 -0
- package/lib/webrtc/src/nonstandard/recorder.js +25 -0
- package/lib/webrtc/src/nonstandard/recorder.js.map +1 -0
- package/lib/webrtc/src/nonstandard/sampleBuilder.d.ts +18 -0
- package/lib/webrtc/src/nonstandard/sampleBuilder.js +60 -0
- package/lib/webrtc/src/nonstandard/sampleBuilder.js.map +1 -0
- package/lib/webrtc/src/nonstandard/userMedia.d.ts +15 -0
- package/lib/webrtc/src/nonstandard/userMedia.js +67 -0
- package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -0
- package/lib/webrtc/src/nonstandard/webm.d.ts +24 -0
- package/lib/webrtc/src/nonstandard/webm.js +308 -0
- package/lib/webrtc/src/nonstandard/webm.js.map +1 -0
- package/lib/webrtc/src/peerConnection.js +1 -0
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +2 -1
- package/lib/webrtc/src/transport/dtls.js +7 -3
- package/lib/webrtc/src/transport/dtls.js.map +1 -1
- package/lib/webrtc/src/utils.d.ts +3 -0
- package/lib/webrtc/src/utils.js +13 -9
- package/lib/webrtc/src/utils.js.map +1 -1
- package/package.json +5 -4
- package/src/const.ts +8 -3
- package/src/helper.ts +4 -4
- package/src/index.ts +6 -2
- package/src/{extension → media/extension}/rtcpFeedback.ts +1 -1
- package/src/{extension → media/extension}/rtpExtension.ts +1 -1
- package/src/media/{nack.ts → receiver/nack.ts} +3 -3
- package/src/media/{statistics.ts → receiver/statistics.ts} +2 -2
- package/src/media/router.ts +1 -1
- package/src/media/rtpReceiver.ts +11 -7
- package/src/media/rtpSender.ts +19 -9
- package/src/media/{senderBWE → sender}/cumulativeResult.ts +0 -0
- package/src/media/{senderBWE → sender}/senderBWE.ts +0 -0
- package/src/media/track.ts +6 -0
- package/src/nonstandard/jitterBuffer.ts +47 -0
- package/src/nonstandard/lipsync.ts +55 -0
- package/src/nonstandard/recorder.ts +26 -0
- package/src/nonstandard/sampleBuilder.ts +71 -0
- package/src/nonstandard/userMedia.ts +74 -0
- package/src/nonstandard/webm.ts +421 -0
- package/src/peerConnection.ts +3 -1
- package/src/transport/dtls.ts +12 -4
- package/src/utils.ts +20 -12
- package/lib/webrtc/src/extension/rtcpFeedback.js.map +0 -1
- package/lib/webrtc/src/extension/rtpExtension.js.map +0 -1
- package/lib/webrtc/src/media/nack.js.map +0 -1
- package/lib/webrtc/src/media/senderBWE/cumulativeResult.js.map +0 -1
- package/lib/webrtc/src/media/senderBWE/senderBWE.js.map +0 -1
- package/lib/webrtc/src/media/statistics.js.map +0 -1
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import * as EBML from "@shinyoshiaki/ebml-builder";
|
|
2
|
+
import { debug } from "debug";
|
|
3
|
+
import { appendFile, readFile, writeFile } from "fs/promises";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
BitWriter,
|
|
7
|
+
BufferChain,
|
|
8
|
+
bufferWriter,
|
|
9
|
+
bufferWriterLE,
|
|
10
|
+
} from "../../../common/src";
|
|
11
|
+
import {
|
|
12
|
+
H264RtpPayload,
|
|
13
|
+
OpusRtpPayload,
|
|
14
|
+
Vp8RtpPayload,
|
|
15
|
+
Vp9RtpPayload,
|
|
16
|
+
} from "../../../rtp/src";
|
|
17
|
+
import { PromiseQueue } from "../helper";
|
|
18
|
+
import { MediaStreamTrack } from "../media/track";
|
|
19
|
+
import { SampleBuilder } from "./sampleBuilder";
|
|
20
|
+
|
|
21
|
+
const log = debug("werift:packages/webrtc/src/nonstandard/webm.ts");
|
|
22
|
+
|
|
23
|
+
export class WebmFactory {
|
|
24
|
+
private relativeTimestamp = 0;
|
|
25
|
+
private disposer?: () => void;
|
|
26
|
+
private position = 0;
|
|
27
|
+
private staticPartOffset = 0;
|
|
28
|
+
private queue = new PromiseQueue();
|
|
29
|
+
|
|
30
|
+
/**video cuePoints (keyframe) */
|
|
31
|
+
private cuePoints: CuePoint[] = [];
|
|
32
|
+
private tracks: {
|
|
33
|
+
audio?: {
|
|
34
|
+
trackNumber: number;
|
|
35
|
+
track: MediaStreamTrack;
|
|
36
|
+
sampleBuilder: SampleBuilder;
|
|
37
|
+
};
|
|
38
|
+
video?: {
|
|
39
|
+
trackNumber: number;
|
|
40
|
+
track: MediaStreamTrack;
|
|
41
|
+
sampleBuilder: SampleBuilder;
|
|
42
|
+
};
|
|
43
|
+
} = {};
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
tracks: MediaStreamTrack[],
|
|
47
|
+
public path: string,
|
|
48
|
+
public options: TrackOptions = {}
|
|
49
|
+
) {
|
|
50
|
+
tracks.forEach((track, i) => {
|
|
51
|
+
const sampleBuilder = (() => {
|
|
52
|
+
if (track.kind === "video") {
|
|
53
|
+
switch (
|
|
54
|
+
track.codec?.name.toLocaleLowerCase() as SupportedVideoCodec
|
|
55
|
+
) {
|
|
56
|
+
case "vp8":
|
|
57
|
+
return new SampleBuilder(Vp8RtpPayload, 90000);
|
|
58
|
+
case "h264":
|
|
59
|
+
return new SampleBuilder(H264RtpPayload, 90000);
|
|
60
|
+
case "vp9":
|
|
61
|
+
return new SampleBuilder(Vp9RtpPayload, 90000);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
return new SampleBuilder(OpusRtpPayload, 48000);
|
|
65
|
+
}
|
|
66
|
+
})();
|
|
67
|
+
this.tracks[track.kind] = { track, trackNumber: i + 1, sampleBuilder };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async start() {
|
|
72
|
+
const entries = createTrackEntries(
|
|
73
|
+
Object.values(this.tracks).map(({ track }) => track),
|
|
74
|
+
this.options
|
|
75
|
+
);
|
|
76
|
+
const segment = EBML.build(createSegment(entries));
|
|
77
|
+
const staticPart = Buffer.concat([ebmlHeader, segment]);
|
|
78
|
+
this.staticPartOffset = staticPart.length;
|
|
79
|
+
|
|
80
|
+
await writeFile(this.path, staticPart);
|
|
81
|
+
this.position += staticPart.length;
|
|
82
|
+
|
|
83
|
+
const length = await this.appendCluster(0.0);
|
|
84
|
+
this.appendCuePoint(0.0);
|
|
85
|
+
this.position += length;
|
|
86
|
+
|
|
87
|
+
const disposers = Object.values(this.tracks).map(
|
|
88
|
+
({ track, sampleBuilder, trackNumber }) => {
|
|
89
|
+
const appendCluster = async () => {
|
|
90
|
+
if (sampleBuilder.relativeTimestamp === 0) return;
|
|
91
|
+
|
|
92
|
+
this.relativeTimestamp += sampleBuilder.relativeTimestamp;
|
|
93
|
+
|
|
94
|
+
const length = await this.appendCluster(this.relativeTimestamp);
|
|
95
|
+
this.appendCuePoint(this.relativeTimestamp);
|
|
96
|
+
this.position += length;
|
|
97
|
+
|
|
98
|
+
Object.values(this.tracks).forEach(({ sampleBuilder }) =>
|
|
99
|
+
sampleBuilder.resetTimestamp()
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const { unSubscribe } = track.onReceiveRtp.subscribe((rtp) => {
|
|
104
|
+
this.queue.push(async () => {
|
|
105
|
+
{
|
|
106
|
+
const frame = sampleBuilder.DePacketizer.deSerialize(rtp.payload);
|
|
107
|
+
if (track.kind === "video" && frame.isKeyframe) {
|
|
108
|
+
await appendCluster();
|
|
109
|
+
} else if (sampleBuilder.relativeTimestamp >= MaxSinged16Int) {
|
|
110
|
+
await appendCluster();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
sampleBuilder.push(rtp);
|
|
114
|
+
const res = sampleBuilder.build();
|
|
115
|
+
if (!res) return;
|
|
116
|
+
const { data, relativeTimestamp, isKeyframe } = res;
|
|
117
|
+
if (relativeTimestamp >= MaxSinged16Int) {
|
|
118
|
+
log("relativeTimestamp exceeded", relativeTimestamp);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (track.kind === "video") {
|
|
123
|
+
const [cuePoint] = this.cuePoints.slice(-1);
|
|
124
|
+
cuePoint.blockNumber++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await this.write(
|
|
128
|
+
data,
|
|
129
|
+
isKeyframe,
|
|
130
|
+
relativeTimestamp,
|
|
131
|
+
trackNumber
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
return unSubscribe;
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
this.disposer = () => {
|
|
140
|
+
disposers.forEach((unSubscribe) => unSubscribe());
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async stop() {
|
|
145
|
+
await this.queue.push(async () => {
|
|
146
|
+
if (!this.disposer) {
|
|
147
|
+
throw new Error();
|
|
148
|
+
}
|
|
149
|
+
this.disposer();
|
|
150
|
+
|
|
151
|
+
const latestTimestamp = Object.values(this.tracks)
|
|
152
|
+
.map(({ sampleBuilder }) => sampleBuilder)
|
|
153
|
+
.sort(
|
|
154
|
+
(a, b) => a.relativeTimestamp - b.relativeTimestamp
|
|
155
|
+
)[0].relativeTimestamp;
|
|
156
|
+
const duration = this.relativeTimestamp + latestTimestamp;
|
|
157
|
+
|
|
158
|
+
const entries = createTrackEntries(
|
|
159
|
+
Object.values(this.tracks).map(({ track }) => track),
|
|
160
|
+
this.options
|
|
161
|
+
);
|
|
162
|
+
const segment = EBML.build(
|
|
163
|
+
createSegment(entries, [
|
|
164
|
+
EBML.element(EBML.ID.Duration, EBML.float(duration)),
|
|
165
|
+
])
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const staticPart = Buffer.concat([ebmlHeader, segment]);
|
|
169
|
+
const staticPartGap = staticPart.length - this.staticPartOffset;
|
|
170
|
+
|
|
171
|
+
let cueSize = 0;
|
|
172
|
+
let cues = getCues(this.cuePoints);
|
|
173
|
+
while (cueSize !== cues.length) {
|
|
174
|
+
cueSize = cues.length;
|
|
175
|
+
this.cuePoints.forEach((cue) => {
|
|
176
|
+
cue.clusterOffset = staticPartGap + cueSize;
|
|
177
|
+
});
|
|
178
|
+
cues = getCues(this.cuePoints);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const clusters = (await readFile(this.path)).slice(this.staticPartOffset);
|
|
182
|
+
|
|
183
|
+
await writeFile(this.path, staticPart);
|
|
184
|
+
await appendFile(this.path, cues);
|
|
185
|
+
await appendFile(this.path, clusters);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private async write(
|
|
190
|
+
data: Buffer,
|
|
191
|
+
isKeyframe: boolean,
|
|
192
|
+
relativeTimestamp: number,
|
|
193
|
+
trackNumber: number
|
|
194
|
+
) {
|
|
195
|
+
const simpleBlock = createSimpleBlock(
|
|
196
|
+
data,
|
|
197
|
+
isKeyframe,
|
|
198
|
+
relativeTimestamp,
|
|
199
|
+
trackNumber
|
|
200
|
+
);
|
|
201
|
+
this.position += simpleBlock.length;
|
|
202
|
+
|
|
203
|
+
await appendFile(this.path, simpleBlock);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private async appendCluster(timecode: number) {
|
|
207
|
+
const buf = EBML.build(
|
|
208
|
+
EBML.unknownSizeElement(EBML.ID.Cluster, [
|
|
209
|
+
EBML.element(EBML.ID.Timecode, EBML.number(timecode)),
|
|
210
|
+
])
|
|
211
|
+
);
|
|
212
|
+
await appendFile(this.path, buf);
|
|
213
|
+
return buf.length;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private appendCuePoint(timecode: number) {
|
|
217
|
+
const trackNumber = this.tracks?.video?.trackNumber;
|
|
218
|
+
if (trackNumber != undefined) {
|
|
219
|
+
this.cuePoints.push(new CuePoint(trackNumber, timecode, this.position));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
///////////////////////////////////////////////
|
|
225
|
+
|
|
226
|
+
const ebmlHeader = EBML.build(
|
|
227
|
+
EBML.element(EBML.ID.EBML, [
|
|
228
|
+
EBML.element(EBML.ID.EBMLVersion, EBML.number(1)),
|
|
229
|
+
EBML.element(EBML.ID.EBMLReadVersion, EBML.number(1)),
|
|
230
|
+
EBML.element(EBML.ID.EBMLMaxIDLength, EBML.number(4)),
|
|
231
|
+
EBML.element(EBML.ID.EBMLMaxSizeLength, EBML.number(8)),
|
|
232
|
+
EBML.element(EBML.ID.DocType, EBML.string("webm")),
|
|
233
|
+
EBML.element(EBML.ID.DocTypeVersion, EBML.number(2)),
|
|
234
|
+
EBML.element(EBML.ID.DocTypeReadVersion, EBML.number(2)),
|
|
235
|
+
])
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
function createSimpleBlock(
|
|
239
|
+
data: Buffer,
|
|
240
|
+
isKeyframe: boolean,
|
|
241
|
+
relativeTimestamp: number,
|
|
242
|
+
trackNumber: number
|
|
243
|
+
) {
|
|
244
|
+
const elementId = Buffer.from([0xa3]);
|
|
245
|
+
const contentSize: Uint8Array = EBML.vintEncodedNumber(
|
|
246
|
+
1 + 2 + 1 + data.length
|
|
247
|
+
).bytes;
|
|
248
|
+
|
|
249
|
+
const flags = new BitWriter(8);
|
|
250
|
+
const keyframe = isKeyframe ? 1 : 0;
|
|
251
|
+
flags.set(1, 0, keyframe);
|
|
252
|
+
flags.set(3, 1, 0);
|
|
253
|
+
flags.set(1, 4, 0);
|
|
254
|
+
flags.set(2, 5, 0);
|
|
255
|
+
flags.set(1, 7, 0);
|
|
256
|
+
|
|
257
|
+
const simpleBlock = Buffer.concat([
|
|
258
|
+
elementId,
|
|
259
|
+
contentSize,
|
|
260
|
+
EBML.vintEncodedNumber(trackNumber).bytes,
|
|
261
|
+
new BufferChain(2).writeInt16BE(relativeTimestamp).buffer,
|
|
262
|
+
new BufferChain(1).writeUInt8(flags.value).buffer,
|
|
263
|
+
data,
|
|
264
|
+
]);
|
|
265
|
+
return simpleBlock;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function createTrackEntries(
|
|
269
|
+
tracks: MediaStreamTrack[],
|
|
270
|
+
options: TrackOptions = {}
|
|
271
|
+
) {
|
|
272
|
+
return tracks.map((track, i) => {
|
|
273
|
+
if (track.kind === "video") {
|
|
274
|
+
const codec =
|
|
275
|
+
track.codec?.name.toLocaleLowerCase() as SupportedVideoCodec;
|
|
276
|
+
const codecName = (() => {
|
|
277
|
+
switch (codec) {
|
|
278
|
+
case "vp8":
|
|
279
|
+
return "VP8";
|
|
280
|
+
case "h264":
|
|
281
|
+
return "MPEG4/ISO/AVC";
|
|
282
|
+
case "vp9":
|
|
283
|
+
return "VP9";
|
|
284
|
+
default:
|
|
285
|
+
throw new Error();
|
|
286
|
+
}
|
|
287
|
+
})();
|
|
288
|
+
const trackElements = [
|
|
289
|
+
EBML.element(EBML.ID.Video, [
|
|
290
|
+
EBML.element(EBML.ID.PixelWidth, EBML.number(options.width)),
|
|
291
|
+
EBML.element(EBML.ID.PixelHeight, EBML.number(options.height)),
|
|
292
|
+
]),
|
|
293
|
+
];
|
|
294
|
+
if (codec === "vp9") {
|
|
295
|
+
const profile = Buffer.concat([
|
|
296
|
+
new BitWriter(8).set(1, 0, 0).set(7, 1, 1).buffer,
|
|
297
|
+
bufferWriter([1, 1], [1, 0]),
|
|
298
|
+
]);
|
|
299
|
+
// const level = Buffer.concat([
|
|
300
|
+
// new BitWriter(8).set(1, 0, 0).set(7, 1, 2).buffer,
|
|
301
|
+
// bufferWriter([1, 1], [1, 10]),
|
|
302
|
+
// ]);
|
|
303
|
+
// const bitDepth = Buffer.concat([
|
|
304
|
+
// new BitWriter(8).set(1, 0, 0).set(7, 1, 3).buffer,
|
|
305
|
+
// bufferWriter([1, 1], [1, 8]),
|
|
306
|
+
// ]);
|
|
307
|
+
// const chroma = Buffer.concat([
|
|
308
|
+
// new BitWriter(8).set(1, 0, 0).set(7, 1, 4).buffer,
|
|
309
|
+
// bufferWriter([1, 1], [1, 0]),
|
|
310
|
+
// ]);
|
|
311
|
+
|
|
312
|
+
// trackElements.push(
|
|
313
|
+
// EBML.element(
|
|
314
|
+
// EBML.ID.CodecPrivate,
|
|
315
|
+
// EBML.bytes(Buffer.concat([profile]))
|
|
316
|
+
// )
|
|
317
|
+
// );
|
|
318
|
+
}
|
|
319
|
+
return createTrackEntry(i + 1, codecName, "video", trackElements);
|
|
320
|
+
} else {
|
|
321
|
+
return createTrackEntry(i + 1, "OPUS", "audio", [
|
|
322
|
+
EBML.element(EBML.ID.Audio, [
|
|
323
|
+
EBML.element(EBML.ID.SamplingFrequency, EBML.float(48000.0)),
|
|
324
|
+
EBML.element(EBML.ID.Channels, EBML.number(2)),
|
|
325
|
+
]),
|
|
326
|
+
EBML.element(
|
|
327
|
+
EBML.ID.CodecPrivate,
|
|
328
|
+
EBML.bytes(
|
|
329
|
+
Buffer.concat([
|
|
330
|
+
Buffer.from("OpusHead"),
|
|
331
|
+
bufferWriter([1, 1], [1, 2]),
|
|
332
|
+
bufferWriterLE([2, 4, 2, 1], [312, 48000, 0, 0]),
|
|
333
|
+
])
|
|
334
|
+
)
|
|
335
|
+
),
|
|
336
|
+
]);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function createTrackEntry(
|
|
342
|
+
trackNumber: number,
|
|
343
|
+
codec: string,
|
|
344
|
+
type: "audio" | "video",
|
|
345
|
+
elements: EBML.EBMLData[] = []
|
|
346
|
+
) {
|
|
347
|
+
return EBML.element(EBML.ID.TrackEntry, [
|
|
348
|
+
EBML.element(EBML.ID.TrackNumber, EBML.number(trackNumber)),
|
|
349
|
+
EBML.element(EBML.ID.TrackUID, EBML.number(trackNumber)),
|
|
350
|
+
EBML.element(EBML.ID.CodecName, EBML.string(codec)),
|
|
351
|
+
EBML.element(EBML.ID.TrackType, EBML.number(type === "video" ? 1 : 2)),
|
|
352
|
+
EBML.element(
|
|
353
|
+
EBML.ID.CodecID,
|
|
354
|
+
EBML.string(`${type === "video" ? "V" : "A"}_${codec}`)
|
|
355
|
+
),
|
|
356
|
+
...elements,
|
|
357
|
+
]);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function createSegment(
|
|
361
|
+
entries: EBML.EBMLData[],
|
|
362
|
+
infoElements: EBML.EBMLData[] = []
|
|
363
|
+
) {
|
|
364
|
+
return EBML.unknownSizeElement(EBML.ID.Segment, [
|
|
365
|
+
EBML.element(EBML.ID.SeekHead, []),
|
|
366
|
+
EBML.element(EBML.ID.Info, [
|
|
367
|
+
EBML.element(EBML.ID.TimecodeScale, EBML.number(millisecond)),
|
|
368
|
+
EBML.element(EBML.ID.MuxingApp, EBML.string("webrtc")),
|
|
369
|
+
EBML.element(EBML.ID.WritingApp, EBML.string("webrtc")),
|
|
370
|
+
...infoElements,
|
|
371
|
+
]),
|
|
372
|
+
EBML.element(EBML.ID.Tracks, entries),
|
|
373
|
+
]);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
class CuePoint {
|
|
377
|
+
private readonly seekHeadPosition = 48;
|
|
378
|
+
clusterOffset: number = 0;
|
|
379
|
+
blockNumber = 0;
|
|
380
|
+
|
|
381
|
+
constructor(
|
|
382
|
+
private trackNumber: number,
|
|
383
|
+
private relativeTimestamp: number,
|
|
384
|
+
private position: number
|
|
385
|
+
) {}
|
|
386
|
+
|
|
387
|
+
build() {
|
|
388
|
+
const cue = EBML.element(EBML.ID.CuePoint, [
|
|
389
|
+
EBML.element(EBML.ID.CueTime, EBML.number(this.relativeTimestamp)),
|
|
390
|
+
EBML.element(EBML.ID.CueTrackPositions, [
|
|
391
|
+
EBML.element(EBML.ID.CueTrack, EBML.number(this.trackNumber)),
|
|
392
|
+
EBML.element(
|
|
393
|
+
EBML.ID.CueClusterPosition,
|
|
394
|
+
EBML.number(
|
|
395
|
+
this.clusterOffset + this.position - this.seekHeadPosition
|
|
396
|
+
)
|
|
397
|
+
),
|
|
398
|
+
EBML.element(EBML.ID.CueBlockNumber, EBML.number(this.blockNumber)),
|
|
399
|
+
]),
|
|
400
|
+
]);
|
|
401
|
+
return cue;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function getCues(cuePoints: CuePoint[]) {
|
|
406
|
+
return EBML.build(
|
|
407
|
+
EBML.element(
|
|
408
|
+
EBML.ID.Cues,
|
|
409
|
+
cuePoints.map((cue) => cue.build())
|
|
410
|
+
)
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const millisecond = 1000000;
|
|
415
|
+
/**32767 */
|
|
416
|
+
const MaxSinged16Int = (0x01 << 16) / 2 - 1;
|
|
417
|
+
|
|
418
|
+
type TrackOptions = Partial<{ width: number; height: number }>;
|
|
419
|
+
|
|
420
|
+
const supportedVideoCodecs = ["h264", "vp8", "vp9"] as const;
|
|
421
|
+
type SupportedVideoCodec = typeof supportedVideoCodecs[number];
|
package/src/peerConnection.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { cloneDeep, isEqual } from "lodash";
|
|
|
3
3
|
import Event from "rx.mini";
|
|
4
4
|
import * as uuid from "uuid";
|
|
5
5
|
|
|
6
|
+
import { Profile } from "../../dtls/src/context/srtp";
|
|
6
7
|
import {
|
|
7
8
|
DISCARD_HOST,
|
|
8
9
|
DISCARD_PORT,
|
|
@@ -157,6 +158,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
157
158
|
});
|
|
158
159
|
|
|
159
160
|
const { iceTransport, dtlsTransport } = this.createTransport([
|
|
161
|
+
SRTP_PROFILE.SRTP_AEAD_AES_128_GCM, // prefer
|
|
160
162
|
SRTP_PROFILE.SRTP_AES128_CM_HMAC_SHA1_80,
|
|
161
163
|
]);
|
|
162
164
|
this.iceTransport = iceTransport;
|
|
@@ -353,7 +355,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
353
355
|
});
|
|
354
356
|
};
|
|
355
357
|
|
|
356
|
-
private createTransport(srtpProfiles:
|
|
358
|
+
private createTransport(srtpProfiles: Profile[] = []) {
|
|
357
359
|
const iceGatherer = new RTCIceGatherer({
|
|
358
360
|
...parseIceServers(this.configuration.iceServers),
|
|
359
361
|
forceTurn: this.configuration.iceTransportPolicy === "relay",
|
package/src/transport/dtls.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
SignatureHash,
|
|
17
17
|
} from "../../../dtls/src/cipher/const";
|
|
18
18
|
import { CipherContext } from "../../../dtls/src/context/cipher";
|
|
19
|
+
import { Profile } from "../../../dtls/src/context/srtp";
|
|
19
20
|
import { Connection } from "../../../ice/src";
|
|
20
21
|
import {
|
|
21
22
|
RtcpPacket,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
SrtcpSession,
|
|
26
27
|
SrtpSession,
|
|
27
28
|
} from "../../../rtp/src";
|
|
29
|
+
import { keyLength, saltLength } from "../../../rtp/src/srtp/const";
|
|
28
30
|
import { RtpRouter } from "../media/router";
|
|
29
31
|
import { fingerprint, isDtls, isMedia, isRtcp } from "../utils";
|
|
30
32
|
import { RTCIceTransport } from "./ice";
|
|
@@ -51,7 +53,7 @@ export class RTCDtlsTransport {
|
|
|
51
53
|
readonly iceTransport: RTCIceTransport,
|
|
52
54
|
readonly router: RtpRouter,
|
|
53
55
|
readonly certificates: RTCCertificate[],
|
|
54
|
-
private readonly srtpProfiles:
|
|
56
|
+
private readonly srtpProfiles: Profile[] = []
|
|
55
57
|
) {}
|
|
56
58
|
|
|
57
59
|
get localParameters() {
|
|
@@ -151,9 +153,15 @@ export class RTCDtlsTransport {
|
|
|
151
153
|
if (this.srtpStarted) return;
|
|
152
154
|
this.srtpStarted = true;
|
|
153
155
|
|
|
156
|
+
const profile = this.dtls.srtp.srtpProfile;
|
|
157
|
+
if (!profile) {
|
|
158
|
+
throw new Error("need srtpProfile");
|
|
159
|
+
}
|
|
160
|
+
log("selected SRTP Profile", profile);
|
|
161
|
+
|
|
154
162
|
const { localKey, localSalt, remoteKey, remoteSalt } =
|
|
155
|
-
this.dtls.extractSessionKeys();
|
|
156
|
-
|
|
163
|
+
this.dtls.extractSessionKeys(keyLength(profile), saltLength(profile));
|
|
164
|
+
|
|
157
165
|
const config = {
|
|
158
166
|
keys: {
|
|
159
167
|
localMasterKey: localKey,
|
|
@@ -161,7 +169,7 @@ export class RTCDtlsTransport {
|
|
|
161
169
|
remoteMasterKey: remoteKey,
|
|
162
170
|
remoteMasterSalt: remoteSalt,
|
|
163
171
|
},
|
|
164
|
-
profile
|
|
172
|
+
profile,
|
|
165
173
|
};
|
|
166
174
|
this.srtp = new SrtpSession(config);
|
|
167
175
|
this.srtcp = new SrtcpSession(config);
|
package/src/utils.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
/* eslint-disable prefer-const */
|
|
2
|
-
import { createHash
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
3
|
import debug from "debug";
|
|
4
|
-
import { jspack } from "jspack";
|
|
5
4
|
import { performance } from "perf_hooks";
|
|
6
5
|
|
|
7
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
bufferReader,
|
|
8
|
+
bufferWriter,
|
|
9
|
+
random16,
|
|
10
|
+
random32,
|
|
11
|
+
uint16Add,
|
|
12
|
+
uint32Add,
|
|
13
|
+
} from "../../common/src";
|
|
8
14
|
import { Address } from "../../ice/src";
|
|
9
15
|
import { RtpHeader, RtpPacket } from "../../rtp/src";
|
|
10
16
|
import { Direction, Directions } from "./media/rtpTransceiver";
|
|
@@ -57,21 +63,23 @@ export const microTime = () => now.micro() as number;
|
|
|
57
63
|
|
|
58
64
|
export const milliTime = () => new Date().getTime();
|
|
59
65
|
|
|
66
|
+
/**https://datatracker.ietf.org/doc/html/rfc3550#section-4 */
|
|
60
67
|
export const ntpTime = () => {
|
|
61
68
|
const now = performance.timeOrigin + performance.now() - Date.UTC(1900, 0, 1);
|
|
62
69
|
|
|
63
|
-
const
|
|
70
|
+
const seconds = now / 1000;
|
|
71
|
+
const [sec, msec] = seconds.toString().split(".").map(Number);
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
const buf = bufferWriter([4, 4], [sec, msec]);
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const high = BigInt(sec);
|
|
70
|
-
const v = BigInt(msec + [...Array(6 - msec.length)].fill(0).join(""));
|
|
71
|
-
|
|
72
|
-
const low = (v * (1n << 32n)) / 1000000n;
|
|
75
|
+
return buf.readBigUInt64BE();
|
|
76
|
+
};
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
/**https://datatracker.ietf.org/doc/html/rfc3550#section-4 */
|
|
79
|
+
export const compactNtp = (ntp: bigint) => {
|
|
80
|
+
const buf = bufferWriter([8], [ntp]);
|
|
81
|
+
const [, sec, msec] = bufferReader(buf, [2, 2, 2, 2]);
|
|
82
|
+
return bufferWriter([2, 2], [sec, msec]).readUInt32BE();
|
|
75
83
|
};
|
|
76
84
|
|
|
77
85
|
export function parseIceServers(iceServers: RTCIceServer[]) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rtcpFeedback.js","sourceRoot":"","sources":["../../../../src/extension/rtcpFeedback.ts"],"names":[],"mappings":";;;AAEO,MAAM,MAAM,GAAG,GAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAA3D,QAAA,MAAM,UAAqD;AAEjE,MAAM,OAAO,GAAG,GAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAA3C,QAAA,OAAO,WAAoC;AAEjD,MAAM,MAAM,GAAG,GAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAA5D,QAAA,MAAM,UAAsD;AAElE,MAAM,OAAO,GAAG,GAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;AAAhD,QAAA,OAAO,WAAyC;AAEtD,MAAM,OAAO,GAAG,GAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;AAAnD,QAAA,OAAO,WAA4C","sourcesContent":["import { RTCPFB } from \"../media/parameters\";\n\nexport const useFIR = (): RTCPFB => ({ type: \"ccm\", parameter: \"fir\" });\n\nexport const useNACK = (): RTCPFB => ({ type: \"nack\" });\n\nexport const usePLI = (): RTCPFB => ({ type: \"nack\", parameter: \"pli\" });\n\nexport const useREMB = (): RTCPFB => ({ type: \"goog-remb\" });\n\nexport const useTWCC = (): RTCPFB => ({ type: \"transport-cc\" });\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rtpExtension.js","sourceRoot":"","sources":["../../../../src/extension/rtpExtension.ts"],"names":[],"mappings":";;;AAAA,oDAAsE;AAEzD,QAAA,iBAAiB,GAAG;IAC/B,OAAO,EAAE,qCAAqC;IAC9C,eAAe,EAAE,+CAA+C;IAChE,mBAAmB,EAAE,wDAAwD;IAC7E,eAAe,EACb,2EAA2E;IAC7E,WAAW,EAAE,4DAA4D;CACjE,CAAC;AAEX,SAAgB,UAAU;IACxB,OAAO,IAAI,4CAA+B,CAAC;QACzC,GAAG,EAAE,yBAAiB,CAAC,OAAO;KAC/B,CAAC,CAAC;AACL,CAAC;AAJD,gCAIC;AAED,SAAgB,kBAAkB;IAChC,OAAO,IAAI,4CAA+B,CAAC;QACzC,GAAG,EAAE,yBAAiB,CAAC,eAAe;KACvC,CAAC,CAAC;AACL,CAAC;AAJD,gDAIC;AAED,SAAgB,sBAAsB;IACpC,OAAO,IAAI,4CAA+B,CAAC;QACzC,GAAG,EAAE,yBAAiB,CAAC,mBAAmB;KAC3C,CAAC,CAAC;AACL,CAAC;AAJD,wDAIC;AAED,SAAgB,kBAAkB;IAChC,OAAO,IAAI,4CAA+B,CAAC;QACzC,GAAG,EAAE,yBAAiB,CAAC,eAAe;KACvC,CAAC,CAAC;AACL,CAAC;AAJD,gDAIC;AAED,SAAgB,cAAc;IAC5B,OAAO,IAAI,4CAA+B,CAAC;QACzC,GAAG,EAAE,yBAAiB,CAAC,WAAW;KACnC,CAAC,CAAC;AACL,CAAC;AAJD,wCAIC","sourcesContent":["import { RTCRtpHeaderExtensionParameters } from \"../media/parameters\";\n\nexport const RTP_EXTENSION_URI = {\n sdesMid: \"urn:ietf:params:rtp-hdrext:sdes:mid\",\n sdesRTPStreamID: \"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\",\n repairedRtpStreamId: \"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\",\n transportWideCC:\n \"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\",\n absSendTime: \"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\",\n} as const;\n\nexport function useSdesMid() {\n return new RTCRtpHeaderExtensionParameters({\n uri: RTP_EXTENSION_URI.sdesMid,\n });\n}\n\nexport function useSdesRTPStreamId() {\n return new RTCRtpHeaderExtensionParameters({\n uri: RTP_EXTENSION_URI.sdesRTPStreamID,\n });\n}\n\nexport function useRepairedRtpStreamId() {\n return new RTCRtpHeaderExtensionParameters({\n uri: RTP_EXTENSION_URI.repairedRtpStreamId,\n });\n}\n\nexport function useTransportWideCC() {\n return new RTCRtpHeaderExtensionParameters({\n uri: RTP_EXTENSION_URI.transportWideCC,\n });\n}\n\nexport function useAbsSendTime() {\n return new RTCRtpHeaderExtensionParameters({\n uri: RTP_EXTENSION_URI.absSendTime,\n });\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nack.js","sourceRoot":"","sources":["../../../../src/media/nack.ts"],"names":[],"mappings":"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","sourcesContent":["import { range } from \"lodash\";\nimport Event from \"rx.mini\";\n\nimport { uint16Add } from \"../../../common/src\";\nimport {\n GenericNack,\n RtcpTransportLayerFeedback,\n RtpPacket,\n} from \"../../../rtp/src\";\nimport { RTCRtpReceiver } from \"./rtpReceiver\";\n\nconst LOST_SIZE = 30 * 5;\n\nexport class Nack {\n private newEstSeqNum = 0;\n private _lost: { [seqNum: number]: number } = {};\n private nackLoop = setInterval(() => this.packetLost(), 20);\n\n readonly onPacketLost = new Event<[GenericNack]>();\n mediaSourceSsrc?: number;\n\n constructor(private receiver: RTCRtpReceiver) {}\n\n get lost() {\n return Object.keys(this._lost).map(Number);\n }\n\n addPacket(packet: RtpPacket) {\n const { sequenceNumber, ssrc } = packet.header;\n this.mediaSourceSsrc = ssrc;\n\n if (this.newEstSeqNum === 0) {\n this.newEstSeqNum = sequenceNumber;\n return;\n }\n\n if (this._lost[sequenceNumber]) {\n delete this._lost[sequenceNumber];\n return;\n }\n\n if (sequenceNumber === uint16Add(this.newEstSeqNum, 1)) {\n this.newEstSeqNum = sequenceNumber;\n } else if (sequenceNumber > uint16Add(this.newEstSeqNum, 1)) {\n // packet lost detected\n range(uint16Add(this.newEstSeqNum, 1), sequenceNumber).forEach((seq) => {\n this._lost[seq] = 1;\n });\n this.receiver.sendRtcpPLI(this.mediaSourceSsrc);\n\n this.newEstSeqNum = sequenceNumber;\n\n if (Object.keys(this._lost).length > LOST_SIZE) {\n this._lost = Object.entries(this._lost)\n .slice(-LOST_SIZE)\n .reduce((acc, [key, v]) => {\n acc[key] = v;\n return acc;\n }, {} as { [seqNum: number]: number });\n }\n }\n }\n\n close() {\n clearInterval(this.nackLoop);\n }\n\n private increment() {\n Object.keys(this._lost).forEach((seq) => {\n if (++this._lost[seq] > 10) {\n delete this._lost[seq];\n }\n });\n }\n\n private packetLost() {\n if (this.lost.length > 0 && this.mediaSourceSsrc) {\n const nack = new GenericNack({\n senderSsrc: this.receiver.rtcpSsrc,\n mediaSourceSsrc: this.mediaSourceSsrc,\n lost: this.lost,\n });\n const rtcp = new RtcpTransportLayerFeedback({\n feedback: nack,\n });\n this.receiver.dtlsTransport.sendRtcp([rtcp]);\n\n this.increment();\n this.onPacketLost.execute(nack);\n }\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cumulativeResult.js","sourceRoot":"","sources":["../../../../../src/media/senderBWE/cumulativeResult.ts"],"names":[],"mappings":";;;AAAA,uDAAiD;AAEjD,qBAAqB;AACrB,MAAa,gBAAgB;IAA7B;QACE,eAAU,GAAG,CAAC,CAAC;QACf,UAAU;QACV,cAAS,GAAG,CAAC,CAAC;QACd,wBAAmB,GAAG,CAAC,CAAC;QACxB,uBAAkB,GAAG,CAAC,CAAC;QACvB,4BAAuB,GAAG,CAAC,CAAC;QAC5B,2BAAsB,GAAG,CAAC,CAAC;IAkD7B,CAAC;IAhDC;;;;;OAKG;IACH,SAAS,CAAC,IAAY,EAAE,QAAgB,EAAE,YAAoB;QAC5D,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,EAAE;YACzB,IAAI,CAAC,mBAAmB,GAAG,QAAQ,CAAC;YACpC,IAAI,CAAC,uBAAuB,GAAG,YAAY,CAAC;YAC5C,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;YACnC,IAAI,CAAC,sBAAsB,GAAG,YAAY,CAAC;SAC5C;aAAM;YACL,IAAI,QAAQ,GAAG,IAAI,CAAC,mBAAmB;gBACrC,IAAI,CAAC,mBAAmB,GAAG,QAAQ,CAAC;YACtC,IAAI,YAAY,GAAG,IAAI,CAAC,uBAAuB;gBAC7C,IAAI,CAAC,uBAAuB,GAAG,YAAY,CAAC;YAC9C,IAAI,QAAQ,GAAG,IAAI,CAAC,kBAAkB;gBACpC,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;YACrC,IAAI,YAAY,GAAG,IAAI,CAAC,sBAAsB;gBAC5C,IAAI,CAAC,sBAAsB,GAAG,YAAY,CAAC;SAC9C;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;IACzB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,mBAAmB,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,uBAAuB,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,sBAAsB,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,cAAc;QAChB,MAAM,cAAc,GAClB,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,uBAAuB,CAAC;QAC7D,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAC7D,OAAO,YAAG,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,WAAW;QACb,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC;QAC1E,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAC7D,OAAO,YAAG,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;CACF;AAzDD,4CAyDC","sourcesContent":["import { Int } from \"../../../../rtp/src/helper\";\n\n// refer by mediasoup\nexport class CumulativeResult {\n numPackets = 0;\n /**byte */\n totalSize = 0;\n firstPacketSentAtMs = 0;\n lastPacketSentAtMs = 0;\n firstPacketReceivedAtMs = 0;\n lastPacketReceivedAtMs = 0;\n\n /**\n *\n * @param size byte\n * @param sentAtMs\n * @param receivedAtMs\n */\n addPacket(size: number, sentAtMs: number, receivedAtMs: number) {\n if (this.numPackets === 0) {\n this.firstPacketSentAtMs = sentAtMs;\n this.firstPacketReceivedAtMs = receivedAtMs;\n this.lastPacketSentAtMs = sentAtMs;\n this.lastPacketReceivedAtMs = receivedAtMs;\n } else {\n if (sentAtMs < this.firstPacketSentAtMs)\n this.firstPacketSentAtMs = sentAtMs;\n if (receivedAtMs < this.firstPacketReceivedAtMs)\n this.firstPacketReceivedAtMs = receivedAtMs;\n if (sentAtMs > this.lastPacketSentAtMs)\n this.lastPacketSentAtMs = sentAtMs;\n if (receivedAtMs > this.lastPacketReceivedAtMs)\n this.lastPacketReceivedAtMs = receivedAtMs;\n }\n\n this.numPackets++;\n this.totalSize += size;\n }\n\n reset() {\n this.numPackets = 0;\n this.totalSize = 0;\n this.firstPacketSentAtMs = 0;\n this.lastPacketSentAtMs = 0;\n this.firstPacketReceivedAtMs = 0;\n this.lastPacketReceivedAtMs = 0;\n }\n\n get receiveBitrate() {\n const recvIntervalMs =\n this.lastPacketReceivedAtMs - this.firstPacketReceivedAtMs;\n const bitrate = (this.totalSize / recvIntervalMs) * 8 * 1000;\n return Int(bitrate);\n }\n\n get sendBitrate() {\n const sendIntervalMs = this.lastPacketSentAtMs - this.firstPacketSentAtMs;\n const bitrate = (this.totalSize / sendIntervalMs) * 8 * 1000;\n return Int(bitrate);\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"senderBWE.js","sourceRoot":"","sources":["../../../../../src/media/senderBWE/senderBWE.ts"],"names":[],"mappings":"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","sourcesContent":["import Event from \"rx.mini\";\n\nimport { TransportWideCC } from \"../../../../rtp/src\";\nimport { Int } from \"../../../../rtp/src/helper\";\nimport { milliTime } from \"../../utils\";\nimport { CumulativeResult } from \"./cumulativeResult\";\n\nconst COUNTER_MAX = 20;\nconst SCORE_MAX = 10;\n\nexport class SenderBandwidthEstimator {\n congestion = false;\n\n readonly onAvailableBitrate = new Event<[number]>();\n /**congestion occur or not */\n readonly onCongestion = new Event<[boolean]>();\n readonly onCongestionScore = new Event<[number]>();\n\n private congestionCounter = 0;\n private cumulativeResult = new CumulativeResult();\n private sentInfos: { [key: number]: SentInfo } = {};\n private _congestionScore = 1;\n /**1~10 big is worth*/\n get congestionScore() {\n return this._congestionScore;\n }\n set congestionScore(v: number) {\n this._congestionScore = v;\n this.onCongestionScore.execute(v);\n }\n private _availableBitrate: number = 0;\n get availableBitrate() {\n return this._availableBitrate;\n }\n set availableBitrate(v: number) {\n this._availableBitrate = v;\n this.onAvailableBitrate.execute(v);\n }\n\n constructor() {}\n\n receiveTWCC(feedback: TransportWideCC) {\n const nowMs = milliTime();\n const elapsedMs = nowMs - this.cumulativeResult.firstPacketSentAtMs;\n if (elapsedMs > 1000) {\n this.cumulativeResult.reset();\n\n // Congestion may be occurring.\n\n if (this.congestionCounter < COUNTER_MAX) {\n this.congestionCounter++;\n } else if (this.congestionScore < SCORE_MAX) {\n this.congestionScore++;\n }\n\n if (this.congestionCounter >= COUNTER_MAX && !this.congestion) {\n this.congestion = true;\n this.onCongestion.execute(this.congestion);\n }\n }\n\n for (const result of feedback.packetResults) {\n if (!result.received) continue;\n\n const wideSeq = result.sequenceNumber;\n const info = this.sentInfos[wideSeq];\n if (!info) continue;\n if (!result.receivedAtMs) continue;\n\n this.cumulativeResult.addPacket(\n info.size,\n info.sendingAtMs,\n result.receivedAtMs\n );\n }\n\n if (elapsedMs >= 100 && this.cumulativeResult.numPackets >= 20) {\n this.availableBitrate = Math.min(\n this.cumulativeResult.sendBitrate,\n this.cumulativeResult.receiveBitrate\n );\n this.cumulativeResult.reset();\n\n if (this.congestionCounter > -COUNTER_MAX) {\n const maxBonus = Int(COUNTER_MAX / 2) + 1;\n const minBonus = Int(COUNTER_MAX / 4) + 1;\n const bonus =\n maxBonus - ((maxBonus - minBonus) / 10) * this.congestionScore;\n\n this.congestionCounter = this.congestionCounter - bonus;\n }\n\n if (this.congestionCounter <= -COUNTER_MAX) {\n if (this.congestionScore > 1) {\n this.congestionScore--;\n this.onCongestion.execute(false);\n }\n this.congestionCounter = 0;\n }\n\n if (this.congestionCounter <= 0 && this.congestion) {\n this.congestion = false;\n this.onCongestion.execute(this.congestion);\n }\n }\n }\n\n rtpPacketSent(sentInfo: SentInfo) {\n Object.keys(sentInfo)\n .map((v) => Number(v))\n .sort()\n .filter((seq) => seq < sentInfo.wideSeq)\n .forEach((seq) => {\n delete this.sentInfos[seq];\n });\n this.sentInfos[sentInfo.wideSeq] = sentInfo;\n }\n}\n\nexport interface SentInfo {\n wideSeq: number;\n /**\n * byte\n */\n size: number;\n isProbation?: boolean;\n sendingAtMs: number;\n sentAtMs: number;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statistics.js","sourceRoot":"","sources":["../../../../src/media/statistics.ts"],"names":[],"mappings":"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","sourcesContent":["import { int, uint16Gt } from \"../../../common/src\";\nimport { RtpPacket } from \"../../../rtp/src\";\n\n// from aiortc\n\nexport class StreamStatistics {\n base_seq?: number;\n max_seq?: number;\n cycles = 0;\n packets_received = 0;\n\n // # jitter\n private clockRate: number;\n jitter_q4 = 0;\n private last_arrival?: number;\n private last_timestamp?: number;\n\n // # fraction lost\n expected_prior = 0;\n received_prior = 0;\n\n constructor(clockRate: number) {\n this.clockRate = clockRate;\n }\n\n add(packet: RtpPacket, now: number = Date.now() / 1000) {\n const inOrder =\n this.max_seq == undefined ||\n uint16Gt(packet.header.sequenceNumber, this.max_seq);\n this.packets_received++;\n\n if (this.base_seq == undefined) {\n this.base_seq = packet.header.sequenceNumber;\n }\n\n if (inOrder) {\n const arrival = int(now * this.clockRate);\n\n if (\n this.max_seq != undefined &&\n packet.header.sequenceNumber < this.max_seq\n ) {\n this.cycles += 1 << 16;\n }\n this.max_seq = packet.header.sequenceNumber;\n\n if (\n packet.header.timestamp !== this.last_timestamp &&\n this.packets_received > 1\n ) {\n const diff = Math.abs(\n arrival -\n (this.last_arrival ?? 0) -\n (packet.header.timestamp - (this.last_timestamp ?? 0))\n );\n this.jitter_q4 += diff - ((this.jitter_q4 + 8) >> 4);\n }\n\n this.last_arrival = arrival;\n this.last_timestamp = packet.header.timestamp;\n }\n }\n\n get fraction_lost() {\n const expected_interval = this.packets_expected - this.expected_prior;\n this.expected_prior = this.packets_expected;\n const received_interval = this.packets_received - this.received_prior;\n this.received_prior = this.packets_received;\n const lost_interval = expected_interval - received_interval;\n if (expected_interval == 0 || lost_interval <= 0) {\n return 0;\n } else {\n return Math.floor((lost_interval << 8) / expected_interval);\n }\n }\n\n get jitter() {\n return this.jitter_q4 >> 4;\n }\n\n get packets_expected() {\n return this.cycles + (this.max_seq ?? 0) - (this.base_seq ?? 0) + 1;\n }\n\n get packets_lost() {\n const lost = this.packets_expected - this.packets_received;\n return lost < 0 ? 0 : lost;\n }\n}\n"]}
|