Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,379 changes: 688 additions & 691 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"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.",
"main": "index.js",
"types": "index.d.ts",
Expand Down Expand Up @@ -37,8 +37,8 @@
"sideEffects": false,
"homepage": "https://github.com/eshaz/codec-parser#readme",
"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"
}
}
36 changes: 31 additions & 5 deletions src/CodecParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
parseFrame,
checkCodecUpdate,
reset,
isLastPage,
} from "./constants.js";
import HeaderCache from "./codecs/HeaderCache.js";
import MPEGParser from "./codecs/mpeg/MPEGParser.js";
Expand Down Expand Up @@ -229,12 +230,37 @@ export default class CodecParser {
[mapFrameStats](frame) {
if (frame[codecFrames]) {
// 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;
frame[totalDuration] =
(this._totalSamples / this._sampleRate) * 1000 || 0;
Expand Down
5 changes: 2 additions & 3 deletions src/codecs/flac/FLACParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,8 @@ export default class FLACParser extends Parser {
))
) {
// found a valid next frame header
let frameData = yield* this._codecParser[readRawData](
nextHeaderOffset,
);
let frameData =
yield* this._codecParser[readRawData](nextHeaderOffset);

if (!this._codecParser._flushing)
frameData = frameData[subarray](0, nextHeaderOffset);
Expand Down
9 changes: 2 additions & 7 deletions src/codecs/opus/OpusFrame.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@
along with this program. If not, see <https://www.gnu.org/licenses/>
*/

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);
}
}
22 changes: 21 additions & 1 deletion src/codecs/opus/OpusParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import {
parseOggPage,
enable,
getHeaderFromUint8Array,
preSkip,
frameSize,
frameCount,
sampleRate,
} from "../../constants.js";
import Parser from "../Parser.js";
import OpusFrame from "./OpusFrame.js";
Expand All @@ -40,6 +44,7 @@ export default class OpusParser extends Parser {

onCodec(this[codec]);
this._identificationHeader = null;
this._preSkipRemaining = null;
}

get [codec]() {
Expand Down Expand Up @@ -67,7 +72,22 @@ export default class OpusParser extends Parser {
this._headerCache,
);

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](
"Failed to parse Ogg Opus Header",
Expand Down
9 changes: 2 additions & 7 deletions src/containers/ogg/OggPageHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import {
uint8Array,
dataView,
} from "../../constants.js";
import { readInt64le } from "../../utilities.js";

export default class OggPageHeader {
static *[getHeader](codecParser, headerCache, readOffset) {
Expand Down Expand Up @@ -111,13 +112,7 @@ export default class OggPageHeader {
// Byte (7-14 of 28)
// * `FFFFFFFF|FFFFFFFF|FFFFFFFF|FFFFFFFF|FFFFFFFF|FFFFFFFF|FFFFFFFF|FFFFFFFF`
// * Absolute Granule Position

/**
* @todo Safari does not support getBigInt64, but it also doesn't support Ogg
*/
try {
header[absoluteGranulePosition] = view.getBigInt64(6, true);
} catch {}
header[absoluteGranulePosition] = readInt64le(view, 6);

// Byte (15-18 of 28)
// * `GGGGGGGG|GGGGGGGG|GGGGGGGG|GGGGGGGG`
Expand Down
13 changes: 13 additions & 0 deletions src/containers/ogg/OggParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
codec,
data,
length,
samples,
segments,
subarray,
vorbis,
Expand All @@ -38,6 +39,7 @@ import {
uint8Array,
isLastPage,
streamSerialNumber,
absoluteGranulePosition,
} from "../../constants.js";

import Parser from "../../codecs/Parser.js";
Expand All @@ -57,6 +59,7 @@ class OggStream {
this._continuedPacket = new uint8Array();
this._codec = null;
this._isSupported = null;
this._previousAbsoluteGranulePosition = null;
}

get [codec]() {
Expand Down Expand Up @@ -149,6 +152,16 @@ class OggStream {
);
}

// 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) {
const frame = this._parser[parseOggPage](oggPage);
this._codecParser[mapFrameStats](frame);
Expand Down
25 changes: 25 additions & 0 deletions src/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,37 @@ class BitReader {
}
}

/**
* @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 {
crc8,
flacCrc16,
crc32Function,
reverse,
concatBuffers,
bytesToString,
readInt64le,
BitReader,
};
3 changes: 2 additions & 1 deletion test/CodecParser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ describe("CodecParser", () => {
testParser("ogg.opus.framesize_60", mimeType, "opus", 251, 0);
testParser("ogg.opus.surround", mimeType, "opus", 737, 0);
testParser("ogg.opus.channel_family_255", mimeType, "opus", 284, 0);
testParser("ogg.opus.last_page_trimming", mimeType, "opus", 51, 0);
});

describe("Ogg Vorbis", () => {
Expand All @@ -322,7 +323,7 @@ describe("CodecParser", () => {
"ogg.vorbis.setup_packets_separate_pages",
mimeType,
"vorbis",
118,
119,
);
testParser("metronome2.vorbis", mimeType, "vorbis", 3);
});
Expand Down
Loading