codec-parser
Advanced tools
Comparing version 2.4.3 to 2.5.0
{ | ||
"name": "codec-parser", | ||
"version": "2.4.3", | ||
"version": "2.5.0", | ||
"description": "Library that parses raw data from audio codecs into frames containing data, header values, duration, and other information.", | ||
@@ -40,6 +40,6 @@ "main": "index.js", | ||
"devDependencies": { | ||
"@types/jest": "^29.5.3", | ||
"jest": "^29.6.2", | ||
"prettier": "^3.0.1" | ||
"@types/jest": "^29.5.13", | ||
"jest": "^29.7.0", | ||
"prettier": "^3.3.3" | ||
} | ||
} |
@@ -45,2 +45,3 @@ /* Copyright 2020-2023 Ethan Halsall | ||
reset, | ||
isLastPage, | ||
} from "./constants.js"; | ||
@@ -233,8 +234,33 @@ import HeaderCache from "./codecs/HeaderCache.js"; | ||
// Ogg container | ||
frame[codecFrames].forEach((codecFrame) => { | ||
frame[duration] += codecFrame[duration]; | ||
frame[samples] += codecFrame[samples]; | ||
this[mapCodecFrameStats](codecFrame); | ||
}); | ||
if (frame[isLastPage]) { | ||
// cut any excess samples that fall outside of the absolute granule position | ||
// some streams put invalid data in absolute granule position, so only do this | ||
// for the end of the stream | ||
let absoluteGranulePositionSamples = frame[samples]; | ||
frame[codecFrames].forEach((codecFrame) => { | ||
const untrimmedCodecSamples = codecFrame[samples]; | ||
if (absoluteGranulePositionSamples < untrimmedCodecSamples) { | ||
codecFrame[samples] = | ||
absoluteGranulePositionSamples > 0 | ||
? absoluteGranulePositionSamples | ||
: 0; | ||
codecFrame[duration] = | ||
(codecFrame[samples] / codecFrame[header][sampleRate]) * 1000; | ||
} | ||
absoluteGranulePositionSamples -= untrimmedCodecSamples; | ||
this[mapCodecFrameStats](codecFrame); | ||
}); | ||
} else { | ||
frame[samples] = 0; | ||
frame[codecFrames].forEach((codecFrame) => { | ||
frame[samples] += codecFrame[samples]; | ||
this[mapCodecFrameStats](codecFrame); | ||
}); | ||
} | ||
frame[duration] = (frame[samples] / this._sampleRate) * 1000 || 0; | ||
frame[totalSamples] = this._totalSamples; | ||
@@ -241,0 +267,0 @@ frame[totalDuration] = |
@@ -103,5 +103,4 @@ /* Copyright 2020-2023 Ethan Halsall | ||
// found a valid next frame header | ||
let frameData = yield* this._codecParser[readRawData]( | ||
nextHeaderOffset, | ||
); | ||
let frameData = | ||
yield* this._codecParser[readRawData](nextHeaderOffset); | ||
@@ -108,0 +107,0 @@ if (!this._codecParser._flushing) |
@@ -19,13 +19,8 @@ /* Copyright 2020-2023 Ethan Halsall | ||
import { sampleRate, frameCount, frameSize } from "../../constants.js"; | ||
import CodecFrame from "../CodecFrame.js"; | ||
export default class OpusFrame extends CodecFrame { | ||
constructor(data, header) { | ||
super( | ||
header, | ||
data, | ||
((header[frameSize] * header[frameCount]) / 1000) * header[sampleRate], | ||
); | ||
constructor(data, header, samples) { | ||
super(header, data, samples); | ||
} | ||
} |
@@ -30,2 +30,6 @@ /* Copyright 2020-2023 Ethan Halsall | ||
getHeaderFromUint8Array, | ||
preSkip, | ||
frameSize, | ||
frameCount, | ||
sampleRate, | ||
} from "../../constants.js"; | ||
@@ -44,2 +48,3 @@ import Parser from "../Parser.js"; | ||
this._identificationHeader = null; | ||
this._preSkipRemaining = null; | ||
} | ||
@@ -72,4 +77,19 @@ | ||
if (header) return new OpusFrame(segment, header); | ||
if (header) { | ||
if (this._preSkipRemaining === null) | ||
this._preSkipRemaining = header[preSkip]; | ||
let samples = | ||
((header[frameSize] * header[frameCount]) / 1000) * | ||
header[sampleRate]; | ||
if (this._preSkipRemaining > 0) { | ||
this._preSkipRemaining -= samples; | ||
samples = | ||
this._preSkipRemaining < 0 ? -this._preSkipRemaining : 0; | ||
} | ||
return new OpusFrame(segment, header, samples); | ||
} | ||
this._codecParser[logError]( | ||
@@ -76,0 +96,0 @@ "Failed to parse Ogg Opus Header", |
@@ -73,2 +73,3 @@ /* Copyright 2020-2023 Ethan Halsall | ||
} from "../../constants.js"; | ||
import { readInt64le } from "../../utilities.js"; | ||
@@ -115,10 +116,4 @@ export default class OggPageHeader { | ||
// * Absolute Granule Position | ||
header[absoluteGranulePosition] = readInt64le(view, 6); | ||
/** | ||
* @todo Safari does not support getBigInt64, but it also doesn't support Ogg | ||
*/ | ||
try { | ||
header[absoluteGranulePosition] = view.getBigInt64(6, true); | ||
} catch {} | ||
// Byte (15-18 of 28) | ||
@@ -125,0 +120,0 @@ // * `GGGGGGGG|GGGGGGGG|GGGGGGGG|GGGGGGGG` |
@@ -29,2 +29,3 @@ /* Copyright 2020-2023 Ethan Halsall | ||
length, | ||
samples, | ||
segments, | ||
@@ -42,2 +43,3 @@ subarray, | ||
streamSerialNumber, | ||
absoluteGranulePosition, | ||
} from "../../constants.js"; | ||
@@ -62,2 +64,3 @@ | ||
this._isSupported = null; | ||
this._previousAbsoluteGranulePosition = null; | ||
} | ||
@@ -155,2 +158,12 @@ | ||
// set total samples in this ogg page | ||
if (this._previousAbsoluteGranulePosition !== null) { | ||
oggPage[samples] = Number( | ||
oggPage[absoluteGranulePosition] - | ||
this._previousAbsoluteGranulePosition, | ||
); | ||
} | ||
this._previousAbsoluteGranulePosition = oggPage[absoluteGranulePosition]; | ||
if (this._isSupported) { | ||
@@ -157,0 +170,0 @@ const frame = this._parser[parseOggPage](oggPage); |
@@ -188,2 +188,26 @@ /* Copyright 2020-2023 Ethan Halsall | ||
/** | ||
* @todo Old versions of Safari do not support BigInt | ||
*/ | ||
const readInt64le = (view, offset) => { | ||
try { | ||
return view.getBigInt64(offset, true); | ||
} catch { | ||
const sign = view.getUint8(offset + 7) & 0x80 ? -1 : 1; | ||
let firstPart = view.getUint32(offset, true); | ||
let secondPart = view.getUint32(offset + 4, true); | ||
if (sign === -1) { | ||
firstPart = ~firstPart + 1; | ||
secondPart = ~secondPart + 1; | ||
} | ||
if (secondPart > 0x000fffff) { | ||
console.warn("This platform does not support BigInt"); | ||
} | ||
return sign * (firstPart + secondPart * 2 ** 32); | ||
} | ||
}; | ||
export { | ||
@@ -196,3 +220,4 @@ crc8, | ||
bytesToString, | ||
readInt64le, | ||
BitReader, | ||
}; |
156571
3708