@audio/decode-mp4 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/decode-mp4.js +24 -19
- package/package.json +1 -1
package/decode-mp4.js
CHANGED
|
@@ -320,7 +320,7 @@ const DTS_TYPES = new Set(['dtsc', 'dtsh', 'dtsl', 'dtse']) // DTS-HD variants c
|
|
|
320
320
|
|
|
321
321
|
async function createCodec({ entry, children, movie }) {
|
|
322
322
|
let { type } = entry
|
|
323
|
-
// AAC and ALAC trim the encoder's priming and padding by the track's edit list or iTunSMPB, MP3 by the edit list (gapless)
|
|
323
|
+
// AAC and ALAC trim the encoder's priming and padding by the track's edit list or iTunSMPB, MP3 and Opus by the edit list (gapless)
|
|
324
324
|
let boxes = { elst: children.elst, mdhd: children.mdhd, stts: children.stts, mvhd: movie?.mvhd, ilst: movie?.ilst ?? children.ilst }
|
|
325
325
|
if (type === 'mp4a') {
|
|
326
326
|
let { oti, dsi } = children.esds ? parseEsds(children.esds) : {}
|
|
@@ -345,7 +345,7 @@ async function createCodec({ entry, children, movie }) {
|
|
|
345
345
|
}
|
|
346
346
|
if (type === 'Opus') {
|
|
347
347
|
if (!children.dOps) throw Error('MP4 Opus track has no dOps box')
|
|
348
|
-
return opus(children.dOps)
|
|
348
|
+
return opus(children.dOps, boxes)
|
|
349
349
|
}
|
|
350
350
|
if (type === 'samr' || type === 'sawb') return amr(type === 'sawb')
|
|
351
351
|
if (type === 'ac-3') return frames(import('@audio/decode-ac3'))
|
|
@@ -403,20 +403,22 @@ async function frames(load) {
|
|
|
403
403
|
// encoder and decoder delay (1105 for LAME, as ffmpeg writes it) and the padding. decode-aac reads the window.
|
|
404
404
|
async function mp3(boxes) {
|
|
405
405
|
let [codec, { gapless }] = await Promise.all([frames(import('@audio/decode-mp3')), import('@audio/decode-aac')])
|
|
406
|
-
|
|
406
|
+
return windowed(codec, gapless?.(boxes))
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// the edit list's window { start, duration } (seconds) over a codec that decodes from the media's first sample
|
|
410
|
+
function windowed(codec, g) {
|
|
407
411
|
if (!g) return codec
|
|
408
412
|
let skip = null, left = null
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
return b <= a ? EMPTY : a || b < n ? { ...r, channelData: r.channelData.map(c => c.subarray(a, b)) } : r
|
|
417
|
-
},
|
|
418
|
-
flush: codec.flush, free: codec.free
|
|
413
|
+
let cut = r => {
|
|
414
|
+
let n = r.channelData?.[0]?.length
|
|
415
|
+
if (!n) return r
|
|
416
|
+
skip ??= Math.round(g.start * r.sampleRate); left ??= g.duration == null ? Infinity : Math.round(g.duration * r.sampleRate)
|
|
417
|
+
let a = Math.min(n, skip), b = Math.min(n, a + left)
|
|
418
|
+
skip -= a; left -= b - a
|
|
419
|
+
return b <= a ? EMPTY : a || b < n ? { ...r, channelData: r.channelData.map(c => c.subarray(a, b)) } : r
|
|
419
420
|
}
|
|
421
|
+
return { feed: frames => cut(codec.feed(frames)), flush: () => cut(codec.flush()), free: codec.free }
|
|
420
422
|
}
|
|
421
423
|
|
|
422
424
|
async function flac(blocks) {
|
|
@@ -434,17 +436,20 @@ async function flac(blocks) {
|
|
|
434
436
|
// as decode-opus reorders Ogg Opus: output channel j takes the coded channel of Vorbis position OPUS_ORDER[n][j]
|
|
435
437
|
const OPUS_ORDER = { 3: [0, 2, 1], 5: [0, 2, 1, 3, 4], 6: [0, 2, 1, 5, 3, 4], 7: [0, 2, 1, 6, 5, 3, 4], 8: [0, 2, 1, 7, 5, 6, 3, 4] }
|
|
436
438
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
439
|
+
// Opus in ISOBMFF: the edit list's media_time covers PreSkip and its duration is the real length, so with an edit
|
|
440
|
+
// list the window trims both ends from the media's first sample; without one, dOps' PreSkip trims the start
|
|
441
|
+
async function opus(dOps, boxes) {
|
|
442
|
+
let [{ createOpusDecoder }, { gapless }] = await Promise.all([import('@audio/decode-opus/core'), import('@audio/decode-aac')])
|
|
443
|
+
let g = gapless?.(boxes), channels = dOps[1]
|
|
444
|
+
let opts = { channels, sampleRate: 48000, preSkip: g ? 0 : r16(dOps, 2), outputGain: (r16(dOps, 8) << 16) >> 16 }
|
|
440
445
|
if (dOps[10] > 0) {
|
|
441
446
|
opts.streamCount = dOps[11]; opts.coupledStreamCount = dOps[12]
|
|
442
447
|
let table = Array.from(dOps.subarray(13, 13 + channels))
|
|
443
448
|
opts.channelMappingTable = dOps[10] === 1 && OPUS_ORDER[channels] ? OPUS_ORDER[channels].map(i => table[i]) : table
|
|
444
449
|
}
|
|
445
|
-
let dec = await
|
|
450
|
+
let dec = await createOpusDecoder()
|
|
446
451
|
dec.configure(opts)
|
|
447
|
-
return {
|
|
452
|
+
return windowed({
|
|
448
453
|
feed: frames => {
|
|
449
454
|
let r = dec.decodeFrames(frames)
|
|
450
455
|
if (!r.samplesDecoded) return EMPTY
|
|
@@ -452,7 +457,7 @@ async function opus(dOps) {
|
|
|
452
457
|
},
|
|
453
458
|
flush: () => EMPTY,
|
|
454
459
|
free: () => dec.free()
|
|
455
|
-
}
|
|
460
|
+
}, g)
|
|
456
461
|
}
|
|
457
462
|
|
|
458
463
|
async function amr(wb) {
|
package/package.json
CHANGED