werift 0.23.0 → 0.24.1

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 (169) hide show
  1. package/README.md +46 -0
  2. package/lib/common/src/binary.js +2 -3
  3. package/lib/common/src/binary.js.map +1 -1
  4. package/lib/common/src/transport.d.ts +22 -9
  5. package/lib/common/src/transport.js +152 -25
  6. package/lib/common/src/transport.js.map +1 -1
  7. package/lib/ice/src/candidate.d.ts +16 -1
  8. package/lib/ice/src/candidate.js +99 -15
  9. package/lib/ice/src/candidate.js.map +1 -1
  10. package/lib/ice/src/ice.d.ts +2 -0
  11. package/lib/ice/src/ice.js +175 -50
  12. package/lib/ice/src/ice.js.map +1 -1
  13. package/lib/ice/src/iceBase.d.ts +23 -2
  14. package/lib/ice/src/iceBase.js +63 -1
  15. package/lib/ice/src/iceBase.js.map +1 -1
  16. package/lib/ice/src/index.d.ts +2 -0
  17. package/lib/ice/src/index.js +2 -0
  18. package/lib/ice/src/index.js.map +1 -1
  19. package/lib/ice/src/internal/selectAddresses.d.ts +9 -0
  20. package/lib/ice/src/internal/selectAddresses.js +40 -0
  21. package/lib/ice/src/internal/selectAddresses.js.map +1 -0
  22. package/lib/ice/src/stun/const.d.ts +1 -26
  23. package/lib/ice/src/stun/const.js +15 -54
  24. package/lib/ice/src/stun/const.js.map +1 -1
  25. package/lib/ice/src/stun/message.d.ts +1 -25
  26. package/lib/ice/src/stun/message.js +15 -149
  27. package/lib/ice/src/stun/message.js.map +1 -1
  28. package/lib/ice/src/stun/protocol.d.ts +1 -1
  29. package/lib/ice/src/stun/protocol.js +2 -2
  30. package/lib/ice/src/stun/protocol.js.map +1 -1
  31. package/lib/ice/src/stun/tcpFrame.d.ts +5 -0
  32. package/lib/ice/src/stun/tcpFrame.js +26 -0
  33. package/lib/ice/src/stun/tcpFrame.js.map +1 -0
  34. package/lib/ice/src/stun/tcpProtocol.d.ts +51 -0
  35. package/lib/ice/src/stun/tcpProtocol.js +326 -0
  36. package/lib/ice/src/stun/tcpProtocol.js.map +1 -0
  37. package/lib/ice/src/stun/transaction.d.ts +2 -1
  38. package/lib/ice/src/stun/transaction.js +9 -3
  39. package/lib/ice/src/stun/transaction.js.map +1 -1
  40. package/lib/ice/src/turn/frame.d.ts +1 -0
  41. package/lib/ice/src/turn/frame.js +18 -0
  42. package/lib/ice/src/turn/frame.js.map +1 -0
  43. package/lib/ice/src/turn/protocol.d.ts +9 -7
  44. package/lib/ice/src/turn/protocol.js +32 -44
  45. package/lib/ice/src/turn/protocol.js.map +1 -1
  46. package/lib/ice/src/types/model.d.ts +1 -1
  47. package/lib/ice/src/types/model.js.map +1 -1
  48. package/lib/ice/src/utils.js +2 -31
  49. package/lib/ice/src/utils.js.map +1 -1
  50. package/lib/{ice → ice-server}/src/stun/attributes.d.ts +12 -5
  51. package/lib/{ice → ice-server}/src/stun/attributes.js +69 -66
  52. package/lib/ice-server/src/stun/attributes.js.map +1 -0
  53. package/lib/ice-server/src/stun/const.d.ts +28 -0
  54. package/lib/ice-server/src/stun/const.js +88 -0
  55. package/lib/ice-server/src/stun/const.js.map +1 -0
  56. package/lib/ice-server/src/stun/ip.d.ts +12 -0
  57. package/lib/ice-server/src/stun/ip.js +88 -0
  58. package/lib/ice-server/src/stun/ip.js.map +1 -0
  59. package/lib/ice-server/src/stun/message.d.ts +37 -0
  60. package/lib/ice-server/src/stun/message.js +190 -0
  61. package/lib/ice-server/src/stun/message.js.map +1 -0
  62. package/lib/ice-server/src/turn/auth.d.ts +1 -0
  63. package/lib/ice-server/src/turn/auth.js +10 -0
  64. package/lib/ice-server/src/turn/auth.js.map +1 -0
  65. package/lib/ice-server/src/turn/frame.d.ts +13 -0
  66. package/lib/ice-server/src/turn/frame.js +69 -0
  67. package/lib/ice-server/src/turn/frame.js.map +1 -0
  68. package/lib/index.mjs +2866 -946
  69. package/lib/nonstandard/index.mjs +1872 -455
  70. package/lib/rtp/src/codec/av1.js +11 -5
  71. package/lib/rtp/src/codec/av1.js.map +1 -1
  72. package/lib/rtp/src/codec/leb128.d.ts +5 -0
  73. package/lib/rtp/src/codec/leb128.js +24 -0
  74. package/lib/rtp/src/codec/leb128.js.map +1 -0
  75. package/lib/rtp/src/extra/container/mp4/container.d.ts +9 -6
  76. package/lib/rtp/src/extra/container/mp4/container.js +258 -173
  77. package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
  78. package/lib/rtp/src/extra/container/mp4/h264.d.ts +1 -0
  79. package/lib/rtp/src/extra/container/mp4/h264.js +13 -0
  80. package/lib/rtp/src/extra/container/mp4/h264.js.map +1 -1
  81. package/lib/rtp/src/extra/processor/mp4.d.ts +11 -7
  82. package/lib/rtp/src/extra/processor/mp4.js +115 -60
  83. package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
  84. package/lib/rtp/src/extra/processor/mp4Callback.d.ts +2 -3
  85. package/lib/rtp/src/extra/processor/mp4Callback.js +5 -3
  86. package/lib/rtp/src/extra/processor/mp4Callback.js.map +1 -1
  87. package/lib/rtp/src/rtp/rtx.js +4 -6
  88. package/lib/rtp/src/rtp/rtx.js.map +1 -1
  89. package/lib/rtp/src/srtp/context/context.d.ts +2 -0
  90. package/lib/rtp/src/srtp/context/context.js +12 -12
  91. package/lib/rtp/src/srtp/context/context.js.map +1 -1
  92. package/lib/sctp/src/param.js +31 -15
  93. package/lib/sctp/src/param.js.map +1 -1
  94. package/lib/sctp/src/sctp.js +10 -9
  95. package/lib/sctp/src/sctp.js.map +1 -1
  96. package/lib/webrtc/src/dataChannel.d.ts +3 -2
  97. package/lib/webrtc/src/dataChannel.js +9 -2
  98. package/lib/webrtc/src/dataChannel.js.map +1 -1
  99. package/lib/webrtc/src/errors.d.ts +3 -0
  100. package/lib/webrtc/src/errors.js +11 -0
  101. package/lib/webrtc/src/errors.js.map +1 -0
  102. package/lib/webrtc/src/helper.d.ts +5 -1
  103. package/lib/webrtc/src/helper.js +33 -1
  104. package/lib/webrtc/src/helper.js.map +1 -1
  105. package/lib/webrtc/src/media/receiver/statistics.d.ts +4 -1
  106. package/lib/webrtc/src/media/receiver/statistics.js +22 -0
  107. package/lib/webrtc/src/media/receiver/statistics.js.map +1 -1
  108. package/lib/webrtc/src/media/rtpReceiver.d.ts +15 -2
  109. package/lib/webrtc/src/media/rtpReceiver.js +135 -20
  110. package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
  111. package/lib/webrtc/src/media/rtpSender.d.ts +32 -7
  112. package/lib/webrtc/src/media/rtpSender.js +179 -31
  113. package/lib/webrtc/src/media/rtpSender.js.map +1 -1
  114. package/lib/webrtc/src/media/rtpTransceiver.d.ts +13 -4
  115. package/lib/webrtc/src/media/rtpTransceiver.js +26 -5
  116. package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
  117. package/lib/webrtc/src/media/stats.d.ts +15 -1
  118. package/lib/webrtc/src/media/stats.js +55 -1
  119. package/lib/webrtc/src/media/stats.js.map +1 -1
  120. package/lib/webrtc/src/media/track.d.ts +2 -1
  121. package/lib/webrtc/src/media/track.js +7 -1
  122. package/lib/webrtc/src/media/track.js.map +1 -1
  123. package/lib/webrtc/src/nonstandard/dummyMedia.d.ts +19 -0
  124. package/lib/webrtc/src/nonstandard/dummyMedia.js +157 -0
  125. package/lib/webrtc/src/nonstandard/dummyMedia.js.map +1 -0
  126. package/lib/webrtc/src/nonstandard/navigator.d.ts +18 -8
  127. package/lib/webrtc/src/nonstandard/navigator.js +96 -32
  128. package/lib/webrtc/src/nonstandard/navigator.js.map +1 -1
  129. package/lib/webrtc/src/nonstandard/userMedia/packetizer.d.ts +16 -0
  130. package/lib/webrtc/src/nonstandard/userMedia/packetizer.js +360 -0
  131. package/lib/webrtc/src/nonstandard/userMedia/packetizer.js.map +1 -0
  132. package/lib/webrtc/src/nonstandard/userMedia.d.ts +71 -31
  133. package/lib/webrtc/src/nonstandard/userMedia.js +454 -108
  134. package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
  135. package/lib/webrtc/src/peerConnection.d.ts +183 -50
  136. package/lib/webrtc/src/peerConnection.js +458 -109
  137. package/lib/webrtc/src/peerConnection.js.map +1 -1
  138. package/lib/webrtc/src/sctpManager.d.ts +1 -1
  139. package/lib/webrtc/src/sctpManager.js +27 -7
  140. package/lib/webrtc/src/sctpManager.js.map +1 -1
  141. package/lib/webrtc/src/sdp.d.ts +10 -7
  142. package/lib/webrtc/src/sdp.js +40 -44
  143. package/lib/webrtc/src/sdp.js.map +1 -1
  144. package/lib/webrtc/src/sdpManager.d.ts +4 -3
  145. package/lib/webrtc/src/sdpManager.js +50 -27
  146. package/lib/webrtc/src/sdpManager.js.map +1 -1
  147. package/lib/webrtc/src/secureTransportManager.d.ts +13 -4
  148. package/lib/webrtc/src/secureTransportManager.js +100 -22
  149. package/lib/webrtc/src/secureTransportManager.js.map +1 -1
  150. package/lib/webrtc/src/transceiverManager.d.ts +5 -4
  151. package/lib/webrtc/src/transceiverManager.js +61 -62
  152. package/lib/webrtc/src/transceiverManager.js.map +1 -1
  153. package/lib/webrtc/src/transport/dtls.d.ts +15 -4
  154. package/lib/webrtc/src/transport/dtls.js +91 -23
  155. package/lib/webrtc/src/transport/dtls.js.map +1 -1
  156. package/lib/webrtc/src/transport/ice.d.ts +21 -3
  157. package/lib/webrtc/src/transport/ice.js +159 -30
  158. package/lib/webrtc/src/transport/ice.js.map +1 -1
  159. package/lib/webrtc/src/transport/sctp.d.ts +1 -0
  160. package/lib/webrtc/src/transport/sctp.js +26 -22
  161. package/lib/webrtc/src/transport/sctp.js.map +1 -1
  162. package/lib/webrtc/src/utils.d.ts +12 -4
  163. package/lib/webrtc/src/utils.js +118 -16
  164. package/lib/webrtc/src/utils.js.map +1 -1
  165. package/package.json +7 -10
  166. package/lib/ice/src/stun/attributes.js.map +0 -1
  167. package/lib/rtp/src/extra/container/mp4/mp4box.d.ts +0 -5
  168. package/lib/rtp/src/extra/container/mp4/mp4box.js +0 -32
  169. package/lib/rtp/src/extra/container/mp4/mp4box.js.map +0 -1
