webcodecs-utils 0.2.5 → 0.2.6

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 (54) hide show
  1. package/README.md +142 -0
  2. package/dist/audio/extract-channels.d.ts +45 -0
  3. package/dist/audio/extract-channels.d.ts.map +1 -0
  4. package/dist/audio/extract-channels.js +25 -0
  5. package/dist/audio/get-sample-rate.d.ts +19 -0
  6. package/dist/audio/get-sample-rate.d.ts.map +1 -0
  7. package/dist/audio/get-sample-rate.js +14 -0
  8. package/dist/audio/mp3.d.ts +162 -0
  9. package/dist/audio/mp3.d.ts.map +1 -0
  10. package/dist/audio/mp3.js +173 -0
  11. package/dist/demux/example-muxer.d.ts +74 -0
  12. package/dist/demux/example-muxer.d.ts.map +1 -0
  13. package/dist/demux/example-muxer.js +40 -0
  14. package/dist/demux/get-chunks.d.ts +81 -0
  15. package/dist/demux/get-chunks.d.ts.map +1 -0
  16. package/dist/demux/get-chunks.js +88 -0
  17. package/dist/demux/mp4-demuxer.d.ts +84 -0
  18. package/dist/demux/mp4-demuxer.d.ts.map +1 -0
  19. package/dist/demux/mp4-demuxer.js +215 -0
  20. package/dist/demux/simple-demuxer.d.ts +49 -0
  21. package/dist/demux/simple-demuxer.d.ts.map +1 -0
  22. package/dist/demux/simple-demuxer.js +87 -0
  23. package/dist/in-memory-storage.d.ts +30 -0
  24. package/dist/in-memory-storage.d.ts.map +1 -0
  25. package/dist/in-memory-storage.js +54 -0
  26. package/dist/index.cjs +5 -298
  27. package/dist/index.d.ts +17 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +36 -13877
  30. package/dist/mux/simple-muxer.d.ts +47 -0
  31. package/dist/mux/simple-muxer.d.ts.map +1 -0
  32. package/dist/mux/simple-muxer.js +83 -0
  33. package/dist/polyfills/media-stream-track-processor.d.ts +5 -0
  34. package/dist/polyfills/media-stream-track-processor.d.ts.map +1 -0
  35. package/dist/polyfills/media-stream-track-processor.js +109 -0
  36. package/dist/streams/video-decode-stream.d.ts +11 -0
  37. package/dist/streams/video-decode-stream.d.ts.map +1 -0
  38. package/dist/streams/video-decode-stream.js +39 -0
  39. package/dist/streams/video-encode-stream.d.ts +15 -0
  40. package/dist/streams/video-encode-stream.d.ts.map +1 -0
  41. package/dist/streams/video-encode-stream.js +40 -0
  42. package/dist/streams/video-process-stream.d.ts +22 -0
  43. package/dist/streams/video-process-stream.d.ts.map +1 -0
  44. package/dist/streams/video-process-stream.js +21 -0
  45. package/dist/video/get-bitrate.d.ts +35 -0
  46. package/dist/video/get-bitrate.d.ts.map +1 -0
  47. package/dist/video/get-bitrate.js +12 -0
  48. package/dist/video/get-codec-string.d.ts +46 -0
  49. package/dist/video/get-codec-string.d.ts.map +1 -0
  50. package/dist/video/get-codec-string.js +195 -0
  51. package/dist/video/gpu-renderer.d.ts +108 -0
  52. package/dist/video/gpu-renderer.d.ts.map +1 -0
  53. package/dist/video/gpu-renderer.js +266 -0
  54. package/package.json +1 -1
package/README.md CHANGED
@@ -10,6 +10,8 @@ npm install webcodecs-utils
10
10
 
11
11
  ## Quick Start
12
12
 
13
+ ### Individual Utilities
14
+
13
15
  ```typescript
14
16
  import { getBitrate, GPUFrameRenderer, extractChannels, MP4Demuxer } from 'webcodecs-utils';
15
17
 
@@ -37,6 +39,35 @@ await demuxer.load();
37
39
  const videoChunks = await demuxer.extractSegment('video', 0, 10);
38
40
  ```
