@mediabunny/ac3 0.1.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 (37) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +103 -0
  3. package/dist/bundles/mediabunny-ac3.js +3829 -0
  4. package/dist/bundles/mediabunny-ac3.min.js +3510 -0
  5. package/dist/bundles/mediabunny-ac3.min.mjs +3509 -0
  6. package/dist/bundles/mediabunny-ac3.mjs +3792 -0
  7. package/dist/mediabunny-ac3.d.ts +20 -0
  8. package/dist/modules/build/ac3.d.ts +3 -0
  9. package/dist/modules/build/ac3.d.ts.map +1 -0
  10. package/dist/modules/build/ac3.js +0 -0
  11. package/dist/modules/src/codec.worker.d.ts +9 -0
  12. package/dist/modules/src/codec.worker.d.ts.map +1 -0
  13. package/dist/modules/src/codec.worker.js +269 -0
  14. package/dist/modules/src/decoder.d.ts +16 -0
  15. package/dist/modules/src/decoder.d.ts.map +1 -0
  16. package/dist/modules/src/decoder.js +61 -0
  17. package/dist/modules/src/encoder.d.ts +16 -0
  18. package/dist/modules/src/encoder.d.ts.map +1 -0
  19. package/dist/modules/src/encoder.js +151 -0
  20. package/dist/modules/src/index.d.ts +10 -0
  21. package/dist/modules/src/index.d.ts.map +1 -0
  22. package/dist/modules/src/index.js +22 -0
  23. package/dist/modules/src/shared.d.ts +96 -0
  24. package/dist/modules/src/shared.d.ts.map +1 -0
  25. package/dist/modules/src/shared.js +15 -0
  26. package/dist/modules/src/worker-client.d.ts +14 -0
  27. package/dist/modules/src/worker-client.d.ts.map +1 -0
  28. package/dist/modules/src/worker-client.js +61 -0
  29. package/dist/modules/tsconfig.tsbuildinfo +1 -0
  30. package/package.json +56 -0
  31. package/src/bridge.c +289 -0
  32. package/src/codec.worker.ts +306 -0
  33. package/src/decoder.ts +70 -0
  34. package/src/encoder.ts +191 -0
  35. package/src/index.ts +21 -0
  36. package/src/shared.ts +103 -0
  37. package/src/worker-client.ts +69 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Registers AC-3 and E-AC-3 decoders, which Mediabunny will then use automatically when applicable. Make sure to call
