summaryrefslogtreecommitdiff
path: root/node_modules/prism-media/src/opus/OggOpus.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/prism-media/src/opus/OggOpus.js')
-rw-r--r--node_modules/prism-media/src/opus/OggOpus.js90
1 files changed, 90 insertions, 0 deletions
diff --git a/node_modules/prism-media/src/opus/OggOpus.js b/node_modules/prism-media/src/opus/OggOpus.js
new file mode 100644
index 0000000..9a938cd
--- /dev/null
+++ b/node_modules/prism-media/src/opus/OggOpus.js
@@ -0,0 +1,90 @@
+const { Transform } = require('stream');
+
+const OGG_PAGE_HEADER_SIZE = 26;
+const STREAM_STRUCTURE_VERSION = 0;
+
+const OGGS_HEADER = Buffer.from('OggS'.split('').map(x => x.charCodeAt(0)));
+const OPUS_HEAD = Buffer.from('OpusHead'.split('').map(x => x.charCodeAt(0)));
+const OPUS_TAGS = Buffer.from('OpusTags'.split('').map(x => x.charCodeAt(0)));
+
+class OggOpusTransform extends Transform {
+ constructor() {
+ super();
+ this._remainder = null;
+ this._head = null;
+ }
+
+ _transform(chunk, encoding, done) {
+ if (this._remainder) {
+ chunk = Buffer.concat([this._remainder, chunk]);
+ this._remainder = null;
+ }
+
+ while (chunk) {
+ try {
+ const result = this.readPage(chunk);
+ if (result) chunk = result;
+ else break;
+ } catch (err) {
+ this.emit('error', err);
+ }
+ }
+ this._remainder = chunk;
+ done();
+ }
+
+ /**
+ * Reads a page from a buffer
+ * @param {Buffer} chunk The chunk containing the page
+ * @returns {boolean|Buffer}
+ */
+ readPage(chunk) {
+ if (chunk.length < OGG_PAGE_HEADER_SIZE) {
+ return false;
+ }
+ if (!chunk.slice(0, 4).equals(OGGS_HEADER)) {
+ throw Error(`capture_pattern is not ${OGGS_HEADER}`);
+ }
+ if (chunk.readUInt8(4) !== STREAM_STRUCTURE_VERSION) {
+ throw Error(`stream_structure_version is not ${STREAM_STRUCTURE_VERSION}`);
+ }
+
+ const pageSegments = chunk.readUInt8(26),
+ table = chunk.slice(27, 27 + pageSegments);
+
+ let sizes = [], totalSize = 0;
+
+ for (let i = 0; i < pageSegments;) {
+ let size = 0, x = 255;
+ while (x === 255) {
+ x = table.readUInt8(i);
+ i++;
+ size += x;
+ }
+ sizes.push(size);
+ totalSize += size;
+ }
+
+ if (chunk.length < 27 + pageSegments + totalSize) {
+ return false;
+ }
+
+ let start = 27 + pageSegments;
+ for (const size of sizes) {
+ const segment = chunk.slice(start, start + size);
+ const header = segment.slice(0, 8);
+ if (this._head) {
+ if (header.equals(OPUS_TAGS)) this.emit('opusTags', segment);
+ else this.push(segment);
+ } else if (header.equals(OPUS_HEAD)) {
+ this._head = segment;
+ } else {
+ throw Error(`Invalid segment ${segment}`);
+ }
+ start += size;
+ }
+ return chunk.slice(start);
+ }
+}
+
+module.exports = OggOpusTransform;