werift 0.15.11 → 0.16.0

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 (79) hide show
  1. package/lib/common/src/index.d.ts +2 -0
  2. package/lib/common/src/index.js +2 -0
  3. package/lib/common/src/index.js.map +1 -1
  4. package/lib/common/src/log.d.ts +11 -0
  5. package/lib/common/src/log.js +17 -0
  6. package/lib/common/src/log.js.map +1 -0
  7. package/lib/common/src/type.d.ts +3 -0
  8. package/lib/common/src/type.js +3 -0
  9. package/lib/common/src/type.js.map +1 -0
  10. package/lib/dtls/src/context/cipher.js.map +1 -1
  11. package/lib/dtls/src/flight/server/flight2.js +10 -0
  12. package/lib/dtls/src/flight/server/flight2.js.map +1 -1
  13. package/lib/ice/src/ice.d.ts +1 -0
  14. package/lib/ice/src/ice.js +4 -0
  15. package/lib/ice/src/ice.js.map +1 -1
  16. package/lib/rtp/src/codec/index.d.ts +18 -0
  17. package/lib/rtp/src/codec/index.js +81 -0
  18. package/lib/rtp/src/codec/index.js.map +1 -0
  19. package/lib/rtp/src/codec/vp8.d.ts +5 -3
  20. package/lib/rtp/src/codec/vp8.js +19 -5
  21. package/lib/rtp/src/codec/vp8.js.map +1 -1
  22. package/lib/rtp/src/container/webm.d.ts +6 -1
  23. package/lib/rtp/src/container/webm.js +9 -2
  24. package/lib/rtp/src/container/webm.js.map +1 -1
  25. package/lib/rtp/src/index.d.ts +2 -6
  26. package/lib/rtp/src/index.js +2 -6
  27. package/lib/rtp/src/index.js.map +1 -1
  28. package/lib/rtp/src/processor/base.d.ts +3 -1
  29. package/lib/rtp/src/processor/base.js +19 -6
  30. package/lib/rtp/src/processor/base.js.map +1 -1
  31. package/lib/rtp/src/processor/jitterBuffer.js +1 -1
  32. package/lib/rtp/src/processor/jitterBuffer.js.map +1 -1
  33. package/lib/rtp/src/processor/lipsync.js +22 -2
  34. package/lib/rtp/src/processor/lipsync.js.map +1 -1
  35. package/lib/rtp/src/processor/webm.d.ts +11 -7
  36. package/lib/rtp/src/processor/webm.js +16 -41
  37. package/lib/rtp/src/processor/webm.js.map +1 -1
  38. package/lib/rtp/src/processor_v2/depacketizer.d.ts +17 -0
  39. package/lib/rtp/src/processor_v2/depacketizer.js +84 -0
  40. package/lib/rtp/src/processor_v2/depacketizer.js.map +1 -0
  41. package/lib/rtp/src/processor_v2/index.d.ts +4 -0
  42. package/lib/rtp/src/processor_v2/index.js +21 -0
  43. package/lib/rtp/src/processor_v2/index.js.map +1 -0
  44. package/lib/rtp/src/processor_v2/jitterBuffer.d.ts +33 -0
  45. package/lib/rtp/src/processor_v2/jitterBuffer.js +154 -0
  46. package/lib/rtp/src/processor_v2/jitterBuffer.js.map +1 -0
  47. package/lib/rtp/src/processor_v2/source/base.d.ts +8 -0
  48. package/lib/rtp/src/processor_v2/source/base.js +16 -0
  49. package/lib/rtp/src/processor_v2/source/base.js.map +1 -0
  50. package/lib/rtp/src/processor_v2/source/index.d.ts +2 -0
  51. package/lib/rtp/src/processor_v2/source/index.js +6 -0
  52. package/lib/rtp/src/processor_v2/source/index.js.map +1 -0
  53. package/lib/rtp/src/processor_v2/source/rtp.d.ts +14 -0
  54. package/lib/rtp/src/processor_v2/source/rtp.js +24 -0
  55. package/lib/rtp/src/processor_v2/source/rtp.js.map +1 -0
  56. package/lib/rtp/src/processor_v2/webmLive.d.ts +51 -0
  57. package/lib/rtp/src/processor_v2/webmLive.js +154 -0
  58. package/lib/rtp/src/processor_v2/webmLive.js.map +1 -0
  59. package/lib/webrtc/src/media/rtpTransceiver.d.ts +1 -1
  60. package/lib/webrtc/src/media/rtpTransceiver.js +3 -2
  61. package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
  62. package/lib/webrtc/src/nonstandard/recorder/index.d.ts +5 -1
  63. package/lib/webrtc/src/nonstandard/recorder/index.js +2 -2
  64. package/lib/webrtc/src/nonstandard/recorder/index.js.map +1 -1
  65. package/lib/webrtc/src/nonstandard/recorder/writer/index.d.ts +1 -1
  66. package/lib/webrtc/src/nonstandard/recorder/writer/index.js +1 -1
  67. package/lib/webrtc/src/nonstandard/recorder/writer/index.js.map +1 -1
  68. package/lib/webrtc/src/nonstandard/recorder/writer/webm.d.ts +3 -3
  69. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +61 -41
  70. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
  71. package/lib/webrtc/src/peerConnection.d.ts +9 -1
  72. package/lib/webrtc/src/peerConnection.js +9 -1
  73. package/lib/webrtc/src/peerConnection.js.map +1 -1
  74. package/package.json +2 -2
  75. package/src/media/rtpTransceiver.ts +4 -2
  76. package/src/nonstandard/recorder/index.ts +6 -2
  77. package/src/nonstandard/recorder/writer/index.ts +1 -1
  78. package/src/nonstandard/recorder/writer/webm.ts +105 -57
  79. package/src/peerConnection.ts +26 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtp.js","sourceRoot":"","sources":["../../../../../../rtp/src/processor_v2/source/rtp.ts"],"names":[],"mappings":";;;AAAA,qCAA+C;AAG/C,iCAAsC;AAOtC,MAAa,eAAgB,SAAQ,mBAAuB;IAG1D,YAAY,EAAsB,EAAE,UAAoC,EAAE;QACxE,KAAK,EAAE,CAAC;QAHO,aAAQ,GAAG,IAAI,uBAAa,EAAE,CAAC;QAK9C,EAAE,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;YACnB,IACE,OAAO,CAAC,WAAW,IAAI,SAAS;gBAChC,OAAO,CAAC,WAAW,KAAK,GAAG,CAAC,MAAM,CAAC,WAAW,EAC9C;gBACA,OAAO;aACR;YAED,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;CACF;AAtBD,0CAsBC","sourcesContent":["import Event, { EventDisposer } from \"rx.mini\";\n\nimport { RtpPacket } from \"../../rtp/rtp\";\nimport { SourceStream } from \"./base\";\n\nexport interface RtpOutput {\n rtp?: RtpPacket;\n eol?: boolean;\n}\n\nexport class RtpSourceStream extends SourceStream<RtpOutput> {\n private readonly disposer = new EventDisposer();\n\n constructor(ev: Event<[RtpPacket]>, options: { payloadType?: number } = {}) {\n super();\n\n ev.subscribe((rtp) => {\n if (\n options.payloadType != undefined &&\n options.payloadType !== rtp.header.payloadType\n ) {\n return;\n }\n\n this.write({ rtp });\n }).disposer(this.disposer);\n }\n\n async stop() {\n this.controller.enqueue({ eol: true });\n this.disposer.dispose();\n }\n}\n"]}