3
+ * this function before starting any decoding task.
4
+ *
5
+ * @group \@mediabunny/ac3
6
+ * @public
7
+ */
8
+ export declare const registerAc3Decoder: () => void;
9
+
10
+ /**
11
+ * Registers AC-3 and E-AC-3 encoders, which Mediabunny will then use automatically when applicable. Make sure to call
12
+ * this function before starting any encoding task.
13
+ *
14
+ * @group \@mediabunny/ac3
15
+ * @public
16
+ */
17
+ export declare const registerAc3Encoder: () => void;
18
+
19
+ export { }
20
+ export as namespace MediabunnyAc3;
@@ -0,0 +1,3 @@
1
+ export default Module;
2
+ declare function Module(moduleArg?: {}): Promise<{}>;
3
+ //# sourceMappingURL=ac3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ac3.d.ts","sourceRoot":"","sources":["../../../build/ac3.js"],"names":[],"mappings":";AAAA,qDACkB"}
Binary file
@@ -0,0 +1,9 @@
1
+ /*!
2
+ * Copyright (c) 2026-present, Vanilagy and contributors
3
+ *
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=codec.worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.worker.d.ts","sourceRoot":"","sources":["../../../src/codec.worker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) 2026-present, Vanilagy and contributors
4
+ *
5
+ * This Source Code Form is subject to the terms of the Mozilla Public
6
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
7
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const ac3_1 = __importDefault(require("../build/ac3"));
14
+ let module;
15
+ let modulePromise = null;
16
+ let initDecoderFn;
17
+ let configureDecodePacket;
18
+ let decodePacket;
19
+ let getDecodedFormat;
20
+ let getDecodedPlanePtr;
21
+ let getDecodedChannels;
22
+ let getDecodedSampleRate;
23
+ let getDecodedSampleCount;
24
+ let getDecodedPts;
25
+ let flushDecoderFn;
26
+ let closeDecoderFn;
27
+ let initEncoderFn;
28
+ let getEncoderFrameSize;
29
+ let getEncodeInputPtr;
30
+ let encodeFrameFn;
31
+ let flushEncoderFn;
32
+ let getEncodedData;
33
+ let getEncodedPts;
34
+ let getEncodedDuration;
35
+ let closeEncoderFn;
36
+ const codecToId = (codec) => codec === 'ac3' ? 0 : 1;
37
+ const ensureModule = async () => {
38
+ if (!module) {
39
+ if (modulePromise) {
40
+ // If we don't do this we can have a race condition
41
+ return modulePromise;
42
+ }
43
+ modulePromise = (0, ac3_1.default)();
44
+ module = await modulePromise;
45
+ modulePromise = null;
46
+ initDecoderFn = module.cwrap('init_decoder', 'number', ['number']);
47
+ configureDecodePacket = module.cwrap('configure_decode_packet', 'number', ['number', 'number']);
48
+ decodePacket = module.cwrap('decode_packet', 'number', ['number', 'number']);
49
+ getDecodedFormat = module.cwrap('get_decoded_format', 'number', ['number']);
50
+ getDecodedPlanePtr = module.cwrap('get_decoded_plane_ptr', 'number', ['number', 'number']);
51
+ getDecodedChannels = module.cwrap('get_decoded_channels', 'number', ['number']);
52
+ getDecodedSampleRate = module.cwrap('get_decoded_sample_rate', 'number', ['number']);
53
+ getDecodedSampleCount = module.cwrap('get_decoded_sample_count', 'number', ['number']);
54
+ getDecodedPts = module.cwrap('get_decoded_pts', 'number', ['number']);
55
+ flushDecoderFn = module.cwrap('flush_decoder', null, ['number']);
56
+ closeDecoderFn = module.cwrap('close_decoder', null, ['number']);
57
+ initEncoderFn = module.cwrap('init_encoder', 'number', ['number', 'number', 'number', 'number']);
58
+ getEncoderFrameSize = module.cwrap('get_encoder_frame_size', 'number', ['number']);
59
+ getEncodeInputPtr = module.cwrap('get_encode_input_ptr', 'number', ['number', 'number']);
60
+ encodeFrameFn = module.cwrap('encode_frame', 'number', ['number', 'number']);
61
+ flushEncoderFn = module.cwrap('flush_encoder', null, ['number']);
62
+ getEncodedData = module.cwrap('get_encoded_data', 'number', ['number']);
63
+ getEncodedPts = module.cwrap('get_encoded_pts', 'number', ['number']);
64
+ getEncodedDuration = module.cwrap('get_encoded_duration', 'number', ['number']);
65
+ closeEncoderFn = module.cwrap('close_encoder', null, ['number']);
66
+ }
67
+ };
68
+ const initDecoder = async (codec) => {
69
+ await ensureModule();
70
+ const ctx = initDecoderFn(codecToId(codec));
71
+ if (ctx === 0) {
72
+ throw new Error('Failed to initialize AC3 decoder.');
73
+ }
74
+ return { ctx, frameSize: 0 };
75
+ };
76
+ // Keys are AVSampleFormat enum values
77
+ const AV_FORMAT_MAP = {
78
+ 0: { format: 'u8', bytesPerSample: 1, planar: false },
79
+ 1: { format: 's16', bytesPerSample: 2, planar: false },
80
+ 2: { format: 's32', bytesPerSample: 4, planar: false },
81
+ 3: { format: 'f32', bytesPerSample: 4, planar: false },
82
+ 5: { format: 'u8-planar', bytesPerSample: 1, planar: true },
83
+ 6: { format: 's16-planar', bytesPerSample: 2, planar: true },
84
+ 7: { format: 's32-planar', bytesPerSample: 4, planar: true },
85
+ 8: { format: 'f32-planar', bytesPerSample: 4, planar: true },
86
+ };
87
+ const decode = (ctx, encodedData, timestamp) => {
88
+ const bytes = new Uint8Array(encodedData);
89
+ const dataPtr = configureDecodePacket(ctx, bytes.length);
90
+ if (dataPtr === 0) {
91
+ throw new Error('Failed to configure decode packet.');
92
+ }
93
+ module.HEAPU8.set(bytes, dataPtr);
94
+ const ret = decodePacket(ctx, timestamp);
95
+ if (ret < 0) {
96
+ throw new Error(`Decode failed with error code ${ret}.`);
97
+ }
98
+ const avFormat = getDecodedFormat(ctx);
99
+ const info = AV_FORMAT_MAP[avFormat];
100
+ if (!info) {
101
+ throw new Error(`Unsupported AVSampleFormat: ${avFormat}`);
102
+ }
103
+ const channels = getDecodedChannels(ctx);
104
+ const sampleRate = getDecodedSampleRate(ctx);
105
+ const sampleCount = getDecodedSampleCount(ctx);
106
+ const pts = getDecodedPts(ctx);
107
+ let pcmData;
108
+ if (info.planar) {
109
+ const planeSize = sampleCount * info.bytesPerSample;
110
+ const buffer = new Uint8Array(planeSize * channels);
111
+ for (let ch = 0; ch < channels; ch++) {
112
+ const ptr = getDecodedPlanePtr(ctx, ch);
113
+ buffer.set(module.HEAPU8.subarray(ptr, ptr + planeSize), ch * planeSize);
114
+ }
115
+ pcmData = buffer.buffer;
116
+ }
117
+ else {
118
+ const totalSize = sampleCount * channels * info.bytesPerSample;
119
+ const ptr = getDecodedPlanePtr(ctx, 0);
120
+ pcmData = module.HEAPU8.slice(ptr, ptr + totalSize).buffer;
121
+ }
122
+ return { pcmData, format: info.format, channels, sampleRate, sampleCount, pts };
123
+ };
124
+ const initEncoder = async (codec, numberOfChannels, sampleRate, bitrate) => {
125
+ await ensureModule();
126
+ const ctx = initEncoderFn(codecToId(codec), numberOfChannels, sampleRate, bitrate);
127
+ if (ctx === 0) {
128
+ throw new Error('Failed to initialize AC3 encoder.');
129
+ }
130
+ return { ctx, frameSize: getEncoderFrameSize(ctx) };
131
+ };
132
+ const encode = (ctx, audioData, timestamp) => {
133
+ const audioBytes = new Uint8Array(audioData);
134
+ const inputPtr = getEncodeInputPtr(ctx, audioBytes.length);
135
+ if (inputPtr === 0) {
136
+ throw new Error('Failed to allocate encoder input buffer.');
137
+ }
138
+ module.HEAPU8.set(audioBytes, inputPtr);
139
+ const bytesWritten = encodeFrameFn(ctx, timestamp);
140
+ if (bytesWritten < 0) {
141
+ throw new Error(`Encode failed with error code ${bytesWritten}.`);
142
+ }
143
+ const ptr = getEncodedData(ctx);
144
+ const encodedData = module.HEAPU8.slice(ptr, ptr + bytesWritten).buffer;
145
+ const pts = getEncodedPts(ctx);
146
+ const duration = getEncodedDuration(ctx);
147
+ return { encodedData, pts, duration };
148
+ };
149
+ const flushEncoder = (ctx) => {
150
+ flushEncoderFn(ctx);
151
+ };
152
+ const onMessage = (data) => {
153
+ const { id, command } = data;
154
+ const handleCommand = async () => {
155
+ try {
156
+ let result;
157
+ const transferables = [];
158
+ switch (command.type) {
159
+ case 'init-decoder':
160
+ {
161
+ const { ctx, frameSize } = await initDecoder(command.data.codec);
162
+ result = { type: command.type, ctx, frameSize };
163
+ }
164
+ ;
165
+ break;
166
+ case 'decode':
167
+ {
168
+ const decoded = decode(command.data.ctx, command.data.encodedData, command.data.timestamp);
169
+ result = {
170
+ type: command.type,
171
+ pcmData: decoded.pcmData,
172
+ format: decoded.format,
173
+ channels: decoded.channels,
174
+ sampleRate: decoded.sampleRate,
175
+ sampleCount: decoded.sampleCount,
176
+ pts: decoded.pts,
177
+ };
178
+ transferables.push(decoded.pcmData);
179
+ }
180
+ ;
181
+ break;
182
+ case 'flush-decoder':
183
+ {
184
+ flushDecoderFn(command.data.ctx);
185
+ result = { type: command.type };
186
+ }
187
+ ;
188
+ break;
189
+ case 'close-decoder':
190
+ {
191
+ closeDecoderFn(command.data.ctx);
192
+ result = { type: command.type };
193
+ }
194
+ ;
195
+ break;
196
+ case 'init-encoder':
197
+ {
198
+ const { ctx, frameSize } = await initEncoder(command.data.codec, command.data.numberOfChannels, command.data.sampleRate, command.data.bitrate);
199
+ result = { type: command.type, ctx, frameSize };
200
+ }
201
+ ;
202
+ break;
203
+ case 'encode':
204
+ {
205
+ const encoded = encode(command.data.ctx, command.data.audioData, command.data.timestamp);
206
+ result = {
207
+ type: command.type,
208
+ encodedData: encoded.encodedData,
209
+ pts: encoded.pts,
210
+ duration: encoded.duration,
211
+ };
212
+ transferables.push(encoded.encodedData);
213
+ }
214
+ ;
215
+ break;
216
+ case 'flush-encoder':
217
+ {
218
+ flushEncoder(command.data.ctx);
219
+ result = { type: command.type };
220
+ }
221
+ ;
222
+ break;
223
+ case 'close-encoder':
224
+ {
225
+ closeEncoderFn(command.data.ctx);
226
+ result = { type: command.type };
227
+ }
228
+ ;
229
+ break;
230
+ }
231
+ const response = {
232
+ id,
233
+ success: true,
234
+ data: result,
235
+ };
236
+ sendMessage(response, transferables);
237
+ }
238
+ catch (error) {
239
+ const response = {
240
+ id,
241
+ success: false,
242
+ error,
243
+ };
244
+ sendMessage(response);
245
+ }
246
+ };
247
+ void handleCommand();
248
+ };
249
+ const sendMessage = (data, transferables) => {
250
+ if (parentPort) {
251
+ parentPort.postMessage(data, transferables ?? []);
252
+ }
253
+ else {
254
+ self.postMessage(data, { transfer: transferables ?? [] });
255
+ }
256
+ };
257
+ let parentPort = null;
258
+ if (typeof self === 'undefined') {
259
+ const workerModule = 'worker_threads';
260
+ // eslint-disable-next-line @stylistic/max-len
261
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-member-access
262
+ parentPort = require(workerModule).parentPort;
263
+ }
264
+ if (parentPort) {
265
+ parentPort.on('message', onMessage);
266
+ }
267
+ else {
268
+ self.addEventListener('message', event => onMessage(event.data));
269
+ }
@@ -0,0 +1,16 @@
1
+ /*!
2
+ * Copyright (c) 2026-present, Vanilagy and contributors
3
+ *
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
+ */
8
+ /**
9
+ * Registers AC-3 and E-AC-3 decoders, which Mediabunny will then use automatically when applicable. Make sure to call
10
+ * this function before starting any decoding task.
11
+ *
12
+ * @group \@mediabunny/ac3
13
+ * @public
14
+ */
15
+ export declare const registerAc3Decoder: () => void;
16
+ //# sourceMappingURL=decoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decoder.d.ts","sourceRoot":"","sources":["../../../src/decoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsDH;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,YAE9B,CAAC"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) 2026-present, Vanilagy and contributors
4
+ *
5
+ * This Source Code Form is subject to the terms of the Mozilla Public
6
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
7
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.registerAc3Decoder = void 0;
11
+ const mediabunny_1 = require("mediabunny");
12
+ const worker_client_1 = require("./worker-client");
13
+ class Ac3Decoder extends mediabunny_1.CustomAudioDecoder {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.ctx = 0;
17
+ }
18
+ static supports(codec) {
19
+ return codec === 'ac3' || codec === 'eac3';
20
+ }
21
+ async init() {
22
+ const result = await (0, worker_client_1.sendCommand)({
23
+ type: 'init-decoder',
24
+ data: { codec: this.codec },
25
+ });
26
+ this.ctx = result.ctx;
27
+ }
28
+ async decode(packet) {
29
+ const encodedData = packet.data.slice().buffer;
30
+ const timestamp = Math.round(packet.timestamp * this.config.sampleRate);
31
+ const result = await (0, worker_client_1.sendCommand)({
32
+ type: 'decode',
33
+ data: { ctx: this.ctx, encodedData, timestamp },
34
+ }, [encodedData]);
35
+ const sample = new mediabunny_1.AudioSample({
36
+ data: result.pcmData,
37
+ format: result.format,
38
+ numberOfChannels: result.channels,
39
+ sampleRate: result.sampleRate,
40
+ timestamp: result.pts / result.sampleRate,
41
+ });
42
+ this.onSample(sample);
43
+ }
44
+ async flush() {
45
+ await (0, worker_client_1.sendCommand)({ type: 'flush-decoder', data: { ctx: this.ctx } });
46
+ }
47
+ close() {
48
+ void (0, worker_client_1.sendCommand)({ type: 'close-decoder', data: { ctx: this.ctx } });
49
+ }
50
+ }
51
+ /**
52
+ * Registers AC-3 and E-AC-3 decoders, which Mediabunny will then use automatically when applicable. Make sure to call
53
+ * this function before starting any decoding task.
54
+ *
55
+ * @group \@mediabunny/ac3
56
+ * @public
57
+ */
58
+ const registerAc3Decoder = () => {
59
+ (0, mediabunny_1.registerDecoder)(Ac3Decoder);
60
+ };
61
+ exports.registerAc3Decoder = registerAc3Decoder;
@@ -0,0 +1,16 @@
1
+ /*!
2
+ * Copyright (c) 2026-present, Vanilagy and contributors
3
+ *
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
+ */
8
+ /**
9
+ * Registers AC-3 and E-AC-3 encoders, which Mediabunny will then use automatically when applicable. Make sure to call
10
+ * this function before starting any encoding task.
11
+ *
12
+ * @group \@mediabunny/ac3
13
+ * @public
14
+ */
15
+ export declare const registerAc3Encoder: () => void;
16
+ //# sourceMappingURL=encoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+KH;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,YAE9B,CAAC"}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) 2026-present, Vanilagy and contributors
4
+ *
5
+ * This Source Code Form is subject to the terms of the Mozilla Public
6
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
7
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.registerAc3Encoder = void 0;
11
+ const mediabunny_1 = require("mediabunny");
12
+ const worker_client_1 = require("./worker-client");
13
+ const shared_1 = require("./shared");
14
+ const ac3_misc_1 = require("../../../shared/ac3-misc");
15
+ class Ac3Encoder extends mediabunny_1.CustomAudioEncoder {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.ctx = 0;
19
+ this.encoderFrameSize = 0;
20
+ this.sampleRate = 0;
21
+ this.numberOfChannels = 0;
22
+ this.chunkMetadata = {};
23
+ // Accumulate interleaved f32 samples until we have a full frame
24
+ this.pendingBuffer = new Float32Array(2 ** 16);
25
+ this.pendingFrames = 0;
26
+ this.nextSampleTimestampInSamples = null;
27
+ this.nextPacketTimestampInSamples = null;
28
+ }
29
+ static supports(codec, config) {
30
+ const sampleRates = codec === 'eac3'
31
+ ? [...ac3_misc_1.AC3_SAMPLE_RATES, ...ac3_misc_1.EAC3_REDUCED_SAMPLE_RATES]
32
+ : ac3_misc_1.AC3_SAMPLE_RATES;
33
+ return (codec === 'ac3' || codec === 'eac3')
34
+ && config.numberOfChannels >= 1
35
+ && config.numberOfChannels <= 8
36
+ && sampleRates.includes(config.sampleRate);
37
+ }
38
+ async init() {
39
+ (0, shared_1.assert)(this.config.bitrate);
40
+ this.sampleRate = this.config.sampleRate;
41
+ this.numberOfChannels = this.config.numberOfChannels;
42
+ const result = await (0, worker_client_1.sendCommand)({
43
+ type: 'init-encoder',
44
+ data: {
45
+ codec: this.codec,
46
+ numberOfChannels: this.config.numberOfChannels,
47
+ sampleRate: this.config.sampleRate,
48
+ bitrate: this.config.bitrate,
49
+ },
50
+ });
51
+ this.ctx = result.ctx;
52
+ this.encoderFrameSize = result.frameSize;
53
+ this.resetInternalState();
54
+ }
55
+ resetInternalState() {
56
+ this.pendingFrames = 0;
57
+ this.nextSampleTimestampInSamples = null;
58
+ this.nextPacketTimestampInSamples = null;
59
+ this.chunkMetadata = {
60
+ decoderConfig: {
61
+ codec: this.codec === 'ac3' ? 'ac-3' : 'ec-3',
62
+ numberOfChannels: this.config.numberOfChannels,
63
+ sampleRate: this.config.sampleRate,
64
+ },
65
+ };
66
+ }
67
+ async encode(audioSample) {
68
+ if (this.nextSampleTimestampInSamples === null) {
69
+ this.nextSampleTimestampInSamples = Math.round(audioSample.timestamp * this.sampleRate);
70
+ this.nextPacketTimestampInSamples = this.nextSampleTimestampInSamples;
71
+ }
72
+ const channels = this.numberOfChannels;
73
+ const incomingFrames = audioSample.numberOfFrames;
74
+ // Extract interleaved f32 data
75
+ const totalBytes = audioSample.allocationSize({ format: 'f32', planeIndex: 0 });
76
+ const audioBytes = new Uint8Array(totalBytes);
77
+ audioSample.copyTo(audioBytes, { format: 'f32', planeIndex: 0 });
78
+ const incomingData = new Float32Array(audioBytes.buffer);
79
+ const requiredSamples = (this.pendingFrames + incomingFrames) * channels;
80
+ if (requiredSamples > this.pendingBuffer.length) {
81
+ let newSize = this.pendingBuffer.length;
82
+ while (newSize < requiredSamples) {
83
+ newSize *= 2;
84
+ }
85
+ const newBuffer = new Float32Array(newSize);
86
+ newBuffer.set(this.pendingBuffer.subarray(0, this.pendingFrames * channels));
87
+ this.pendingBuffer = newBuffer;
88
+ }
89
+ this.pendingBuffer.set(incomingData, this.pendingFrames * channels);
90
+ this.pendingFrames += incomingFrames;
91
+ while (this.pendingFrames >= this.encoderFrameSize) {
92
+ await this.encodeOneFrame();
93
+ }
94
+ }
95
+ async flush() {
96
+ // Pad remaining samples with silence to fill a full frame
97
+ if (this.pendingFrames > 0) {
98
+ const channels = this.numberOfChannels;
99
+ const frameSize = this.encoderFrameSize;
100
+ const usedSamples = this.pendingFrames * channels;
101
+ const frameSamples = frameSize * channels;
102
+ this.pendingBuffer.fill(0, usedSamples, frameSamples);
103
+ this.pendingFrames = frameSize;
104
+ await this.encodeOneFrame();
105
+ }
106
+ await (0, worker_client_1.sendCommand)({ type: 'flush-encoder', data: { ctx: this.ctx } });
107
+ this.resetInternalState();
108
+ }
109
+ close() {
110
+ void (0, worker_client_1.sendCommand)({ type: 'close-encoder', data: { ctx: this.ctx } });
111
+ }
112
+ async encodeOneFrame() {
113
+ (0, shared_1.assert)(this.nextSampleTimestampInSamples !== null);
114
+ (0, shared_1.assert)(this.nextPacketTimestampInSamples !== null);
115
+ const channels = this.numberOfChannels;
116
+ const frameSize = this.encoderFrameSize;
117
+ const frameSamples = frameSize * channels;
118
+ const frameData = this.pendingBuffer.slice(0, frameSamples);
119
+ // Shift remaining using copyWithin
120
+ this.pendingFrames -= frameSize;
121
+ if (this.pendingFrames > 0) {
122
+ this.pendingBuffer.copyWithin(0, frameSamples, frameSamples + this.pendingFrames * channels);
123
+ }
124
+ const audioData = frameData.buffer;
125
+ const result = await (0, worker_client_1.sendCommand)({
126
+ type: 'encode',
127
+ data: {
128
+ ctx: this.ctx,
129
+ audioData,
130
+ timestamp: this.nextSampleTimestampInSamples,
131
+ },
132
+ }, [audioData]);
133
+ this.nextSampleTimestampInSamples += frameSize;
134
+ // We always get exactly one packet because we encode the correct frame size
135
+ const packet = new mediabunny_1.EncodedPacket(new Uint8Array(result.encodedData), 'key', this.nextPacketTimestampInSamples / this.sampleRate, result.duration / this.sampleRate);
136
+ this.nextPacketTimestampInSamples += result.duration;
137
+ this.onPacket(packet, this.chunkMetadata);
138
+ this.chunkMetadata = {};
139
+ }
140
+ }
141
+ /**
142
+ * Registers AC-3 and E-AC-3 encoders, which Mediabunny will then use automatically when applicable. Make sure to call
143
+ * this function before starting any encoding task.
144
+ *
145
+ * @group \@mediabunny/ac3
146
+ * @public
147
+ */
148
+ const registerAc3Encoder = () => {
149
+ (0, mediabunny_1.registerEncoder)(Ac3Encoder);
150
+ };
151
+ exports.registerAc3Encoder = registerAc3Encoder;
@@ -0,0 +1,10 @@
1
+ /*!
2
+ * Copyright (c) 2026-present, Vanilagy and contributors
3
+ *
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
+ */
8
+ export { registerAc3Decoder } from './decoder';
9
+ export { registerAc3Encoder } from './encoder';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) 2026-present, Vanilagy and contributors
4
+ *
5
+ * This Source Code Form is subject to the terms of the Mozilla Public
6
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
7
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.registerAc3Encoder = exports.registerAc3Decoder = void 0;
11
+ const AC3_LOADED_SYMBOL = Symbol.for('@mediabunny/ac3 loaded');
12
+ if (globalThis[AC3_LOADED_SYMBOL]) {
13
+ console.error('[WARNING]\n@mediabunny/ac3 was loaded twice.'
14
+ + ' This will likely cause the encoder/decoder not to work correctly.'
15
+ + ' Check if multiple dependencies are importing different versions of @mediabunny/ac3,'
16
+ + ' or if something is being bundled incorrectly.');
17
+ }
18
+ globalThis[AC3_LOADED_SYMBOL] = true;
19
+ var decoder_1 = require("./decoder");
20
+ Object.defineProperty(exports, "registerAc3Decoder", { enumerable: true, get: function () { return decoder_1.registerAc3Decoder; } });
21
+ var encoder_1 = require("./encoder");
22
+ Object.defineProperty(exports, "registerAc3Encoder", { enumerable: true, get: function () { return encoder_1.registerAc3Encoder; } });