Skip to content

Commit

Permalink
Merge pull request #26 from eshaz/fix-vorbis-setup-packet
Browse files Browse the repository at this point in the history
Vorbis Comment / Setup packet parsing
  • Loading branch information
eshaz committed Jun 9, 2023
2 parents c65ac5e + 17a5324 commit c0cdff1
Show file tree
Hide file tree
Showing 10 changed files with 34,349 additions and 23,189 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codec-parser",
"version": "2.4.1",
"version": "2.4.2",
"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
60 changes: 33 additions & 27 deletions src/codecs/vorbis/VorbisParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default class VorbisParser extends Parser {
onCodec(this[codec]);

this._identificationHeader = null;
this._setupComplete = false;

this._mode = {
count: 0,
Expand All @@ -58,23 +59,26 @@ export default class VorbisParser extends Parser {
}

[parseOggPage](oggPage) {
const oggPageSegments = frameStore.get(oggPage)[segments];

if (oggPage[pageSequenceNumber] === 0) {
// Identification header

this._headerCache[enable]();
this._identificationHeader = oggPage[data];
} else if (oggPage[pageSequenceNumber] === 1) {
// gather WEBM CodecPrivate data
if (oggPageSegments[1]) {
this._vorbisComments = oggPageSegments[0];
this._vorbisSetup = oggPageSegments[1];

this._mode = this._parseSetupHeader(oggPageSegments[1]);
}
} else {
oggPage[codecFrames] = oggPageSegments.map((segment) => {
oggPage[codecFrames] = [];

for (const oggPageSegment of frameStore.get(oggPage)[segments]) {
if (oggPageSegment[0] === 1) {
// Identification header

this._headerCache[enable]();
this._identificationHeader = oggPage[data];
this._setupComplete = false;
} else if (oggPageSegment[0] === 3) {
// comment header

this._vorbisComments = oggPageSegment;
} else if (oggPageSegment[0] === 5) {
// setup header

this._vorbisSetup = oggPageSegment;
this._mode = this._parseSetupHeader(oggPageSegment);
this._setupComplete = true;
} else if (this._setupComplete) {
const header = VorbisHeader[getHeaderFromUint8Array](
this._identificationHeader,
this._headerCache,
Expand All @@ -83,18 +87,20 @@ export default class VorbisParser extends Parser {
);

if (header) {
return new VorbisFrame(
segment,
header,
this._getSamples(segment, header)
oggPage[codecFrames].push(
new VorbisFrame(
oggPageSegment,
header,
this._getSamples(oggPageSegment, header)
)
);
} else {
this._codecParser[logError](
"Failed to parse Ogg Vorbis Header",
"Not a valid Ogg Vorbis file"
);
}

this._codecParser[logError](
"Failed to parse Ogg Vorbis Header",
"Not a valid Ogg Vorbis file"
);
});
}
}

return oggPage;
Expand Down
73 changes: 58 additions & 15 deletions src/containers/ogg/OggParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import {
parseOggPage,
reset,
uint8Array,
isLastPage,
streamSerialNumber,
} from "../../constants.js";

import Parser from "../../codecs/Parser.js";
Expand All @@ -46,17 +48,15 @@ import FLACParser from "../../codecs/flac/FLACParser.js";
import OpusParser from "../../codecs/opus/OpusParser.js";
import VorbisParser from "../../codecs/vorbis/VorbisParser.js";

export default class OggParser extends Parser {
class OggStream {
constructor(codecParser, headerCache, onCodec) {
super(codecParser, headerCache);

this._codecParser = codecParser;
this._headerCache = headerCache;
this._onCodec = onCodec;
this.Frame = OggPage;
this.Header = OggPageHeader;
this._codec = null;
this._continuedPacket = new uint8Array();

this._pageSequenceNumber = 0;
this._continuedPacket = new uint8Array();
this._codec = null;
this._isSupported = null;
}

get [codec]() {
Expand All @@ -75,13 +75,11 @@ export default class OggParser extends Parser {
}
}

_checkForIdentifier({ data }) {
_checkCodecSupport({ data }) {
const idString = bytesToString(data[subarray](0, 8));

switch (idString) {
case "fishead\0":
case "fisbone\0":
case "index\0\0\0":
return false; // ignore ogg skeleton packets
case "OpusHead":
this._updateCodec("opus", OpusParser);
Expand All @@ -92,6 +90,8 @@ export default class OggParser extends Parser {
case /^\x01vorbis/.test(idString) && idString:
this._updateCodec(vorbis, VorbisParser);
return true;
default:
return false;
}
}

Expand All @@ -112,16 +112,18 @@ export default class OggParser extends Parser {
this._pageSequenceNumber = oggPage[pageSequenceNumber];
}

*[parseFrame]() {
const oggPage = yield* this[fixedLengthFrameSync](true);
_parsePage(oggPage) {
if (this._isSupported === null) {
this._pageSequenceNumber = oggPage[pageSequenceNumber];
this._isSupported = this._checkCodecSupport(oggPage);
}

this._checkPageSequenceNumber(oggPage);

const oggPageStore = frameStore.get(oggPage);
const headerData = headerStore.get(oggPageStore[header]);

let offset = 0;

oggPageStore[segments] = headerData[pageSegmentTable].map((segmentLength) =>
oggPage[data][subarray](offset, (offset += segmentLength))
);
Expand All @@ -147,10 +149,51 @@ export default class OggParser extends Parser {
);
}

if (this._codec || this._checkForIdentifier(oggPage)) {
if (this._isSupported) {
const frame = this._parser[parseOggPage](oggPage);
this._codecParser[mapFrameStats](frame);

return frame;
} else {
return oggPage;
}
}
}

export default class OggParser extends Parser {
constructor(codecParser, headerCache, onCodec) {
super(codecParser, headerCache);

this._onCodec = onCodec;
this.Frame = OggPage;
this.Header = OggPageHeader;

this._streams = new Map();
this._currentSerialNumber = null;
}

get [codec]() {
const oggStream = this._streams.get(this._currentSerialNumber);

return oggStream ? oggStream.codec : "";
}

*[parseFrame]() {
const oggPage = yield* this[fixedLengthFrameSync](true);
this._currentSerialNumber = oggPage[streamSerialNumber];

let oggStream = this._streams.get(this._currentSerialNumber);
if (!oggStream) {
oggStream = new OggStream(
this._codecParser,
this._headerCache,
this._onCodec
);
this._streams.set(this._currentSerialNumber, oggStream);
}

if (oggPage[isLastPage]) this._streams.delete(this._currentSerialNumber);

return oggStream._parsePage(oggPage);
}
}
14 changes: 8 additions & 6 deletions test/CodecParser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,7 @@ describe("CodecParser", () => {
const codecParser = new CodecParser(mimeType);

file.set([0, 0, 0, 0], 0x0);
const frames = [
...codecParser.parseChunk(file.subarray(0x0, 0x2e8c)),
];
const frames = [...codecParser.parseAll(file.subarray(0x0, 46))];

expect(frames).toEqual([]);
}
Expand All @@ -236,9 +234,7 @@ describe("CodecParser", () => {
const codecParser = new CodecParser(mimeType);

file.set([0b00001000], 0x5);
const frames = [
...codecParser.parseChunk(file.subarray(0x0, 0x2e8c)),
];
const frames = [...codecParser.parseAll(file.subarray(0x0, 46))];

expect(frames).toEqual([]);
}
Expand Down Expand Up @@ -269,6 +265,12 @@ describe("CodecParser", () => {
testParser("ogg.vorbis.extra_metadata", mimeType, "vorbis", 1647);
testParser("ogg.vorbis.fishead", mimeType, "vorbis", 1365);
testParser("ogg.vorbis.continued", mimeType, "vorbis", 1151);
testParser(
"ogg.vorbis.setup_packets_separate_pages",
mimeType,
"vorbis",
118
);
});
});

Expand Down
Loading

0 comments on commit c0cdff1

Please sign in to comment.