@@ -0,0 +1,51 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { ReadableStream, WritableStream } from "stream/web";
4
+ import { SupportedCodec } from "../container/webm";
5
+ import { DepacketizerOutput } from "./depacketizer";
6
+ export declare type WebmLiveInput = DepacketizerOutput;
7
+ export declare type WebmLiveOutput = {
8
+ packet?: Buffer;
9
+ eol?: {
10
+ /**ms */
11
+ duration: number;
12
+ durationElement: Uint8Array;
13
+ };
14
+ };
15
+ export interface WebmLiveOption {
16
+ /**ms */
17
+ duration?: number;
18
+ }
19
+ export declare class WebmLiveSink {
20
+ tracks: {
21
+ width?: number;
22
+ height?: number;
23
+ kind: "audio" | "video";
24
+ codec: SupportedCodec;
25
+ clockRate: number;
26
+ trackNumber: number;
27
+ }[];
28
+ private options;
29
+ private builder;
30
+ private queue;
31
+ private relativeTimestamp;
32
+ private timestamps;
33
+ private cuePoints;
34
+ private position;
35
+ stopped: boolean;
36
+ audioStream: WritableStream<WebmLiveInput>;
37
+ videoStream: WritableStream<WebmLiveInput>;
38
+ webmStream: ReadableStream<WebmLiveOutput>;
39
+ private controller;
40
+ constructor(tracks: {
41
+ width?: number;
42
+ height?: number;
43
+ kind: "audio" | "video";
44
+ codec: SupportedCodec;
45
+ clockRate: number;
46
+ trackNumber: number;
47
+ }[], options?: WebmLiveOption);
48
+ private init;
49
+ private onFrameReceived;
50
+ private stop;
51
+ }
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.WebmLiveSink = void 0;
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const web_1 = require("stream/web");
9
+ const __1 = require("..");
10
+ const webm_1 = require("../container/webm");
11
+ const sourcePath = `werift-rtp : packages/rtp/src/processor_v2/webmLive.ts`;
12
+ const log = (0, debug_1.default)(sourcePath);
13
+ class WebmLiveSink {
14
+ constructor(tracks, options = {}) {
15
+ this.tracks = tracks;
16
+ this.options = options;
17
+ this.queue = new __1.PromiseQueue();
18
+ this.relativeTimestamp = 0;
19
+ this.timestamps = {};
20
+ this.cuePoints = [];
21
+ this.position = 0;
22
+ this.stopped = false;
23
+ this.builder = new webm_1.WEBMBuilder(tracks);
24
+ tracks.forEach((t) => {
25
+ this.timestamps[t.trackNumber] = new ClusterTimestamp(t.clockRate);
26
+ });
27
+ const createStream = (trackNumber) => new web_1.WritableStream({
28
+ write: ({ frame, eol }) => {
29
+ if (this.stopped)
30
+ return;
31
+ if (!frame) {
32
+ if (eol) {
33
+ this.stop();
34
+ }
35
+ return;
36
+ }
37
+ this.queue.push(() => this.onFrameReceived({ ...frame, trackNumber }));
38
+ },
39
+ });
40
+ const audioTrack = tracks.find((t) => t.kind === "audio");
41
+ if (audioTrack) {
42
+ this.audioStream = createStream(audioTrack.trackNumber);
43
+ }
44
+ const videoTrack = tracks.find((t) => t.kind === "video");
45
+ if (videoTrack) {
46
+ this.videoStream = createStream(videoTrack.trackNumber);
47
+ }
48
+ this.webmStream = new web_1.ReadableStream({
49
+ start: (controller) => {
50
+ this.controller = controller;
51
+ },
52
+ });
53
+ this.queue.push(() => this.init());
54
+ }
55
+ async init() {
56
+ const staticPart = Buffer.concat([
57
+ this.builder.ebmlHeader,
58
+ this.builder.createSegment(this.options.duration),
59
+ ]);
60
+ this.controller.enqueue({ packet: staticPart });
61
+ this.position += staticPart.length;
62
+ const video = this.tracks.find((t) => t.kind === "video");
63
+ if (video) {
64
+ this.cuePoints.push(new CuePoint(this.builder, video.trackNumber, 0.0, this.position));
65
+ }
66
+ }
67
+ async onFrameReceived(frame) {
68
+ const track = this.tracks.find((t) => t.trackNumber === frame.trackNumber);
69
+ if (!track) {
70
+ return;
71
+ }
72
+ const timestampManager = this.timestamps[track.trackNumber];
73
+ let elapsed = timestampManager.update(frame.timestamp);
74
+ if ((track.kind === "video" && frame.isKeyframe) ||
75
+ elapsed > MaxSinged16Int) {
76
+ this.relativeTimestamp += elapsed;
77
+ const cluster = this.builder.createCluster(this.relativeTimestamp);
78
+ this.controller.enqueue({ packet: Buffer.from(cluster) });
79
+ if (elapsed !== 0) {
80
+ this.cuePoints.push(new CuePoint(this.builder, track.trackNumber, this.relativeTimestamp, this.position));
81
+ }
82
+ this.position += cluster.length;
83
+ Object.values(this.timestamps).forEach((t) => t.reset());
84
+ elapsed = timestampManager.update(frame.timestamp);
85
+ }
86
+ const block = this.builder.createSimpleBlock(frame.data, frame.isKeyframe, track.trackNumber, elapsed);
87
+ this.controller.enqueue({ packet: block });
88
+ this.position += block.length;
89
+ const [cuePoint] = this.cuePoints.slice(-1);
90
+ if (cuePoint) {
91
+ cuePoint.blockNumber++;
92
+ }
93
+ }
94
+ async stop() {
95
+ if (this.stopped) {
96
+ return;
97
+ }
98
+ this.stopped = true;
99
+ log("stop");
100
+ const cues = this.builder.createCues(this.cuePoints.map((c) => c.build()));
101
+ this.controller.enqueue({ packet: Buffer.from(cues) });
102
+ const latestTimestamp = Object.values(this.timestamps).sort((a, b) => a.elapsed - b.elapsed)[0].elapsed;
103
+ const duration = this.relativeTimestamp + latestTimestamp;
104
+ const durationElement = this.builder.createDuration(duration);
105
+ this.controller.enqueue({ eol: { duration, durationElement } });
106
+ this.controller.close();
107
+ }
108
+ }
109
+ exports.WebmLiveSink = WebmLiveSink;
110
+ class ClusterTimestamp {
111
+ constructor(clockRate) {
112
+ this.clockRate = clockRate;
113
+ this.elapsed = 0;
114
+ }
115
+ reset() {
116
+ this.baseTimestamp = undefined;
117
+ }
118
+ update(timestamp) {
119
+ if (this.baseTimestamp == undefined) {
120
+ this.baseTimestamp = timestamp;
121
+ }
122
+ const rotate = Math.abs(timestamp - this.baseTimestamp) > (Max32Uint / 4) * 3;
123
+ if (rotate) {
124
+ log("rotate", { baseTimestamp: this.baseTimestamp, timestamp });
125
+ }
126
+ const elapsed = rotate
127
+ ? timestamp + Max32Uint - this.baseTimestamp
128
+ : timestamp - this.baseTimestamp;
129
+ this.elapsed = (0, __1.int)((elapsed / this.clockRate) * 1000);
130
+ return this.elapsed;
131
+ }
132
+ }
133
+ class CuePoint {
134
+ constructor(builder, trackNumber, relativeTimestamp, position) {
135
+ this.builder = builder;
136
+ this.trackNumber = trackNumber;
137
+ this.relativeTimestamp = relativeTimestamp;
138
+ this.position = position;
139
+ /**
140
+ * cuesの後のclusterのあるべき位置
141
+ * cuesはclusterの前に挿入される
142
+ */
143
+ this.cuesLength = 0;
144
+ this.blockNumber = 0;
145
+ }
146
+ build() {
147
+ return this.builder.createCuePoint(this.relativeTimestamp, this.trackNumber, this.position - 48 + this.cuesLength, this.blockNumber);
148
+ }
149
+ }
150
+ /**4294967295 */
151
+ const Max32Uint = Number(0x01n << 32n) - 1;
152
+ /**32767 */
153
+ const MaxSinged16Int = (0x01 << 16) / 2 - 1;
154
+ //# sourceMappingURL=webmLive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webmLive.js","sourceRoot":"","sources":["../../../../../rtp/src/processor_v2/webmLive.ts"],"names":[],"mappings":"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","sourcesContent":["import debug from \"debug\";\nimport {\n ReadableStream,\n ReadableStreamController,\n WritableStream,\n} from \"stream/web\";\n\nimport { int, PromiseQueue } from \"..\";\nimport { SupportedCodec, WEBMBuilder } from \"../container/webm\";\nimport { DepacketizerOutput } from \"./depacketizer\";\n\nconst sourcePath = `werift-rtp : packages/rtp/src/processor_v2/webmLive.ts`;\nconst log = debug(sourcePath);\n\nexport type WebmLiveInput = DepacketizerOutput;\n\nexport type WebmLiveOutput = {\n packet?: Buffer;\n eol?: {\n /**ms */\n duration: number;\n durationElement: Uint8Array;\n };\n};\n\nexport interface WebmLiveOption {\n /**ms */\n duration?: number;\n}\n\nexport class WebmLiveSink {\n private builder: WEBMBuilder;\n private queue = new PromiseQueue();\n private relativeTimestamp = 0;\n private timestamps: { [pt: number]: ClusterTimestamp } = {};\n private cuePoints: CuePoint[] = [];\n private position = 0;\n stopped = false;\n audioStream!: WritableStream<WebmLiveInput>;\n videoStream!: WritableStream<WebmLiveInput>;\n webmStream: ReadableStream<WebmLiveOutput>;\n private controller!: ReadableStreamController<WebmLiveOutput>;\n\n constructor(\n public tracks: {\n width?: number;\n height?: number;\n kind: \"audio\" | \"video\";\n codec: SupportedCodec;\n clockRate: number;\n trackNumber: number;\n }[],\n private options: WebmLiveOption = {}\n ) {\n this.builder = new WEBMBuilder(tracks);\n\n tracks.forEach((t) => {\n this.timestamps[t.trackNumber] = new ClusterTimestamp(t.clockRate);\n });\n\n const createStream = (trackNumber: number) =>\n new WritableStream({\n write: ({ frame, eol }: WebmLiveInput) => {\n if (this.stopped) return;\n if (!frame) {\n if (eol) {\n this.stop();\n }\n return;\n }\n\n this.queue.push(() =>\n this.onFrameReceived({ ...frame, trackNumber })\n );\n },\n });\n\n const audioTrack = tracks.find((t) => t.kind === \"audio\");\n if (audioTrack) {\n this.audioStream = createStream(audioTrack.trackNumber);\n }\n\n const videoTrack = tracks.find((t) => t.kind === \"video\");\n if (videoTrack) {\n this.videoStream = createStream(videoTrack.trackNumber);\n }\n\n this.webmStream = new ReadableStream<WebmLiveOutput>({\n start: (controller) => {\n this.controller = controller;\n },\n });\n\n this.queue.push(() => this.init());\n }\n\n private async init() {\n const staticPart = Buffer.concat([\n this.builder.ebmlHeader,\n this.builder.createSegment(this.options.duration),\n ]);\n this.controller.enqueue({ packet: staticPart });\n this.position += staticPart.length;\n\n const video = this.tracks.find((t) => t.kind === \"video\");\n if (video) {\n this.cuePoints.push(\n new CuePoint(this.builder, video.trackNumber, 0.0, this.position)\n );\n }\n }\n\n private async onFrameReceived(\n frame: WebmLiveInput[\"frame\"] & { trackNumber: number }\n ) {\n const track = this.tracks.find((t) => t.trackNumber === frame.trackNumber);\n if (!track) {\n return;\n }\n\n const timestampManager = this.timestamps[track.trackNumber];\n let elapsed = timestampManager.update(frame.timestamp);\n\n if (\n (track.kind === \"video\" && frame.isKeyframe) ||\n elapsed > MaxSinged16Int\n ) {\n this.relativeTimestamp += elapsed;\n\n const cluster = this.builder.createCluster(this.relativeTimestamp);\n this.controller.enqueue({ packet: Buffer.from(cluster) });\n\n if (elapsed !== 0) {\n this.cuePoints.push(\n new CuePoint(\n this.builder,\n track.trackNumber,\n this.relativeTimestamp,\n this.position\n )\n );\n }\n this.position += cluster.length;\n Object.values(this.timestamps).forEach((t) => t.reset());\n elapsed = timestampManager.update(frame.timestamp);\n }\n\n const block = this.builder.createSimpleBlock(\n frame.data,\n frame.isKeyframe,\n track.trackNumber,\n elapsed\n );\n this.controller.enqueue({ packet: block });\n this.position += block.length;\n const [cuePoint] = this.cuePoints.slice(-1);\n if (cuePoint) {\n cuePoint.blockNumber++;\n }\n }\n\n private async stop() {\n if (this.stopped) {\n return;\n }\n this.stopped = true;\n\n log(\"stop\");\n\n const cues = this.builder.createCues(this.cuePoints.map((c) => c.build()));\n this.controller.enqueue({ packet: Buffer.from(cues) });\n\n const latestTimestamp = Object.values(this.timestamps).sort(\n (a, b) => a.elapsed - b.elapsed\n )[0].elapsed;\n const duration = this.relativeTimestamp + latestTimestamp;\n const durationElement = this.builder.createDuration(duration);\n this.controller.enqueue({ eol: { duration, durationElement } });\n\n this.controller.close();\n }\n}\n\nclass ClusterTimestamp {\n baseTimestamp?: number;\n elapsed = 0;\n\n constructor(public clockRate: number) {}\n\n reset() {\n this.baseTimestamp = undefined;\n }\n\n update(timestamp: number) {\n if (this.baseTimestamp == undefined) {\n this.baseTimestamp = timestamp;\n }\n const rotate =\n Math.abs(timestamp - this.baseTimestamp) > (Max32Uint / 4) * 3;\n\n if (rotate) {\n log(\"rotate\", { baseTimestamp: this.baseTimestamp, timestamp });\n }\n\n const elapsed = rotate\n ? timestamp + Max32Uint - this.baseTimestamp\n : timestamp - this.baseTimestamp;\n\n this.elapsed = int((elapsed / this.clockRate) * 1000);\n return this.elapsed;\n }\n}\n\nclass CuePoint {\n /**\n * cuesの後のclusterのあるべき位置\n * cuesはclusterの前に挿入される\n */\n cuesLength = 0;\n blockNumber = 0;\n\n constructor(\n private readonly builder: WEBMBuilder,\n private readonly trackNumber: number,\n private readonly relativeTimestamp: number,\n public position: number\n ) {}\n\n build() {\n return this.builder.createCuePoint(\n this.relativeTimestamp,\n this.trackNumber,\n this.position - 48 + this.cuesLength,\n this.blockNumber\n );\n }\n}\n\n/**4294967295 */\nconst Max32Uint = Number(0x01n << 32n) - 1;\n/**32767 */\nconst MaxSinged16Int = (0x01 << 16) / 2 - 1;\n"]}
@@ -12,7 +12,7 @@ export declare class RTCRtpTransceiver {
12
12
  /**RFC 8829 4.2.4. direction the transceiver was initialized with */
13
13
  direction: Direction;
14
14
  readonly uuid: string;
15
- readonly onTrack: Event<[MediaStreamTrack]>;
15
+ readonly onTrack: Event<[MediaStreamTrack, RTCRtpTransceiver]>;
16
16
  mid?: string;
17
17
  mLineIndex?: number;
18
18
  usedForSender: boolean;
@@ -76,8 +76,9 @@ class RTCRtpTransceiver {
76
76
  }
77
77
  addTrack(track) {
78
78
  const res = this.receiver.addTrack(track);
79
- if (res)
80
- this.onTrack.execute(track);
79
+ if (res) {
80
+ this.onTrack.execute(track, this);
81
+ }
81
82
  }
82
83
  // todo impl
83
84
  // https://www.w3.org/TR/webrtc/#methods-8
@@ -1 +1 @@
1
- {"version":3,"file":"rtpTransceiver.js","sourceRoot":"","sources":["../../../../src/media/rtpTransceiver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,sDAA4B;AAC5B,2CAA6B;AAG7B,oCAA4C;AAU5C,MAAa,iBAAiB;IA+B5B,YACkB,IAAU,EAC1B,aAA+B,EACxB,QAAwB,EACxB,MAAoB;IAC3B,qEAAqE;IAC9D,SAAoB;QALX,SAAI,GAAJ,IAAI,CAAM;QAEnB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAc;QAEpB,cAAS,GAAT,SAAS,CAAW;QApCpB,SAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;QACjB,YAAO,GAAG,IAAI,iBAAK,EAAsB,CAAC;QAGnD,kBAAa,GAAG,KAAK,CAAC;QActB,YAAO,GAA4B,EAAE,CAAC;QAOtC,qBAAgB,GAAsC,EAAE,CAAC;QACzD,YAAO,GAAgC,EAAE,CAAC;QAC1C,aAAQ,GAAG,KAAK,CAAC;QACjB,YAAO,GAAG,KAAK,CAAC;QAUd,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACvC,CAAC;IAjCD,IAAI,gBAAgB,CAAC,SAA4C;QAC/D,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;QACnC,IAAI,wBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE;YAC3D,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;SAC3B;IACH,CAAC;IACD,+CAA+C;IAC/C,IAAI,gBAAgB;QAClB,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAID,IAAI,MAAM,CAAC,MAA+B;QACxC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IACD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAiBD,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;IACrC,CAAC;IAED,gBAAgB,CAAC,IAAsB;QACrC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,GAAG;YAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,YAAY;IACZ,0CAA0C;IAC1C,IAAI;QACF,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAE1B,oDAAoD;QAEpD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,cAAc,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAChC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAC9D,EAAE,WAAW,CAAC;IACjB,CAAC;CACF;AA3ED,8CA2EC;AAEY,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AAEtB,QAAA,UAAU,GAAG,CAAC,gBAAQ,EAAE,gBAAQ,EAAE,gBAAQ,EAAE,gBAAQ,CAAU,CAAC","sourcesContent":["import Event from \"rx.mini\";\nimport * as uuid from \"uuid\";\n\nimport { RTCDtlsTransport } from \"..\";\nimport { SenderDirections } from \"../const\";\nimport { Kind } from \"../types/domain\";\nimport {\n RTCRtpCodecParameters,\n RTCRtpHeaderExtensionParameters,\n} from \"./parameters\";\nimport { RTCRtpReceiver } from \"./rtpReceiver\";\nimport { RTCRtpSender } from \"./rtpSender\";\nimport { MediaStreamTrack } from \"./track\";\n\nexport class RTCRtpTransceiver {\n readonly uuid = uuid.v4();\n readonly onTrack = new Event<[MediaStreamTrack]>();\n mid?: string;\n mLineIndex?: number;\n usedForSender = false;\n private _currentDirection?: Direction | \"stopped\";\n set currentDirection(direction: Direction | \"stopped\" | undefined) {\n this._currentDirection = direction;\n if (SenderDirections.includes(this._currentDirection || \"\")) {\n this.usedForSender = true;\n }\n }\n /**RFC 8829 4.2.5. last negotiated direction */\n get currentDirection(): Direction | \"stopped\" | undefined {\n return this._currentDirection;\n }\n\n offerDirection!: Direction;\n _codecs: RTCRtpCodecParameters[] = [];\n set codecs(codecs: RTCRtpCodecParameters[]) {\n this._codecs = codecs;\n }\n get codecs() {\n return this._codecs;\n }\n headerExtensions: RTCRtpHeaderExtensionParameters[] = [];\n options: Partial<TransceiverOptions> = {};\n stopping = false;\n stopped = false;\n\n constructor(\n public readonly kind: Kind,\n dtlsTransport: RTCDtlsTransport,\n public receiver: RTCRtpReceiver,\n public sender: RTCRtpSender,\n /**RFC 8829 4.2.4. direction the transceiver was initialized with */\n public direction: Direction\n ) {\n this.setDtlsTransport(dtlsTransport);\n }\n\n get dtlsTransport() {\n return this.receiver.dtlsTransport;\n }\n\n setDtlsTransport(dtls: RTCDtlsTransport) {\n this.receiver.setDtlsTransport(dtls);\n this.sender.setDtlsTransport(dtls);\n }\n\n get msid() {\n return `${this.sender.streamId} ${this.sender.trackId}`;\n }\n\n addTrack(track: MediaStreamTrack) {\n const res = this.receiver.addTrack(track);\n if (res) this.onTrack.execute(track);\n }\n\n // todo impl\n // https://www.w3.org/TR/webrtc/#methods-8\n stop() {\n if (this.stopping) return;\n\n // todo Stop sending and receiving with transceiver.\n\n this.stopping = true;\n }\n\n getPayloadType(mimeType: string) {\n return this.codecs.find((codec) =>\n codec.mimeType.toLowerCase().includes(mimeType.toLowerCase())\n )?.payloadType;\n }\n}\n\nexport const Inactive = \"inactive\";\nexport const Sendonly = \"sendonly\";\nexport const Recvonly = \"recvonly\";\nexport const Sendrecv = \"sendrecv\";\n\nexport const Directions = [Inactive, Sendonly, Recvonly, Sendrecv] as const;\n\nexport type Direction = typeof Directions[number];\n\ntype SimulcastDirection = \"send\" | \"recv\";\n\nexport interface TransceiverOptions {\n direction: Direction;\n simulcast: { direction: SimulcastDirection; rid: string }[];\n}\n"]}
1
+ {"version":3,"file":"rtpTransceiver.js","sourceRoot":"","sources":["../../../../src/media/rtpTransceiver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,sDAA4B;AAC5B,2CAA6B;AAG7B,oCAA4C;AAU5C,MAAa,iBAAiB;IA+B5B,YACkB,IAAU,EAC1B,aAA+B,EACxB,QAAwB,EACxB,MAAoB;IAC3B,qEAAqE;IAC9D,SAAoB;QALX,SAAI,GAAJ,IAAI,CAAM;QAEnB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAc;QAEpB,cAAS,GAAT,SAAS,CAAW;QApCpB,SAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;QACjB,YAAO,GAAG,IAAI,iBAAK,EAAyC,CAAC;QAGtE,kBAAa,GAAG,KAAK,CAAC;QActB,YAAO,GAA4B,EAAE,CAAC;QAOtC,qBAAgB,GAAsC,EAAE,CAAC;QACzD,YAAO,GAAgC,EAAE,CAAC;QAC1C,aAAQ,GAAG,KAAK,CAAC;QACjB,YAAO,GAAG,KAAK,CAAC;QAUd,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACvC,CAAC;IAjCD,IAAI,gBAAgB,CAAC,SAA4C;QAC/D,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;QACnC,IAAI,wBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE;YAC3D,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;SAC3B;IACH,CAAC;IACD,+CAA+C;IAC/C,IAAI,gBAAgB;QAClB,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAID,IAAI,MAAM,CAAC,MAA+B;QACxC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IACD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAiBD,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;IACrC,CAAC;IAED,gBAAgB,CAAC,IAAsB;QACrC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,GAAG,EAAE;YACP,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;SACnC;IACH,CAAC;IAED,YAAY;IACZ,0CAA0C;IAC1C,IAAI;QACF,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAE1B,oDAAoD;QAEpD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,cAAc,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAChC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAC9D,EAAE,WAAW,CAAC;IACjB,CAAC;CACF;AA7ED,8CA6EC;AAEY,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AAEtB,QAAA,UAAU,GAAG,CAAC,gBAAQ,EAAE,gBAAQ,EAAE,gBAAQ,EAAE,gBAAQ,CAAU,CAAC","sourcesContent":["import Event from \"rx.mini\";\nimport * as uuid from \"uuid\";\n\nimport { RTCDtlsTransport } from \"..\";\nimport { SenderDirections } from \"../const\";\nimport { Kind } from \"../types/domain\";\nimport {\n RTCRtpCodecParameters,\n RTCRtpHeaderExtensionParameters,\n} from \"./parameters\";\nimport { RTCRtpReceiver } from \"./rtpReceiver\";\nimport { RTCRtpSender } from \"./rtpSender\";\nimport { MediaStreamTrack } from \"./track\";\n\nexport class RTCRtpTransceiver {\n readonly uuid = uuid.v4();\n readonly onTrack = new Event<[MediaStreamTrack, RTCRtpTransceiver]>();\n mid?: string;\n mLineIndex?: number;\n usedForSender = false;\n private _currentDirection?: Direction | \"stopped\";\n set currentDirection(direction: Direction | \"stopped\" | undefined) {\n this._currentDirection = direction;\n if (SenderDirections.includes(this._currentDirection || \"\")) {\n this.usedForSender = true;\n }\n }\n /**RFC 8829 4.2.5. last negotiated direction */\n get currentDirection(): Direction | \"stopped\" | undefined {\n return this._currentDirection;\n }\n\n offerDirection!: Direction;\n _codecs: RTCRtpCodecParameters[] = [];\n set codecs(codecs: RTCRtpCodecParameters[]) {\n this._codecs = codecs;\n }\n get codecs() {\n return this._codecs;\n }\n headerExtensions: RTCRtpHeaderExtensionParameters[] = [];\n options: Partial<TransceiverOptions> = {};\n stopping = false;\n stopped = false;\n\n constructor(\n public readonly kind: Kind,\n dtlsTransport: RTCDtlsTransport,\n public receiver: RTCRtpReceiver,\n public sender: RTCRtpSender,\n /**RFC 8829 4.2.4. direction the transceiver was initialized with */\n public direction: Direction\n ) {\n this.setDtlsTransport(dtlsTransport);\n }\n\n get dtlsTransport() {\n return this.receiver.dtlsTransport;\n }\n\n setDtlsTransport(dtls: RTCDtlsTransport) {\n this.receiver.setDtlsTransport(dtls);\n this.sender.setDtlsTransport(dtls);\n }\n\n get msid() {\n return `${this.sender.streamId} ${this.sender.trackId}`;\n }\n\n addTrack(track: MediaStreamTrack) {\n const res = this.receiver.addTrack(track);\n if (res) {\n this.onTrack.execute(track, this);\n }\n }\n\n // todo impl\n // https://www.w3.org/TR/webrtc/#methods-8\n stop() {\n if (this.stopping) return;\n\n // todo Stop sending and receiving with transceiver.\n\n this.stopping = true;\n }\n\n getPayloadType(mimeType: string) {\n return this.codecs.find((codec) =>\n codec.mimeType.toLowerCase().includes(mimeType.toLowerCase())\n )?.payloadType;\n }\n}\n\nexport const Inactive = \"inactive\";\nexport const Sendonly = \"sendonly\";\nexport const Recvonly = \"recvonly\";\nexport const Sendrecv = \"sendrecv\";\n\nexport const Directions = [Inactive, Sendonly, Recvonly, Sendrecv] as const;\n\nexport type Direction = typeof Directions[number];\n\ntype SimulcastDirection = \"send\" | \"recv\";\n\nexport interface TransceiverOptions {\n direction: Direction;\n simulcast: { direction: SimulcastDirection; rid: string }[];\n}\n"]}
@@ -8,10 +8,14 @@ export declare class MediaRecorder {
8
8
  ext: string;
9
9
  constructor(tracks: MediaStreamTrack[], path: string, options?: Partial<MediaRecorderOptions>);
10
10
  addTrack(track: MediaStreamTrack): void;
11
- start(): void;
11
+ start(): Promise<void>;
12
12
  stop(): Promise<void>;
13
13
  }
14
14
  export interface MediaRecorderOptions {
15
15
  width: number;
16
16
  height: number;
17
+ jitterBufferLatency: number;
18
+ jitterBufferSize: number;
19
+ waitForKeyframe: boolean;
20
+ defaultDuration: number;
17
21
  }
@@ -20,8 +20,8 @@ class MediaRecorder {
20
20
  addTrack(track) {
21
21
  this.tracks.push(track);
22
22
  }
23
- start() {
24
- this.writer.start(this.tracks);
23
+ async start() {
24
+ await this.writer.start(this.tracks);
25
25
  }
26
26
  async stop() {
27
27
  await this.writer.stop();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/nonstandard/recorder/index.ts"],"names":[],"mappings":";;;AAEA,wCAA4C;AAE5C,MAAa,aAAa;IAIxB,YACS,MAA0B,EAC1B,IAAY,EACZ,UAAyC,EAAE;QAF3C,WAAM,GAAN,MAAM,CAAoB;QAC1B,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAAoC;QAElD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE;YAClB,QAAQ,IAAI,CAAC,GAAG,EAAE;gBAChB,KAAK,MAAM;oBACT,OAAO,IAAI,kBAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACxC;oBACE,MAAM,IAAI,KAAK,EAAE,CAAC;aACrB;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF;AA/BD,sCA+BC","sourcesContent":["import { MediaStreamTrack } from \"../../media/track\";\nimport { MediaWriter } from \"./writer\";\nimport { WebmFactory } from \"./writer/webm\";\n\nexport class MediaRecorder {\n writer: MediaWriter;\n ext: string;\n\n constructor(\n public tracks: MediaStreamTrack[],\n public path: string,\n public options: Partial<MediaRecorderOptions> = {}\n ) {\n this.ext = path.split(\".\").slice(-1)[0];\n this.writer = (() => {\n switch (this.ext) {\n case \"webm\":\n return new WebmFactory(path, options);\n default:\n throw new Error();\n }\n })();\n }\n\n addTrack(track: MediaStreamTrack) {\n this.tracks.push(track);\n }\n\n start() {\n this.writer.start(this.tracks);\n }\n\n async stop() {\n await this.writer.stop();\n }\n}\n\nexport interface MediaRecorderOptions {\n width: number;\n height: number;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/nonstandard/recorder/index.ts"],"names":[],"mappings":";;;AAEA,wCAA4C;AAE5C,MAAa,aAAa;IAIxB,YACS,MAA0B,EAC1B,IAAY,EACZ,UAAyC,EAAE;QAF3C,WAAM,GAAN,MAAM,CAAoB;QAC1B,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAAoC;QAElD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE;YAClB,QAAQ,IAAI,CAAC,GAAG,EAAE;gBAChB,KAAK,MAAM;oBACT,OAAO,IAAI,kBAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACxC;oBACE,MAAM,IAAI,KAAK,EAAE,CAAC;aACrB;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF;AA/BD,sCA+BC","sourcesContent":["import { MediaStreamTrack } from \"../../media/track\";\nimport { MediaWriter } from \"./writer\";\nimport { WebmFactory } from \"./writer/webm\";\n\nexport class MediaRecorder {\n writer: MediaWriter;\n ext: string;\n\n constructor(\n public tracks: MediaStreamTrack[],\n public path: string,\n public options: Partial<MediaRecorderOptions> = {}\n ) {\n this.ext = path.split(\".\").slice(-1)[0];\n this.writer = (() => {\n switch (this.ext) {\n case \"webm\":\n return new WebmFactory(path, options);\n default:\n throw new Error();\n }\n })();\n }\n\n addTrack(track: MediaStreamTrack) {\n this.tracks.push(track);\n }\n\n async start() {\n await this.writer.start(this.tracks);\n }\n\n async stop() {\n await this.writer.stop();\n }\n}\n\nexport interface MediaRecorderOptions {\n width: number;\n height: number;\n jitterBufferLatency: number;\n jitterBufferSize: number;\n waitForKeyframe: boolean;\n defaultDuration: number;\n}\n"]}
@@ -4,6 +4,6 @@ export declare abstract class MediaWriter {
4
4
  protected path: string;
5
5
  protected options: Partial<MediaRecorderOptions>;
6
6
  constructor(path: string, options: Partial<MediaRecorderOptions>);
7
- start(tracks: MediaStreamTrack[]): void;
7
+ start(tracks: MediaStreamTrack[]): Promise<void>;
8
8
  stop(): Promise<void>;
9
9
  }
@@ -6,7 +6,7 @@ class MediaWriter {
6
6
  this.path = path;
7
7
  this.options = options;
8
8
  }
9
- start(tracks) { }
9
+ async start(tracks) { }
10
10
  async stop() { }
11
11
  }
12
12
  exports.MediaWriter = MediaWriter;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/index.ts"],"names":[],"mappings":";;;AAGA,MAAsB,WAAW;IAC/B,YACY,IAAY,EACZ,OAAsC;QADtC,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAA+B;IAC/C,CAAC;IAEJ,KAAK,CAAC,MAA0B,IAAG,CAAC;IAEpC,KAAK,CAAC,IAAI,KAAI,CAAC;CAChB;AATD,kCASC","sourcesContent":["import { MediaStreamTrack } from \"../../..\";\nimport { MediaRecorderOptions } from \"..\";\n\nexport abstract class MediaWriter {\n constructor(\n protected path: string,\n protected options: Partial<MediaRecorderOptions>\n ) {}\n\n start(tracks: MediaStreamTrack[]) {}\n\n async stop() {}\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/index.ts"],"names":[],"mappings":";;;AAGA,MAAsB,WAAW;IAC/B,YACY,IAAY,EACZ,OAAsC;QADtC,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAA+B;IAC/C,CAAC;IAEJ,KAAK,CAAC,KAAK,CAAC,MAA0B,IAAG,CAAC;IAE1C,KAAK,CAAC,IAAI,KAAI,CAAC;CAChB;AATD,kCASC","sourcesContent":["import { MediaStreamTrack } from \"../../..\";\nimport { MediaRecorderOptions } from \"..\";\n\nexport abstract class MediaWriter {\n constructor(\n protected path: string,\n protected options: Partial<MediaRecorderOptions>\n ) {}\n\n async start(tracks: MediaStreamTrack[]) {}\n\n async stop() {}\n}\n"]}
@@ -1,7 +1,7 @@
1
- import { MediaStreamTrack, WebmOutput } from "../../..";
1
+ import { MediaStreamTrack, RtpSourceStream } from "../../..";
2
2
  import { MediaWriter } from ".";
3
3
  export declare class WebmFactory extends MediaWriter {
4
- webm?: WebmOutput;
5
- start(tracks: MediaStreamTrack[]): void;
4
+ rtpSources: RtpSourceStream[];
5
+ start(tracks: MediaStreamTrack[]): Promise<void>;
6
6
  stop(): Promise<void>;
7
7
  }
@@ -1,35 +1,18 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.WebmFactory = void 0;
27
- const fs = __importStar(require("fs/promises"));
4
+ const promises_1 = require("fs/promises");
28
5
  const __1 = require("../../..");
29
6
  const _1 = require(".");
7
+ const sourcePath = "packages/webrtc/src/nonstandard/recorder/writer/webm.ts";
30
8
  class WebmFactory extends _1.MediaWriter {
31
- start(tracks) {
32
- this.webm = new __1.WebmOutput(fs, this.path, tracks.map((track, i) => {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.rtpSources = [];
12
+ }
13
+ async start(tracks) {
14
+ await (0, promises_1.unlink)(this.path).catch((e) => e);
15
+ const inputTracks = tracks.map((track, i) => {
33
16
  const trackNumber = i + 1;
34
17
  const payloadType = track.codec.payloadType;
35
18
  if (track.kind === "video") {
@@ -44,41 +27,78 @@ class WebmFactory extends _1.MediaWriter {
44
27
  case "av1x":
45
28
  return "AV1";
46
29
  default:
47
- throw new Error();
30
+ throw new __1.WeriftError({
31
+ message: "unsupported codec",
32
+ payload: { track, path: sourcePath },
33
+ });
48
34
  }
49
35
  })();
50
36
  return {
51
37
  kind: "video",
38
+ codec,
52
39
  clockRate: 90000,
53
- payloadType,
54
40
  trackNumber,
55
- codec,
56
41
  width: this.options.width,
57
42
  height: this.options.height,
43
+ payloadType,
44
+ track,
58
45
  };
59
46
  }
60
47
  else {
61
48
  return {
62
49
  kind: "audio",
50
+ codec: "OPUS",
63
51
  clockRate: 48000,
64
- payloadType,
65
52
  trackNumber,
66
- codec: "OPUS",
53
+ payloadType,
54
+ track,
67
55
  };
68
56
  }
69
- }));
70
- tracks.forEach((track) => {
71
- const sampleBuilder = track.kind === "video"
72
- ? new __1.SampleBuilder((h) => !!h.marker).pipe(this.webm)
73
- : new __1.SampleBuilder(() => true).pipe(this.webm);
74
- new __1.JitterBuffer({
75
- rtpStream: track.onReceiveRtp,
76
- rtcpStream: track.onReceiveRtcp,
77
- }).pipe(sampleBuilder);
78
57
  });
58
+ const webm = new __1.WebmLiveSink(inputTracks, {
59
+ duration: this.options.defaultDuration ?? 1000 * 60 * 60 * 24,
60
+ });
61
+ this.rtpSources = inputTracks.map(({ track, clockRate, codec }) => {
62
+ const rtpSource = new __1.RtpSourceStream(track.onReceiveRtp);
63
+ const jitterBuffer = (0, __1.jitterBufferTransformer)(clockRate, {
64
+ latency: this.options.jitterBufferLatency,
65
+ bufferSize: this.options.jitterBufferSize,
66
+ });
67
+ if (track.kind === "video") {
68
+ rtpSource.readable
69
+ .pipeThrough(jitterBuffer)
70
+ .pipeThrough((0, __1.depacketizeTransformer)((h) => h.marker, codec, {
71
+ waitForKeyframe: this.options.waitForKeyframe,
72
+ }))
73
+ .pipeTo(webm.videoStream);
74
+ }
75
+ else {
76
+ rtpSource.readable
77
+ .pipeThrough(jitterBuffer)
78
+ .pipeThrough((0, __1.depacketizeTransformer)(() => true, codec))
79
+ .pipeTo(webm.audioStream);
80
+ }
81
+ return rtpSource;
82
+ });
83
+ const reader = webm.webmStream.getReader();
84
+ const readChunk = async ({ value, done, }) => {
85
+ if (done)
86
+ return;
87
+ if (value.packet) {
88
+ await (0, promises_1.appendFile)(this.path, value.packet);
89
+ }
90
+ else if (value.eol) {
91
+ const { durationElement } = value.eol;
92
+ const handler = await (0, promises_1.open)(this.path, "r+");
93
+ await handler.write(durationElement, 0, durationElement.length, 83);
94
+ await handler.close();
95
+ }
96
+ reader.read().then(readChunk);
97
+ };
98
+ reader.read().then(readChunk);
79
99
  }
80
100
  async stop() {
81
- await this.webm.stop();
101
+ await Promise.all(this.rtpSources.map((r) => r.stop()));
82
102
  }
83
103
  }
84
104
  exports.WebmFactory = WebmFactory;
@@ -1 +1 @@
1
- {"version":3,"file":"webm.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/webm.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAGlC,gCAKkB;AAClB,wBAAgC;AAEhC,MAAa,WAAY,SAAQ,cAAW;IAG1C,KAAK,CAAC,MAA0B;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,cAAU,CACxB,EAAE,EACF,IAAI,CAAC,IAAI,EACT,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACtB,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1B,MAAM,WAAW,GAAG,KAAK,CAAC,KAAM,CAAC,WAAW,CAAC;YAE7C,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE;gBAC1B,MAAM,KAAK,GAAG,CAAC,GAAmB,EAAE;oBAClC,QAAQ,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAyB,EAAE;wBAC9D,KAAK,KAAK;4BACR,OAAO,KAAK,CAAC;wBACf,KAAK,KAAK;4BACR,OAAO,KAAK,CAAC;wBACf,KAAK,MAAM;4BACT,OAAO,eAAe,CAAC;wBACzB,KAAK,MAAM;4BACT,OAAO,KAAK,CAAC;wBACf;4BACE,MAAM,IAAI,KAAK,EAAE,CAAC;qBACrB;gBACH,CAAC,CAAC,EAAE,CAAC;gBACL,OAAO;oBACL,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,KAAK;oBAChB,WAAW;oBACX,WAAW;oBACX,KAAK;oBACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;oBACzB,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;iBAC5B,CAAC;aACH;iBAAM;gBACL,OAAO;oBACL,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,KAAK;oBAChB,WAAW;oBACX,WAAW;oBACX,KAAK,EAAE,MAAM;iBACd,CAAC;aACH;QACH,CAAC,CAAC,CACH,CAAC;QAEF,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACvB,MAAM,aAAa,GACjB,KAAK,CAAC,IAAI,KAAK,OAAO;gBACpB,CAAC,CAAC,IAAI,iBAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAK,CAAC;gBACvD,CAAC,CAAC,IAAI,iBAAa,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAK,CAAC,CAAC;YACrD,IAAI,gBAAY,CAAC;gBACf,SAAS,EAAE,KAAK,CAAC,YAAY;gBAC7B,UAAU,EAAE,KAAK,CAAC,aAAa;aAChC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,IAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;CACF;AA9DD,kCA8DC;AAED,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAU,CAAC","sourcesContent":["import * as fs from \"fs/promises\";\n\nimport { SupportedCodec } from \"../../../../../rtp/src/container/webm\";\nimport {\n JitterBuffer,\n MediaStreamTrack,\n SampleBuilder,\n WebmOutput,\n} from \"../../..\";\nimport { MediaWriter } from \".\";\n\nexport class WebmFactory extends MediaWriter {\n webm?: WebmOutput;\n\n start(tracks: MediaStreamTrack[]) {\n this.webm = new WebmOutput(\n fs,\n this.path,\n tracks.map((track, i) => {\n const trackNumber = i + 1;\n const payloadType = track.codec!.payloadType;\n\n if (track.kind === \"video\") {\n const codec = ((): SupportedCodec => {\n switch (track.codec?.name.toLowerCase() as SupportedVideoCodec) {\n case \"vp8\":\n return \"VP8\";\n case \"vp9\":\n return \"VP9\";\n case \"h264\":\n return \"MPEG4/ISO/AVC\";\n case \"av1x\":\n return \"AV1\";\n default:\n throw new Error();\n }\n })();\n return {\n kind: \"video\",\n clockRate: 90000,\n payloadType,\n trackNumber,\n codec,\n width: this.options.width,\n height: this.options.height,\n };\n } else {\n return {\n kind: \"audio\",\n clockRate: 48000,\n payloadType,\n trackNumber,\n codec: \"OPUS\",\n };\n }\n })\n );\n\n tracks.forEach((track) => {\n const sampleBuilder =\n track.kind === \"video\"\n ? new SampleBuilder((h) => !!h.marker).pipe(this.webm!)\n : new SampleBuilder(() => true).pipe(this.webm!);\n new JitterBuffer({\n rtpStream: track.onReceiveRtp,\n rtcpStream: track.onReceiveRtcp,\n }).pipe(sampleBuilder);\n });\n }\n\n async stop() {\n await this.webm!.stop();\n }\n}\n\nconst supportedVideoCodecs = [\"h264\", \"vp8\", \"vp9\", \"av1x\"] as const;\ntype SupportedVideoCodec = typeof supportedVideoCodecs[number];\n"]}
1
+ {"version":3,"file":"webm.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/webm.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFile, open, unlink } from \"fs/promises\";\nimport { ReadableStreamDefaultReadResult } from \"stream/web\";\n\nimport { SupportedCodec } from \"../../../../../rtp/src/container/webm\";\nimport {\n depacketizeTransformer,\n jitterBufferTransformer,\n MediaStreamTrack,\n RtpSourceStream,\n WebmLiveOutput,\n WebmLiveSink,\n WeriftError,\n} from \"../../..\";\nimport { MediaWriter } from \".\";\n\nconst sourcePath = \"packages/webrtc/src/nonstandard/recorder/writer/webm.ts\";\n\nexport class WebmFactory extends MediaWriter {\n rtpSources: RtpSourceStream[] = [];\n\n async start(tracks: MediaStreamTrack[]) {\n await unlink(this.path).catch((e) => e);\n\n const inputTracks = tracks.map((track, i) => {\n const trackNumber = i + 1;\n const payloadType = track.codec!.payloadType;\n\n if (track.kind === \"video\") {\n const codec = ((): SupportedCodec => {\n switch (track.codec?.name.toLowerCase() as SupportedVideoCodec) {\n case \"vp8\":\n return \"VP8\";\n case \"vp9\":\n return \"VP9\";\n case \"h264\":\n return \"MPEG4/ISO/AVC\";\n case \"av1x\":\n return \"AV1\";\n default:\n throw new WeriftError({\n message: \"unsupported codec\",\n payload: { track, path: sourcePath },\n });\n }\n })();\n return {\n kind: \"video\" as const,\n codec,\n clockRate: 90000,\n trackNumber,\n width: this.options.width,\n height: this.options.height,\n payloadType,\n track,\n };\n } else {\n return {\n kind: \"audio\" as const,\n codec: \"OPUS\" as const,\n clockRate: 48000,\n trackNumber,\n payloadType,\n track,\n };\n }\n });\n\n const webm = new WebmLiveSink(inputTracks, {\n duration: this.options.defaultDuration ?? 1000 * 60 * 60 * 24,\n });\n\n this.rtpSources = inputTracks.map(({ track, clockRate, codec }) => {\n const rtpSource = new RtpSourceStream(track.onReceiveRtp);\n\n const jitterBuffer = jitterBufferTransformer(clockRate, {\n latency: this.options.jitterBufferLatency,\n bufferSize: this.options.jitterBufferSize,\n });\n\n if (track.kind === \"video\") {\n rtpSource.readable\n .pipeThrough(jitterBuffer)\n .pipeThrough(\n depacketizeTransformer((h) => h.marker, codec, {\n waitForKeyframe: this.options.waitForKeyframe,\n })\n )\n .pipeTo(webm.videoStream);\n } else {\n rtpSource.readable\n .pipeThrough(jitterBuffer)\n .pipeThrough(depacketizeTransformer(() => true, codec))\n .pipeTo(webm.audioStream);\n }\n\n return rtpSource;\n });\n\n const reader = webm.webmStream.getReader();\n const readChunk = async ({\n value,\n done,\n }: ReadableStreamDefaultReadResult<WebmLiveOutput>) => {\n if (done) return;\n\n if (value.packet) {\n await appendFile(this.path, value.packet);\n } else if (value.eol) {\n const { durationElement } = value.eol;\n const handler = await open(this.path, \"r+\");\n await handler.write(durationElement, 0, durationElement.length, 83);\n await handler.close();\n }\n reader.read().then(readChunk);\n };\n reader.read().then(readChunk);\n }\n\n async stop() {\n await Promise.all(this.rtpSources.map((r) => r.stop()));\n }\n}\n\nconst supportedVideoCodecs = [\"h264\", \"vp8\", \"vp9\", \"av1x\"] as const;\ntype SupportedVideoCodec = typeof supportedVideoCodecs[number];\n"]}
@@ -1,5 +1,7 @@
1
1
  import Event from "rx.mini";
