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