@livepeer-frameworks/player-core 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +19493 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +19398 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/player.css +2140 -0
- package/dist/types/core/ABRController.d.ts +164 -0
- package/dist/types/core/CodecUtils.d.ts +54 -0
- package/dist/types/core/Disposable.d.ts +61 -0
- package/dist/types/core/EventEmitter.d.ts +73 -0
- package/dist/types/core/GatewayClient.d.ts +144 -0
- package/dist/types/core/InteractionController.d.ts +121 -0
- package/dist/types/core/LiveDurationProxy.d.ts +102 -0
- package/dist/types/core/MetaTrackManager.d.ts +220 -0
- package/dist/types/core/MistReporter.d.ts +163 -0
- package/dist/types/core/MistSignaling.d.ts +148 -0
- package/dist/types/core/PlayerController.d.ts +665 -0
- package/dist/types/core/PlayerInterface.d.ts +230 -0
- package/dist/types/core/PlayerManager.d.ts +182 -0
- package/dist/types/core/PlayerRegistry.d.ts +27 -0
- package/dist/types/core/QualityMonitor.d.ts +184 -0
- package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
- package/dist/types/core/SeekingUtils.d.ts +142 -0
- package/dist/types/core/StreamStateClient.d.ts +108 -0
- package/dist/types/core/SubtitleManager.d.ts +111 -0
- package/dist/types/core/TelemetryReporter.d.ts +79 -0
- package/dist/types/core/TimeFormat.d.ts +97 -0
- package/dist/types/core/TimerManager.d.ts +83 -0
- package/dist/types/core/UrlUtils.d.ts +81 -0
- package/dist/types/core/detector.d.ts +149 -0
- package/dist/types/core/index.d.ts +49 -0
- package/dist/types/core/scorer.d.ts +167 -0
- package/dist/types/core/selector.d.ts +9 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/types/lib/utils.d.ts +2 -0
- package/dist/types/players/DashJsPlayer.d.ts +102 -0
- package/dist/types/players/HlsJsPlayer.d.ts +70 -0
- package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
- package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
- package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
- package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
- package/dist/types/players/MistPlayer.d.ts +25 -0
- package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
- package/dist/types/players/NativePlayer.d.ts +143 -0
- package/dist/types/players/VideoJsPlayer.d.ts +59 -0
- package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
- package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
- package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
- package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
- package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
- package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
- package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
- package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
- package/dist/types/players/index.d.ts +14 -0
- package/dist/types/styles/index.d.ts +11 -0
- package/dist/types/types.d.ts +363 -0
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
- package/dist/types/vanilla/index.d.ts +19 -0
- package/dist/workers/decoder.worker.js +989 -0
- package/dist/workers/decoder.worker.js.map +1 -0
- package/package.json +80 -0
- package/src/core/ABRController.ts +550 -0
- package/src/core/CodecUtils.ts +257 -0
- package/src/core/Disposable.ts +120 -0
- package/src/core/EventEmitter.ts +113 -0
- package/src/core/GatewayClient.ts +439 -0
- package/src/core/InteractionController.ts +712 -0
- package/src/core/LiveDurationProxy.ts +270 -0
- package/src/core/MetaTrackManager.ts +753 -0
- package/src/core/MistReporter.ts +543 -0
- package/src/core/MistSignaling.ts +346 -0
- package/src/core/PlayerController.ts +2829 -0
- package/src/core/PlayerInterface.ts +432 -0
- package/src/core/PlayerManager.ts +900 -0
- package/src/core/PlayerRegistry.ts +149 -0
- package/src/core/QualityMonitor.ts +597 -0
- package/src/core/ScreenWakeLockManager.ts +163 -0
- package/src/core/SeekingUtils.ts +364 -0
- package/src/core/StreamStateClient.ts +457 -0
- package/src/core/SubtitleManager.ts +297 -0
- package/src/core/TelemetryReporter.ts +308 -0
- package/src/core/TimeFormat.ts +205 -0
- package/src/core/TimerManager.ts +209 -0
- package/src/core/UrlUtils.ts +179 -0
- package/src/core/detector.ts +382 -0
- package/src/core/index.ts +140 -0
- package/src/core/scorer.ts +553 -0
- package/src/core/selector.ts +16 -0
- package/src/global.d.ts +11 -0
- package/src/index.ts +75 -0
- package/src/lib/utils.ts +6 -0
- package/src/players/DashJsPlayer.ts +642 -0
- package/src/players/HlsJsPlayer.ts +483 -0
- package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
- package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
- package/src/players/MewsWsPlayer/index.ts +1065 -0
- package/src/players/MewsWsPlayer/types.ts +106 -0
- package/src/players/MistPlayer.ts +188 -0
- package/src/players/MistWebRTCPlayer/index.ts +703 -0
- package/src/players/NativePlayer.ts +820 -0
- package/src/players/VideoJsPlayer.ts +643 -0
- package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
- package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
- package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
- package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
- package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
- package/src/players/WebCodecsPlayer/index.ts +1650 -0
- package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
- package/src/players/WebCodecsPlayer/types.ts +542 -0
- package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
- package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
- package/src/players/index.ts +22 -0
- package/src/styles/animations.css +21 -0
- package/src/styles/index.ts +52 -0
- package/src/styles/player.css +2126 -0
- package/src/styles/tailwind.css +1015 -0
- package/src/types.ts +421 -0
- package/src/vanilla/FrameWorksPlayer.ts +367 -0
- package/src/vanilla/index.ts +22 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaStreamTrackGenerator Polyfill
|
|
3
|
+
*
|
|
4
|
+
* Provides fallback for browsers without native MediaStreamTrackGenerator (Firefox).
|
|
5
|
+
*
|
|
6
|
+
* Video: Uses Canvas2D + captureStream()
|
|
7
|
+
* Audio: Uses AudioWorklet + createMediaStreamDestination()
|
|
8
|
+
*
|
|
9
|
+
* Based on legacy rawws.js polyfill with improvements:
|
|
10
|
+
* - TypeScript types
|
|
11
|
+
* - Pull-based audio to prevent tin-can distortion
|
|
12
|
+
* - Better resource cleanup
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if native MediaStreamTrackGenerator is available
|
|
17
|
+
*/
|
|
18
|
+
export function hasNativeMediaStreamTrackGenerator(): boolean {
|
|
19
|
+
return typeof (globalThis as any).MediaStreamTrackGenerator !== 'undefined';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Polyfill for MediaStreamTrackGenerator (video)
|
|
24
|
+
*
|
|
25
|
+
* Uses an offscreen canvas and captureStream() to create a MediaStreamTrack
|
|
26
|
+
* that can be fed VideoFrames via a WritableStream.
|
|
27
|
+
*/
|
|
28
|
+
export class VideoTrackGeneratorPolyfill {
|
|
29
|
+
private canvas: HTMLCanvasElement;
|
|
30
|
+
private ctx: CanvasRenderingContext2D;
|
|
31
|
+
private track: MediaStreamTrack;
|
|
32
|
+
private _writable: WritableStream<VideoFrame>;
|
|
33
|
+
private closed = false;
|
|
34
|
+
|
|
35
|
+
constructor() {
|
|
36
|
+
// Create offscreen canvas
|
|
37
|
+
this.canvas = document.createElement('canvas');
|
|
38
|
+
this.canvas.width = 1920; // Will be resized on first frame
|
|
39
|
+
this.canvas.height = 1080;
|
|
40
|
+
|
|
41
|
+
const ctx = this.canvas.getContext('2d', { desynchronized: true });
|
|
42
|
+
if (!ctx) {
|
|
43
|
+
throw new Error('Failed to create canvas 2D context');
|
|
44
|
+
}
|
|
45
|
+
this.ctx = ctx;
|
|
46
|
+
|
|
47
|
+
// Capture stream from canvas
|
|
48
|
+
const stream = this.canvas.captureStream();
|
|
49
|
+
const tracks = stream.getVideoTracks();
|
|
50
|
+
if (tracks.length === 0) {
|
|
51
|
+
throw new Error('Failed to capture stream from canvas');
|
|
52
|
+
}
|
|
53
|
+
this.track = tracks[0];
|
|
54
|
+
|
|
55
|
+
// Create writable stream that draws frames to canvas
|
|
56
|
+
this._writable = new WritableStream<VideoFrame>({
|
|
57
|
+
write: (frame: VideoFrame) => {
|
|
58
|
+
if (this.closed) {
|
|
59
|
+
frame.close();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Resize canvas to match frame if needed
|
|
64
|
+
if (
|
|
65
|
+
this.canvas.width !== frame.displayWidth ||
|
|
66
|
+
this.canvas.height !== frame.displayHeight
|
|
67
|
+
) {
|
|
68
|
+
this.canvas.width = frame.displayWidth;
|
|
69
|
+
this.canvas.height = frame.displayHeight;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Draw frame to canvas
|
|
73
|
+
this.ctx.drawImage(
|
|
74
|
+
frame as unknown as CanvasImageSource,
|
|
75
|
+
0,
|
|
76
|
+
0,
|
|
77
|
+
this.canvas.width,
|
|
78
|
+
this.canvas.height
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Close the frame to release resources
|
|
82
|
+
frame.close();
|
|
83
|
+
},
|
|
84
|
+
close: () => {
|
|
85
|
+
this.close();
|
|
86
|
+
},
|
|
87
|
+
abort: () => {
|
|
88
|
+
this.close();
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the writable stream for writing VideoFrames
|
|
95
|
+
*/
|
|
96
|
+
get writable(): WritableStream<VideoFrame> {
|
|
97
|
+
return this._writable;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the MediaStreamTrack for adding to MediaStream
|
|
102
|
+
*/
|
|
103
|
+
getTrack(): MediaStreamTrack {
|
|
104
|
+
return this.track;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Close and cleanup resources
|
|
109
|
+
*/
|
|
110
|
+
close(): void {
|
|
111
|
+
if (this.closed) return;
|
|
112
|
+
this.closed = true;
|
|
113
|
+
|
|
114
|
+
this.track.stop();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Polyfill for MediaStreamTrackGenerator (audio)
|
|
120
|
+
*
|
|
121
|
+
* Uses AudioWorklet to create a pull-based audio output that prevents
|
|
122
|
+
* the "tin-can" distortion issue from the legacy implementation.
|
|
123
|
+
*
|
|
124
|
+
* Key improvement: Audio samples are pulled by the worklet at a fixed rate,
|
|
125
|
+
* and we feed samples proactively rather than pushing them as they arrive.
|
|
126
|
+
*/
|
|
127
|
+
export class AudioTrackGeneratorPolyfill {
|
|
128
|
+
private audioContext: AudioContext;
|
|
129
|
+
private destination: MediaStreamAudioDestinationNode;
|
|
130
|
+
private workletNode: AudioWorkletNode | null = null;
|
|
131
|
+
private track: MediaStreamTrack;
|
|
132
|
+
private _writable: WritableStream<AudioData>;
|
|
133
|
+
private closed = false;
|
|
134
|
+
private initialized = false;
|
|
135
|
+
private initPromise: Promise<void>;
|
|
136
|
+
|
|
137
|
+
// Ring buffer for samples (main thread side)
|
|
138
|
+
private sampleBuffer: Float32Array[] = [];
|
|
139
|
+
private maxBufferChunks = 20; // ~400ms at 48kHz with 1024 samples/chunk
|
|
140
|
+
|
|
141
|
+
constructor() {
|
|
142
|
+
// Create audio context
|
|
143
|
+
this.audioContext = new AudioContext({ latencyHint: 'interactive' });
|
|
144
|
+
|
|
145
|
+
// Create destination for MediaStreamTrack
|
|
146
|
+
this.destination = this.audioContext.createMediaStreamDestination();
|
|
147
|
+
const tracks = this.destination.stream.getAudioTracks();
|
|
148
|
+
if (tracks.length === 0) {
|
|
149
|
+
throw new Error('Failed to create audio destination');
|
|
150
|
+
}
|
|
151
|
+
this.track = tracks[0];
|
|
152
|
+
|
|
153
|
+
// Initialize worklet
|
|
154
|
+
this.initPromise = this.initializeWorklet();
|
|
155
|
+
|
|
156
|
+
// Create writable stream for AudioData
|
|
157
|
+
this._writable = new WritableStream<AudioData>({
|
|
158
|
+
write: (data: AudioData) => {
|
|
159
|
+
if (this.closed) {
|
|
160
|
+
data.close();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Convert AudioData to Float32Array
|
|
165
|
+
const samples = this.audioDataToSamples(data);
|
|
166
|
+
data.close();
|
|
167
|
+
|
|
168
|
+
// Add to ring buffer
|
|
169
|
+
this.sampleBuffer.push(samples);
|
|
170
|
+
if (this.sampleBuffer.length > this.maxBufferChunks) {
|
|
171
|
+
this.sampleBuffer.shift(); // Drop oldest
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Feed samples to worklet
|
|
175
|
+
this.feedSamples();
|
|
176
|
+
},
|
|
177
|
+
close: () => {
|
|
178
|
+
this.close();
|
|
179
|
+
},
|
|
180
|
+
abort: () => {
|
|
181
|
+
this.close();
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Initialize the AudioWorklet
|
|
188
|
+
*/
|
|
189
|
+
private async initializeWorklet(): Promise<void> {
|
|
190
|
+
// Create worklet code as blob
|
|
191
|
+
const workletCode = `
|
|
192
|
+
class SyncedAudioProcessor extends AudioWorkletProcessor {
|
|
193
|
+
constructor() {
|
|
194
|
+
super();
|
|
195
|
+
this.ringBuffer = [];
|
|
196
|
+
this.maxBufferSize = 10;
|
|
197
|
+
|
|
198
|
+
this.port.onmessage = (e) => {
|
|
199
|
+
if (e.data.type === 'samples') {
|
|
200
|
+
// Add samples to ring buffer
|
|
201
|
+
this.ringBuffer.push(e.data.samples);
|
|
202
|
+
if (this.ringBuffer.length > this.maxBufferSize) {
|
|
203
|
+
this.ringBuffer.shift(); // Drop oldest
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
process(inputs, outputs, parameters) {
|
|
210
|
+
const output = outputs[0];
|
|
211
|
+
if (!output || output.length === 0) return true;
|
|
212
|
+
|
|
213
|
+
const channel = output[0];
|
|
214
|
+
|
|
215
|
+
if (this.ringBuffer.length > 0) {
|
|
216
|
+
const samples = this.ringBuffer.shift();
|
|
217
|
+
const len = Math.min(samples.length, channel.length);
|
|
218
|
+
for (let i = 0; i < len; i++) {
|
|
219
|
+
channel[i] = samples[i];
|
|
220
|
+
}
|
|
221
|
+
// Fill remainder with last sample (smooth transition)
|
|
222
|
+
for (let i = len; i < channel.length; i++) {
|
|
223
|
+
channel[i] = samples[len - 1] || 0;
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
// No samples - output silence
|
|
227
|
+
channel.fill(0);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
registerProcessor('synced-audio-processor', SyncedAudioProcessor);
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
const blob = new Blob([workletCode], { type: 'application/javascript' });
|
|
238
|
+
const url = URL.createObjectURL(blob);
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
await this.audioContext.audioWorklet.addModule(url);
|
|
242
|
+
|
|
243
|
+
this.workletNode = new AudioWorkletNode(
|
|
244
|
+
this.audioContext,
|
|
245
|
+
'synced-audio-processor'
|
|
246
|
+
);
|
|
247
|
+
this.workletNode.connect(this.destination);
|
|
248
|
+
|
|
249
|
+
this.initialized = true;
|
|
250
|
+
} finally {
|
|
251
|
+
URL.revokeObjectURL(url);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Convert AudioData to mono Float32Array
|
|
257
|
+
*/
|
|
258
|
+
private audioDataToSamples(data: AudioData): Float32Array {
|
|
259
|
+
const channels = data.numberOfChannels;
|
|
260
|
+
const frames = data.numberOfFrames;
|
|
261
|
+
|
|
262
|
+
// Get all samples
|
|
263
|
+
const allSamples = new Float32Array(frames * channels);
|
|
264
|
+
data.copyTo(allSamples, { planeIndex: 0 });
|
|
265
|
+
|
|
266
|
+
// Convert to mono if needed
|
|
267
|
+
if (channels === 1) {
|
|
268
|
+
return allSamples;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Mix down to mono
|
|
272
|
+
const mono = new Float32Array(frames);
|
|
273
|
+
for (let i = 0; i < frames; i++) {
|
|
274
|
+
let sum = 0;
|
|
275
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
276
|
+
sum += allSamples[i * channels + ch];
|
|
277
|
+
}
|
|
278
|
+
mono[i] = sum / channels;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return mono;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Feed samples to the worklet
|
|
286
|
+
*/
|
|
287
|
+
private feedSamples(): void {
|
|
288
|
+
if (!this.initialized || !this.workletNode || this.sampleBuffer.length === 0) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Send oldest chunk to worklet
|
|
293
|
+
const samples = this.sampleBuffer.shift();
|
|
294
|
+
if (samples) {
|
|
295
|
+
this.workletNode.port.postMessage(
|
|
296
|
+
{ type: 'samples', samples },
|
|
297
|
+
{ transfer: [samples.buffer] }
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get the writable stream for writing AudioData
|
|
304
|
+
*/
|
|
305
|
+
get writable(): WritableStream<AudioData> {
|
|
306
|
+
return this._writable;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get the MediaStreamTrack for adding to MediaStream
|
|
311
|
+
*/
|
|
312
|
+
getTrack(): MediaStreamTrack {
|
|
313
|
+
return this.track;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Wait for initialization to complete
|
|
318
|
+
*/
|
|
319
|
+
async waitForInit(): Promise<void> {
|
|
320
|
+
await this.initPromise;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Close and cleanup resources
|
|
325
|
+
*/
|
|
326
|
+
close(): void {
|
|
327
|
+
if (this.closed) return;
|
|
328
|
+
this.closed = true;
|
|
329
|
+
|
|
330
|
+
this.track.stop();
|
|
331
|
+
this.workletNode?.disconnect();
|
|
332
|
+
this.audioContext.close();
|
|
333
|
+
this.sampleBuffer = [];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Create appropriate track generator based on browser support
|
|
339
|
+
*
|
|
340
|
+
* @param kind - 'video' or 'audio'
|
|
341
|
+
* @returns Native generator or polyfill
|
|
342
|
+
*/
|
|
343
|
+
export function createTrackGenerator(
|
|
344
|
+
kind: 'video' | 'audio'
|
|
345
|
+
): {
|
|
346
|
+
writable: WritableStream<VideoFrame | AudioData>;
|
|
347
|
+
getTrack: () => MediaStreamTrack;
|
|
348
|
+
close: () => void;
|
|
349
|
+
waitForInit?: () => Promise<void>;
|
|
350
|
+
} {
|
|
351
|
+
// Try native first
|
|
352
|
+
if (hasNativeMediaStreamTrackGenerator()) {
|
|
353
|
+
const Generator = (globalThis as any).MediaStreamTrackGenerator;
|
|
354
|
+
const generator = new Generator({ kind });
|
|
355
|
+
return {
|
|
356
|
+
writable: generator.writable,
|
|
357
|
+
getTrack: () => generator,
|
|
358
|
+
close: () => generator.stop?.(),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Fall back to polyfill
|
|
363
|
+
if (kind === 'video') {
|
|
364
|
+
const polyfill = new VideoTrackGeneratorPolyfill();
|
|
365
|
+
return {
|
|
366
|
+
writable: polyfill.writable as WritableStream<VideoFrame | AudioData>,
|
|
367
|
+
getTrack: () => polyfill.getTrack(),
|
|
368
|
+
close: () => polyfill.close(),
|
|
369
|
+
};
|
|
370
|
+
} else {
|
|
371
|
+
const polyfill = new AudioTrackGeneratorPolyfill();
|
|
372
|
+
return {
|
|
373
|
+
writable: polyfill.writable as WritableStream<VideoFrame | AudioData>,
|
|
374
|
+
getTrack: () => polyfill.getTrack(),
|
|
375
|
+
close: () => polyfill.close(),
|
|
376
|
+
waitForInit: () => polyfill.waitForInit(),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|