2
- import { InterfaceAddresses } from ".";
2
+ import { Message } from "../../ice/src/stun/message";
3
+ import { Protocol } from "../../ice/src/types/model";
4
+ import { Address, InterfaceAddresses } from ".";
3
5
  import { DtlsKeys } from ".";
4
6
  import { RTCDataChannel } from "./dataChannel";
5
7
  import { EventTarget } from "./helper";
@@ -34,6 +36,7 @@ export declare class RTCPeerConnection extends EventTarget {
34
36
  readonly onTransceiverAdded: Event<[RTCRtpTransceiver]>;
35
37
  readonly onIceCandidate: Event<[RTCIceCandidate]>;
36
38
  readonly onNegotiationneeded: Event<[]>;
39
+ readonly onTrack: Event<[MediaStreamTrack]>;
37
40
  ondatachannel?: CallbackWithValue<RTCDataChannelEvent>;
38
41
  onicecandidate?: CallbackWithValue<RTCPeerConnectionIceEvent>;
39
42
  onnegotiationneeded?: CallbackWithValue<any>;
@@ -135,6 +138,11 @@ export interface PeerConfig {
135
138
  /**Minimum port and Maximum port must not be the same value */
136
139
  icePortRange: [number, number] | undefined;
137
140
  iceInterfaceAddresses: InterfaceAddresses | undefined;
141
+ iceUseIpv4: boolean;
142
+ iceUseIpv6: boolean;
143
+ /** If provided, is called on each STUN request.
144
+ * Return `true` if a STUN response should be sent, false if it should be skipped. */
145
+ iceFilterStunResponse: ((message: Message, addr: Address, protocol: Protocol) => boolean) | undefined;
138
146
  dtls: Partial<{
139
147
  keys: DtlsKeys;
140
148
  }>;
@@ -70,6 +70,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
70
70
  this.onTransceiverAdded = new rx_mini_1.default();
71
71
  this.onIceCandidate = new rx_mini_1.default();
72
72
  this.onNegotiationneeded = new rx_mini_1.default();
73
+ this.onTrack = new rx_mini_1.default();
73
74
  this.router = new router_1.RtpRouter();
74
75
  this.certificates = [];
75
76
  this.seenMid = new Set();
@@ -219,7 +220,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
219
220
  }
220
221
  if (transceiver.headerExtensions.length === 0) {
221
222
  transceiver.headerExtensions =
222
- this.config.headerExtensions[transceiver.kind];
223
+ this.config.headerExtensions[transceiver.kind] ?? [];
223
224
  }
224
225
  });
