Skip to content

Commit c0cdff1

Browse files
authored
Merge pull request #26 from eshaz/fix-vorbis-setup-packet
Vorbis Comment / Setup packet parsing
2 parents c65ac5e + 17a5324 commit c0cdff1

10 files changed

+34349
-23189
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codec-parser",
3-
"version": "2.4.1",
3+
"version": "2.4.2",
44
"description": "Library that parses raw data from audio codecs into frames containing data, header values, duration, and other information.",
55
"main": "index.js",
66
"types": "index.d.ts",

src/codecs/vorbis/VorbisParser.js

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export default class VorbisParser extends Parser {
4545
onCodec(this[codec]);
4646

4747
this._identificationHeader = null;
48+
this._setupComplete = false;
4849

4950
this._mode = {
5051
count: 0,
@@ -58,23 +59,26 @@ export default class VorbisParser extends Parser {
5859
}
5960

6061
[parseOggPage](oggPage) {
61-
const oggPageSegments = frameStore.get(oggPage)[segments];
62-
63-
if (oggPage[pageSequenceNumber] === 0) {
64-
// Identification header
65-
66-
this._headerCache[enable]();
67-
this._identificationHeader = oggPage[data];
68-
} else if (oggPage[pageSequenceNumber] === 1) {
69-
// gather WEBM CodecPrivate data
70-
if (oggPageSegments[1]) {
71-
this._vorbisComments = oggPageSegments[0];
72-
this._vorbisSetup = oggPageSegments[1];
73-
74-
this._mode = this._parseSetupHeader(oggPageSegments[1]);
75-
}
76-
} else {
77-
oggPage[codecFrames] = oggPageSegments.map((segment) => {
62+
oggPage[codecFrames] = [];
63+
64+
for (const oggPageSegment of frameStore.get(oggPage)[segments]) {
65+
if (oggPageSegment[0] === 1) {
66+
// Identification header
67+
68+
this._headerCache[enable]();
69+
this._identificationHeader = oggPage[data];
70+
this._setupComplete = false;
71+
} else if (oggPageSegment[0] === 3) {
72+
// comment header
73+
74+
this._vorbisComments = oggPageSegment;
75+
} else if (oggPageSegment[0] === 5) {
76+
// setup header
77+
78+
this._vorbisSetup = oggPageSegment;
79+
this._mode = this._parseSetupHeader(oggPageSegment);
80+
this._setupComplete = true;
81+
} else if (this._setupComplete) {
7882
const header = VorbisHeader[getHeaderFromUint8Array](
7983
this._identificationHeader,
8084
this._headerCache,
@@ -83,18 +87,20 @@ export default class VorbisParser extends Parser {
8387
);
8488

8589
if (header) {
86-
return new VorbisFrame(
87-
segment,
88-
header,
89-
this._getSamples(segment, header)
90+
oggPage[codecFrames].push(
91+
new VorbisFrame(
92+
oggPageSegment,
93+
header,
94+
this._getSamples(oggPageSegment, header)
95+
)
96+
);
97+
} else {
98+
this._codecParser[logError](
99+
"Failed to parse Ogg Vorbis Header",
100+
"Not a valid Ogg Vorbis file"
90101
);
91102
}
92-
93-
this._codecParser[logError](
94-
"Failed to parse Ogg Vorbis Header",
95-
"Not a valid Ogg Vorbis file"
96-
);
97-
});
103+
}
98104
}
99105

100106
return oggPage;

src/containers/ogg/OggParser.js

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import {
3636
parseOggPage,
3737
reset,
3838
uint8Array,
39+
isLastPage,
40+
streamSerialNumber,
3941
} from "../../constants.js";
4042

4143
import Parser from "../../codecs/Parser.js";
@@ -46,17 +48,15 @@ import FLACParser from "../../codecs/flac/FLACParser.js";
4648
import OpusParser from "../../codecs/opus/OpusParser.js";
4749
import VorbisParser from "../../codecs/vorbis/VorbisParser.js";
4850

49-
export default class OggParser extends Parser {
51+
class OggStream {
5052
constructor(codecParser, headerCache, onCodec) {
51-
super(codecParser, headerCache);
52-
53+
this._codecParser = codecParser;
54+
this._headerCache = headerCache;
5355
this._onCodec = onCodec;
54-
this.Frame = OggPage;
55-
this.Header = OggPageHeader;
56-
this._codec = null;
57-
this._continuedPacket = new uint8Array();
5856

59-
this._pageSequenceNumber = 0;
57+
this._continuedPacket = new uint8Array();
58+
this._codec = null;
59+
this._isSupported = null;
6060
}
6161

6262
get [codec]() {
@@ -75,13 +75,11 @@ export default class OggParser extends Parser {
7575
}
7676
}
7777

78-
_checkForIdentifier({ data }) {
78+
_checkCodecSupport({ data }) {
7979
const idString = bytesToString(data[subarray](0, 8));
8080

8181
switch (idString) {
8282
case "fishead\0":
83-
case "fisbone\0":
84-
case "index\0\0\0":
8583
return false; // ignore ogg skeleton packets
8684
case "OpusHead":
8785
this._updateCodec("opus", OpusParser);
@@ -92,6 +90,8 @@ export default class OggParser extends Parser {
9290
case /^\x01vorbis/.test(idString) && idString:
9391
this._updateCodec(vorbis, VorbisParser);
9492
return true;
93+
default:
94+
return false;
9595
}
9696
}
9797

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

115-
*[parseFrame]() {
116-
const oggPage = yield* this[fixedLengthFrameSync](true);
115+
_parsePage(oggPage) {
116+
if (this._isSupported === null) {
117+
this._pageSequenceNumber = oggPage[pageSequenceNumber];
118+
this._isSupported = this._checkCodecSupport(oggPage);
119+
}
117120

118121
this._checkPageSequenceNumber(oggPage);
119122

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

123126
let offset = 0;
124-
125127
oggPageStore[segments] = headerData[pageSegmentTable].map((segmentLength) =>
126128
oggPage[data][subarray](offset, (offset += segmentLength))
127129
);
@@ -147,10 +149,51 @@ export default class OggParser extends Parser {
147149
);
148150
}
149151

150-
if (this._codec || this._checkForIdentifier(oggPage)) {
152+
if (this._isSupported) {
151153
const frame = this._parser[parseOggPage](oggPage);
152154
this._codecParser[mapFrameStats](frame);
155+
153156
return frame;
157+
} else {
158+
return oggPage;
154159
}
155160
}
156161
}
162+
163+
export default class OggParser extends Parser {
164+
constructor(codecParser, headerCache, onCodec) {
165+
super(codecParser, headerCache);
166+
167+
this._onCodec = onCodec;
168+
this.Frame = OggPage;
169+
this.Header = OggPageHeader;
170+
171+
this._streams = new Map();
172+
this._currentSerialNumber = null;
173+
}
174+
175+
get [codec]() {
176+
const oggStream = this._streams.get(this._currentSerialNumber);
177+
178+
return oggStream ? oggStream.codec : "";
179+
}
180+
181+
*[parseFrame]() {
182+
const oggPage = yield* this[fixedLengthFrameSync](true);
183+
this._currentSerialNumber = oggPage[streamSerialNumber];
184+
185+
let oggStream = this._streams.get(this._currentSerialNumber);
186+
if (!oggStream) {
187+
oggStream = new OggStream(
188+
this._codecParser,
189+
this._headerCache,
190+
this._onCodec
191+
);
192+
this._streams.set(this._currentSerialNumber, oggStream);
193+
}
194+
195+
if (oggPage[isLastPage]) this._streams.delete(this._currentSerialNumber);
196+
197+
return oggStream._parsePage(oggPage);
198+
}
199+
}

test/CodecParser.test.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,7 @@ describe("CodecParser", () => {
220220
const codecParser = new CodecParser(mimeType);
221221

222222
file.set([0, 0, 0, 0], 0x0);
223-
const frames = [
224-
...codecParser.parseChunk(file.subarray(0x0, 0x2e8c)),
225-
];
223+
const frames = [...codecParser.parseAll(file.subarray(0x0, 46))];
226224

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

238236
file.set([0b00001000], 0x5);
239-
const frames = [
240-
...codecParser.parseChunk(file.subarray(0x0, 0x2e8c)),
241-
];
237+
const frames = [...codecParser.parseAll(file.subarray(0x0, 46))];
242238

243239
expect(frames).toEqual([]);
244240
}
@@ -269,6 +265,12 @@ describe("CodecParser", () => {
269265
testParser("ogg.vorbis.extra_metadata", mimeType, "vorbis", 1647);
270266
testParser("ogg.vorbis.fishead", mimeType, "vorbis", 1365);
271267
testParser("ogg.vorbis.continued", mimeType, "vorbis", 1151);
268+
testParser(
269+
"ogg.vorbis.setup_packets_separate_pages",
270+
mimeType,
271+
"vorbis",
272+
118
273+
);
272274
});
273275
});
274276

0 commit comments

Comments
 (0)