39
41
 
42
+ ### Streaming Pipeline
43
+
44
+ ```typescript
45
+ import {
46
+ SimpleDemuxer,
47
+ VideoDecodeStream,
48
+ VideoProcessStream,
49
+ VideoEncodeStream,
50
+ SimpleMuxer
51
+ } from 'webcodecs-utils';
52
+
53
+ // Build a composable streaming pipeline with automatic backpressure
54
+ const demuxer = new SimpleDemuxer(file);
55
+ await demuxer.load();
56
+
57
+ const muxer = new SimpleMuxer({ video: 'avc' });
58
+
59
+ await demuxer.videoStream()
60
+ .pipeThrough(new VideoDecodeStream(await demuxer.getVideoDecoderConfig()))
61
+ .pipeThrough(new VideoProcessStream(async (frame) => {
62
+ // Custom processing: upscaling, filters, etc.
63
+ return frame;
64
+ }))
65
+ .pipeThrough(new VideoEncodeStream(encoderConfig))
66
+ .pipeTo(muxer.videoSink());
67
+
68
+ const blob = await muxer.finalize();
69
+ ```
70
+
40
71
  ## Utilities
41
72
 
42
73
  ### Video
@@ -197,6 +228,117 @@ console.log(`Video: ${videoTrack.codec}, ${videoTrack.codedWidth}x${videoTrack.c
197
228
  const videoChunks = await demuxer.extractSegment('video', 0, 10);
198
229
  ```
199
230
 
231
+ ### Streaming Pipelines
232
+
233
+ Build production-ready video processing pipelines using the Streams API with automatic backpressure management.
234
+
235
+ - 📄 [Documentation](./src/streams/README.md)
236
+ - 🎮 [Transcode Demo](./demos/transcode-pipeline.html)
237
+ - 🎮 [AI Upscaling Demo](./demos/upscale-pipeline.html)
238
+
239
+ #### **VideoDecodeStream**
240
+ TransformStream that decodes EncodedVideoChunks into VideoFrames with automatic backpressure.
241
+
242
+ ```typescript
243
+ class VideoDecodeStream extends TransformStream<EncodedVideoChunk, VideoFrame> {
244
+ constructor(
245
+ config: VideoDecoderConfig,
246
+ options?: {
247
+ highWaterMark?: number; // default: 10
248
+ maxDecodeQueueSize?: number; // default: 20
249
+ }
250
+ )
251
+ }
252
+ ```
253
+
254
+ #### **VideoEncodeStream**
255
+ TransformStream that encodes VideoFrames into EncodedVideoChunks with automatic backpressure.
256
+
257
+ ```typescript
258
+ class VideoEncodeStream extends TransformStream<
259
+ VideoFrame,
260
+ { chunk: EncodedVideoChunk; meta?: EncodedVideoChunkMetadata }
261
+ > {
262
+ constructor(
263
+ config: VideoEncoderConfig,
264
+ options?: {
265
+ highWaterMark?: number; // default: 10
266
+ maxEncodeQueueSize?: number; // default: 20
267
+ keyFrameInterval?: number; // default: 60
268
+ }
269
+ )
270
+ }
271
+ ```
272
+
273
+ #### **VideoProcessStream**
274
+ TransformStream that applies a custom processing function to each VideoFrame.
275
+
276
+ ```typescript
277
+ class VideoProcessStream extends TransformStream<VideoFrame, VideoFrame> {
278
+ constructor(
279
+ transformFn: (frame: VideoFrame) => Promise<VideoFrame> | VideoFrame,
280
+ options?: {
281
+ highWaterMark?: number; // default: 5
282
+ }
283
+ )
284
+ }
285
+ ```
286
+
287
+ **Example - AI Upscaling:**
288
+ ```typescript
289
+ import WebSR from '@websr/websr';
290
+
291
+ const websr = new WebSR({ resolution, network, weights, gpu, canvas });
292
+
293
+ const upscaleStream = new VideoProcessStream(async (frame) => {
294
+ await websr.render(frame); // AI upscaling with WebGPU
295
+ return new VideoFrame(canvas, {
296
+ timestamp: frame.timestamp,
297
+ duration: frame.duration
298
+ });
299
+ });
300
+
301
+ // Use in pipeline
302
+ await videoStream
303
+ .pipeThrough(new VideoDecodeStream(config))
304
+ .pipeThrough(upscaleStream)
305
+ .pipeThrough(new VideoEncodeStream(config))
306
+ .pipeTo(muxer.videoSink());
307
+ ```
308
+
309
+ #### **SimpleDemuxer** ⚠️ Demo/Learning Only
310
+ Simple wrapper around web-demuxer for easier usage in demos. For production, use [web-demuxer](https://github.com/bilibili/web-demuxer) or [MediaBunny](https://mediabunny.dev/) directly.
311
+
312
+ ```typescript
313
+ class SimpleDemuxer {
314
+ constructor(file: File, options?: { wasmFilePath?: string })
315
+
316
+ async load(): Promise<void>
317
+ videoStream(startTime?: number): ReadableStream<EncodedVideoChunk>
318
+ audioStream(startTime?: number): ReadableStream<EncodedAudioChunk>
319
+ async getVideoDecoderConfig(): Promise<VideoDecoderConfig>
320
+ async getAudioDecoderConfig(): Promise<AudioDecoderConfig>
321
+ async getSegment(type: 'video' | 'audio', start: number, end: number): Promise<EncodedVideoChunk[] | EncodedAudioChunk[]>
322
+ async getMediaInfo(): Promise<MediaInfo>
323
+ }
324
+ ```
325
+
326
+ #### **SimpleMuxer** ⚠️ Demo/Learning Only
327
+ Simple wrapper around MediaBunny's Output for easier muxing in demos. For production, use [MediaBunny](https://mediabunny.dev/) directly.
328
+
329
+ ```typescript
330
+ class SimpleMuxer {
331
+ constructor(config: {
332
+ video?: 'avc' | 'hevc' | 'vp8' | 'vp9' | 'av1';
333
+ audio?: 'aac' | 'opus' | 'mp3' | 'vorbis' | 'flac';
334
+ })
335
+
336
+ videoSink(): WritableStream<{ chunk: EncodedVideoChunk; meta?: EncodedVideoChunkMetadata }>
337
+ audioSink(): WritableStream<EncodedAudioChunk>
338
+ async finalize(): Promise<Blob>
339
+ }
340
+ ```
341
+
200
342
  ## Browser Support
201
343
 
202
344
  These utilities require:
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Extract audio channels from AudioData as separate Float32Array buffers.
3
+ *
4
+ * AudioData can store samples in two formats:
5
+ * - **Planar (f32-planar)**: Each channel is stored separately (most common)
6
+ * - **Interleaved (f32)**: All channels are mixed together (L, R, L, R, L, R...)
7
+ *
8
+ * This function handles both formats automatically and returns an array of Float32Array,
9
+ * where each array represents one channel (typically [left, right] for stereo).
10
+ *
11
+ * @param audioData - The AudioData object to extract channels from
12
+ * @returns Array of Float32Array, one per channel (e.g., [leftChannel, rightChannel])
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // Extract channels from decoded audio
17
+ * const decoder = new AudioDecoder({
18
+ * output: (audioData) => {
19
+ * const channels = extractChannels(audioData);
20
+ * const leftChannel = channels[0];
21
+ * const rightChannel = channels[1]; // if stereo
22
+ *
23
+ * // Process audio samples...
24
+ * for (let i = 0; i < leftChannel.length; i++) {
25
+ * leftChannel[i] *= 0.5; // Reduce volume by 50%
26
+ * }
27
+ * },
28
+ * error: (e) => console.error(e)
29
+ * });
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * // Mix two audio sources
35
+ * const source1Channels = extractChannels(audioData1);
36
+ * const source2Channels = extractChannels(audioData2);
37
+ *
38
+ * const mixedLeft = new Float32Array(source1Channels[0].length);
39
+ * for (let i = 0; i < mixedLeft.length; i++) {
40
+ * mixedLeft[i] = source1Channels[0][i] + source2Channels[0][i];
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function extractChannels(audioData: AudioData): Float32Array[];
45
+ //# sourceMappingURL=extract-channels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-channels.d.ts","sourceRoot":"","sources":["../../src/audio/extract-channels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,EAAE,CA8BpE"}
@@ -0,0 +1,25 @@
1
+ function c(e) {
2
+ var s;
3
+ const l = [];
4
+ if ((s = e.format) != null && s.includes("planar"))
5
+ for (let n = 0; n < e.numberOfChannels; n++) {
6
+ const r = new Float32Array(e.numberOfFrames);
7
+ e.copyTo(r, { frameOffset: 0, planeIndex: n }), l.push(r);
8
+ }
9
+ else {
10
+ const n = new Float32Array(
11
+ e.numberOfFrames * e.numberOfChannels
12
+ );
13
+ e.copyTo(n, { frameOffset: 0, planeIndex: 0 });
14
+ for (let r = 0; r < e.numberOfChannels; r++) {
15
+ const m = new Float32Array(e.numberOfFrames);
16
+ for (let f = 0; f < e.numberOfFrames; f++)
17
+ m[f] = n[f * e.numberOfChannels + r];
18
+ l.push(m);
19
+ }
20
+ }
21
+ return l;
22
+ }
23
+ export {
24
+ c as extractChannels
25
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Get the actual sample rate from an audio MediaStreamTrack.
3
+ *
4
+ * Firefox doesn't always expose sampleRate in track.getSettings(), so this
5
+ * function creates a temporary AudioContext to determine the actual sample rate.
6
+ *
7
+ * @param track - The audio MediaStreamTrack to get the sample rate from
8
+ * @returns The actual sample rate in Hz
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
13
+ * const audioTrack = stream.getAudioTracks()[0];
14
+ * const sampleRate = await getSampleRate(audioTrack);
15
+ * console.log(`Sample rate: ${sampleRate} Hz`);
16
+ * ```
17
+ */
18
+ export declare function getSampleRate(track: MediaStreamTrack): Promise<number>;
19
+ //# sourceMappingURL=get-sample-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-sample-rate.d.ts","sourceRoot":"","sources":["../../src/audio/get-sample-rate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwB5E"}
@@ -0,0 +1,14 @@
1
+ async function i(e) {
2
+ if (e.kind !== "audio")
3
+ throw new Error("Track must be an audio track");
4
+ const a = e.getSettings();
5
+ if (a.sampleRate)
6
+ return a.sampleRate;
7
+ const t = new AudioContext(), n = new MediaStreamAudioSourceNode(t, {
8
+ mediaStream: new MediaStream([e])
9
+ }), o = t.sampleRate;
10
+ return n.disconnect(), await t.close(), o;
11
+ }
12
+ export {
13
+ i as getSampleRate
14
+ };
@@ -0,0 +1,162 @@
1
+ interface MP3EncoderConfig {
2
+ sampleRate: number;
3
+ bitRate: number;
4
+ channels: number;
5
+ }
6
+ /**
7
+ * Encode audio to MP3 format using LameJS.
8
+ *
9
+ * This encoder converts AudioData objects to MP3 format. It handles both
10
+ * planar (f32-planar) and interleaved (f32) audio formats automatically.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // Create encoder
15
+ * const encoder = new MP3Encoder({
16
+ * sampleRate: 48000,
17
+ * bitRate: 192, // 192 kbps
18
+ * channels: 2 // Stereo
19
+ * });
20
+ *
21
+ * // Encode AudioData objects
22
+ * for (const audioData of audioDataArray) {
23
+ * const mp3Chunk = encoder.processBatch(audioData);
24
+ * encoder.encodedData.push(mp3Chunk);
25
+ * }
26
+ *
27
+ * // Get final MP3 file
28
+ * const mp3Blob = encoder.finish();
29
+ * ```
30
+ *
31
+ * @remarks
32
+ * This encoder uses LameJS for encoding. For production use, consider using
33
+ * the native AudioEncoder API with opus codec, or a server-side encoder.
34
+ */
35
+ declare class MP3Encoder {
36
+ private mp3encoder;
37
+ private config;
38
+ encodedData: Uint8Array[];
39
+ /**
40
+ * Create a new MP3Encoder.
41
+ *
42
+ * @param config - Encoder configuration
43
+ * @param config.sampleRate - Audio sample rate in Hz (e.g., 48000)
44
+ * @param config.bitRate - MP3 bitrate in kbps (e.g., 192)
45
+ * @param config.channels - Number of audio channels (1 = mono, 2 = stereo)
46
+ */
47
+ constructor(config: MP3EncoderConfig);
48
+ private convertAudioDataToInt16;
49
+ /**
50
+ * Encode a single AudioData object to MP3.
51
+ *
52
+ * @param audioData - The AudioData object to encode
53
+ * @returns Encoded MP3 data as Uint8Array (add this to encodedData array)
54
+ */
55
+ processBatch(audioData: AudioData): Uint8Array;
56
+ /**
57
+ * Finalize encoding and get the complete MP3 file as a Blob.
58
+ *
59
+ * This method flushes any remaining data and combines all encoded chunks
60
+ * into a single MP3 file blob.
61
+ *
62
+ * @returns Complete MP3 file as a Blob
63
+ */
64
+ finish(): Blob;
65
+ getEncodedSize(): number;
66
+ }
67
+ export interface MP3DecoderOutput {
68
+ channels: Float32Array[];
69
+ sampleRate: number;
70
+ numberOfChannels: number;
71
+ }
72
+ /**
73
+ * Decode MP3 files to raw PCM samples or AudioData objects.
74
+ *
75
+ * This decoder uses mpg123-decoder (WebAssembly) to decode MP3 files.
76
+ * It can output either raw Float32Array samples or AudioData objects
77
+ * ready for use with WebCodecs APIs.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Decode to raw samples
82
+ * const decoder = new MP3Decoder();
83
+ * await decoder.initialize();
84
+ *
85
+ * const mp3Buffer = await file.arrayBuffer();
86
+ * const { channels, sampleRate, numberOfChannels } = await decoder.toSamples(mp3Buffer);
87
+ *
88
+ * // channels[0] = left channel (Float32Array)
89
+ * // channels[1] = right channel (Float32Array) if stereo
90
+ * ```
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * // Decode to AudioData objects
95
+ * const decoder = new MP3Decoder();
96
+ * await decoder.initialize();
97
+ *
98
+ * const mp3Buffer = await file.arrayBuffer();
99
+ * const audioDataArray = await decoder.toAudioData(mp3Buffer);
100
+ *
101
+ * // Use with AudioEncoder or other WebCodecs APIs
102
+ * for (const audioData of audioDataArray) {
103
+ * encoder.encode(audioData);
104
+ * audioData.close();
105
+ * }
106
+ *
107
+ * await decoder.destroy();
108
+ * ```
109
+ */
110
+ declare class MP3Decoder {
111
+ private decoder;
112
+ private isReady;
113
+ /**
114
+ * Create a new MP3Decoder.
115
+ *
116
+ * The decoder auto-detects sample rate and channel configuration from the MP3 file.
117
+ * Call initialize() before using.
118
+ */
119
+ constructor();
120
+ /**
121
+ * Initialize the decoder (async)
122
+ */
123
+ initialize(): Promise<void>;
124
+ /**
125
+ * Decode MP3 buffer to raw PCM samples
126
+ * @param mp3Buffer - The MP3 data as ArrayBuffer
127
+ * @returns Promise<{channels: Float32Array[], sampleRate: number, numberOfChannels: number}>
128
+ */
129
+ toSamples(mp3Buffer: ArrayBuffer): Promise<{
130
+ channels: Float32Array[];
131
+ sampleRate: number;
132
+ numberOfChannels: number;
133
+ }>;
134
+ /**
135
+ * Decode MP3 to AudioData objects.
136
+ * Internally calls decodeMP3ToSamples and converts the Float32Array channels to AudioData.
137
+ *
138
+ * @param mp3Buffer - The MP3 data as ArrayBuffer
139
+ * @returns Promise<AudioData[]> - Array of AudioData objects
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const decoder = new MP3Decoder();
144
+ * await decoder.initialize();
145
+ * const mp3Buffer = await file.arrayBuffer();
146
+ * const audioDataArray = await decoder.decode(mp3Buffer);
147
+ *
148
+ * // Use with AudioEncoder or other WebCodecs APIs
149
+ * for (const audioData of audioDataArray) {
150
+ * encoder.encode(audioData);
151
+ * audioData.close();
152
+ * }
153
+ * ```
154
+ */
155
+ toAudioData(mp3Buffer: ArrayBuffer): Promise<AudioData[]>;
156
+ /**
157
+ * Clean up decoder resources
158
+ */
159
+ destroy(): Promise<void>;
160
+ }
161
+ export { MP3Encoder, MP3Decoder };
162
+ //# sourceMappingURL=mp3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mp3.d.ts","sourceRoot":"","sources":["../../src/audio/mp3.ts"],"names":[],"mappings":"AAmBA,UAAU,gBAAgB;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,cAAM,UAAU;IACZ,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,MAAM,CAAmB;IACjC,WAAW,EAAE,UAAU,EAAE,CAAC;IAE1B;;;;;;;OAOG;gBACS,MAAM,EAAE,gBAAgB;IAapC,OAAO,CAAC,uBAAuB;IAmB/B;;;;;OAKG;IACH,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU;IAwB9C;;;;;;;OAOG;IACH,MAAM,IAAI,IAAI;IAuBd,cAAc,IAAI,MAAM;CAG3B;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,cAAM,UAAU;IACZ,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,OAAO,CAAkB;IAEjC;;;;;OAKG;;IAKH;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC;;;;OAIG;IACG,SAAS,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC;QAC7C,QAAQ,EAAE,YAAY,EAAE,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAA;KAC3B,CAAC;IAmCF;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,WAAW,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAiD/D;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAOjC;AAED,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,173 @@
1
+ import g from "lamejs";
2
+ import w from "lamejs/src/js/MPEGMode";
3
+ import D from "lamejs/src/js/Lame";
4
+ import M from "lamejs/src/js/BitStream";
5
+ import { MPEGDecoderWebWorker as b } from "mpg123-decoder";
6
+ import { extractChannels as A } from "./extract-channels.js";
7
+ globalThis.MPEGMode = w;
8
+ globalThis.Lame = D;
9
+ globalThis.BitStream = M;
10
+ class E {
11
+ /**
12
+ * Create a new MP3Encoder.
13
+ *
14
+ * @param config - Encoder configuration
15
+ * @param config.sampleRate - Audio sample rate in Hz (e.g., 48000)
16
+ * @param config.bitRate - MP3 bitrate in kbps (e.g., 192)
17
+ * @param config.channels - Number of audio channels (1 = mono, 2 = stereo)
18
+ */
19
+ constructor(e) {
20
+ this.config = e, this.mp3encoder = new g.Mp3Encoder(
21
+ e.channels,
22
+ e.sampleRate,
23
+ e.bitRate
24
+ ), this.encodedData = [];
25
+ }
26
+ // Convert AudioData to interleaved Int16 samples
27
+ convertAudioDataToInt16(e) {
28
+ const t = e.numberOfChannels, a = e.numberOfFrames, r = A(e), o = new Int16Array(a * t);
29
+ for (let n = 0; n < a; n++)
30
+ for (let s = 0; s < t; s++) {
31
+ const i = Math.max(-1, Math.min(1, r[s][n]));
32
+ o[n * t + s] = i * 32767;
33
+ }
34
+ return o;
35
+ }
36
+ /**
37
+ * Encode a single AudioData object to MP3.
38
+ *
39
+ * @param audioData - The AudioData object to encode
40
+ * @returns Encoded MP3 data as Uint8Array (add this to encodedData array)
41
+ */
42
+ processBatch(e) {
43
+ const t = this.convertAudioDataToInt16(e);
44
+ let a;
45
+ if (this.config.channels === 2) {
46
+ const r = new Int16Array(t.length / 2), o = new Int16Array(t.length / 2);
47
+ for (let n = 0; n < t.length / 2; n++)
48
+ r[n] = t[n * 2], o[n] = t[n * 2 + 1];
49
+ a = this.mp3encoder.encodeBuffer(r, o);
50
+ } else
51
+ a = this.mp3encoder.encodeBuffer(t);
52
+ return a;
53
+ }
54
+ /**
55
+ * Finalize encoding and get the complete MP3 file as a Blob.
56
+ *
57
+ * This method flushes any remaining data and combines all encoded chunks
58
+ * into a single MP3 file blob.
59
+ *
60
+ * @returns Complete MP3 file as a Blob
61
+ */
62
+ finish() {
63
+ const e = this.mp3encoder.flush();
64
+ e.length > 0 && this.encodedData.push(e);
65
+ const t = this.encodedData.reduce((o, n) => o + n.length, 0), a = new Uint8Array(t);
66
+ let r = 0;
67
+ for (const o of this.encodedData)
68
+ a.set(o, r), r += o.length;
69
+ return this.encodedData = [], new Blob([a], { type: "audio/mp3" });
70
+ }
71
+ // Optional: Get current size of encoded data
72
+ getEncodedSize() {
73
+ return this.encodedData.reduce((e, t) => e + t.length, 0);
74
+ }
75
+ }
76
+ class I {
77
+ /**
78
+ * Create a new MP3Decoder.
79
+ *
80
+ * The decoder auto-detects sample rate and channel configuration from the MP3 file.
81
+ * Call initialize() before using.
82
+ */
83
+ constructor() {
84
+ this.isReady = !1;
85
+ }
86
+ /**
87
+ * Initialize the decoder (async)
88
+ */
89
+ async initialize() {
90
+ if (!this.isReady)
91
+ try {
92
+ this.decoder = new b(), await this.decoder.ready, this.isReady = !0;
93
+ } catch (e) {
94
+ throw console.error("Failed to initialize MP3 decoder:", e), e;
95
+ }
96
+ }
97
+ /**
98
+ * Decode MP3 buffer to raw PCM samples
99
+ * @param mp3Buffer - The MP3 data as ArrayBuffer
100
+ * @returns Promise<{channels: Float32Array[], sampleRate: number, numberOfChannels: number}>
101
+ */
102
+ async toSamples(e) {
103
+ this.isReady || await this.initialize();
104
+ try {
105
+ const t = await this.decoder.decode(new Uint8Array(e)), { channelData: a, sampleRate: r } = t, o = a.length;
106
+ return {
107
+ channels: a.map((s) => {
108
+ const i = new Float32Array(s.length);
109
+ for (let c = 0; c < s.length; c++)
110
+ i[c] = s[c];
111
+ return i;
112
+ }),
113
+ sampleRate: r,
114
+ numberOfChannels: o
115
+ };
116
+ } catch (t) {
117
+ throw console.error("Failed to decode MP3:", t), t;
118
+ }
119
+ }
120
+ /**
121
+ * Decode MP3 to AudioData objects.
122
+ * Internally calls decodeMP3ToSamples and converts the Float32Array channels to AudioData.
123
+ *
124
+ * @param mp3Buffer - The MP3 data as ArrayBuffer
125
+ * @returns Promise<AudioData[]> - Array of AudioData objects
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * const decoder = new MP3Decoder();
130
+ * await decoder.initialize();
131
+ * const mp3Buffer = await file.arrayBuffer();
132
+ * const audioDataArray = await decoder.decode(mp3Buffer);
133
+ *
134
+ * // Use with AudioEncoder or other WebCodecs APIs
135
+ * for (const audioData of audioDataArray) {
136
+ * encoder.encode(audioData);
137
+ * audioData.close();
138
+ * }
139
+ * ```
140
+ */
141
+ async toAudioData(e) {
142
+ const { channels: t, sampleRate: a, numberOfChannels: r } = await this.toSamples(e), o = 1024, n = t[0].length, s = [];
143
+ console.log("Samples", n);
144
+ for (let i = 0; i < n; i += o) {
145
+ const c = n - i, h = Math.min(o, c), u = t.map(
146
+ (l) => l.slice(i, i + h)
147
+ ), m = new Float32Array(h * r);
148
+ for (let l = 0; l < h; l++)
149
+ for (let d = 0; d < r; d++)
150
+ m[l * r + d] = u[d][l];
151
+ const p = i / a * 1e6, y = new AudioData({
152
+ format: "f32",
153
+ sampleRate: a,
154
+ numberOfFrames: h,
155
+ numberOfChannels: r,
156
+ timestamp: Math.round(p),
157
+ data: m
158
+ });
159
+ s.push(y);
160
+ }
161
+ return s;
162
+ }
163
+ /**
164
+ * Clean up decoder resources
165
+ */
166
+ async destroy() {
167
+ this.decoder && (await this.decoder.free(), this.decoder = null, this.isReady = !1);
168
+ }
169
+ }
170
+ export {
171
+ I as MP3Decoder,
172
+ E as MP3Encoder
173
+ };
@@ -0,0 +1,74 @@
1
+ import { EncodedVideoPacketSource, EncodedAudioPacketSource, BufferTarget, Output } from 'mediabunny';
2
+ /**
3
+ * Simple muxer for creating MP4 files from encoded video or audio chunks.
4
+ *
5
+ * This class wraps MediaBunny's muxing functionality to provide a simplified
6
+ * API for demos and learning purposes. It handles a single video or audio track.
7
+ *
8
+ * **⚠️ Demo/Learning Only**: This utility is intended for demos and learning.
9
+ * For production use, please use MediaBunny directly: https://mediabunny.dev/
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Create muxer for video
14
+ * const muxer = new ExampleMuxer('video');
15
+ *
16
+ * // In VideoEncoder output callback:
17
+ * encoder.configure({
18
+ * output: (chunk, metadata) => {
19
+ * muxer.addChunk(chunk, metadata);
20
+ * },
21
+ * error: (e) => console.error(e)
22
+ * });
23
+ *
24
+ * // ... encode frames ...
25
+ * await encoder.flush();
26
+ *
27
+ * // Get final MP4
28
+ * const mp4Buffer = await muxer.finish();
29
+ * const blob = new Blob([mp4Buffer], { type: 'video/mp4' });
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * // Create muxer for audio
35
+ * const muxer = new ExampleMuxer('audio');
36
+ *
37
+ * // In AudioEncoder output callback:
38
+ * encoder.configure({
39
+ * output: (chunk) => {
40
+ * muxer.addChunk(chunk);
41
+ * },
42
+ * error: (e) => console.error(e)
43
+ * });
44
+ * ```
45
+ */
46
+ export declare class ExampleMuxer {
47
+ type: 'audio' | 'video';
48
+ output: Output;
49
+ source: EncodedVideoPacketSource | EncodedAudioPacketSource;
50
+ started: boolean;
51
+ target: BufferTarget;
52
+ /**
53
+ * Create a new ExampleMuxer.
54
+ *
55
+ * @param type - Track type: 'video' (default) or 'audio'
56
+ */
57
+ constructor(type?: 'audio' | 'video');
58
+ /**
59
+ * Add an encoded chunk to the MP4 file.
60
+ *
61
+ * @param chunk - EncodedVideoChunk or EncodedAudioChunk from encoder output
62
+ * @param meta - Optional metadata from encoder (for video, contains decoderConfig)
63
+ */
64
+ addChunk(chunk: EncodedAudioChunk | EncodedVideoChunk, meta?: any): void;
65
+ /**
66
+ * Finalize the MP4 file and return the complete buffer.
67
+ *
68
+ * Call this after all chunks have been added.
69
+ *
70
+ * @returns Complete MP4 file as ArrayBuffer
71
+ */
72
+ finish(): Promise<ArrayBuffer>;
73
+ }
74
+ //# sourceMappingURL=example-muxer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example-muxer.d.ts","sourceRoot":"","sources":["../../src/demux/example-muxer.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,wBAAwB,EACxB,wBAAwB,EACxB,YAAY,EAEZ,MAAM,EACP,MAAM,YAAY,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,YAAY;IAErB,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,wBAAwB,GAAG,wBAAwB,CAAA;IAC3D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,CAAA;IAEpB;;;;OAIG;gBACS,IAAI,CAAC,EAAE,OAAO,GAAC,OAAO;IAyBlC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,IAAI,CAAC,EAAE,GAAG;IASjE;;;;;;OAMG;IACG,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC;CAYvC"}