225
226
  const description = new sdp_1.SessionDescription();
@@ -339,6 +340,9 @@ class RTCPeerConnection extends helper_1.EventTarget {
339
340
  forceTurn: this.config.iceTransportPolicy === "relay",
340
341
  portRange: this.config.icePortRange,
341
342
  interfaceAddresses: this.config.iceInterfaceAddresses,
343
+ filterStunResponse: this.config.iceFilterStunResponse,
344
+ useIpv4: this.config.iceUseIpv4,
345
+ useIpv6: this.config.iceUseIpv6,
342
346
  });
343
347
  if (existing) {
344
348
  iceGatherer.connection.localUserName = existing.connection.localUserName;
@@ -828,6 +832,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
828
832
  transceiver,
829
833
  receiver: transceiver.receiver,
830
834
  };
835
+ this.onTrack.execute(track);
831
836
  this.emit("track", event);
832
837
  if (this.ontrack)
833
838
  this.ontrack(event);
@@ -1206,6 +1211,9 @@ exports.defaultPeerConfig = {
1206
1211
  iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
1207
1212
  icePortRange: undefined,
1208
1213
  iceInterfaceAddresses: undefined,
1214
+ iceUseIpv4: true,
1215
+ iceUseIpv6: true,
1216
+ iceFilterStunResponse: undefined,
1209
1217
  dtls: {},
1210
1218
  bundlePolicy: "max-compat",
1211
1219
  debug: {},