-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathogg-opus-demuxer.js
More file actions
126 lines (108 loc) · 3.38 KB
/
ogg-opus-demuxer.js
File metadata and controls
126 lines (108 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"use strict";
const OGG_PAGE_HEADER_SIZE = 26;
class BufferStream {
constructor(buffer) {
this._buffer = buffer;
this._offset = 0;
}
get ended() { return this._offset >= this._buffer.length; }
get available() { return this._buffer.length - this._offset; }
skip(bytes) { this.read(bytes); }
read(bytes) {
if (bytes <= 0) return null;
if (this.ended) return null;
if (this._offset + bytes > this._buffer.length) return null;
var offset = this._offset;
this._offset += bytes;
return this._buffer.slice(offset, offset + bytes);
}
readByte() { return (this.read(1) || [null])[0]; }
readString(size) {
var v = this.read(size);
return v ? v.toString("utf8", 0) : null;
}
readInt32LE() {
var v = this.read(4);
return v ? v.readInt32LE(0) : null;
}
}
const Transform = require("stream").Transform;
class OggOpusDemuxer extends Transform {
constructor() {
super({
writableObjectMode: true,
readableObjectMode: true
});
}
readSegments(reader) {
var tableSize = reader.readByte();
if (reader.available < tableSize) return null;
var segmentSizes = [];
for (var i = 0; i < tableSize; ) {
var read = reader.readByte(); i++;
var size = read;
while (read === 255) {
read = reader.readByte(); i++;
size += read;
}
segmentSizes.push(size);
}
var dataSize = segmentSizes.reduce((c, n) => c + n, 0);
if (reader.available < dataSize) return null;
return segmentSizes.map(size => reader.read(size));
}
readPage(reader, done) {
if (reader.available < OGG_PAGE_HEADER_SIZE) return false;
var magic = reader.readString(4);
if (magic !== "OggS")
return new Error("OGG magic does not match");
var version = reader.readByte();
var headerType = reader.readByte();
var isContinuation = headerType & (1 << 0);
if (isContinuation)
return new Error("OGG page continuation handling not implemented");
var isBeginning = headerType & (1 << 1);
var isEnd = headerType & (1 << 2);
reader.skip(8); // granule position
reader.skip(4); // stream serial number
var pageSeq = reader.readInt32LE();
reader.skip(4); // checksum
var segments = this.readSegments(reader);
if (segments == null) return false;
if (segments.indexOf(null) >= 0) return false;
var packets = [];
for (var segment of segments) {
var header = segment.toString("utf8", 0, 8);
if (header === "OpusHead") {
this._opusHeader = segment;
this.emit("OpusHead", this._opusHeader);
} else if (header === "OpusTags") {
this._opusTags = segment;
this.emit("OpusTags", this._opusTags);
} else packets.push(segment);
}
if (!this._opusHeader) return true;
packets.forEach(packet => this.push(packet));
return true;
}
_transform(chunk, encoding, done) {
if (this._leftover) {
chunk = Buffer.concat([this._leftover, chunk]);
this._leftover = null;
}
var r = new BufferStream(chunk);
while(!r.ended) {
// save current position if page reading fails
var offset = r._offset;
var ok = this.readPage(r, done);
if (ok instanceof Error) return done(ok);
if (!ok) {
// save remaining buffer
this._leftover = chunk.slice(offset, chunk.length);
break;
}
}
done();
}
}
module.exports = OggOpusDemuxer;