@@ -1,164 +1,510 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MediaPlayerWebm = exports.MediaPlayerMp4 = exports.getUserMedia = void 0;
4
- const child_process_1 = require("child_process");
3
+ exports.getUserMedia = void 0;
5
4
  const crypto_1 = require("crypto");
6
- const dgram_1 = require("dgram");
7
- const promises_1 = require("timers/promises");
5
+ const os_1 = require("os");
6
+ const path_1 = require("path");
7
+ const stream_1 = require("stream");
8
+ const promises_1 = require("fs/promises");
9
+ const promises_2 = require("timers/promises");
10
+ const mediabunny_1 = require("mediabunny");
8
11
  const common_1 = require("../imports/common");
9
- const rtp_1 = require("../imports/rtp");
10
12
  const track_1 = require("../media/track");
11
- const getUserMedia = async ({ path, loop, width, height, }) => {
12
- const audioPort = await (0, common_1.randomPort)();
13
- const videoPort = await (0, common_1.randomPort)();
14
- if (path.endsWith(".mp4")) {
15
- return new MediaPlayerMp4({
16
- audioPort,
17
- videoPort,
18
- path,
19
- loop,
20
- width,
21
- height,
22
- });
23
- }
24
- else {
25
- return new MediaPlayerWebm({
26
- audioPort,
27
- videoPort,
28
- path,
29
- loop,
30
- width,
31
- height,
13
+ const sdp_1 = require("../sdp");
14
+ const packetizer_1 = require("./userMedia/packetizer");
15
+ const getUserMedia = async (options) => {
16
+ assertSupportedOptions(options);
17
+ const replayableSource = await createReplayableSource(options);
18
+ const input = await createInput(replayableSource);
19
+ try {
20
+ if (!(await input.canRead())) {
21
+ throw new Error("Only MP4 and WebM file playback is supported");
22
+ }
23
+ const streamId = (0, crypto_1.randomUUID)().toString();
24
+ const playbackTracks = await selectPlaybackTracks(input, streamId);
25
+ if (playbackTracks.length === 0) {
26
+ throw new Error("The media source does not contain playable audio or video tracks");
27
+ }
28
+ const mediaStartTimestamp = await input.getFirstTimestamp(playbackTracks.map(({ inputTrack }) => inputTrack));
29
+ return new MediaPlayer({
30
+ source: replayableSource,
31
+ loop: options.loop ?? false,
32
+ streamId,
33
+ session: createPlaybackSession({
34
+ input,
35
+ mediaStartTimestamp,
36
+ playbackTracks,
37
+ }),
32
38
  });
33
39
  }
40
+ catch (error) {
41
+ input.dispose();
42
+ throw error;
43
+ }
34
44
  };
35
45
  exports.getUserMedia = getUserMedia;
36
46
  class MediaPlayer {
37
- constructor(props) {
38
- Object.defineProperty(this, "props", {
47
+ constructor({ source, loop, streamId, session, }) {
48
+ Object.defineProperty(this, "audio", {
39
49
  enumerable: true,
40
50
  configurable: true,
41
51
  writable: true,
42
- value: props
52
+ value: void 0
53
+ });
54
+ Object.defineProperty(this, "video", {
55
+ enumerable: true,
56
+ configurable: true,
57
+ writable: true,
58
+ value: void 0
59
+ });
60
+ Object.defineProperty(this, "onError", {
61
+ enumerable: true,
62
+ configurable: true,
63
+ writable: true,
64
+ value: new common_1.Event()
65
+ });
66
+ Object.defineProperty(this, "source", {
67
+ enumerable: true,
68
+ configurable: true,
69
+ writable: true,
70
+ value: void 0
71
+ });
72
+ Object.defineProperty(this, "loop", {
73
+ enumerable: true,
74
+ configurable: true,
75
+ writable: true,
76
+ value: void 0
43
77
  });
44
78
  Object.defineProperty(this, "streamId", {
45
79
  enumerable: true,
46
80
  configurable: true,
47
81
  writable: true,
48
- value: (0, crypto_1.randomUUID)().toString()
82
+ value: void 0
49
83
  });
50
- Object.defineProperty(this, "audio", {
84
+ Object.defineProperty(this, "session", {
51
85
  enumerable: true,
52
86
  configurable: true,
53
87
  writable: true,
54
- value: new track_1.MediaStreamTrack({ kind: "audio", streamId: this.streamId })
88
+ value: void 0
55
89
  });
56
- Object.defineProperty(this, "video", {
90
+ Object.defineProperty(this, "running", {
57
91
  enumerable: true,
58
92
  configurable: true,
59
93
  writable: true,
60
- value: new track_1.MediaStreamTrack({ kind: "video", streamId: this.streamId })
94
+ value: void 0
61
95
  });
62
- Object.defineProperty(this, "process", {
96
+ Object.defineProperty(this, "abortController", {
63
97
  enumerable: true,
64
98
  configurable: true,
65
99
  writable: true,
66
100
  value: void 0
67
101
  });
68
- Object.defineProperty(this, "stopped", {
102
+ Object.defineProperty(this, "hasStartedPlayback", {
69
103
  enumerable: true,
70
104
  configurable: true,
71
105
  writable: true,
72
106
  value: false
73
107
  });
74
- Object.defineProperty(this, "setupTrack", {
108
+ Object.defineProperty(this, "stopped", {
75
109
  enumerable: true,
76
110
  configurable: true,
77
111
  writable: true,
78
- value: (port, track) => {
79
- let payloadType = 0;
80
- const socket = (0, dgram_1.createSocket)("udp4");
81
- socket.bind(port);
82
- socket.on("message", async (buf) => {
83
- const rtp = rtp_1.RtpPacket.deSerialize(buf);
84
- if (!payloadType) {
85
- payloadType = rtp.header.payloadType;
86
- }
87
- // detect gStreamer restarted
88
- if (payloadType !== rtp.header.payloadType) {
89
- payloadType = rtp.header.payloadType;
90
- track.onSourceChanged.execute(rtp.header);
91
- }
92
- track.writeRtp(buf);
93
- });
112
+ value: false
113
+ });
114
+ this.source = source;
115
+ this.loop = loop;
116
+ this.streamId = streamId;
117
+ this.session = session;
118
+ this.audio = session.playbackTracks.find(({ track }) => track.kind === "audio")?.track;
119
+ this.video = session.playbackTracks.find(({ track }) => track.kind === "video")?.track;
120
+ }
121
+ async start() {
122
+ if (this.stopped) {
123
+ throw new Error("The media source has already been stopped");
124
+ }
125
+ if (this.running) {
126
+ return;
127
+ }
128
+ const abortController = new AbortController();
129
+ this.abortController = abortController;
130
+ let startupPending = true;
131
+ let session;
132
+ const playback = (async () => {
133
+ session = await this.getOrCreateSession();
134
+ if (abortController.signal.aborted || this.stopped) {
135
+ return;
136
+ }
137
+ session.runners.forEach((runner) => runner.assertReady());
138
+ const initialSourceChanged = this.hasStartedPlayback;
139
+ this.hasStartedPlayback = true;
140
+ await this.run(session, abortController, initialSourceChanged);
141
+ })();
142
+ const observedPlayback = playback.catch((error) => {
143
+ if (abortController.signal.aborted) {
144
+ return;
145
+ }
146
+ this.onError.execute(toError(error));
147
+ if (startupPending) {
148
+ throw error;
94
149
  }
95
150
  });
96
- this.setupTrack(props.audioPort, this.audio);
97
- this.setupTrack(props.videoPort, this.video);
151
+ this.running = observedPlayback.finally(() => {
152
+ if (this.abortController === abortController) {
153
+ this.abortController = undefined;
154
+ }
155
+ this.running = undefined;
156
+ this.disposeSession(session);
157
+ });
158
+ try {
159
+ await Promise.race([this.running, (0, promises_2.setImmediate)()]);
160
+ }
161
+ finally {
162
+ startupPending = false;
163
+ }
98
164
  }
99
165
  stop() {
166
+ if (this.stopped) {
167
+ return;
168
+ }
169
+ // File playback mirrors the previous process-backed behavior: stop() is terminal.
100
170
  this.stopped = true;
101
- this.process.kill("SIGINT");
171
+ this.abortController?.abort();
172
+ if (!this.running) {
173
+ this.disposeSession();
174
+ }
102
175
  }
103
- }
104
- class MediaPlayerMp4 extends MediaPlayer {
105
- async start() {
106
- let payloadType = 96;
107
- const run = async () => {
108
- if (payloadType > 100)
109
- payloadType = 96;
110
- let cmd = "";
111
- if (this.props.width && this.props.height) {
112
- cmd = `gst-launch-1.0 filesrc location= ${this.props.path} ! \
113
- decodebin ! videoscale ! video/x-raw,width=${this.props.width},height=${this.props.height} ! x264enc ! \
114
- h264parse ! rtph264pay config-interval=10 pt=${payloadType++} ! \
115
- udpsink host=127.0.0.1 port=${this.props.videoPort}`;
176
+ async run(session, abortController, initialSourceChanged) {
177
+ let sourceChanged = initialSourceChanged;
178
+ do {
179
+ const startedAt = performance.now();
180
+ await Promise.all(session.runners.map((runner) => runner.play({
181
+ startedAt,
182
+ signal: abortController.signal,
183
+ sourceChanged,
184
+ })));
185
+ if (!this.loop || abortController.signal.aborted) {
186
+ break;
116
187
  }
117
- else {
118
- cmd = `gst-launch-1.0 filesrc location= ${this.props.path} ! \
119
- qtdemux name=d ! \
120
- queue ! h264parse ! rtph264pay config-interval=10 pt=${payloadType++} ! \
121
- udpsink host=127.0.0.1 port=${this.props.videoPort} d. ! \
122
- queue ! aacparse ! avdec_aac ! audioresample ! audioconvert ! opusenc ! rtpopuspay pt=${payloadType++} ! \
123
- udpsink host=127.0.0.1 port=${this.props.audioPort}`;
188
+ sourceChanged = true;
189
+ } while (!abortController.signal.aborted);
190
+ }
191
+ async getOrCreateSession() {
192
+ if (this.session) {
193
+ return this.session;
194
+ }
195
+ const input = await createInput(this.source);
196
+ try {
197
+ if (!(await input.canRead())) {
198
+ throw new Error("Only MP4 and WebM file playback is supported");
124
199
  }
125
- console.log(cmd);
126
- this.process = (0, child_process_1.exec)(cmd);
127
- this.process.on("error", (e) => console.error("gst error", e));
128
- if (this.props.loop) {
129
- await new Promise((r) => this.process.on("close", r));
130
- if (!this.stopped) {
131
- run();
200
+ const playbackTracks = await selectPlaybackTracks(input, this.streamId, buildExistingTrackMap(this.audio, this.video));
201
+ const mediaStartTimestamp = await input.getFirstTimestamp(playbackTracks.map(({ inputTrack }) => inputTrack));
202
+ const session = createPlaybackSession({
203
+ input,
204
+ mediaStartTimestamp,
205
+ playbackTracks,
206
+ });
207
+ this.session = session;
208
+ return session;
209
+ }
210
+ catch (error) {
211
+ input.dispose();
212
+ throw error;
213
+ }
214
+ }
215
+ disposeSession(session = this.session) {
216
+ if (!session) {
217
+ return;
218
+ }
219
+ session.input.dispose();
220
+ if (this.session === session) {
221
+ this.session = undefined;
222
+ }
223
+ }
224
+ }
225
+ class TrackPlaybackRunner {
226
+ constructor(props) {
227
+ Object.defineProperty(this, "props", {
228
+ enumerable: true,
229
+ configurable: true,
230
+ writable: true,
231
+ value: props
232
+ });
233
+ }
234
+ async play({ startedAt, signal, sourceChanged, }) {
235
+ const codec = this.requireNegotiatedCodec();
236
+ const packetizer = (0, packetizer_1.createPacketizer)({
237
+ codec,
238
+ sourceCodec: this.props.sourceCodec,
239
+ decoderDescription: this.props.decoderDescription,
240
+ });
241
+ const packetSink = new mediabunny_1.EncodedPacketSink(this.props.inputTrack);
242
+ let sourceChangeNotified = false;
243
+ try {
244
+ for await (const packet of packetSink.packets(undefined, undefined, {
245
+ verifyKeyPackets: this.props.track.kind === "video",
246
+ })) {
247
+ signal.throwIfAborted?.();
248
+ const dueAtMs = (packet.timestamp - this.props.mediaStartTimestamp) * 1000;
249
+ const remainingMs = dueAtMs - (performance.now() - startedAt);
250
+ if (remainingMs > 0) {
251
+ await (0, promises_2.setTimeout)(remainingMs, undefined, { signal });
252
+ }
253
+ const rtpTimestamp = toRtpTimestamp(packet.timestamp, this.props.mediaStartTimestamp, codec.clockRate);
254
+ const packets = packetizer.packetize(packet, rtpTimestamp);
255
+ if (packets.length === 0) {
256
+ continue;
257
+ }
258
+ if (sourceChanged && !sourceChangeNotified) {
259
+ this.props.track.onSourceChanged.execute(packets[0].header);
260
+ sourceChangeNotified = true;
261
+ }
262
+ for (const rtpPacket of packets) {
263
+ this.props.track.writeRtp(rtpPacket);
132
264
  }
133
265
  }
134
- };
135
- await (0, promises_1.setImmediate)();
136
- run();
266
+ }
267
+ catch (error) {
268
+ if (signal.aborted || isAbortError(error)) {
269
+ return;
270
+ }
271
+ throw error;
272
+ }
273
+ }
274
+ assertReady() {
275
+ const codec = this.requireNegotiatedCodec();
276
+ (0, packetizer_1.createPacketizer)({
277
+ codec,
278
+ sourceCodec: this.props.sourceCodec,
279
+ decoderDescription: this.props.decoderDescription,
280
+ });
281
+ }
282
+ requireNegotiatedCodec() {
283
+ const codec = this.props.track.codec;
284
+ if (!codec) {
285
+ throw new Error(`Cannot start ${this.props.track.kind} playback before the track is attached to an RTCRtpSender`);
286
+ }
287
+ const expectedMimeType = (0, packetizer_1.toSupportedMimeType)(this.props.sourceCodec);
288
+ const negotiatedMimeType = normalizeMimeType(codec.mimeType);
289
+ if (negotiatedMimeType !== expectedMimeType) {
290
+ throw new Error(`Input ${this.props.track.kind} codec ${expectedMimeType} does not match negotiated codec ${codec.mimeType}`);
291
+ }
292
+ const expectedClockRate = this.props.track.kind === "audio" ? 48000 : 90000;
293
+ if (codec.clockRate !== expectedClockRate) {
294
+ throw new Error(`Input ${this.props.track.kind} clock rate ${expectedClockRate} does not match negotiated clock rate ${codec.clockRate}`);
295
+ }
296
+ assertCodecParameters({
297
+ trackKind: this.props.track.kind === "audio" ? "audio" : "video",
298
+ codec,
299
+ sourceCodec: this.props.sourceCodec,
300
+ decoderDescription: this.props.decoderDescription,
301
+ sourceChannels: this.props.sourceChannels,
302
+ });
303
+ return codec;
137
304
  }
138
305
  }
139
- exports.MediaPlayerMp4 = MediaPlayerMp4;
140
- class MediaPlayerWebm extends MediaPlayer {
141
- async start() {
142
- let payloadType = 96;
143
- const run = async () => {
144
- if (payloadType > 100)
145
- payloadType = 96;
146
- const cmd = `gst-launch-1.0 filesrc location=${this.props.path} ! matroskademux name=d \
147
- d.video_0 ! queue ! rtpvp8pay pt=${payloadType++} ! \
148
- udpsink host=127.0.0.1 port=${this.props.videoPort} \
149
- d.audio_0 ! queue ! rtpopuspay pt=${payloadType++} ! \
150
- udpsink host=127.0.0.1 port=${this.props.audioPort}`;
151
- this.process = (0, child_process_1.exec)(cmd);
152
- if (this.props.loop) {
153
- await new Promise((r) => this.process.on("close", r));
154
- if (!this.stopped) {
155
- run();
156
- }
306
+ async function selectPlaybackTracks(input, streamId, existingTracks = {}) {
307
+ const primaryVideoTrack = await input.getPrimaryVideoTrack();
308
+ const primaryAudioTrack = primaryVideoTrack
309
+ ? ((await primaryVideoTrack.getPrimaryPairableAudioTrack()) ??
310
+ (await input.getPrimaryAudioTrack()))
311
+ : await input.getPrimaryAudioTrack();
312
+ const playbackTracks = [];
313
+ if (primaryAudioTrack) {
314
+ playbackTracks.push(await createPlaybackTrack(primaryAudioTrack, streamId, existingTracks.audio));
315
+ }
316
+ if (primaryVideoTrack) {
317
+ playbackTracks.push(await createPlaybackTrack(primaryVideoTrack, streamId, existingTracks.video));
318
+ }
319
+ if (!primaryVideoTrack && !primaryAudioTrack) {
320
+ const fallbackAudioTrack = await input.getPrimaryAudioTrack();
321
+ if (fallbackAudioTrack) {
322
+ playbackTracks.push(await createPlaybackTrack(fallbackAudioTrack, streamId, existingTracks.audio));
323
+ }
324
+ }
325
+ return playbackTracks;
326
+ }
327
+ async function createPlaybackTrack(inputTrack, streamId, track) {
328
+ const sourceCodec = await requireSupportedCodec(inputTrack);
329
+ const decoderConfig = await inputTrack.getDecoderConfig();
330
+ const kind = inputTrack.isAudioTrack() ? "audio" : "video";
331
+ return {
332
+ inputTrack,
333
+ sourceCodec,
334
+ decoderDescription: decoderConfig && "description" in decoderConfig
335
+ ? (decoderConfig.description ?? null)
336
+ : null,
337
+ sourceChannels: decoderConfig && "numberOfChannels" in decoderConfig
338
+ ? decoderConfig.numberOfChannels
339
+ : undefined,
340
+ track: track ??
341
+ new track_1.MediaStreamTrack({
342
+ kind,
343
+ streamId,
344
+ }),
345
+ };
346
+ }
347
+ async function requireSupportedCodec(inputTrack) {
348
+ const codec = await inputTrack.getCodec();
349
+ if (codec && packetizer_1.supportedSourceCodecs.includes(codec)) {
350
+ return codec;
351
+ }
352
+ const codecLabel = codec ??
353
+ (await inputTrack.getCodecParameterString()) ??
354
+ String((await inputTrack.getInternalCodecId()) ?? "unknown");
355
+ throw new Error(`Unsupported ${inputTrack.type} codec "${codecLabel}". File playback supports H264, VP8, VP9, AV1, and Opus only.`);
356
+ }
357
+ async function createReplayableSource(options) {
358
+ if (typeof options.path === "string") {
359
+ return { path: options.path };
360
+ }
361
+ const buffer = options.buffer != undefined
362
+ ? toBuffer(options.buffer)
363
+ : await readStreamToBuffer(options.stream);
364
+ return { buffer };
365
+ }
366
+ async function createInput(options) {
367
+ if (typeof options.path === "string") {
368
+ const buffer = await (0, promises_1.readFile)(resolveUserMediaPath(options.path));
369
+ return new mediabunny_1.Input({
370
+ source: new mediabunny_1.BufferSource(buffer),
371
+ formats: [mediabunny_1.MP4, mediabunny_1.WEBM],
372
+ });
373
+ }
374
+ return new mediabunny_1.Input({
375
+ source: new mediabunny_1.BufferSource(options.buffer),
376
+ formats: [mediabunny_1.MP4, mediabunny_1.WEBM],
377
+ });
378
+ }
379
+ function resolveUserMediaPath(path) {
380
+ if (path === "~") {
381
+ return (0, os_1.homedir)();
382
+ }
383
+ if (path.startsWith("~/")) {
384
+ return (0, path_1.resolve)((0, os_1.homedir)(), path.slice(2));
385
+ }
386
+ return (0, path_1.isAbsolute)(path) ? path : (0, path_1.resolve)(path);
387
+ }
388
+ async function readStreamToBuffer(stream) {
389
+ const chunks = [];
390
+ if (stream instanceof stream_1.Readable) {
391
+ for await (const chunk of stream) {
392
+ chunks.push(toBuffer(chunk));
393
+ }
394
+ return Buffer.concat(chunks);
395
+ }
396
+ const reader = stream.getReader();
397
+ try {
398
+ for (;;) {
399
+ const { done, value } = await reader.read();
400
+ if (done) {
401
+ break;
157
402
  }
158
- };
159
- await (0, promises_1.setImmediate)();
160
- run();
403
+ if (value) {
404
+ chunks.push(Buffer.from(value.buffer, value.byteOffset, value.byteLength));
405
+ }
406
+ }
407
+ }
408
+ finally {
409
+ reader.releaseLock();
410
+ }
411
+ return Buffer.concat(chunks);
412
+ }
413
+ function toBuffer(value) {
414
+ if (Buffer.isBuffer(value)) {
415
+ return value;
416
+ }
417
+ if (typeof value === "string") {
418
+ return Buffer.from(value);
419
+ }
420
+ if (ArrayBuffer.isView(value)) {
421
+ return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
422
+ }
423
+ return Buffer.from(new Uint8Array(value));
424
+ }
425
+ function normalizeMimeType(mimeType) {
426
+ return mimeType.toLowerCase();
427
+ }
428
+ function assertSupportedOptions(options) {
429
+ const legacyOptions = options;
430
+ if (legacyOptions.width != undefined || legacyOptions.height != undefined) {
431
+ throw new Error("getUserMedia({ width, height }) is no longer supported for file playback. Resize or re-encode the source before calling getUserMedia().");
432
+ }
433
+ }
434
+ function createPlaybackSession({ input, mediaStartTimestamp, playbackTracks, }) {
435
+ return {
436
+ input,
437
+ mediaStartTimestamp,
438
+ playbackTracks,
439
+ runners: playbackTracks.map((playbackTrack) => new TrackPlaybackRunner({
440
+ mediaStartTimestamp,
441
+ ...playbackTrack,
442
+ })),
443
+ };
444
+ }
445
+ function buildExistingTrackMap(audio, video) {
446
+ return {
447
+ ...(audio ? { audio } : {}),
448
+ ...(video ? { video } : {}),
449
+ };
450
+ }
451
+ function assertCodecParameters({ trackKind, codec, sourceCodec, decoderDescription, sourceChannels, }) {
452
+ if (sourceCodec === "opus" && sourceChannels && codec.channels != undefined) {
453
+ if (codec.channels !== sourceChannels) {
454
+ throw new Error(`Input ${trackKind} channels ${sourceChannels} do not match negotiated channels ${codec.channels}`);
455
+ }
456
+ return;
457
+ }
458
+ if (sourceCodec !== "avc") {
459
+ // mediabunny exposes codec-specific decoder configuration for H264 only here.
460
+ return;
461
+ }
462
+ const negotiatedParameters = (0, sdp_1.codecParametersFromString)(codec.parameters ?? "");
463
+ const negotiatedPacketizationMode = Number(negotiatedParameters["packetization-mode"] ?? 1);
464
+ if (negotiatedPacketizationMode !== 1) {
465
+ throw new Error(`Input ${trackKind} packetization-mode 1 does not match negotiated packetization-mode ${negotiatedPacketizationMode}`);
466
+ }
467
+ const sourceProfileLevelId = getH264ProfileLevelId(decoderDescription);
468
+ const negotiatedProfileLevelId = normalizeOptionalHexParameter(negotiatedParameters["profile-level-id"]);
469
+ if (!sourceProfileLevelId) {
470
+ return;
471
+ }
472
+ if (!negotiatedProfileLevelId) {
473
+ throw new Error(`Input ${trackKind} profile-level-id ${sourceProfileLevelId} does not match negotiated profile-level-id <missing>`);
474
+ }
475
+ if (sourceProfileLevelId !== negotiatedProfileLevelId) {
476
+ throw new Error(`Input ${trackKind} profile-level-id ${sourceProfileLevelId} does not match negotiated profile-level-id ${negotiatedProfileLevelId}`);
477
+ }
478
+ }
479
+ function getH264ProfileLevelId(decoderDescription) {
480
+ if (!decoderDescription) {
481
+ return undefined;
482
+ }
483
+ const description = toDescriptionBuffer(decoderDescription);
484
+ if (description.length < 4) {
485
+ throw new Error("invalid H264 decoder configuration");
486
+ }
487
+ return description.subarray(1, 4).toString("hex");
488
+ }
489
+ function normalizeOptionalHexParameter(value) {
490
+ return typeof value === "string" ? value.toLowerCase() : undefined;
491
+ }
492
+ function toRtpTimestamp(packetTimestamp, mediaStartTimestamp, clockRate) {
493
+ const relativeSeconds = Math.max(0, packetTimestamp - mediaStartTimestamp);
494
+ return Math.round(relativeSeconds * clockRate) >>> 0;
495
+ }
496
+ function isAbortError(error) {
497
+ return (error instanceof Error &&
498
+ (error.name === "AbortError" ||
499
+ error.message === "The operation was aborted"));
500
+ }
501
+ function toError(error) {
502
+ return error instanceof Error ? error : new Error(String(error));
503
+ }
504
+ function toDescriptionBuffer(decoderDescription) {
505
+ if (ArrayBuffer.isView(decoderDescription)) {
506
+ return Buffer.from(decoderDescription.buffer, decoderDescription.byteOffset, decoderDescription.byteLength);
161
507
  }
508
+ return Buffer.from(decoderDescription);
162
509
  }
163
- exports.MediaPlayerWebm = MediaPlayerWebm;
164
510
  //# sourceMappingURL=userMedia.js.map