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.
Files changed (162) hide show
  1. package/lib/common/src/binary.d.ts +9 -0
  2. package/lib/common/src/binary.js +37 -1
  3. package/lib/common/src/binary.js.map +1 -1
  4. package/lib/dtls/src/context/srtp.d.ts +6 -2
  5. package/lib/dtls/src/context/srtp.js +9 -3
  6. package/lib/dtls/src/context/srtp.js.map +1 -1
  7. package/lib/dtls/src/flight/client/flight5.js.map +1 -1
  8. package/lib/dtls/src/flight/server/flight2.js.map +1 -1
  9. package/lib/dtls/src/socket.d.ts +3 -3
  10. package/lib/dtls/src/socket.js +6 -8
  11. package/lib/dtls/src/socket.js.map +1 -1
  12. package/lib/ice/src/utils.d.ts +1 -0
  13. package/lib/ice/src/utils.js +5 -1
  14. package/lib/ice/src/utils.js.map +1 -1
  15. package/lib/rtp/src/codec/base.d.ts +8 -0
  16. package/lib/rtp/src/codec/base.js +16 -0
  17. package/lib/rtp/src/codec/base.js.map +1 -0
  18. package/lib/rtp/src/codec/h264.d.ts +23 -2
  19. package/lib/rtp/src/codec/h264.js +66 -3
  20. package/lib/rtp/src/codec/h264.js.map +1 -1
  21. package/lib/rtp/src/codec/opus.d.ts +9 -0
  22. package/lib/rtp/src/codec/opus.js +18 -0
  23. package/lib/rtp/src/codec/opus.js.map +1 -0
  24. package/lib/rtp/src/codec/vp8.d.ts +18 -11
  25. package/lib/rtp/src/codec/vp8.js +51 -29
  26. package/lib/rtp/src/codec/vp8.js.map +1 -1
  27. package/lib/rtp/src/codec/vp9.d.ts +38 -10
  28. package/lib/rtp/src/codec/vp9.js +115 -26
  29. package/lib/rtp/src/codec/vp9.js.map +1 -1
  30. package/lib/rtp/src/index.d.ts +2 -0
  31. package/lib/rtp/src/index.js +2 -0
  32. package/lib/rtp/src/index.js.map +1 -1
  33. package/lib/rtp/src/rtcp/psfb/index.d.ts +2 -2
  34. package/lib/rtp/src/rtcp/psfb/index.js.map +1 -1
  35. package/lib/rtp/src/rtcp/rr.d.ts +2 -2
  36. package/lib/rtp/src/rtcp/rr.js.map +1 -1
  37. package/lib/rtp/src/rtcp/rtpfb/index.d.ts +2 -2
  38. package/lib/rtp/src/rtcp/rtpfb/index.js.map +1 -1
  39. package/lib/rtp/src/rtcp/sdes.d.ts +2 -2
  40. package/lib/rtp/src/rtcp/sdes.js.map +1 -1
  41. package/lib/rtp/src/rtcp/sr.d.ts +2 -2
  42. package/lib/rtp/src/rtcp/sr.js +36 -0
  43. package/lib/rtp/src/rtcp/sr.js.map +1 -1
  44. package/lib/rtp/src/rtp/rtp.d.ts +2 -0
  45. package/lib/rtp/src/rtp/rtp.js +2 -0
  46. package/lib/rtp/src/rtp/rtp.js.map +1 -1
  47. package/lib/rtp/src/srtp/cipher/ctr.d.ts +17 -0
  48. package/lib/rtp/src/srtp/cipher/ctr.js +103 -0
  49. package/lib/rtp/src/srtp/cipher/ctr.js.map +1 -0
  50. package/lib/rtp/src/srtp/cipher/gcm.d.ts +15 -0
  51. package/lib/rtp/src/srtp/cipher/gcm.js +106 -0
  52. package/lib/rtp/src/srtp/cipher/gcm.js.map +1 -0
  53. package/lib/rtp/src/srtp/cipher/index.d.ts +14 -0
  54. package/lib/rtp/src/srtp/cipher/index.js +25 -0
  55. package/lib/rtp/src/srtp/cipher/index.js.map +1 -0
  56. package/lib/rtp/src/srtp/const.d.ts +5 -0
  57. package/lib/rtp/src/srtp/const.js +23 -0
  58. package/lib/rtp/src/srtp/const.js.map +1 -0
  59. package/lib/rtp/src/srtp/context/context.d.ts +13 -11
  60. package/lib/rtp/src/srtp/context/context.js +23 -23
  61. package/lib/rtp/src/srtp/context/context.js.map +1 -1
  62. package/lib/rtp/src/srtp/context/srtcp.d.ts +3 -2
  63. package/lib/rtp/src/srtp/context/srtcp.js +10 -34
  64. package/lib/rtp/src/srtp/context/srtcp.js.map +1 -1
  65. package/lib/rtp/src/srtp/context/srtp.d.ts +4 -3
  66. package/lib/rtp/src/srtp/context/srtp.js +9 -28
  67. package/lib/rtp/src/srtp/context/srtp.js.map +1 -1
  68. package/lib/rtp/src/srtp/srtp.js +2 -2
  69. package/lib/rtp/src/srtp/srtp.js.map +1 -1
  70. package/lib/webrtc/src/const.d.ts +4 -3
  71. package/lib/webrtc/src/const.js +5 -4
  72. package/lib/webrtc/src/const.js.map +1 -1
  73. package/lib/webrtc/src/helper.d.ts +2 -2
  74. package/lib/webrtc/src/helper.js +2 -2
  75. package/lib/webrtc/src/helper.js.map +1 -1
  76. package/lib/webrtc/src/index.d.ts +6 -2
  77. package/lib/webrtc/src/index.js +6 -2
  78. package/lib/webrtc/src/index.js.map +1 -1
  79. package/lib/webrtc/src/{extension → media/extension}/rtcpFeedback.d.ts +1 -1
  80. package/lib/webrtc/src/{extension → media/extension}/rtcpFeedback.js +0 -0
  81. package/lib/webrtc/src/media/extension/rtcpFeedback.js.map +1 -0
  82. package/lib/webrtc/src/{extension → media/extension}/rtpExtension.d.ts +1 -1
  83. package/lib/webrtc/src/{extension → media/extension}/rtpExtension.js +1 -1
  84. package/lib/webrtc/src/media/extension/rtpExtension.js.map +1 -0
  85. package/lib/webrtc/src/media/{nack.d.ts → receiver/nack.d.ts} +2 -2
  86. package/lib/webrtc/src/media/{nack.js → receiver/nack.js} +2 -2
  87. package/lib/webrtc/src/media/receiver/nack.js.map +1 -0
  88. package/lib/webrtc/src/media/{statistics.d.ts → receiver/statistics.d.ts} +1 -1
  89. package/lib/webrtc/src/media/{statistics.js → receiver/statistics.js} +1 -1
  90. package/lib/webrtc/src/media/receiver/statistics.js.map +1 -0
  91. package/lib/webrtc/src/media/router.js +1 -1
  92. package/lib/webrtc/src/media/router.js.map +1 -1
  93. package/lib/webrtc/src/media/rtpReceiver.d.ts +4 -2
  94. package/lib/webrtc/src/media/rtpReceiver.js +13 -6
  95. package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
  96. package/lib/webrtc/src/media/rtpSender.d.ts +4 -4
  97. package/lib/webrtc/src/media/rtpSender.js +12 -6
  98. package/lib/webrtc/src/media/rtpSender.js.map +1 -1
  99. package/lib/webrtc/src/media/{senderBWE → sender}/cumulativeResult.d.ts +0 -0
  100. package/lib/webrtc/src/media/{senderBWE → sender}/cumulativeResult.js +0 -0
  101. package/lib/webrtc/src/media/sender/cumulativeResult.js.map +1 -0
  102. package/lib/webrtc/src/media/{senderBWE → sender}/senderBWE.d.ts +0 -0
  103. package/lib/webrtc/src/media/{senderBWE → sender}/senderBWE.js +0 -0
  104. package/lib/webrtc/src/media/sender/senderBWE.js.map +1 -0
  105. package/lib/webrtc/src/media/track.d.ts +3 -0
  106. package/lib/webrtc/src/media/track.js +2 -0
  107. package/lib/webrtc/src/media/track.js.map +1 -1
  108. package/lib/webrtc/src/nonstandard/jitterBuffer.d.ts +12 -0
  109. package/lib/webrtc/src/nonstandard/jitterBuffer.js +48 -0
  110. package/lib/webrtc/src/nonstandard/jitterBuffer.js.map +1 -0
  111. package/lib/webrtc/src/nonstandard/lipsync.d.ts +20 -0
  112. package/lib/webrtc/src/nonstandard/lipsync.js +39 -0
  113. package/lib/webrtc/src/nonstandard/lipsync.js.map +1 -0
  114. package/lib/webrtc/src/nonstandard/recorder.d.ts +18 -0
  115. package/lib/webrtc/src/nonstandard/recorder.js +25 -0
  116. package/lib/webrtc/src/nonstandard/recorder.js.map +1 -0
  117. package/lib/webrtc/src/nonstandard/sampleBuilder.d.ts +18 -0
  118. package/lib/webrtc/src/nonstandard/sampleBuilder.js +60 -0
  119. package/lib/webrtc/src/nonstandard/sampleBuilder.js.map +1 -0
  120. package/lib/webrtc/src/nonstandard/userMedia.d.ts +15 -0
  121. package/lib/webrtc/src/nonstandard/userMedia.js +67 -0
  122. package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -0
  123. package/lib/webrtc/src/nonstandard/webm.d.ts +24 -0
  124. package/lib/webrtc/src/nonstandard/webm.js +308 -0
  125. package/lib/webrtc/src/nonstandard/webm.js.map +1 -0
  126. package/lib/webrtc/src/peerConnection.js +1 -0
  127. package/lib/webrtc/src/peerConnection.js.map +1 -1
  128. package/lib/webrtc/src/transport/dtls.d.ts +2 -1
  129. package/lib/webrtc/src/transport/dtls.js +7 -3
  130. package/lib/webrtc/src/transport/dtls.js.map +1 -1
  131. package/lib/webrtc/src/utils.d.ts +3 -0
  132. package/lib/webrtc/src/utils.js +13 -9
  133. package/lib/webrtc/src/utils.js.map +1 -1
  134. package/package.json +5 -4
  135. package/src/const.ts +8 -3
  136. package/src/helper.ts +4 -4
  137. package/src/index.ts +6 -2
  138. package/src/{extension → media/extension}/rtcpFeedback.ts +1 -1
  139. package/src/{extension → media/extension}/rtpExtension.ts +1 -1
  140. package/src/media/{nack.ts → receiver/nack.ts} +3 -3
  141. package/src/media/{statistics.ts → receiver/statistics.ts} +2 -2
  142. package/src/media/router.ts +1 -1
  143. package/src/media/rtpReceiver.ts +11 -7
  144. package/src/media/rtpSender.ts +19 -9
  145. package/src/media/{senderBWE → sender}/cumulativeResult.ts +0 -0
  146. package/src/media/{senderBWE → sender}/senderBWE.ts +0 -0
  147. package/src/media/track.ts +6 -0
  148. package/src/nonstandard/jitterBuffer.ts +47 -0
  149. package/src/nonstandard/lipsync.ts +55 -0
  150. package/src/nonstandard/recorder.ts +26 -0
  151. package/src/nonstandard/sampleBuilder.ts +71 -0
  152. package/src/nonstandard/userMedia.ts +74 -0
  153. package/src/nonstandard/webm.ts +421 -0
  154. package/src/peerConnection.ts +3 -1
  155. package/src/transport/dtls.ts +12 -4
  156. package/src/utils.ts +20 -12
  157. package/lib/webrtc/src/extension/rtcpFeedback.js.map +0 -1
  158. package/lib/webrtc/src/extension/rtpExtension.js.map +0 -1
  159. package/lib/webrtc/src/media/nack.js.map +0 -1
  160. package/lib/webrtc/src/media/senderBWE/cumulativeResult.js.map +0 -1
  161. package/lib/webrtc/src/media/senderBWE/senderBWE.js.map +0 -1
  162. 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];
@@ -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: number[] = []) {
358
+ private createTransport(srtpProfiles: Profile[] = []) {
357
359
  const iceGatherer = new RTCIceGatherer({
358
360
  ...parseIceServers(this.configuration.iceServers),
359
361
  forceTurn: this.configuration.iceTransportPolicy === "relay",
@@ -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: number[] = []
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
- if (!this.dtls.srtp.srtpProfile) throw new Error("need srtpProfile");
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: this.dtls.srtp.srtpProfile,
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, randomBytes } from "crypto";
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 { random16, random32, uint16Add, uint32Add } from "../../common/src";
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 div = now / 1000;
70
+ const seconds = now / 1000;
71
+ const [sec, msec] = seconds.toString().split(".").map(Number);
64
72
 
65
- let [sec, msec] = div.toString().slice(0, 14).split(".");
73
+ const buf = bufferWriter([4, 4], [sec, msec]);
66
74
 
67
- if (!msec) msec = "0";
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
- return (high << 32n) | low;
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"]}