From b4e85d51497a46d550b773537776b38a42407ee2 Mon Sep 17 00:00:00 2001 From: liufulong Date: Sat, 15 Jan 2022 18:53:03 +0800 Subject: [PATCH] fix browser apply buffer error --- lib/ebml/EBML.d.ts | 103 ++ lib/ebml/EBML.js | 2 + lib/ebml/EBMLDecoder.d.ts | 34 + lib/ebml/EBMLDecoder.js | 239 +++ lib/ebml/EBMLEncoder.d.ts | 14 + lib/ebml/EBMLEncoder.js | 126 ++ lib/ebml/EBMLReader.d.ts | 123 ++ lib/ebml/EBMLReader.js | 360 +++++ lib/ebml/ebmlID.d.ts | 2041 +++++++++++++++++++++++++ lib/ebml/ebmlID.js | 2051 ++++++++++++++++++++++++++ lib/ebml/index.d.ts | 6 + lib/ebml/index.js | 22 + lib/ebml/tools-ebml.d.ts | 80 + lib/ebml/tools-ebml.js | 265 ++++ lib/ebml/tools.d.ts | 99 ++ lib/ebml/tools.js | 668 +++++++++ lib/index.d.ts | 8 + lib/index.js | 85 ++ package.json | 30 +- src/ebml/EBML.ts | 108 ++ src/ebml/EBMLDecoder.ts | 258 ++++ src/ebml/EBMLEncoder.ts | 146 ++ src/ebml/EBMLReader.ts | 377 +++++ src/ebml/ebmlID.ts | 2048 +++++++++++++++++++++++++ src/ebml/index.ts | 12 + src/ebml/tools-ebml.ts | 264 ++++ src/ebml/tools.ts | 703 +++++++++ webm-duration-fix.js => src/index.ts | 6 +- tsconfig.json | 74 + webm-duration-fix.d.ts | 6 - yarn.lock | 1490 +++++++++++++++++++ 31 files changed, 11834 insertions(+), 14 deletions(-) create mode 100644 lib/ebml/EBML.d.ts create mode 100644 lib/ebml/EBML.js create mode 100644 lib/ebml/EBMLDecoder.d.ts create mode 100644 lib/ebml/EBMLDecoder.js create mode 100644 lib/ebml/EBMLEncoder.d.ts create mode 100644 lib/ebml/EBMLEncoder.js create mode 100644 lib/ebml/EBMLReader.d.ts create mode 100644 lib/ebml/EBMLReader.js create mode 100644 lib/ebml/ebmlID.d.ts create mode 100644 lib/ebml/ebmlID.js create mode 100644 lib/ebml/index.d.ts create mode 100644 lib/ebml/index.js create mode 100644 lib/ebml/tools-ebml.d.ts create mode 100644 lib/ebml/tools-ebml.js create mode 100644 lib/ebml/tools.d.ts create mode 100644 lib/ebml/tools.js create mode 100644 lib/index.d.ts create mode 100644 lib/index.js create mode 100644 src/ebml/EBML.ts create mode 100644 src/ebml/EBMLDecoder.ts create mode 100644 src/ebml/EBMLEncoder.ts create mode 100644 src/ebml/EBMLReader.ts create mode 100644 src/ebml/ebmlID.ts create mode 100644 src/ebml/index.ts create mode 100644 src/ebml/tools-ebml.ts create mode 100644 src/ebml/tools.ts rename webm-duration-fix.js => src/index.ts (85%) create mode 100644 tsconfig.json delete mode 100644 webm-duration-fix.d.ts create mode 100644 yarn.lock diff --git a/lib/ebml/EBML.d.ts b/lib/ebml/EBML.d.ts new file mode 100644 index 0000000..0bae83b --- /dev/null +++ b/lib/ebml/EBML.d.ts @@ -0,0 +1,103 @@ +/// +export declare type ChildElementsValue = NumberElement | StringElement | BinaryElement | DateElement; +export declare type EBMLElementValue = MasterElement | ChildElementsValue; +export declare type ChildElementBuffer = ChildElement & { + data: Buffer; +}; +export declare type EBMLElementBuffer = MasterElement | ChildElementBuffer; +export declare type EBMLElementBufferValue = MasterElement | (ChildElementsValue & { + data: Buffer; +}); +export declare type EBMLElementDetail = (MasterElement | (ChildElementsValue & { + data: Buffer; +})) & ElementDetail; +export interface IElement { + name: string; + type: "m" | "u" | "i" | "f" | "s" | "8" | "b" | "d"; +} +export interface ChildElement extends IElement { + type: "u" | "i" | "f" | "s" | "8" | "b" | "d"; +} +export interface MasterElement extends IElement { + type: "m"; + isEnd: boolean; + unknownSize?: boolean; +} +export interface ChildElementValue extends ChildElement { + value: any; +} +export interface NumberElement extends ChildElementValue { + type: "u" | "i" | "f"; + value: number; +} +export interface StringElement extends ChildElementValue { + type: "s" | "8"; + value: string; +} +export interface BinaryElement extends ChildElementValue { + type: "b"; + value: Buffer; +} +export interface DateElement extends ChildElementValue { + type: "d"; + /** + * Date - signed 8 octets integer in nanoseconds with 0 indicating the precise + * beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC) + */ + value: Date; +} +export interface ElementDetail { + schema: Schema; + /** + * hex EBML ID + */ + EBML_ID: string; + /** + * The level within an EBML tree that the element may occur at. + * + is for a recursive level (can be its own child). + * g: global element (can be found at any level) + */ + level: number; + /** + * このタグのバッファ全体における開始オフセット位置 + */ + tagStart: number; + /** + * このタグのバッファ全体における終了オフセット位置 + */ + tagEnd: number; + /** + * size vint start + */ + sizeStart: number; + /** + * size vint end + */ + sizeEnd: number; + /** + * 要素の中身の開始位置 + */ + dataStart: number; + /** + * 要素の中身の終了位置 + */ + dataEnd: number; + /** + * dataEnd - dataStart + */ + dataSize: number; +} +export interface SimpleBlock { + discardable: boolean; + frames: Buffer[]; + invisible: boolean; + keyframe: boolean; + timecode: number; + trackNumber: number; +} +export interface Schema { + name: string; + level: number; + type: string; + description: string; +} diff --git a/lib/ebml/EBML.js b/lib/ebml/EBML.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/ebml/EBML.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/ebml/EBMLDecoder.d.ts b/lib/ebml/EBMLDecoder.d.ts new file mode 100644 index 0000000..83a1233 --- /dev/null +++ b/lib/ebml/EBMLDecoder.d.ts @@ -0,0 +1,34 @@ +import * as EBML from "./EBML"; +export default class EBMLDecoder { + private _buffer; + private _tag_stack; + private _state; + /** + * _buffer の先頭からの位置 + */ + private _cursor; + /** + * 全体におけるポインタ + */ + private _total; + private _schema; + private _result; + constructor(); + decode(chunk: ArrayBuffer): EBML.EBMLElementDetail[]; + private readChunk; + private getSchemaInfo; + /** + * vint された parsing tag + * @return - return false when waiting for more data + */ + private readTag; + /** + * vint された現在のタグの内容の大きさを読み込む + * @return - return false when waiting for more data + */ + private readSize; + /** + * データ読み込み + */ + private readContent; +} diff --git a/lib/ebml/EBMLDecoder.js b/lib/ebml/EBMLDecoder.js new file mode 100644 index 0000000..130b0c9 --- /dev/null +++ b/lib/ebml/EBMLDecoder.js @@ -0,0 +1,239 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var int64_buffer_1 = require("int64-buffer"); +var tools_1 = require("./tools"); +var tools = require("./tools"); +var ebmlID_1 = require("./ebmlID"); +var State; +(function (State) { + State[State["STATE_TAG"] = 1] = "STATE_TAG"; + State[State["STATE_SIZE"] = 2] = "STATE_SIZE"; + State[State["STATE_CONTENT"] = 3] = "STATE_CONTENT"; +})(State || (State = {})); +var EBMLDecoder = /** @class */ (function () { + function EBMLDecoder() { + this._buffer = new tools_1.Buffer(0); + this._tag_stack = []; + this._state = State.STATE_TAG; + this._cursor = 0; + this._total = 0; + this._schema = ebmlID_1.byEbmlID; + this._result = []; + } + EBMLDecoder.prototype.decode = function (chunk) { + this.readChunk(chunk); + var diff = this._result; + this._result = []; + return diff; + }; + EBMLDecoder.prototype.readChunk = function (chunk) { + // 読みかけの(読めなかった) this._buffer と 新しい chunk を合わせて読み直す + this._buffer = tools.concat([this._buffer, new tools_1.Buffer(chunk)]); + while (this._cursor < this._buffer.length) { + // console.log(this._cursor, this._total, this._tag_stack); + if (this._state === State.STATE_TAG && !this.readTag()) { + break; + } + if (this._state === State.STATE_SIZE && !this.readSize()) { + break; + } + if (this._state === State.STATE_CONTENT && !this.readContent()) { + break; + } + } + }; + EBMLDecoder.prototype.getSchemaInfo = function (tagNum) { + return this._schema[tagNum] || { + name: "unknown", + level: -1, + type: "unknown", + description: "unknown" + }; + }; + /** + * vint された parsing tag + * @return - return false when waiting for more data + */ + EBMLDecoder.prototype.readTag = function () { + // tag.length が buffer の外にある + if (this._cursor >= this._buffer.length) { + return false; + } + // read ebml id vint without first byte + var tag = (0, tools_1.readVint)(this._buffer, this._cursor); + // tag が読めなかった + if (tag == null) { + return false; + } + // >>>>>>>>> + // tag 識別子 + //const tagStr = this._buffer.toString("hex", this._cursor, this._cursor + tag.length); + //const tagNum = parseInt(tagStr, 16); + // 上と等価 + var buf = this._buffer.slice(this._cursor, this._cursor + tag.length); + var tagNum = buf.reduce(function (o, v, i, arr) { return o + v * Math.pow(16, 2 * (arr.length - 1 - i)); }, 0); + var schema = this.getSchemaInfo(tagNum); + var tagObj = { + EBML_ID: tagNum.toString(16), + schema: schema, + type: schema.type, + name: schema.name, + level: schema.level, + tagStart: this._total, + tagEnd: this._total + tag.length, + sizeStart: this._total + tag.length, + sizeEnd: null, + dataStart: null, + dataEnd: null, + dataSize: null, + data: null + }; + // | tag: vint | size: vint | data: Buffer(size) | + this._tag_stack.push(tagObj); + // <<<<<<<< + // ポインタを進める + this._cursor += tag.length; + this._total += tag.length; + // 読み込み状態変更 + this._state = State.STATE_SIZE; + return true; + }; + /** + * vint された現在のタグの内容の大きさを読み込む + * @return - return false when waiting for more data + */ + EBMLDecoder.prototype.readSize = function () { + // tag.length が buffer の外にある + if (this._cursor >= this._buffer.length) { + return false; + } + // read ebml datasize vint without first byte + var size = (0, tools_1.readVint)(this._buffer, this._cursor); + // まだ読めない + if (size == null) { + return false; + } + // >>>>>>>>> + // current tag の data size 決定 + var tagObj = this._tag_stack[this._tag_stack.length - 1]; + tagObj.sizeEnd = tagObj.sizeStart + size.length; + tagObj.dataStart = tagObj.sizeEnd; + tagObj.dataSize = size.value; + if (size.value === -1) { + // unknown size + tagObj.dataEnd = -1; + if (tagObj.type === "m") { + tagObj.unknownSize = true; + } + } + else { + tagObj.dataEnd = tagObj.sizeEnd + size.value; + } + // <<<<<<<< + // ポインタを進める + this._cursor += size.length; + this._total += size.length; + this._state = State.STATE_CONTENT; + return true; + }; + /** + * データ読み込み + */ + EBMLDecoder.prototype.readContent = function () { + var tagObj = this._tag_stack[this._tag_stack.length - 1]; + // master element は子要素を持つので生データはない + if (tagObj.type === 'm') { + // console.log('content should be tags'); + tagObj.isEnd = false; + this._result.push(tagObj); + this._state = State.STATE_TAG; + // この Mastert Element は空要素か + if (tagObj.dataSize === 0) { + // 即座に終了タグを追加 + var elm = Object.assign({}, tagObj, { isEnd: true }); + this._result.push(elm); + this._tag_stack.pop(); // スタックからこのタグを捨てる + } + return true; + } + // waiting for more data + if (this._buffer.length < this._cursor + tagObj.dataSize) { + return false; + } + // タグの中身の生データ + var data = this._buffer.slice(this._cursor, this._cursor + tagObj.dataSize); + // 読み終わったバッファを捨てて読み込んでいる部分のバッファのみ残す + this._buffer = this._buffer.slice(this._cursor + tagObj.dataSize); + tagObj.data = data; + // >>>>>>>>> + switch (tagObj.type) { + //case "m": break; + // Master-Element - contains other EBML sub-elements of the next lower level + case "u": + tagObj.value = data.readUIntBE(0, data.length); + break; + // Unsigned Integer - Big-endian, any size from 1 to 8 octets + case "i": + tagObj.value = data.readIntBE(0, data.length); + break; + // Signed Integer - Big-endian, any size from 1 to 8 octets + case "f": + tagObj.value = tagObj.dataSize === 4 ? data.readFloatBE(0) : + tagObj.dataSize === 8 ? data.readDoubleBE(0) : + (console.warn("cannot read ".concat(tagObj.dataSize, " octets float. failback to 0")), 0); + break; + // Float - Big-endian, defined for 4 and 8 octets (32, 64 bits) + case "s": + tagObj.value = data.toString("ascii"); + break; // ascii + // Printable ASCII (0x20 to 0x7E), zero-padded when needed + case "8": + tagObj.value = data.toString("utf8"); + break; + // Unicode string, zero padded when needed (RFC 2279) + case "b": + tagObj.value = data; + break; + // Binary - not interpreted by the parser + case "d": + tagObj.value = (0, tools_1.convertEBMLDateToJSDate)(new int64_buffer_1.Int64BE(data).toNumber()); + break; + // nano second; Date.UTC(2001,1,1,0,0,0,0) === 980985600000 + // Date - signed 8 octets integer in nanoseconds with 0 indicating + // the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC) + } + if (tagObj.value === null) { + throw new Error("unknown tag type:" + tagObj.type); + } + this._result.push(tagObj); + // <<<<<<<< + // ポインタを進める + this._total += tagObj.dataSize; + // タグ待ちモードに変更 + this._state = State.STATE_TAG; + this._cursor = 0; + this._tag_stack.pop(); // remove the object from the stack + while (this._tag_stack.length > 0) { + var topEle = this._tag_stack[this._tag_stack.length - 1]; + // 親が不定長サイズなので閉じタグは期待できない + if (topEle.dataEnd < 0) { + this._tag_stack.pop(); // 親タグを捨てる + return true; + } + // 閉じタグの来るべき場所まで来たかどうか + if (this._total < topEle.dataEnd) { + break; + } + // 閉じタグを挿入すべきタイミングが来た + if (topEle.type !== "m") { + throw new Error("parent element is not master element"); + } + var elm = Object.assign({}, topEle, { isEnd: true }); + this._result.push(elm); + this._tag_stack.pop(); + } + return true; + }; + return EBMLDecoder; +}()); +exports.default = EBMLDecoder; diff --git a/lib/ebml/EBMLEncoder.d.ts b/lib/ebml/EBMLEncoder.d.ts new file mode 100644 index 0000000..1f27da5 --- /dev/null +++ b/lib/ebml/EBMLEncoder.d.ts @@ -0,0 +1,14 @@ +import * as EBML from "./EBML"; +export default class EBMLEncoder { + private _buffers; + private _stack; + private _schema; + constructor(); + encode(elms: EBML.EBMLElementBuffer[]): ArrayBuffer; + private encodeChunk; + private flush; + private getSchemaInfo; + private writeTag; + private startTag; + private endTag; +} diff --git a/lib/ebml/EBMLEncoder.js b/lib/ebml/EBMLEncoder.js new file mode 100644 index 0000000..4ca3159 --- /dev/null +++ b/lib/ebml/EBMLEncoder.js @@ -0,0 +1,126 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tools = require("./tools"); +var tools_1 = require("./tools"); +var ebmlID_1 = require("./ebmlID"); +var EBMLEncoder = /** @class */ (function () { + function EBMLEncoder() { + this._schema = ebmlID_1.byEbmlID; + this._buffers = []; + this._stack = []; + } + EBMLEncoder.prototype.encode = function (elms) { + var _this = this; + return tools.concat(elms.reduce(function (lst, elm) { + return lst.concat(_this.encodeChunk(elm)); + }, [])).buffer; + }; + EBMLEncoder.prototype.encodeChunk = function (elm) { + if (elm.type === "m") { + if (!elm.isEnd) { + this.startTag(elm); + } + else { + this.endTag(elm); + } + } + else { + // ensure that we are working with an internal `Buffer` instance + elm.data = tools_1.Buffer.from(elm.data); + this.writeTag(elm); + } + return this.flush(); + }; + EBMLEncoder.prototype.flush = function () { + var ret = this._buffers; + this._buffers = []; + return ret; + }; + EBMLEncoder.prototype.getSchemaInfo = function (tagName) { + var tagNums = Object.keys(this._schema).map(Number); + for (var i = 0; i < tagNums.length; i++) { + var tagNum = tagNums[i]; + if (this._schema[tagNum].name === tagName) { + return new tools_1.Buffer(tagNum.toString(16), 'hex'); + } + } + return null; + }; + EBMLEncoder.prototype.writeTag = function (elm) { + var tagName = elm.name; + var tagId = this.getSchemaInfo(tagName); + var tagData = elm.data; + if (tagId == null) { + throw new Error('No schema entry found for ' + tagName); + } + var data = tools.encodeTag(tagId, tagData); + /** + * 親要素が閉じタグあり(isEnd)なら閉じタグが来るまで待つ(children queに入る) + */ + if (this._stack.length > 0) { + var last = this._stack[this._stack.length - 1]; + last.children.push({ + tagId: tagId, + elm: elm, + children: [], + data: data + }); + return; + } + this._buffers = this._buffers.concat(data); + return; + }; + EBMLEncoder.prototype.startTag = function (elm) { + var tagName = elm.name; + var tagId = this.getSchemaInfo(tagName); + if (tagId == null) { + throw new Error('No schema entry found for ' + tagName); + } + /** + * 閉じタグ不定長の場合はスタックに積まずに即時バッファに書き込む + */ + if (elm.unknownSize) { + var data = tools.encodeTag(tagId, new tools_1.Buffer(0), elm.unknownSize); + this._buffers = this._buffers.concat(data); + return; + } + var tag = { + tagId: tagId, + elm: elm, + children: [], + data: null + }; + if (this._stack.length > 0) { + this._stack[this._stack.length - 1].children.push(tag); + } + this._stack.push(tag); + }; + EBMLEncoder.prototype.endTag = function (elm) { + var tagName = elm.name; + var tag = this._stack.pop(); + if (tag == null) { + throw new Error("EBML structure is broken"); + } + if (tag.elm.name !== elm.name) { + throw new Error("EBML structure is broken"); + } + var childTagDataBuffers = tag.children.reduce(function (lst, child) { + if (child.data === null) { + throw new Error("EBML structure is broken"); + } + return lst.concat(child.data); + }, []); + var childTagDataBuffer = tools.concat(childTagDataBuffers); + if (tag.elm.type === "m") { + tag.data = tools.encodeTag(tag.tagId, childTagDataBuffer, tag.elm.unknownSize); + } + else { + tag.data = tools.encodeTag(tag.tagId, childTagDataBuffer); + } + if (this._stack.length < 1) { + this._buffers = this._buffers.concat(tag.data); + } + }; + return EBMLEncoder; +}()); +exports.default = EBMLEncoder; diff --git a/lib/ebml/EBMLReader.d.ts b/lib/ebml/EBMLReader.d.ts new file mode 100644 index 0000000..0d3cd97 --- /dev/null +++ b/lib/ebml/EBMLReader.d.ts @@ -0,0 +1,123 @@ +/// +import { EventEmitter } from "events"; +import * as EBML from './EBML'; +/** + * This is an informal code for reference. + * EBMLReader is a class for getting information to enable seeking Webm recorded by MediaRecorder. + * So please do not use for regular WebM files. + */ +export default class EBMLReader extends EventEmitter { + private metadataloaded; + private stack; + private chunks; + private segmentOffset; + private last2SimpleBlockVideoTrackTimecode; + private last2SimpleBlockAudioTrackTimecode; + private lastClusterTimecode; + private lastClusterPosition; + private firstVideoBlockRead; + private firstAudioBlockRead; + timecodeScale: number; + metadataSize: number; + metadatas: EBML.EBMLElementDetail[]; + private currentTrack; + private trackTypes; + private trackDefaultDuration; + private trackCodecDelay; + private first_video_simpleblock_of_cluster_is_loaded; + private ended; + trackInfo: { + type: "video" | "audio" | "both"; + trackNumber: number; + } | { + type: "nothing"; + }; + /** + * usefull for thumbnail creation. + */ + use_webp: boolean; + use_duration_every_simpleblock: boolean; + logging: boolean; + logGroup: string; + private hasLoggingStarted; + /** + * usefull for recording chunks. + */ + use_segment_info: boolean; + /** see: https://bugs.chromium.org/p/chromium/issues/detail?id=606000#c22 */ + drop_default_duration: boolean; + cues: { + CueTrack: number; + CueClusterPosition: number; + CueTime: number; + }[]; + constructor(); + /** + * emit final state. + */ + stop(): void; + /** + * emit chunk info + */ + private emit_segment_info; + read(elm: EBML.EBMLElementDetail): void; + /** + * DefaultDuration が定義されている場合は最後のフレームのdurationも考慮する + * 単位 timecodeScale + * + * !!! if you need duration with seconds !!! + * ```js + * const nanosec = reader.duration * reader.timecodeScale; + * const sec = nanosec / 1000 / 1000 / 1000; + * ``` + */ + get duration(): number; + /** + * @deprecated + * emit on every segment + * https://www.matroska.org/technical/specs/notes.html#Position_References + */ + addListener(event: "segment_offset", listener: (ev: number) => void): this; + /** + * @deprecated + * emit on every cluster element start. + * Offset byte from __file start__. It is not an offset from the Segment element. + */ + addListener(event: "cluster_ptr", listener: (ev: number) => void): this; + /** @deprecated + * emit on every cue point for cluster to create seekable webm file from MediaRecorder + * */ + addListener(event: "cue_info", listener: (ev: CueInfo) => void): this; + /** emit on every cue point for cluster to create seekable webm file from MediaRecorder */ + addListener(event: "cue", listener: (ev: CueInfo) => void): this; + /** latest EBML > Info > TimecodeScale and EBML > Info > Duration to create seekable webm file from MediaRecorder */ + addListener(event: "duration", listener: (ev: DurationInfo) => void): this; + /** EBML header without Cluster Element for recording metadata chunk */ + addListener(event: "metadata", listener: (ev: SegmentInfo & { + metadataSize: number; + }) => void): this; + /** emit every Cluster Element and its children for recording chunk */ + addListener(event: "cluster", listener: (ev: SegmentInfo & { + timecode: number; + }) => void): this; + /** for thumbnail */ + addListener(event: "webp", listener: (ev: ThumbnailInfo) => void): this; + put(elm: EBML.EBMLElementDetail): void; +} +/** CueClusterPosition: Offset byte from __file start__. It is not an offset from the Segment element. */ +export interface CueInfo { + CueTrack: number; + CueClusterPosition: number; + CueTime: number; +} +export interface SegmentInfo { + data: EBML.EBMLElementDetail[]; +} +export interface DurationInfo { + duration: number; + timecodeScale: number; +} +export interface ThumbnailInfo { + webp: Blob; + currentTime: number; +} diff --git a/lib/ebml/EBMLReader.js b/lib/ebml/EBMLReader.js new file mode 100644 index 0000000..3d47c9b --- /dev/null +++ b/lib/ebml/EBMLReader.js @@ -0,0 +1,360 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var events_1 = require("events"); +var tools = require("./tools"); +/** + * This is an informal code for reference. + * EBMLReader is a class for getting information to enable seeking Webm recorded by MediaRecorder. + * So please do not use for regular WebM files. + */ +var EBMLReader = /** @class */ (function (_super) { + __extends(EBMLReader, _super); + function EBMLReader() { + var _this = _super.call(this) || this; + _this.logGroup = ""; + _this.hasLoggingStarted = false; + _this.metadataloaded = false; + _this.chunks = []; + _this.stack = []; + _this.segmentOffset = 0; + _this.last2SimpleBlockVideoTrackTimecode = [0, 0]; + _this.last2SimpleBlockAudioTrackTimecode = [0, 0]; + _this.lastClusterTimecode = 0; + _this.lastClusterPosition = 0; + _this.timecodeScale = 1000000; // webm default TimecodeScale is 1ms + _this.metadataSize = 0; + _this.metadatas = []; + _this.cues = []; + _this.firstVideoBlockRead = false; + _this.firstAudioBlockRead = false; + _this.currentTrack = { TrackNumber: -1, TrackType: -1, DefaultDuration: null, CodecDelay: null }; + _this.trackTypes = []; + _this.trackDefaultDuration = []; + _this.trackCodecDelay = []; + _this.trackInfo = { type: "nothing" }; + _this.ended = false; + _this.logging = false; + _this.use_duration_every_simpleblock = false; + _this.use_webp = false; + _this.use_segment_info = true; + _this.drop_default_duration = true; + return _this; + } + /** + * emit final state. + */ + EBMLReader.prototype.stop = function () { + this.ended = true; + this.emit_segment_info(); + // clean up any unclosed Master Elements at the end of the stream. + while (this.stack.length) { + this.stack.pop(); + if (this.logging) { + console.groupEnd(); + } + } + // close main group if set, logging is enabled, and has actually logged anything. + if (this.logging && this.hasLoggingStarted && this.logGroup) { + console.groupEnd(); + } + }; + /** + * emit chunk info + */ + EBMLReader.prototype.emit_segment_info = function () { + var data = this.chunks; + this.chunks = []; + if (!this.metadataloaded) { + this.metadataloaded = true; + this.metadatas = data; + var videoTrackNum = this.trackTypes.indexOf(1); // find first video track + var audioTrackNum = this.trackTypes.indexOf(2); // find first audio track + this.trackInfo = videoTrackNum >= 0 && audioTrackNum >= 0 ? { type: "both", trackNumber: videoTrackNum } + : videoTrackNum >= 0 ? { type: "video", trackNumber: videoTrackNum } + : audioTrackNum >= 0 ? { type: "audio", trackNumber: audioTrackNum } + : { type: "nothing" }; + if (!this.use_segment_info) { + return; + } + this.emit("metadata", { data: data, metadataSize: this.metadataSize }); + } + else { + if (!this.use_segment_info) { + return; + } + var timecode = this.lastClusterTimecode; + var duration = this.duration; + var timecodeScale = this.timecodeScale; + this.emit("cluster", { timecode: timecode, data: data }); + this.emit("duration", { timecodeScale: timecodeScale, duration: duration }); + } + }; + EBMLReader.prototype.read = function (elm) { + var _this = this; + var drop = false; + if (this.ended) { + // reader is finished + return; + } + if (elm.type === "m") { + // 閉じタグの自動挿入 + if (elm.isEnd) { + this.stack.pop(); + } + else { + var parent_1 = this.stack[this.stack.length - 1]; + if (parent_1 != null && parent_1.level >= elm.level) { + // 閉じタグなしでレベルが下がったら閉じタグを挿入 + this.stack.pop(); + // From http://w3c.github.io/media-source/webm-byte-stream-format.html#webm-media-segments + // This fixes logging for webm streams with Cluster of unknown length and no Cluster closing elements. + if (this.logging) { + console.groupEnd(); + } + parent_1.dataEnd = elm.dataEnd; + parent_1.dataSize = elm.dataEnd - parent_1.dataStart; + parent_1.unknownSize = false; + var o = Object.assign({}, parent_1, { name: parent_1.name, type: parent_1.type, isEnd: true }); + this.chunks.push(o); + } + this.stack.push(elm); + } + } + if (elm.type === "m" && elm.name == "Segment") { + if (this.segmentOffset != 0) { + console.warn("Multiple segments detected!"); + } + this.segmentOffset = elm.dataStart; + this.emit("segment_offset", this.segmentOffset); + } + else if (elm.type === "b" && elm.name === "SimpleBlock") { + var _a = tools.ebmlBlock(elm.data), timecode = _a.timecode, trackNumber = _a.trackNumber, frames_1 = _a.frames; + if (this.trackTypes[trackNumber] === 1) { // trackType === 1 => video track + if (!this.firstVideoBlockRead) { + this.firstVideoBlockRead = true; + if (this.trackInfo.type === "both" || this.trackInfo.type === "video") { + var CueTime = this.lastClusterTimecode + timecode; + this.cues.push({ CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: CueTime }); + this.emit("cue_info", { CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: this.lastClusterTimecode }); + this.emit("cue", { CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: CueTime }); + } + } + this.last2SimpleBlockVideoTrackTimecode = [this.last2SimpleBlockVideoTrackTimecode[1], timecode]; + } + else if (this.trackTypes[trackNumber] === 2) { // trackType === 2 => audio track + if (!this.firstAudioBlockRead) { + this.firstAudioBlockRead = true; + if (this.trackInfo.type === "audio") { + var CueTime = this.lastClusterTimecode + timecode; + this.cues.push({ CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: CueTime }); + this.emit("cue_info", { CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: this.lastClusterTimecode }); + this.emit("cue", { CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: CueTime }); + } + } + this.last2SimpleBlockAudioTrackTimecode = [this.last2SimpleBlockAudioTrackTimecode[1], timecode]; + } + if (this.use_duration_every_simpleblock) { + this.emit("duration", { timecodeScale: this.timecodeScale, duration: this.duration }); + } + if (this.use_webp) { + frames_1.forEach(function (frame) { + var startcode = frame.slice(3, 6).toString("hex"); + if (startcode !== "9d012a") { + return; + } + ; // VP8 の場合 + var webpBuf = tools.VP8BitStreamToRiffWebPBuffer(frame); + var webp = new Blob([webpBuf], { type: "image/webp" }); + var currentTime = _this.duration; + _this.emit("webp", { currentTime: currentTime, webp: webp }); + }); + } + } + else if (elm.type === "m" && elm.name === "Cluster" && elm.isEnd === false) { + this.firstVideoBlockRead = false; + this.firstAudioBlockRead = false; + this.emit_segment_info(); + this.emit("cluster_ptr", elm.tagStart); + this.lastClusterPosition = elm.tagStart; + } + else if (elm.type === "u" && elm.name === "Timecode") { + this.lastClusterTimecode = elm.value; + } + else if (elm.type === "u" && elm.name === "TimecodeScale") { + this.timecodeScale = elm.value; + } + else if (elm.type === "m" && elm.name === "TrackEntry") { + if (elm.isEnd) { + this.trackTypes[this.currentTrack.TrackNumber] = this.currentTrack.TrackType; + this.trackDefaultDuration[this.currentTrack.TrackNumber] = this.currentTrack.DefaultDuration; + this.trackCodecDelay[this.currentTrack.TrackNumber] = this.currentTrack.CodecDelay; + } + else { + this.currentTrack = { TrackNumber: -1, TrackType: -1, DefaultDuration: null, CodecDelay: null }; + } + } + else if (elm.type === "u" && elm.name === "TrackType") { + this.currentTrack.TrackType = elm.value; + } + else if (elm.type === "u" && elm.name === "TrackNumber") { + this.currentTrack.TrackNumber = elm.value; + } + else if (elm.type === "u" && elm.name === "CodecDelay") { + this.currentTrack.CodecDelay = elm.value; + } + else if (elm.type === "u" && elm.name === "DefaultDuration") { + // media source api は DefaultDuration を計算するとバグる。 + // https://bugs.chromium.org/p/chromium/issues/detail?id=606000#c22 + // chrome 58 ではこれを回避するために DefaultDuration 要素を抜き取った。 + // chrome 58 以前でもこのタグを抜き取ることで回避できる + if (this.drop_default_duration) { + console.warn("DefaultDuration detected!, remove it"); + drop = true; + } + else { + this.currentTrack.DefaultDuration = elm.value; + } + } + else if (elm.name === "unknown") { + console.warn(elm); + } + if (!this.metadataloaded && elm.dataEnd > 0) { + this.metadataSize = elm.dataEnd; + } + if (!drop) { + this.chunks.push(elm); + } + if (this.logging) { + this.put(elm); + } + }; + Object.defineProperty(EBMLReader.prototype, "duration", { + /** + * DefaultDuration が定義されている場合は最後のフレームのdurationも考慮する + * 単位 timecodeScale + * + * !!! if you need duration with seconds !!! + * ```js + * const nanosec = reader.duration * reader.timecodeScale; + * const sec = nanosec / 1000 / 1000 / 1000; + * ``` + */ + get: function () { + if (this.trackInfo.type === "nothing") { + console.warn("no video, no audio track"); + return 0; + } + // defaultDuration は 生の nano sec + var defaultDuration = 0; + // nanoseconds + var codecDelay = 0; + var lastTimecode = 0; + var _defaultDuration = this.trackDefaultDuration[this.trackInfo.trackNumber]; + if (typeof _defaultDuration === "number") { + defaultDuration = _defaultDuration; + } + else { + // https://bugs.chromium.org/p/chromium/issues/detail?id=606000#c22 + // default duration がないときに使う delta + if (this.trackInfo.type === "both") { + if (this.last2SimpleBlockAudioTrackTimecode[1] > this.last2SimpleBlockVideoTrackTimecode[1]) { + // audio diff + defaultDuration = (this.last2SimpleBlockAudioTrackTimecode[1] - this.last2SimpleBlockAudioTrackTimecode[0]) * this.timecodeScale; + // audio delay + var delay = this.trackCodecDelay[this.trackTypes.indexOf(2)]; // 2 => audio + if (typeof delay === "number") { + codecDelay = delay; + } + // audio timecode + lastTimecode = this.last2SimpleBlockAudioTrackTimecode[1]; + } + else { + // video diff + defaultDuration = (this.last2SimpleBlockVideoTrackTimecode[1] - this.last2SimpleBlockVideoTrackTimecode[0]) * this.timecodeScale; + // video delay + var delay = this.trackCodecDelay[this.trackTypes.indexOf(1)]; // 1 => video + if (typeof delay === "number") { + codecDelay = delay; + } + // video timecode + lastTimecode = this.last2SimpleBlockVideoTrackTimecode[1]; + } + } + else if (this.trackInfo.type === "video") { + defaultDuration = (this.last2SimpleBlockVideoTrackTimecode[1] - this.last2SimpleBlockVideoTrackTimecode[0]) * this.timecodeScale; + var delay = this.trackCodecDelay[this.trackInfo.trackNumber]; // 2 => audio + if (typeof delay === "number") { + codecDelay = delay; + } + lastTimecode = this.last2SimpleBlockVideoTrackTimecode[1]; + } + else if (this.trackInfo.type === "audio") { + defaultDuration = (this.last2SimpleBlockAudioTrackTimecode[1] - this.last2SimpleBlockAudioTrackTimecode[0]) * this.timecodeScale; + var delay = this.trackCodecDelay[this.trackInfo.trackNumber]; // 1 => video + if (typeof delay === "number") { + codecDelay = delay; + } + lastTimecode = this.last2SimpleBlockAudioTrackTimecode[1]; + } // else { not reached } + } + // convert to timecodescale + var duration_nanosec = ((this.lastClusterTimecode + lastTimecode) * this.timecodeScale) + defaultDuration - codecDelay; + var duration = duration_nanosec / this.timecodeScale; + return Math.floor(duration); + }, + enumerable: false, + configurable: true + }); + EBMLReader.prototype.addListener = function (event, listener) { + return _super.prototype.addListener.call(this, event, listener); + }; + EBMLReader.prototype.put = function (elm) { + if (!this.hasLoggingStarted) { + this.hasLoggingStarted = true; + if (this.logging && this.logGroup) { + console.groupCollapsed(this.logGroup); + } + } + if (elm.type === "m") { + if (elm.isEnd) { + console.groupEnd(); + } + else { + console.group(elm.name + ":" + elm.tagStart); + } + } + else if (elm.type === "b") { + // for debug + //if(elm.name === "SimpleBlock"){ + //const o = EBML.tools.ebmlBlock(elm.value); + //console.log(elm.name, elm.type, o.trackNumber, o.timecode); + //}else{ + console.log(elm.name, elm.type); + //} + } + else { + console.log(elm.name, elm.tagStart, elm.type, elm.value); + } + }; + return EBMLReader; +}(events_1.EventEmitter)); +exports.default = EBMLReader; +; +; +; +; diff --git a/lib/ebml/ebmlID.d.ts b/lib/ebml/ebmlID.d.ts new file mode 100644 index 0000000..6d26028 --- /dev/null +++ b/lib/ebml/ebmlID.d.ts @@ -0,0 +1,2041 @@ +export declare const byEbmlID: { + 128: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 131: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + range: string; + description: string; + }; + 133: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 134: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + }; + 136: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + default: number; + range: string; + description: string; + }; + 137: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 145: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 146: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 150: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 151: { + name: string; + level: number; + type: string; + mandatory: boolean; + webm: boolean; + description: string; + }; + 152: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 16980: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 16981: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 17026: { + name: string; + level: number; + type: string; + mandatory: boolean; + default: string; + minver: number; + description: string; + }; + 17029: { + name: string; + level: number; + type: string; + mandatory: boolean; + default: number; + minver: number; + description: string; + }; + 17030: { + name: string; + level: number; + type: string; + mandatory: boolean; + default: number; + minver: number; + description: string; + }; + 17031: { + name: string; + level: number; + type: string; + mandatory: boolean; + default: number; + minver: number; + description: string; + }; + 17476: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + bytesize: number; + description: string; + }; + 17505: { + name: string; + level: number; + type: string; + minver: number; + description: string; + }; + 17540: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 17541: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 17543: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 17545: { + name: string; + level: number; + type: string; + minver: number; + range: string; + description: string; + }; + 17816: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 18016: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 18017: { + name: string; + level: number; + type: string; + divx: boolean; + description: string; + }; + 18018: { + name: string; + level: number; + type: string; + divx: boolean; + description: string; + }; + 18037: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 20529: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 20530: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 20531: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 20532: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 20533: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 21368: { + name: string; + level: number; + type: string; + minver: number; + default: number; + range: string; + description: string; + }; + 22100: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 22337: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + }; + 22612: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 25152: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 25188: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + range: string; + description: string; + }; + 25906: { + name: string; + level: number; + type: string; + multiple: boolean; + webm: boolean; + description: string; + }; + 26148: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26897: { + name: string; + cppname: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26914: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26916: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26931: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26948: { + name: string; + cppname: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26965: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 29555: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 29572: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 29766: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 2459272: { + name: string; + level: number; + type: string; + minver: number; + description: string; + }; + 408125543: { + name: string; + level: string; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + description: string; + }; + 17530: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: string; + description: string; + }; + 17827: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26568: { + name: string; + cppname: string; + level: number; + recursive: string; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 25542: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 25540: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 25545: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 25541: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 25546: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + strong: string; + description: string; + }; + 26826: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 25536: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 307544935: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 17677: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 17278: { + name: string; + cppname: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 17276: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + default: string; + description: string; + }; + 143: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 25539: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 28348: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 28263: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + range: string; + bytesize: number; + description: string; + }; + 29636: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 182: { + name: string; + level: number; + recursive: string; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 17885: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 17883: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 17853: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 17852: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 17849: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 272869232: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 18094: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 18012: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 18030: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 18046: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 24999: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 423732329: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 235: { + name: string; + level: number; + type: string; + webm: boolean; + default: number; + description: string; + }; + 21343: { + name: string; + level: number; + type: string; + webm: boolean; + default: number; + range: string; + description: string; + }; + 219: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 234: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 178: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 240: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + position: string; + }; + 241: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + position: string; + }; + 247: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + range: string; + description: string; + }; + 183: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + description: string; + }; + 179: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + }; + 187: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + description: string; + }; + 475249515: { + name: string; + level: number; + type: string; + minver: number; + description: string; + }; + 18406: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 18405: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 18404: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 18403: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 18402: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 18401: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 28032: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 196: { + name: string; + level: number; + type: string; + divx: boolean; + bytesize: number; + description: string; + }; + 199: { + name: string; + level: number; + type: string; + divx: boolean; + description: string; + }; + 198: { + name: string; + level: number; + type: string; + divx: boolean; + default: number; + description: string; + }; + 193: { + name: string; + level: number; + type: string; + divx: boolean; + bytesize: number; + description: string; + }; + 192: { + name: string; + level: number; + type: string; + divx: boolean; + description: string; + }; + 237: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 233: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 230: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 229: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + range: string; + description: string; + }; + 228: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 227: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 226: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 32123: { + name: string; + cppname: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 159: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + default: number; + range: string; + description: string; + }; + 30901: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: string; + range: string; + description: string; + }; + 181: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + default: number; + range: string; + description: string; + }; + 225: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + description: string; + }; + 2327523: { + name: string; + cppname: string; + level: number; + type: string; + range: string; + strong: string; + description: string; + }; + 3126563: { + name: string; + cppname: string; + level: number; + type: string; + webm: boolean; + range: string; + description: string; + }; + 3061028: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + bytesize: number; + description: string; + }; + 21683: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: number; + description: string; + }; + 21682: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: number; + description: string; + }; + 21690: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: string; + range: string; + description: string; + }; + 21680: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: string; + range: string; + description: string; + }; + 21725: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: number; + description: string; + }; + 21708: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: number; + description: string; + }; + 21691: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: number; + description: string; + }; + 21674: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: number; + description: string; + }; + 186: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + range: string; + description: string; + }; + 176: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + range: string; + description: string; + }; + 21433: { + name: string; + level: number; + type: string; + maxver: string; + webm: boolean; + divx: boolean; + description: string; + }; + 21440: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 21432: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 154: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 224: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + description: string; + }; + 26277: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26303: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 26364: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 22203: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + default: number; + minver: number; + webm: boolean; + description: string; + }; + 22186: { + name: string; + level: number; + type: string; + multiple: boolean; + default: number; + minver: number; + webm: boolean; + description: string; + }; + 28587: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 170: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 2536000: { + name: string; + level: number; + type: string; + multiple: boolean; + webm: boolean; + description: string; + }; + 3883072: { + name: string; + level: number; + type: string; + multiple: boolean; + webm: boolean; + description: string; + }; + 3839639: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 25506: { + name: string; + level: number; + type: string; + minver: number; + description: string; + }; + 2274716: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: string; + description: string; + }; + 21358: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + description: string; + }; + 21998: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 21375: { + name: string; + level: number; + type: string; + webm: boolean; + default: number; + description: string; + }; + 2306383: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + maxver: string; + webm: boolean; + default: number; + range: string; + description: string; + }; + 2313850: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + range: string; + description: string; + }; + 2352003: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + range: string; + description: string; + }; + 28152: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 28135: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 156: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + default: number; + range: string; + description: string; + }; + 21930: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + default: number; + range: string; + description: string; + }; + 185: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 29637: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + range: string; + description: string; + }; + 215: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + range: string; + description: string; + }; + 174: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + description: string; + }; + 374648427: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + description: string; + }; + 175: { + name: string; + level: number; + type: string; + multiple: boolean; + webm: boolean; + description: string; + }; + 202: { + name: string; + level: number; + type: string; + multiple: boolean; + mandatory: boolean; + minver: number; + webm: boolean; + divx: boolean; + description: string; + }; + 201: { + name: string; + level: number; + type: string; + multiple: boolean; + mandatory: boolean; + minver: number; + webm: boolean; + divx: boolean; + description: string; + }; + 200: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + divx: boolean; + description: string; + }; + 207: { + name: string; + level: number; + type: string; + default: number; + description: string; + }; + 206: { + name: string; + cppname: string; + level: number; + type: string; + default: number; + description: string; + }; + 203: { + name: string; + cppname: string; + level: number; + type: string; + default: number; + description: string; + }; + 205: { + name: string; + cppname: string; + level: number; + type: string; + default: number; + description: string; + }; + 204: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + default: number; + divx: boolean; + description: string; + }; + 232: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + divx: boolean; + description: string; + }; + 142: { + name: string; + level: number; + type: string; + minver: number; + divx: boolean; + description: string; + }; + 30114: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 164: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 253: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 251: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + description: string; + }; + 250: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + description: string; + }; + 155: { + name: string; + level: number; + type: string; + minver: number; + default: string; + description: string; + }; + 165: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 238: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + default: number; + range: string; + description: string; + }; + 166: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 30113: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 162: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 161: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + }; + 160: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + description: string; + }; + 163: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + divx: boolean; + description: string; + }; + 171: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + description: string; + position: string; + }; + 167: { + name: string; + cppname: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + position: string; + }; + 22743: { + name: string; + cppname: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 231: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + }; + 524531317: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + description: string; + }; + 19840: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + }; + 31657: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 2807730: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + default: string; + description: string; + }; + 2807729: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + default: string; + description: string; + }; + 27045: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 27071: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + webm: boolean; + description: string; + }; + 27132: { + name: string; + level: number; + type: string; + multiple: boolean; + minver: number; + webm: boolean; + description: string; + }; + 4096955: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 4110627: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + bytesize: number; + description: string; + }; + 3965867: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + }; + 3979555: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + bytesize: number; + description: string; + }; + 29604: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + range: string; + bytesize: number; + description: string; + }; + 357149030: { + name: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + description: string; + }; + 21420: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + position: string; + }; + 21419: { + name: string; + level: number; + type: string; + mandatory: boolean; + minver: number; + description: string; + type2: string; + }; + 19899: { + name: string; + cppname: string; + level: number; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + description: string; + }; + 290298740: { + name: string; + cppname: string; + level: number; + type: string; + multiple: boolean; + minver: number; + description: string; + }; + 32379: { + name: string; + level: number; + type: string; + multiple: boolean; + webm: boolean; + i: string; + description: string; + }; + 32347: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 32437: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 32421: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 32410: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 32394: { + name: string; + level: number; + type: string; + webm: boolean; + description: string; + }; + 458458727: { + name: string; + level: number; + type: string; + multiple: boolean; + webm: boolean; + description: string; + }; + 191: { + name: string; + level: number; + type: string; + minver: number; + webm: boolean; + description: string; + crc: boolean; + }; + 236: { + name: string; + level: number; + type: string; + minver: number; + description: string; + }; + 17139: { + name: string; + level: number; + type: string; + mandatory: boolean; + default: number; + minver: number; + description: string; + }; + 17138: { + name: string; + level: number; + type: string; + mandatory: boolean; + default: number; + minver: number; + description: string; + }; + 17143: { + name: string; + level: number; + type: string; + mandatory: boolean; + default: number; + minver: number; + description: string; + }; + 440786851: { + name: string; + level: string; + type: string; + mandatory: boolean; + multiple: boolean; + minver: number; + description: string; + }; +}; diff --git a/lib/ebml/ebmlID.js b/lib/ebml/ebmlID.js new file mode 100644 index 0000000..b51b28d --- /dev/null +++ b/lib/ebml/ebmlID.js @@ -0,0 +1,2051 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.byEbmlID = void 0; +exports.byEbmlID = { + 0x80: { + name: "ChapterDisplay", + level: 4, + type: "m", + multiple: true, + minver: 1, + webm: true, + description: "Contains all possible strings to use for the chapter display." + }, + 0x83: { + name: "TrackType", + level: 3, + type: "u", + mandatory: true, + minver: 1, + range: "1-254", + description: "A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control)." + }, + 0x85: { + name: "ChapString", + cppname: "ChapterString", + level: 5, + type: "8", + mandatory: true, + minver: 1, + webm: true, + description: "Contains the string to use as the chapter atom." + }, + 0x86: { + name: "CodecID", + level: 3, + type: "s", + mandatory: true, + minver: 1, + description: "An ID corresponding to the codec, see the codec page for more info." + }, + 0x88: { + name: "FlagDefault", + cppname: "TrackFlagDefault", + level: 3, + type: "u", + mandatory: true, + minver: 1, + "default": 1, + range: "0-1", + description: "Set if that track (audio, video or subs) SHOULD be active if no language found matches the user preference. (1 bit)" + }, + 0x89: { + name: "ChapterTrackNumber", + level: 5, + type: "u", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + range: "not 0", + description: "UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks." + }, + 0x91: { + name: "ChapterTimeStart", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: true, + description: "Timestamp of the start of Chapter (not scaled)." + }, + 0x92: { + name: "ChapterTimeEnd", + level: 4, + type: "u", + minver: 1, + webm: false, + description: "Timestamp of the end of Chapter (timestamp excluded, not scaled)." + }, + 0x96: { + name: "CueRefTime", + level: 5, + type: "u", + mandatory: true, + minver: 2, + webm: false, + description: "Timestamp of the referenced Block." + }, + 0x97: { + name: "CueRefCluster", + level: 5, + type: "u", + mandatory: true, + webm: false, + description: "The Position of the Cluster containing the referenced Block." + }, + 0x98: { + name: "ChapterFlagHidden", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks; see flag notes). (1 bit)" + }, + 0x4254: { + name: "ContentCompAlgo", + level: 6, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + // "br": [ "", "", "", "" ], + // "del": [ "1 - bzlib,", "2 - lzo1x" ], + description: "The compression algorithm used. Algorithms that have been specified so far are: 0 - zlib, 3 - Header Stripping" + }, + 0x4255: { + name: "ContentCompSettings", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track." + }, + 0x4282: { + name: "DocType", + level: 1, + type: "s", + mandatory: true, + "default": "matroska", + minver: 1, + description: "A string that describes the type of document that follows this EBML header. 'matroska' in our case or 'webm' for webm files." + }, + 0x4285: { + name: "DocTypeReadVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The minimum DocType version an interpreter has to support to read this file." + }, + 0x4286: { + name: "EBMLVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The version of EBML parser used to create the file." + }, + 0x4287: { + name: "DocTypeVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The version of DocType interpreter used to create the file." + }, + 0x4444: { + name: "SegmentFamily", + level: 2, + type: "b", + multiple: true, + minver: 1, + webm: false, + bytesize: 16, + description: "A randomly generated unique ID that all segments related to each other must use (128 bits)." + }, + 0x4461: { + name: "DateUTC", + level: 2, + type: "d", + minver: 1, + description: "Date of the origin of timestamp (value 0), i.e. production date." + }, + 0x4484: { + name: "TagDefault", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "0-1", + description: "Indication to know if this is the default/original language to use for the given tag. (1 bit)" + }, + 0x4485: { + name: "TagBinary", + level: 4, + type: "b", + minver: 1, + webm: false, + description: "The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString." + }, + 0x4487: { + name: "TagString", + level: 4, + type: "8", + minver: 1, + webm: false, + description: "The value of the Element." + }, + 0x4489: { + name: "Duration", + level: 2, + type: "f", + minver: 1, + range: "> 0", + description: "Duration of the segment (based on TimecodeScale)." + }, + 0x4598: { + name: "ChapterFlagEnabled", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "0-1", + description: "Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter (see flag notes). (1 bit)" + }, + 0x4660: { + name: "FileMimeType", + level: 3, + type: "s", + mandatory: true, + minver: 1, + webm: false, + description: "MIME type of the file." + }, + 0x4661: { + name: "FileUsedStartTime", + level: 3, + type: "u", + divx: true, + description: "DivX font extension" + }, + 0x4662: { + name: "FileUsedEndTime", + level: 3, + type: "u", + divx: true, + description: "DivX font extension" + }, + 0x4675: { + name: "FileReferral", + level: 3, + type: "b", + webm: false, + description: "A binary value that a track/codec can refer to when the attachment is needed." + }, + 0x5031: { + name: "ContentEncodingOrder", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment." + }, + 0x5032: { + name: "ContentEncodingScope", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "not 0", + // "br": [ "", "", "" ], + description: "A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values: 1 - all frame contents, 2 - the track's private data, 4 - the next ContentEncoding (next ContentEncodingOrder. Either the data inside ContentCompression and/or ContentEncryption)" + }, + 0x5033: { + name: "ContentEncodingType", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + // "br": [ "", "" ], + description: "A value describing what kind of transformation has been done. Possible values: 0 - compression, 1 - encryption" + }, + 0x5034: { + name: "ContentCompression", + level: 5, + type: "m", + minver: 1, + webm: false, + description: "Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking." + }, + 0x5035: { + name: "ContentEncryption", + level: 5, + type: "m", + minver: 1, + webm: false, + description: "Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise." + }, + 0x5378: { + name: "CueBlockNumber", + level: 4, + type: "u", + minver: 1, + "default": 1, + range: "not 0", + description: "Number of the Block in the specified Cluster." + }, + 0x5654: { + name: "ChapterStringUID", + level: 4, + type: "8", + mandatory: false, + minver: 3, + webm: true, + description: "A unique string ID to identify the Chapter. Use for WebVTT cue identifier storage." + }, + 0x5741: { + name: "WritingApp", + level: 2, + type: "8", + mandatory: true, + minver: 1, + description: "Writing application (\"mkvmerge-0.3.3\")." + }, + 0x5854: { + name: "SilentTracks", + cppname: "ClusterSilentTracks", + level: 2, + type: "m", + minver: 1, + webm: false, + description: "The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use." + }, + 0x6240: { + name: "ContentEncoding", + level: 4, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Settings for one content encoding like compression or encryption." + }, + 0x6264: { + name: "BitDepth", + cppname: "AudioBitDepth", + level: 4, + type: "u", + minver: 1, + range: "not 0", + description: "Bits per sample, mostly used for PCM." + }, + 0x6532: { + name: "SignedElement", + level: 3, + type: "b", + multiple: true, + webm: false, + description: "An element ID whose data will be used to compute the signature." + }, + 0x6624: { + name: "TrackTranslate", + level: 3, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "The track identification for the given Chapter Codec." + }, + 0x6911: { + name: "ChapProcessCommand", + cppname: "ChapterProcessCommand", + level: 5, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "Contains all the commands associated to the Atom." + }, + 0x6922: { + name: "ChapProcessTime", + cppname: "ChapterProcessTime", + level: 6, + type: "u", + mandatory: true, + minver: 1, + webm: false, + description: "Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter)." + }, + 0x6924: { + name: "ChapterTranslate", + level: 2, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "A tuple of corresponding ID used by chapter codecs to represent this segment." + }, + 0x6933: { + name: "ChapProcessData", + cppname: "ChapterProcessData", + level: 6, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands." + }, + 0x6944: { + name: "ChapProcess", + cppname: "ChapterProcess", + level: 4, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "Contains all the commands associated to the Atom." + }, + 0x6955: { + name: "ChapProcessCodecID", + cppname: "ChapterProcessCodecID", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later." + }, + 0x7373: { + name: "Tag", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Element containing elements specific to Tracks/Chapters." + }, + 0x7384: { + name: "SegmentFilename", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "A filename corresponding to this segment." + }, + 0x7446: { + name: "AttachmentLink", + cppname: "TrackAttachmentLink", + level: 3, + type: "u", + minver: 1, + webm: false, + range: "not 0", + description: "The UID of an attachment that is used by this codec." + }, + 0x258688: { + name: "CodecName", + level: 3, + type: "8", + minver: 1, + description: "A human-readable string specifying the codec." + }, + 0x18538067: { + name: "Segment", + level: "0", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment." + }, + 0x447a: { + name: "TagLanguage", + level: 4, + type: "s", + mandatory: true, + minver: 1, + webm: false, + "default": "und", + description: "Specifies the language of the tag specified, in the Matroska languages form." + }, + 0x45a3: { + name: "TagName", + level: 4, + type: "8", + mandatory: true, + minver: 1, + webm: false, + description: "The name of the Tag that is going to be stored." + }, + 0x67c8: { + name: "SimpleTag", + cppname: "TagSimple", + level: 3, + "recursive": "1", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Contains general information about the target." + }, + 0x63c6: { + name: "TagAttachmentUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment." + }, + 0x63c4: { + name: "TagChapterUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment." + }, + 0x63c9: { + name: "TagEditionUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment." + }, + 0x63c5: { + name: "TagTrackUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment." + }, + 0x63ca: { + name: "TargetType", + cppname: "TagTargetType", + level: 4, + type: "s", + minver: 1, + webm: false, + "strong": "informational", + description: "An string that can be used to display the logical level of the target like \"ALBUM\", \"TRACK\", \"MOVIE\", \"CHAPTER\", etc (see TargetType)." + }, + 0x68ca: { + name: "TargetTypeValue", + cppname: "TagTargetTypeValue", + level: 4, + type: "u", + minver: 1, + webm: false, + "default": 50, + description: "A number to indicate the logical level of the target (see TargetType)." + }, + 0x63c0: { + name: "Targets", + cppname: "TagTargets", + level: 3, + type: "m", + mandatory: true, + minver: 1, + webm: false, + description: "Contain all UIDs where the specified meta data apply. It is empty to describe everything in the segment." + }, + 0x1254c367: { + name: "Tags", + level: 1, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "Element containing elements specific to Tracks/Chapters. A list of valid tags can be found here." + }, + 0x450d: { + name: "ChapProcessPrivate", + cppname: "ChapterProcessPrivate", + level: 5, + type: "b", + minver: 1, + webm: false, + description: "Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the \"DVD level\" equivalent." + }, + 0x437e: { + name: "ChapCountry", + cppname: "ChapterCountry", + level: 5, + type: "s", + multiple: true, + minver: 1, + webm: false, + description: "The countries corresponding to the string, same 2 octets as in Internet domains." + }, + 0x437c: { + name: "ChapLanguage", + cppname: "ChapterLanguage", + level: 5, + type: "s", + mandatory: true, + multiple: true, + minver: 1, + webm: true, + "default": "eng", + description: "The languages corresponding to the string, in the bibliographic ISO-639-2 form." + }, + 0x8f: { + name: "ChapterTrack", + level: 4, + type: "m", + minver: 1, + webm: false, + description: "List of tracks on which the chapter applies. If this element is not present, all tracks apply" + }, + 0x63c3: { + name: "ChapterPhysicalEquiv", + level: 4, + type: "u", + minver: 1, + webm: false, + description: "Specify the physical equivalent of this ChapterAtom like \"DVD\" (60) or \"SIDE\" (50), see complete list of values." + }, + 0x6ebc: { + name: "ChapterSegmentEditionUID", + level: 4, + type: "u", + minver: 1, + webm: false, + range: "not 0", + description: "The EditionUID to play from the segment linked in ChapterSegmentUID." + }, + 0x6e67: { + name: "ChapterSegmentUID", + level: 4, + type: "b", + minver: 1, + webm: false, + range: ">0", + bytesize: 16, + description: "A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used." + }, + 0x73c4: { + name: "ChapterUID", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: true, + range: "not 0", + description: "A unique ID to identify the Chapter." + }, + 0xb6: { + name: "ChapterAtom", + level: 3, + "recursive": "1", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: true, + description: "Contains the atom information to use as the chapter atom (apply to all tracks)." + }, + 0x45dd: { + name: "EditionFlagOrdered", + level: 3, + type: "u", + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "Specify if the chapters can be defined multiple times and the order to play them is enforced. (1 bit)" + }, + 0x45db: { + name: "EditionFlagDefault", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "If a flag is set (1) the edition should be used as the default one. (1 bit)" + }, + 0x45bd: { + name: "EditionFlagHidden", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks; see flag notes). (1 bit)" + }, + 0x45bc: { + name: "EditionUID", + level: 3, + type: "u", + minver: 1, + webm: false, + range: "not 0", + description: "A unique ID to identify the edition. It's useful for tagging an edition." + }, + 0x45b9: { + name: "EditionEntry", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: true, + description: "Contains all information about a segment edition." + }, + 0x1043a770: { + name: "Chapters", + level: 1, + type: "m", + minver: 1, + webm: true, + description: "A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation." + }, + 0x46ae: { + name: "FileUID", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + range: "not 0", + description: "Unique ID representing the file, as random as possible." + }, + 0x465c: { + name: "FileData", + level: 3, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "The data of the file." + }, + 0x466e: { + name: "FileName", + level: 3, + type: "8", + mandatory: true, + minver: 1, + webm: false, + description: "Filename of the attached file." + }, + 0x467e: { + name: "FileDescription", + level: 3, + type: "8", + minver: 1, + webm: false, + description: "A human-friendly name for the attached file." + }, + 0x61a7: { + name: "AttachedFile", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "An attached file." + }, + 0x1941a469: { + name: "Attachments", + level: 1, + type: "m", + minver: 1, + webm: false, + description: "Contain attached files." + }, + 0xeb: { + name: "CueRefCodecState", + level: 5, + type: "u", + webm: false, + "default": 0, + description: "The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry." + }, + 0x535f: { + name: "CueRefNumber", + level: 5, + type: "u", + webm: false, + "default": 1, + range: "not 0", + description: "Number of the referenced Block of Track X in the specified Cluster." + }, + 0xdb: { + name: "CueReference", + level: 4, + type: "m", + multiple: true, + minver: 2, + webm: false, + description: "The Clusters containing the required referenced Blocks." + }, + 0xea: { + name: "CueCodecState", + level: 4, + type: "u", + minver: 2, + webm: false, + "default": 0, + description: "The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry." + }, + 0xb2: { + name: "CueDuration", + level: 4, + type: "u", + mandatory: false, + minver: 4, + webm: false, + description: "The duration of the block according to the segment time base. If missing the track's DefaultDuration does not apply and no duration information is available in terms of the cues." + }, + 0xf0: { + name: "CueRelativePosition", + level: 4, + type: "u", + mandatory: false, + minver: 4, + webm: false, + description: "The relative position of the referenced block inside the cluster with 0 being the first possible position for an element inside that cluster.", + position: "clusterRelative" + }, + 0xf1: { + name: "CueClusterPosition", + level: 4, + type: "u", + mandatory: true, + minver: 1, + description: "The position of the Cluster containing the required Block.", + position: "segment", + }, + 0xf7: { + name: "CueTrack", + level: 4, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "The track for which a position is given." + }, + 0xb7: { + name: "CueTrackPositions", + level: 3, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contain positions for different tracks corresponding to the timestamp." + }, + 0xb3: { + name: "CueTime", + level: 3, + type: "u", + mandatory: true, + minver: 1, + description: "Absolute timestamp according to the segment time base." + }, + 0xbb: { + name: "CuePoint", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contains all information relative to a seek point in the segment." + }, + 0x1c53bb6b: { + name: "Cues", + level: 1, + type: "m", + minver: 1, + description: "A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non \"live\" streams." + }, + 0x47e6: { + name: "ContentSigHashAlgo", + level: 6, + type: "u", + minver: 1, + webm: false, + "default": 0, + // "br": [ "", "" ], + description: "The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values: 1 - SHA1-160 2 - MD5" + }, + 0x47e5: { + name: "ContentSigAlgo", + level: 6, + type: "u", + minver: 1, + webm: false, + "default": 0, + // "br": "", + description: "The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values: 1 - RSA" + }, + 0x47e4: { + name: "ContentSigKeyID", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "This is the ID of the private key the data was signed with." + }, + 0x47e3: { + name: "ContentSignature", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "A cryptographic signature of the contents." + }, + 0x47e2: { + name: "ContentEncKeyID", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "For public key algorithms this is the ID of the public key the the data was encrypted with." + }, + 0x47e1: { + name: "ContentEncAlgo", + level: 6, + type: "u", + minver: 1, + webm: false, + "default": 0, + // "br": "", + description: "The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values: 1 - DES, 2 - 3DES, 3 - Twofish, 4 - Blowfish, 5 - AES" + }, + 0x6d80: { + name: "ContentEncodings", + level: 3, + type: "m", + minver: 1, + webm: false, + description: "Settings for several content encoding mechanisms like compression or encryption." + }, + 0xc4: { + name: "TrickMasterTrackSegmentUID", + level: 3, + type: "b", + divx: true, + bytesize: 16, + description: "DivX trick track extenstions" + }, + 0xc7: { + name: "TrickMasterTrackUID", + level: 3, + type: "u", + divx: true, + description: "DivX trick track extenstions" + }, + 0xc6: { + name: "TrickTrackFlag", + level: 3, + type: "u", + divx: true, + "default": 0, + description: "DivX trick track extenstions" + }, + 0xc1: { + name: "TrickTrackSegmentUID", + level: 3, + type: "b", + divx: true, + bytesize: 16, + description: "DivX trick track extenstions" + }, + 0xc0: { + name: "TrickTrackUID", + level: 3, + type: "u", + divx: true, + description: "DivX trick track extenstions" + }, + 0xed: { + name: "TrackJoinUID", + level: 5, + type: "u", + mandatory: true, + multiple: true, + minver: 3, + webm: false, + range: "not 0", + description: "The trackUID number of a track whose blocks are used to create this virtual track." + }, + 0xe9: { + name: "TrackJoinBlocks", + level: 4, + type: "m", + minver: 3, + webm: false, + description: "Contains the list of all tracks whose Blocks need to be combined to create this virtual track" + }, + 0xe6: { + name: "TrackPlaneType", + level: 6, + type: "u", + mandatory: true, + minver: 3, + webm: false, + description: "The kind of plane this track corresponds to (0: left eye, 1: right eye, 2: background)." + }, + 0xe5: { + name: "TrackPlaneUID", + level: 6, + type: "u", + mandatory: true, + minver: 3, + webm: false, + range: "not 0", + description: "The trackUID number of the track representing the plane." + }, + 0xe4: { + name: "TrackPlane", + level: 5, + type: "m", + mandatory: true, + multiple: true, + minver: 3, + webm: false, + description: "Contains a video plane track that need to be combined to create this 3D track" + }, + 0xe3: { + name: "TrackCombinePlanes", + level: 4, + type: "m", + minver: 3, + webm: false, + description: "Contains the list of all video plane tracks that need to be combined to create this 3D track" + }, + 0xe2: { + name: "TrackOperation", + level: 3, + type: "m", + minver: 3, + webm: false, + description: "Operation that needs to be applied on tracks to create this virtual track. For more details look at the Specification Notes on the subject." + }, + 0x7d7b: { + name: "ChannelPositions", + cppname: "AudioPosition", + level: 4, + type: "b", + webm: false, + description: "Table of horizontal angles for each successive channel, see appendix." + }, + 0x9f: { + name: "Channels", + cppname: "AudioChannels", + level: 4, + type: "u", + mandatory: true, + minver: 1, + "default": 1, + range: "not 0", + description: "Numbers of channels in the track." + }, + 0x78b5: { + name: "OutputSamplingFrequency", + cppname: "AudioOutputSamplingFreq", + level: 4, + type: "f", + minver: 1, + "default": "Sampling Frequency", + range: "> 0", + description: "Real output sampling frequency in Hz (used for SBR techniques)." + }, + 0xb5: { + name: "SamplingFrequency", + cppname: "AudioSamplingFreq", + level: 4, + type: "f", + mandatory: true, + minver: 1, + "default": 8000.0, + range: "> 0", + description: "Sampling frequency in Hz." + }, + 0xe1: { + name: "Audio", + cppname: "TrackAudio", + level: 3, + type: "m", + minver: 1, + description: "Audio settings." + }, + 0x2383e3: { + name: "FrameRate", + cppname: "VideoFrameRate", + level: 4, + type: "f", + range: "> 0", + "strong": "Informational", + description: "Number of frames per second. only." + }, + 0x2fb523: { + name: "GammaValue", + cppname: "VideoGamma", + level: 4, + type: "f", + webm: false, + range: "> 0", + description: "Gamma Value." + }, + 0x2eb524: { + name: "ColourSpace", + cppname: "VideoColourSpace", + level: 4, + type: "b", + minver: 1, + webm: false, + bytesize: 4, + description: "Same value as in AVI (32 bits)." + }, + 0x54b3: { + name: "AspectRatioType", + cppname: "VideoAspectRatio", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed)." + }, + 0x54b2: { + name: "DisplayUnit", + cppname: "VideoDisplayUnit", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "How DisplayWidth & DisplayHeight should be interpreted (0: pixels, 1: centimeters, 2: inches, 3: Display Aspect Ratio)." + }, + 0x54ba: { + name: "DisplayHeight", + cppname: "VideoDisplayHeight", + level: 4, + type: "u", + minver: 1, + "default": "PixelHeight", + range: "not 0", + description: "Height of the video frames to display. The default value is only valid when DisplayUnit is 0." + }, + 0x54b0: { + name: "DisplayWidth", + cppname: "VideoDisplayWidth", + level: 4, + type: "u", + minver: 1, + "default": "PixelWidth", + range: "not 0", + description: "Width of the video frames to display. The default value is only valid when DisplayUnit is 0." + }, + 0x54dd: { + name: "PixelCropRight", + cppname: "VideoPixelCropRight", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove on the right of the image." + }, + 0x54cc: { + name: "PixelCropLeft", + cppname: "VideoPixelCropLeft", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove on the left of the image." + }, + 0x54bb: { + name: "PixelCropTop", + cppname: "VideoPixelCropTop", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove at the top of the image." + }, + 0x54aa: { + name: "PixelCropBottom", + cppname: "VideoPixelCropBottom", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove at the bottom of the image (for HDTV content)." + }, + 0xba: { + name: "PixelHeight", + cppname: "VideoPixelHeight", + level: 4, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "Height of the encoded video frames in pixels." + }, + 0xb0: { + name: "PixelWidth", + cppname: "VideoPixelWidth", + level: 4, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "Width of the encoded video frames in pixels." + }, + 0x53b9: { + name: "OldStereoMode", + level: 4, + type: "u", + "maxver": "0", + webm: false, + divx: false, + description: "DEPRECATED, DO NOT USE. Bogus StereoMode value used in old versions of libmatroska. (0: mono, 1: right eye, 2: left eye, 3: both eyes)." + }, + 0x53c0: { + name: "AlphaMode", + cppname: "VideoAlphaMode", + level: 4, + type: "u", + minver: 3, + webm: true, + "default": 0, + description: "Alpha Video Mode. Presence of this element indicates that the BlockAdditional element could contain Alpha data." + }, + 0x53b8: { + name: "StereoMode", + cppname: "VideoStereoMode", + level: 4, + type: "u", + minver: 3, + webm: true, + "default": 0, + description: "Stereo-3D video mode (0: mono, 1: side by side (left eye is first), 2: top-bottom (right eye is first), 3: top-bottom (left eye is first), 4: checkboard (right is first), 5: checkboard (left is first), 6: row interleaved (right is first), 7: row interleaved (left is first), 8: column interleaved (right is first), 9: column interleaved (left is first), 10: anaglyph (cyan/red), 11: side by side (right eye is first), 12: anaglyph (green/magenta), 13 both eyes laced in one Block (left eye is first), 14 both eyes laced in one Block (right eye is first)) . There are some more details on 3D support in the Specification Notes." + }, + 0x9a: { + name: "FlagInterlaced", + cppname: "VideoFlagInterlaced", + level: 4, + type: "u", + mandatory: true, + minver: 2, + webm: true, + "default": 0, + range: "0-1", + description: "Set if the video is interlaced. (1 bit)" + }, + 0xe0: { + name: "Video", + cppname: "TrackVideo", + level: 3, + type: "m", + minver: 1, + description: "Video settings." + }, + 0x66a5: { + name: "TrackTranslateTrackID", + level: 4, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used." + }, + 0x66bf: { + name: "TrackTranslateCodec", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + description: "The chapter codec using this ID (0: Matroska Script, 1: DVD-menu)." + }, + 0x66fc: { + name: "TrackTranslateEditionUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment." + }, + 0x56bb: { + name: "SeekPreRoll", + level: 3, + type: "u", + mandatory: true, + multiple: false, + "default": 0, + minver: 4, + webm: true, + description: "After a discontinuity, SeekPreRoll is the duration in nanoseconds of the data the decoder must decode before the decoded data is valid." + }, + 0x56aa: { + name: "CodecDelay", + level: 3, + type: "u", + multiple: false, + "default": 0, + minver: 4, + webm: true, + description: "CodecDelay is The codec-built-in delay in nanoseconds. This value must be subtracted from each block timestamp in order to get the actual timestamp. The value should be small so the muxing of tracks with the same actual timestamp are in the same Cluster." + }, + 0x6fab: { + name: "TrackOverlay", + level: 3, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see SilentTracks) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc." + }, + 0xaa: { + name: "CodecDecodeAll", + level: 3, + type: "u", + mandatory: true, + minver: 2, + webm: false, + "default": 1, + range: "0-1", + description: "The codec can decode potentially damaged data (1 bit)." + }, + 0x26b240: { + name: "CodecDownloadURL", + level: 3, + type: "s", + multiple: true, + webm: false, + description: "A URL to download about the codec used." + }, + 0x3b4040: { + name: "CodecInfoURL", + level: 3, + type: "s", + multiple: true, + webm: false, + description: "A URL to find information about the codec used." + }, + 0x3a9697: { + name: "CodecSettings", + level: 3, + type: "8", + webm: false, + description: "A string describing the encoding setting used." + }, + 0x63a2: { + name: "CodecPrivate", + level: 3, + type: "b", + minver: 1, + description: "Private data only known to the codec." + }, + 0x22b59c: { + name: "Language", + cppname: "TrackLanguage", + level: 3, + type: "s", + minver: 1, + "default": "eng", + description: "Specifies the language of the track in the Matroska languages form." + }, + 0x536e: { + name: "Name", + cppname: "TrackName", + level: 3, + type: "8", + minver: 1, + description: "A human-readable track name." + }, + 0x55ee: { + name: "MaxBlockAdditionID", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "The maximum value of BlockAdditions for this track." + }, + 0x537f: { + name: "TrackOffset", + level: 3, + type: "i", + webm: false, + "default": 0, + description: "A value to add to the Block's Timestamp. This can be used to adjust the playback offset of a track." + }, + 0x23314f: { + name: "TrackTimecodeScale", + level: 3, + type: "f", + mandatory: true, + minver: 1, + "maxver": "3", + webm: false, + "default": 1.0, + range: "> 0", + description: "DEPRECATED, DO NOT USE. The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs)." + }, + 0x234e7a: { + name: "DefaultDecodedFieldDuration", + cppname: "TrackDefaultDecodedFieldDuration", + level: 3, + type: "u", + minver: 4, + range: "not 0", + description: "The period in nanoseconds (not scaled by TimcodeScale)\nbetween two successive fields at the output of the decoding process (see the notes)" + }, + 0x23e383: { + name: "DefaultDuration", + cppname: "TrackDefaultDuration", + level: 3, + type: "u", + minver: 1, + range: "not 0", + description: "Number of nanoseconds (not scaled via TimecodeScale) per frame ('frame' in the Matroska sense -- one element put into a (Simple)Block)." + }, + 0x6df8: { + name: "MaxCache", + cppname: "TrackMaxCache", + level: 3, + type: "u", + minver: 1, + webm: false, + description: "The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed." + }, + 0x6de7: { + name: "MinCache", + cppname: "TrackMinCache", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used." + }, + 0x9c: { + name: "FlagLacing", + cppname: "TrackFlagLacing", + level: 3, + type: "u", + mandatory: true, + minver: 1, + "default": 1, + range: "0-1", + description: "Set if the track may contain blocks using lacing. (1 bit)" + }, + 0x55aa: { + name: "FlagForced", + cppname: "TrackFlagForced", + level: 3, + type: "u", + mandatory: true, + minver: 1, + "default": 0, + range: "0-1", + description: "Set if that track MUST be active during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind. (1 bit)" + }, + 0xb9: { + name: "FlagEnabled", + cppname: "TrackFlagEnabled", + level: 3, + type: "u", + mandatory: true, + minver: 2, + webm: true, + "default": 1, + range: "0-1", + description: "Set if the track is usable. (1 bit)" + }, + 0x73c5: { + name: "TrackUID", + level: 3, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file." + }, + 0xd7: { + name: "TrackNumber", + level: 3, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number)." + }, + 0xae: { + name: "TrackEntry", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Describes a track with all elements." + }, + 0x1654ae6b: { + name: "Tracks", + level: 1, + type: "m", + multiple: true, + minver: 1, + description: "A top-level block of information with many tracks described." + }, + 0xaf: { + name: "EncryptedBlock", + level: 2, + type: "b", + multiple: true, + webm: false, + description: "Similar to EncryptedBlock Structure)" + }, + 0xca: { + name: "ReferenceTimeCode", + level: 4, + type: "u", + multiple: false, + mandatory: true, + minver: 0, + webm: false, + divx: true, + description: "DivX trick track extenstions" + }, + 0xc9: { + name: "ReferenceOffset", + level: 4, + type: "u", + multiple: false, + mandatory: true, + minver: 0, + webm: false, + divx: true, + description: "DivX trick track extenstions" + }, + 0xc8: { + name: "ReferenceFrame", + level: 3, + type: "m", + multiple: false, + minver: 0, + webm: false, + divx: true, + description: "DivX trick track extenstions" + }, + 0xcf: { + name: "SliceDuration", + level: 5, + type: "u", + "default": 0, + description: "The (scaled) duration to apply to the element." + }, + 0xce: { + name: "Delay", + cppname: "SliceDelay", + level: 5, + type: "u", + "default": 0, + description: "The (scaled) delay to apply to the element." + }, + 0xcb: { + name: "BlockAdditionID", + cppname: "SliceBlockAddID", + level: 5, + type: "u", + "default": 0, + description: "The ID of the BlockAdditional element (0 is the main Block)." + }, + 0xcd: { + name: "FrameNumber", + cppname: "SliceFrameNumber", + level: 5, + type: "u", + "default": 0, + description: "The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame)." + }, + 0xcc: { + name: "LaceNumber", + cppname: "SliceLaceNumber", + level: 5, + type: "u", + minver: 1, + "default": 0, + divx: false, + description: "The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback." + }, + 0xe8: { + name: "TimeSlice", + level: 4, + type: "m", + multiple: true, + minver: 1, + divx: false, + description: "Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback." + }, + 0x8e: { + name: "Slices", + level: 3, + type: "m", + minver: 1, + divx: false, + description: "Contains slices description." + }, + 0x75a2: { + name: "DiscardPadding", + level: 3, + type: "i", + minver: 4, + webm: true, + description: "Duration in nanoseconds of the silent data added to the Block (padding at the end of the Block for positive value, at the beginning of the Block for negative value). The duration of DiscardPadding is not calculated in the duration of the TrackEntry and should be discarded during playback." + }, + 0xa4: { + name: "CodecState", + level: 3, + type: "b", + minver: 2, + webm: false, + description: "The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry." + }, + 0xfd: { + name: "ReferenceVirtual", + level: 3, + type: "i", + webm: false, + description: "Relative position of the data that should be in position of the virtual block." + }, + 0xfb: { + name: "ReferenceBlock", + level: 3, + type: "i", + multiple: true, + minver: 1, + description: "Timestamp of another frame used as a reference (ie: B or P frame). The timestamp is relative to the block it's attached to." + }, + 0xfa: { + name: "ReferencePriority", + cppname: "FlagReferenced", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced." + }, + 0x9b: { + name: "BlockDuration", + level: 3, + type: "u", + minver: 1, + "default": "TrackDuration", + description: "The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track (but can be omitted as other default values). When not written and with no DefaultDuration, the value is assumed to be the difference between the timestamp of this Block and the timestamp of the next Block in \"display\" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks. When set to 0 that means the frame is not a keyframe." + }, + 0xa5: { + name: "BlockAdditional", + level: 5, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "Interpreted by the codec as it wishes (using the BlockAddID)." + }, + 0xee: { + name: "BlockAddID", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "not 0", + description: "An ID to identify the BlockAdditional level." + }, + 0xa6: { + name: "BlockMore", + level: 4, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Contain the BlockAdditional and some parameters." + }, + 0x75a1: { + name: "BlockAdditions", + level: 3, + type: "m", + minver: 1, + webm: false, + description: "Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block structure could still see and use/skip these data." + }, + 0xa2: { + name: "BlockVirtual", + level: 3, + type: "b", + webm: false, + description: "A Block with no data. It must be stored in the stream at the place the real Block should be in display order. (see Block Virtual)" + }, + 0xa1: { + name: "Block", + level: 3, + type: "b", + mandatory: true, + minver: 1, + description: "Block containing the actual data to be rendered and a timestamp relative to the Cluster Timecode. (see Block Structure)" + }, + 0xa0: { + name: "BlockGroup", + level: 2, + type: "m", + multiple: true, + minver: 1, + description: "Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock." + }, + 0xa3: { + name: "SimpleBlock", + level: 2, + type: "b", + multiple: true, + minver: 2, + webm: true, + divx: true, + description: "Similar to SimpleBlock Structure" + }, + 0xab: { + name: "PrevSize", + cppname: "ClusterPrevSize", + level: 2, + type: "u", + minver: 1, + description: "Size of the previous Cluster, in octets. Can be useful for backward playing.", + position: "prevCluster" + }, + 0xa7: { + name: "Position", + cppname: "ClusterPosition", + level: 2, + type: "u", + minver: 1, + webm: false, + description: "The Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.", + position: "segment" + }, + 0x58d7: { + name: "SilentTrackNumber", + cppname: "ClusterSilentTrackNumber", + level: 3, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster." + }, + 0xe7: { + name: "Timecode", + cppname: "ClusterTimecode", + level: 2, + type: "u", + mandatory: true, + minver: 1, + description: "Absolute timestamp of the cluster (based on TimecodeScale)." + }, + 0x1f43b675: { + name: "Cluster", + level: 1, + type: "m", + multiple: true, + minver: 1, + description: "The lower level element containing the (monolithic) Block structure." + }, + 0x4d80: { + name: "MuxingApp", + level: 2, + type: "8", + mandatory: true, + minver: 1, + description: "Muxing application or library (\"libmatroska-0.4.3\")." + }, + 0x7ba9: { + name: "Title", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "General name of the segment." + }, + 0x2ad7b2: { + name: "TimecodeScaleDenominator", + level: 2, + type: "u", + mandatory: true, + minver: 4, + "default": "1000000000", + description: "Timestamp scale numerator, see TimecodeScale." + }, + 0x2ad7b1: { + name: "TimecodeScale", + level: 2, + type: "u", + mandatory: true, + minver: 1, + "default": "1000000", + description: "Timestamp scale in nanoseconds (1.000.000 means all timestamps in the segment are expressed in milliseconds)." + }, + 0x69a5: { + name: "ChapterTranslateID", + level: 3, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used." + }, + 0x69bf: { + name: "ChapterTranslateCodec", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + description: "The chapter codec using this ID (0: Matroska Script, 1: DVD-menu)." + }, + 0x69fc: { + name: "ChapterTranslateEditionUID", + level: 3, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment." + }, + 0x3e83bb: { + name: "NextFilename", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "An escaped filename corresponding to the next segment." + }, + 0x3eb923: { + name: "NextUID", + level: 2, + type: "b", + minver: 1, + webm: false, + bytesize: 16, + description: "A unique ID to identify the next chained segment (128 bits)." + }, + 0x3c83ab: { + name: "PrevFilename", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "An escaped filename corresponding to the previous segment." + }, + 0x3cb923: { + name: "PrevUID", + level: 2, + type: "b", + minver: 1, + webm: false, + bytesize: 16, + description: "A unique ID to identify the previous chained segment (128 bits)." + }, + 0x73a4: { + name: "SegmentUID", + level: 2, + type: "b", + minver: 1, + webm: false, + range: "not 0", + bytesize: 16, + description: "A randomly generated unique ID to identify the current segment between many others (128 bits)." + }, + 0x1549a966: { + name: "Info", + level: 1, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contains miscellaneous general information and statistics on the file." + }, + 0x53ac: { + name: "SeekPosition", + level: 3, + type: "u", + mandatory: true, + minver: 1, + description: "The position of the element in the segment in octets (0 = first level 1 element).", + position: "segment" + }, + 0x53ab: { + name: "SeekID", + level: 3, + type: "b", + mandatory: true, + minver: 1, + description: "The binary ID corresponding to the element name.", + type2: "ebmlID" + }, + 0x4dbb: { + name: "Seek", + cppname: "SeekPoint", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contains a single seek entry to an EBML element." + }, + 0x114d9b74: { + name: "SeekHead", + cppname: "SeekHeader", + level: 1, + type: "m", + multiple: true, + minver: 1, + description: "Contains the position of other level 1 elements." + }, + 0x7e7b: { + name: "SignatureElementList", + level: 2, + type: "m", + multiple: true, + webm: false, + i: "Cluster|Block|BlockAdditional", + description: "A list consists of a number of consecutive elements that represent one case where data is used in signature. Ex: means that the BlockAdditional of all Blocks in all Clusters is used for encryption." + }, + 0x7e5b: { + name: "SignatureElements", + level: 1, + type: "m", + webm: false, + description: "Contains elements that will be used to compute the signature." + }, + 0x7eb5: { + name: "Signature", + level: 1, + type: "b", + webm: false, + description: "The signature of the data (until a new." + }, + 0x7ea5: { + name: "SignaturePublicKey", + level: 1, + type: "b", + webm: false, + description: "The public key to use with the algorithm (in the case of a PKI-based signature)." + }, + 0x7e9a: { + name: "SignatureHash", + level: 1, + type: "u", + webm: false, + description: "Hash algorithm used (1=SHA1-160, 2=MD5)." + }, + 0x7e8a: { + name: "SignatureAlgo", + level: 1, + type: "u", + webm: false, + description: "Signature algorithm used (1=RSA, 2=elliptic)." + }, + 0x1b538667: { + name: "SignatureSlot", + level: -1, + type: "m", + multiple: true, + webm: false, + description: "Contain signature of some (coming) elements in the stream." + }, + 0xbf: { + name: "CRC-32", + level: -1, + type: "b", + minver: 1, + webm: false, + description: "The CRC is computed on all the data of the Master element it's in. The CRC element should be the first in it's parent master for easier reading. All level 1 elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian", + crc: true + }, + 0xec: { + name: "Void", + level: -1, + type: "b", + minver: 1, + description: "Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use." + }, + 0x42f3: { + name: "EBMLMaxSizeLength", + level: 1, + type: "u", + mandatory: true, + "default": 8, + minver: 1, + description: "The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid." + }, + 0x42f2: { + name: "EBMLMaxIDLength", + level: 1, + type: "u", + mandatory: true, + "default": 4, + minver: 1, + description: "The maximum length of the IDs you'll find in this file (4 or less in Matroska)." + }, + 0x42f7: { + name: "EBMLReadVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The minimum EBML version a parser has to support to read this file." + }, + 0x1a45dfa3: { + name: "EBML", + level: "0", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Set the EBML characteristics of the data to follow. Each EBML document has to start with this." + } +}; diff --git a/lib/ebml/index.d.ts b/lib/ebml/index.d.ts new file mode 100644 index 0000000..67353db --- /dev/null +++ b/lib/ebml/index.d.ts @@ -0,0 +1,6 @@ +export * from "./EBML"; +import Decoder from "./EBMLDecoder"; +import Encoder from "./EBMLEncoder"; +import Reader from "./EBMLReader"; +import * as tools from "./tools"; +export { Decoder, Encoder, Reader, tools }; diff --git a/lib/ebml/index.js b/lib/ebml/index.js new file mode 100644 index 0000000..283f47d --- /dev/null +++ b/lib/ebml/index.js @@ -0,0 +1,22 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.tools = exports.Reader = exports.Encoder = exports.Decoder = void 0; +__exportStar(require("./EBML"), exports); +var EBMLDecoder_1 = require("./EBMLDecoder"); +exports.Decoder = EBMLDecoder_1.default; +var EBMLEncoder_1 = require("./EBMLEncoder"); +exports.Encoder = EBMLEncoder_1.default; +var EBMLReader_1 = require("./EBMLReader"); +exports.Reader = EBMLReader_1.default; +var tools = require("./tools"); +exports.tools = tools; diff --git a/lib/ebml/tools-ebml.d.ts b/lib/ebml/tools-ebml.d.ts new file mode 100644 index 0000000..646c1f9 --- /dev/null +++ b/lib/ebml/tools-ebml.d.ts @@ -0,0 +1,80 @@ +/// +export default class Tools { + /** + * read variable length integer per + * https://www.matroska.org/technical/specs/index.html#EBML_ex + * @static + * @param {Buffer} buffer containing input + * @param {Number} [start=0] position in buffer + * @returns {{length: Number, value: number}} value / length object + */ + static readVint(buffer: any, start?: number): { + length: number; + value: number; + } | null; + /** + * write variable length integer + * @static + * @param {Number} value to store into buffer + * @returns {Buffer} containing the value + */ + static writeVint(value: any): Buffer; + /** + * * + * concatenate two arrays of bytes + * @static + * @param {Buffer} a1 First array + * @param {Buffer} a2 Second array + * @returns {Buffer} concatenated arrays + */ + static concatenate(a1: any, a2: any): any; + /** + * get a hex text string from Buff[start,end) + * @param {Buffer} buff from which to read the string + * @param {Number} [start=0] starting point (default 0) + * @param {Number} [end=buff.byteLength] ending point (default the whole buffer) + * @returns {string} the hex string + */ + static readHexString(buff: any, start?: number, end?: any): string; + /** + * tries to read out a UTF-8 encoded string + * @param {Buffer} buff the buffer to attempt to read from + * @return {string|null} the decoded text, or null if unable to + */ + static readUtf8(buff: any): string | null; + /** + * get an unsigned number from a buffer + * @param {Buffer} buff from which to read variable-length unsigned number + * @returns {number|string} result (in hex for lengths > 6) + */ + static readUnsigned(buff: any): any; + /** + * get an signed number from a buffer + * @static + * @param {Buffer} buff from which to read variable-length signed number + * @returns {number} result + */ + static readSigned(buff: any): number; + /** + * get an floating-point number from a buffer + * @static + * @param {Buffer} buff from which to read variable-length floating-point number + * @returns {number} result + */ + static readFloat(buff: any): number; + /** + * get a date from a buffer + * @static + * @param {Buffer} buff from which to read the date + * @return {Date} result + */ + static readDate(buff: any): Date; + /** + * Reads the data from a tag + * @static + * @param {TagData} tagObj The tag object to be read + * @param {Buffer} data Data to be transformed + * @return {Tag} result + */ + static readDataFromTag(tagObj: any, data: any): any; +} diff --git a/lib/ebml/tools-ebml.js b/lib/ebml/tools-ebml.js new file mode 100644 index 0000000..c1f716f --- /dev/null +++ b/lib/ebml/tools-ebml.js @@ -0,0 +1,265 @@ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var tools_1 = require("./tools"); +var Tools = /** @class */ (function () { + function Tools() { + } + /** + * read variable length integer per + * https://www.matroska.org/technical/specs/index.html#EBML_ex + * @static + * @param {Buffer} buffer containing input + * @param {Number} [start=0] position in buffer + * @returns {{length: Number, value: number}} value / length object + */ + Tools.readVint = function (buffer, start) { + if (start === void 0) { start = 0; } + var length = 8 - Math.floor(Math.log2(buffer[start])); + if (length > 8) { + var number = Tools.readHexString(buffer, start, start + length); + throw new Error("Unrepresentable length: ".concat(length, " ").concat(number)); + } + if (start + length > buffer.length) { + return null; + } + var value = buffer[start] & ((1 << (8 - length)) - 1); + for (var i = 1; i < length; i += 1) { + if (i === 7) { + if (value >= Math.pow(2, 8) && buffer[start + 7] > 0) { + return { length: length, value: -1 }; + } + } + value *= Math.pow(2, 8); + value += buffer[start + i]; + } + return { length: length, value: value }; + }; + /** + * write variable length integer + * @static + * @param {Number} value to store into buffer + * @returns {Buffer} containing the value + */ + Tools.writeVint = function (value) { + if (value < 0 || value > Math.pow(2, 53)) { + throw new Error("Unrepresentable value: ".concat(value)); + } + var length = 1; + for (length = 1; length <= 8; length += 1) { + if (value < Math.pow(2, (7 * length)) - 1) { + break; + } + } + var buffer = tools_1.Buffer.alloc(length); + var val = value; + for (var i = 1; i <= length; i += 1) { + var b = val & 0xff; + buffer[length - i] = b; + val -= b; + val /= Math.pow(2, 8); + } + buffer[0] |= 1 << (8 - length); + return buffer; + }; + /** + * * + * concatenate two arrays of bytes + * @static + * @param {Buffer} a1 First array + * @param {Buffer} a2 Second array + * @returns {Buffer} concatenated arrays + */ + Tools.concatenate = function (a1, a2) { + // both null or undefined + if (!a1 && !a2) { + return tools_1.Buffer.from([]); + } + if (!a1 || a1.byteLength === 0) { + return a2; + } + if (!a2 || a2.byteLength === 0) { + return a1; + } + return tools_1.Buffer.from(__spreadArray(__spreadArray([], a1, true), a2, true)); + }; + /** + * get a hex text string from Buff[start,end) + * @param {Buffer} buff from which to read the string + * @param {Number} [start=0] starting point (default 0) + * @param {Number} [end=buff.byteLength] ending point (default the whole buffer) + * @returns {string} the hex string + */ + Tools.readHexString = function (buff, start, end) { + if (start === void 0) { start = 0; } + if (end === void 0) { end = buff.byteLength; } + return Array.from(buff.slice(start, end)) + .map(function (q) { return Number(q).toString(16); }) + .reduce(function (acc, current) { return "".concat(acc).concat(current.padStart(2, '0')); }, ''); + }; + /** + * tries to read out a UTF-8 encoded string + * @param {Buffer} buff the buffer to attempt to read from + * @return {string|null} the decoded text, or null if unable to + */ + Tools.readUtf8 = function (buff) { + try { + return tools_1.Buffer.from(buff).toString('utf8'); + } + catch (exception) { + return null; + } + }; + /** + * get an unsigned number from a buffer + * @param {Buffer} buff from which to read variable-length unsigned number + * @returns {number|string} result (in hex for lengths > 6) + */ + Tools.readUnsigned = function (buff) { + var b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 1: + return b.getUint8(0); + case 2: + return b.getUint16(0); + case 4: + return b.getUint32(0); + default: + break; + } + if (buff.byteLength <= 6) { + return buff.reduce(function (acc, current) { return acc * 256 + current; }, 0); + } + return Tools.readHexString(buff, 0, buff.byteLength); + }; + /** + * get an signed number from a buffer + * @static + * @param {Buffer} buff from which to read variable-length signed number + * @returns {number} result + */ + Tools.readSigned = function (buff) { + var b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 1: + return b.getInt8(0); + case 2: + return b.getInt16(0); + case 4: + return b.getInt32(0); + default: + return NaN; + } + }; + /** + * get an floating-point number from a buffer + * @static + * @param {Buffer} buff from which to read variable-length floating-point number + * @returns {number} result + */ + Tools.readFloat = function (buff) { + var b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 4: + return b.getFloat32(0); + case 8: + return b.getFloat64(0); + default: + return NaN; + } + }; + /** + * get a date from a buffer + * @static + * @param {Buffer} buff from which to read the date + * @return {Date} result + */ + Tools.readDate = function (buff) { + var b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 1: + return new Date(b.getUint8(0)); + case 2: + return new Date(b.getUint16(0)); + case 4: + return new Date(b.getUint32(0)); + case 8: + return new Date(Number.parseInt(Tools.readHexString(buff), 16)); + default: + return new Date(0); + } + }; + /** + * Reads the data from a tag + * @static + * @param {TagData} tagObj The tag object to be read + * @param {Buffer} data Data to be transformed + * @return {Tag} result + */ + Tools.readDataFromTag = function (tagObj, data) { + var type = tagObj.type, name = tagObj.name; + var track = tagObj.track; + var discardable = tagObj.discardable || false; + var keyframe = tagObj.keyframe || false; + var payload = null; + var value; + switch (type) { + case 'u': + value = Tools.readUnsigned(data); + break; + case 'f': + value = Tools.readFloat(data); + break; + case 'i': + value = Tools.readSigned(data); + break; + case 's': + value = String.fromCharCode.apply(String, data); + break; + case '8': + value = Tools.readUtf8(data); + break; + case 'd': + value = Tools.readDate(data); + break; + default: + break; + } + if (name === 'SimpleBlock' || name === 'Block') { + var p = 0; + var _a = Tools.readVint(data, p), length_1 = _a.length, trak = _a.value; + p += length_1; + track = trak; + value = Tools.readSigned(data.subarray(p, p + 2)); + p += 2; + if (name === 'SimpleBlock') { + keyframe = Boolean(data[length_1 + 2] & 0x80); + discardable = Boolean(data[length_1 + 2] & 0x01); + } + p += 1; + payload = data.subarray(p); + } + return __assign(__assign({}, tagObj), { data: data, discardable: discardable, keyframe: keyframe, payload: payload, track: track, value: value }); + }; + return Tools; +}()); +exports.default = Tools; diff --git a/lib/ebml/tools.d.ts b/lib/ebml/tools.d.ts new file mode 100644 index 0000000..ce8b6ce --- /dev/null +++ b/lib/ebml/tools.d.ts @@ -0,0 +1,99 @@ +/// +import * as EBML from "./EBML"; +export declare const Buffer: typeof global.Buffer; +export declare const readVint: (buffer: Buffer, start: number) => null | ({ + length: number; + value: number; +}); +export declare const writeVint: (val: number) => Buffer; +export declare const ebmlBlock: (buf: Buffer) => EBML.SimpleBlock; +export declare function readBlock(buf: ArrayBuffer): EBML.SimpleBlock; +/** + * @param end - if end === false then length is unknown + */ +export declare function encodeTag(tagId: Buffer, tagData: Buffer, unknownSize?: boolean): Buffer; +/** + * WebP ファイルにできる SimpleBlock の パスフィルタ + */ +export declare function WebPBlockFilter(elms: EBML.EBMLElementDetail[]): (EBML.BinaryElement & EBML.ElementDetail & { + data: Buffer; +})[]; +/** + * @param frame - VP8 BitStream のうち startcode をもつ frame + * @return - WebP ファイルの ArrayBuffer + */ +export declare function VP8BitStreamToRiffWebPBuffer(frame: Buffer): Buffer; +/** + * RIFF データチャンクを作る + */ +export declare function createRIFFChunk(FourCC: string, chunk: Buffer): Buffer; +/** + * convert the metadata from a streaming webm bytestream to a seekable file by inserting Duration, Seekhead and Cues + * @param originalMetadata - orginal metadata (everything before the clusters start) from media recorder + * @param duration - Duration (TimecodeScale) + * @param cues - cue points for clusters + */ +export declare function makeMetadataSeekable(originalMetadata: EBML.EBMLElementDetail[], duration: number, cuesInfo: { + CueTrack: number; + CueClusterPosition: number; + CueTime: number; +}[]): ArrayBuffer; +/** + * print all element id names in a list + + * @param metadata - array of EBML elements to print + * +export function printElementIds(metadata: EBML.EBMLElementBuffer[]) { + + let result: EBML.EBMLElementBuffer[] = []; + let start: number = -1; + + for (let i = 0; i < metadata.length; i++) { + console.error("\t id: " + metadata[i].name); + } +} +*/ +/** + * remove all occurances of an EBML element from an array of elements + * If it's a MasterElement you will also remove the content. (everything between start and end) + * @param idName - name of the EBML Element to remove. + * @param metadata - array of EBML elements to search + */ +export declare function removeElement(idName: string, metadata: EBML.EBMLElementBuffer[]): void; +/** + * extract the first occurance of an EBML tag from a flattened array of EBML data. + * If it's a MasterElement you will also get the content. (everything between start and end) + * @param idName - name of the EBML Element to extract. + * @param metadata - array of EBML elements to search + */ +export declare function extractElement(idName: string, metadata: EBML.EBMLElementBuffer[]): EBML.EBMLElementBuffer[]; +/** + * @deprecated + * metadata に対して duration と seekhead を追加した metadata を返す + * @param metadata - 変更前の webm における ファイル先頭から 最初の Cluster 要素までの 要素 + * @param duration - Duration (TimecodeScale) + * @param cues - cue points for clusters + * @deprecated @param clusterPtrs - 変更前の webm における SeekHead に追加する Cluster 要素 への start pointer + * @deprecated @param cueInfos - please use cues. + */ +export declare function putRefinedMetaData(metadata: EBML.EBMLElementDetail[], info: { + duration?: number; + cues?: { + CueTrack: number; + CueClusterPosition: number; + CueTime: number; + }[]; + clusterPtrs?: number[]; + cueInfos?: { + CueTrack: number; + CueClusterPosition: number; + CueTime: number; + }[]; +}): ArrayBuffer; +export declare function concat(list: Buffer[]): Buffer; +export declare function encodeValueToBuffer(elm: EBML.MasterElement): EBML.MasterElement; +export declare function encodeValueToBuffer(elm: EBML.ChildElementsValue): EBML.ChildElementBuffer; +export declare function createUIntBuffer(value: number): Buffer; +export declare function createIntBuffer(value: number): Buffer; +export declare function createFloatBuffer(value: number, bytes?: 4 | 8): Buffer; +export declare function convertEBMLDateToJSDate(int64str: number | string | Date): Date; diff --git a/lib/ebml/tools.js b/lib/ebml/tools.js new file mode 100644 index 0000000..fea4e84 --- /dev/null +++ b/lib/ebml/tools.js @@ -0,0 +1,668 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.convertEBMLDateToJSDate = exports.createFloatBuffer = exports.createIntBuffer = exports.createUIntBuffer = exports.encodeValueToBuffer = exports.concat = exports.putRefinedMetaData = exports.extractElement = exports.removeElement = exports.makeMetadataSeekable = exports.createRIFFChunk = exports.VP8BitStreamToRiffWebPBuffer = exports.WebPBlockFilter = exports.encodeTag = exports.readBlock = exports.ebmlBlock = exports.writeVint = exports.readVint = exports.Buffer = void 0; +/// +var int64_buffer_1 = require("int64-buffer"); +var EBMLEncoder_1 = require("./EBMLEncoder"); +var _Buffer = require("buffer"); +var tools_ebml_1 = require("./tools-ebml"); +var _block = require("ebml-block"); +exports.Buffer = _Buffer.Buffer; +exports.readVint = tools_ebml_1.default.readVint; +exports.writeVint = tools_ebml_1.default.writeVint; +exports.ebmlBlock = _block; +function readBlock(buf) { + return (0, exports.ebmlBlock)(new exports.Buffer(buf)); +} +exports.readBlock = readBlock; +/** + * @param end - if end === false then length is unknown + */ +function encodeTag(tagId, tagData, unknownSize) { + if (unknownSize === void 0) { unknownSize = false; } + return concat([ + tagId, + unknownSize ? + new exports.Buffer('01ffffffffffffff', 'hex') : + (0, exports.writeVint)(tagData.length), + tagData + ]); +} +exports.encodeTag = encodeTag; +/** + * WebP ファイルにできる SimpleBlock の パスフィルタ + */ +function WebPBlockFilter(elms) { + return elms.reduce(function (lst, elm) { + if (elm.type !== "b") { + return lst; + } + if (elm.name !== "SimpleBlock") { + return lst; + } + var o = (0, exports.ebmlBlock)(elm.data); + var hasWebP = o.frames.some(function (frame) { + // https://tools.ietf.org/html/rfc6386#section-19.1 + var startcode = frame.slice(3, 6).toString("hex"); + return startcode === "9d012a"; + }); + if (!hasWebP) { + return lst; + } + return lst.concat(elm); + }, []); +} +exports.WebPBlockFilter = WebPBlockFilter; +/** + * @param frame - VP8 BitStream のうち startcode をもつ frame + * @return - WebP ファイルの ArrayBuffer + */ +function VP8BitStreamToRiffWebPBuffer(frame) { + var VP8Chunk = createRIFFChunk("VP8 ", frame); + var WebPChunk = concat([ + new exports.Buffer("WEBP", "ascii"), + VP8Chunk + ]); + return createRIFFChunk("RIFF", WebPChunk); +} +exports.VP8BitStreamToRiffWebPBuffer = VP8BitStreamToRiffWebPBuffer; +/** + * RIFF データチャンクを作る + */ +function createRIFFChunk(FourCC, chunk) { + var chunkSize = new exports.Buffer(4); + chunkSize.writeUInt32LE(chunk.byteLength, 0); + return concat([ + new exports.Buffer(FourCC.substr(0, 4), "ascii"), + chunkSize, + chunk, + new exports.Buffer(chunk.byteLength % 2 === 0 ? 0 : 1) // padding + ]); +} +exports.createRIFFChunk = createRIFFChunk; +/* Original Metadata + + m 0 EBML + u 1 EBMLVersion 1 + u 1 EBMLReadVersion 1 + u 1 EBMLMaxIDLength 4 + u 1 EBMLMaxSizeLength 8 + s 1 DocType webm + u 1 DocTypeVersion 4 + u 1 DocTypeReadVersion 2 + m 0 Segment + m 1 Info segmentContentStartPos, all CueClusterPositions provided in info.cues will be relative to here and will need adjusted + u 2 TimecodeScale 1000000 + 8 2 MuxingApp Chrome + 8 2 WritingApp Chrome + m 1 Tracks tracksStartPos + m 2 TrackEntry + u 3 TrackNumber 1 + u 3 TrackUID 31790271978391090 + u 3 TrackType 2 + s 3 CodecID A_OPUS + b 3 CodecPrivate + m 3 Audio + f 4 SamplingFrequency 48000 + u 4 Channels 1 + m 2 TrackEntry + u 3 TrackNumber 2 + u 3 TrackUID 24051277436254136 + u 3 TrackType 1 + s 3 CodecID V_VP8 + m 3 Video + u 4 PixelWidth 1024 + u 4 PixelHeight 576 + m 1 Cluster clusterStartPos + u 2 Timecode 0 + b 2 SimpleBlock track:2 timecode:0 keyframe:true invisible:false discardable:false lacing:1 +*/ +/* Desired Metadata + + m 0 EBML + u 1 EBMLVersion 1 + u 1 EBMLReadVersion 1 + u 1 EBMLMaxIDLength 4 + u 1 EBMLMaxSizeLength 8 + s 1 DocType webm + u 1 DocTypeVersion 4 + u 1 DocTypeReadVersion 2 + m 0 Segment + m 1 SeekHead -> This is SeekPosition 0, so all SeekPositions can be calculated as (bytePos - segmentContentStartPos), which is 44 in this case + m 2 Seek + b 3 SeekID -> Buffer([0x15, 0x49, 0xA9, 0x66]) Info + u 3 SeekPosition -> infoStartPos = + m 2 Seek + b 3 SeekID -> Buffer([0x16, 0x54, 0xAE, 0x6B]) Tracks + u 3 SeekPosition { tracksStartPos } + m 2 Seek + b 3 SeekID -> Buffer([0x1C, 0x53, 0xBB, 0x6B]) Cues + u 3 SeekPosition { cuesStartPos } + m 1 Info + f 2 Duration 32480 -> overwrite, or insert if it doesn't exist + u 2 TimecodeScale 1000000 + 8 2 MuxingApp Chrome + 8 2 WritingApp Chrome + m 1 Tracks + m 2 TrackEntry + u 3 TrackNumber 1 + u 3 TrackUID 31790271978391090 + u 3 TrackType 2 + s 3 CodecID A_OPUS + b 3 CodecPrivate + m 3 Audio + f 4 SamplingFrequency 48000 + u 4 Channels 1 + m 2 TrackEntry + u 3 TrackNumber 2 + u 3 TrackUID 24051277436254136 + u 3 TrackType 1 + s 3 CodecID V_VP8 + m 3 Video + u 4 PixelWidth 1024 + u 4 PixelHeight 576 + m 1 Cues -> cuesStartPos + m 2 CuePoint + u 3 CueTime 0 + m 3 CueTrackPositions + u 4 CueTrack 1 + u 4 CueClusterPosition 3911 + m 2 CuePoint + u 3 CueTime 600 + m 3 CueTrackPositions + u 4 CueTrack 1 + u 4 CueClusterPosition 3911 + m 1 Cluster + u 2 Timecode 0 + b 2 SimpleBlock track:2 timecode:0 keyframe:true invisible:false discardable:false lacing:1 +*/ +/** + * convert the metadata from a streaming webm bytestream to a seekable file by inserting Duration, Seekhead and Cues + * @param originalMetadata - orginal metadata (everything before the clusters start) from media recorder + * @param duration - Duration (TimecodeScale) + * @param cues - cue points for clusters + */ +function makeMetadataSeekable(originalMetadata, duration, cuesInfo) { + // extract the header, we can reuse this as-is + var header = extractElement("EBML", originalMetadata); + var headerSize = encodedSizeOfEbml(header); + //console.error("Header size: " + headerSize); + //printElementIds(header); + // After the header comes the Segment open tag, which in this implementation is always 12 bytes (4 byte id, 8 byte 'unknown length') + // After that the segment content starts. All SeekPositions and CueClusterPosition must be relative to segmentContentStartPos + var segmentContentStartPos = headerSize + 12; + //console.error("segmentContentStartPos: " + segmentContentStartPos); + // find the original metadata size, and adjust it for header size and Segment start element so we can keep all positions relative to segmentContentStartPos + var originalMetadataSize = originalMetadata[originalMetadata.length - 1].dataEnd - segmentContentStartPos; + //console.error("Original Metadata size: " + originalMetadataSize); + //printElementIds(originalMetadata); + // extract the segment info, remove the potentially existing Duration element, and add our own one. + var info = extractElement("Info", originalMetadata); + removeElement("Duration", info); + info.splice(1, 0, { name: "Duration", type: "f", data: createFloatBuffer(duration, 8) }); + var infoSize = encodedSizeOfEbml(info); + //console.error("Info size: " + infoSize); + //printElementIds(info); + // extract the track info, we can re-use this as is + var tracks = extractElement("Tracks", originalMetadata); + var tracksSize = encodedSizeOfEbml(tracks); + //console.error("Tracks size: " + tracksSize); + //printElementIds(tracks); + var seekHeadSize = 47; // Initial best guess, but could be slightly larger if the Cues element is huge. + var seekHead = []; + var cuesSize = 5 + cuesInfo.length * 15; // very rough initial approximation, depends a lot on file size and number of CuePoints + var cues = []; + var lastSizeDifference = -1; // + // The size of SeekHead and Cues elements depends on how many bytes the offsets values can be encoded in. + // The actual offsets in CueClusterPosition depend on the final size of the SeekHead and Cues elements + // We need to iteratively converge to a stable solution. + var maxIterations = 10; + var _loop_1 = function (i) { + // SeekHead starts at 0 + var infoStart = seekHeadSize; // Info comes directly after SeekHead + var tracksStart = infoStart + infoSize; // Tracks comes directly after Info + var cuesStart = tracksStart + tracksSize; // Cues starts directly after + var newMetadataSize = cuesStart + cuesSize; // total size of metadata + // This is the offset all CueClusterPositions should be adjusted by due to the metadata size changing. + var sizeDifference = newMetadataSize - originalMetadataSize; + // console.error(`infoStart: ${infoStart}, infoSize: ${infoSize}`); + // console.error(`tracksStart: ${tracksStart}, tracksSize: ${tracksSize}`); + // console.error(`cuesStart: ${cuesStart}, cuesSize: ${cuesSize}`); + // console.error(`originalMetadataSize: ${originalMetadataSize}, newMetadataSize: ${newMetadataSize}, sizeDifference: ${sizeDifference}`); + // create the SeekHead element + seekHead = []; + seekHead.push({ name: "SeekHead", type: "m", isEnd: false }); + seekHead.push({ name: "Seek", type: "m", isEnd: false }); + seekHead.push({ name: "SeekID", type: "b", data: new exports.Buffer([0x15, 0x49, 0xA9, 0x66]) }); // Info + seekHead.push({ name: "SeekPosition", type: "u", data: createUIntBuffer(infoStart) }); + seekHead.push({ name: "Seek", type: "m", isEnd: true }); + seekHead.push({ name: "Seek", type: "m", isEnd: false }); + seekHead.push({ name: "SeekID", type: "b", data: new exports.Buffer([0x16, 0x54, 0xAE, 0x6B]) }); // Tracks + seekHead.push({ name: "SeekPosition", type: "u", data: createUIntBuffer(tracksStart) }); + seekHead.push({ name: "Seek", type: "m", isEnd: true }); + seekHead.push({ name: "Seek", type: "m", isEnd: false }); + seekHead.push({ name: "SeekID", type: "b", data: new exports.Buffer([0x1C, 0x53, 0xBB, 0x6B]) }); // Cues + seekHead.push({ name: "SeekPosition", type: "u", data: createUIntBuffer(cuesStart) }); + seekHead.push({ name: "Seek", type: "m", isEnd: true }); + seekHead.push({ name: "SeekHead", type: "m", isEnd: true }); + seekHeadSize = encodedSizeOfEbml(seekHead); + //console.error("SeekHead size: " + seekHeadSize); + //printElementIds(seekHead); + // create the Cues element + cues = []; + cues.push({ name: "Cues", type: "m", isEnd: false }); + cuesInfo.forEach(function (_a) { + var CueTrack = _a.CueTrack, CueClusterPosition = _a.CueClusterPosition, CueTime = _a.CueTime; + cues.push({ name: "CuePoint", type: "m", isEnd: false }); + cues.push({ name: "CueTime", type: "u", data: createUIntBuffer(CueTime) }); + cues.push({ name: "CueTrackPositions", type: "m", isEnd: false }); + cues.push({ name: "CueTrack", type: "u", data: createUIntBuffer(CueTrack) }); + //console.error(`CueClusterPosition: ${CueClusterPosition}, Corrected to: ${CueClusterPosition - segmentContentStartPos} , offset by ${sizeDifference} to become ${(CueClusterPosition - segmentContentStartPos) + sizeDifference - segmentContentStartPos}`); + // EBMLReader returns CueClusterPosition with absolute byte offsets. The Cues section expects them as offsets from the first level 1 element of the Segment, so we need to adjust it. + CueClusterPosition -= segmentContentStartPos; + // We also need to adjust to take into account the change in metadata size from when EBMLReader read the original metadata. + CueClusterPosition += sizeDifference; + cues.push({ name: "CueClusterPosition", type: "u", data: createUIntBuffer(CueClusterPosition) }); + cues.push({ name: "CueTrackPositions", type: "m", isEnd: true }); + cues.push({ name: "CuePoint", type: "m", isEnd: true }); + }); + cues.push({ name: "Cues", type: "m", isEnd: true }); + cuesSize = encodedSizeOfEbml(cues); + //console.error("Cues size: " + cuesSize); + //console.error("Cue count: " + cuesInfo.length); + //printElementIds(cues); + // If the new MetadataSize is not the same as the previous iteration, we need to run once more. + if (lastSizeDifference !== sizeDifference) { + lastSizeDifference = sizeDifference; + if (i === maxIterations - 1) { + throw new Error("Failed to converge to a stable metadata size"); + } + } + else { + return "break"; + } + }; + for (var i = 0; i < maxIterations; i++) { + var state_1 = _loop_1(i); + if (state_1 === "break") + break; + } + var finalMetadata = [].concat.apply([], [ + header, + { name: "Segment", type: "m", isEnd: false, unknownSize: true }, + seekHead, + info, + tracks, + cues + ]); + var result = new EBMLEncoder_1.default().encode(finalMetadata); + //printElementIds(finalMetadata); + //console.error(`Final metadata buffer size: ${result.byteLength}`); + //console.error(`Final metadata buffer size without header and segment: ${result.byteLength-segmentContentStartPos}`); + return result; +} +exports.makeMetadataSeekable = makeMetadataSeekable; +/** + * print all element id names in a list + + * @param metadata - array of EBML elements to print + * +export function printElementIds(metadata: EBML.EBMLElementBuffer[]) { + + let result: EBML.EBMLElementBuffer[] = []; + let start: number = -1; + + for (let i = 0; i < metadata.length; i++) { + console.error("\t id: " + metadata[i].name); + } +} +*/ +/** + * remove all occurances of an EBML element from an array of elements + * If it's a MasterElement you will also remove the content. (everything between start and end) + * @param idName - name of the EBML Element to remove. + * @param metadata - array of EBML elements to search + */ +function removeElement(idName, metadata) { + var result = []; + var start = -1; + for (var i = 0; i < metadata.length; i++) { + var element = metadata[i]; + if (element.name === idName) { + // if it's a Master element, extract the start and end element, and everything in between + if (element.type === "m") { + if (!element.isEnd) { + start = i; + } + else { + // we've reached the end, extract the whole thing + if (start == -1) + throw new Error("Detected ".concat(idName, " closing element before finding the start")); + metadata.splice(start, i - start + 1); + return; + } + } + else { + // not a Master element, so we've found what we're looking for. + metadata.splice(i, 1); + return; + } + } + } +} +exports.removeElement = removeElement; +/** + * extract the first occurance of an EBML tag from a flattened array of EBML data. + * If it's a MasterElement you will also get the content. (everything between start and end) + * @param idName - name of the EBML Element to extract. + * @param metadata - array of EBML elements to search + */ +function extractElement(idName, metadata) { + var result = []; + var start = -1; + for (var i = 0; i < metadata.length; i++) { + var element = metadata[i]; + if (element.name === idName) { + // if it's a Master element, extract the start and end element, and everything in between + if (element.type === "m") { + if (!element.isEnd) { + start = i; + } + else { + // we've reached the end, extract the whole thing + if (start == -1) + throw new Error("Detected ".concat(idName, " closing element before finding the start")); + result = metadata.slice(start, i + 1); + break; + } + } + else { + // not a Master element, so we've found what we're looking for. + result.push(metadata[i]); + break; + } + } + } + return result; +} +exports.extractElement = extractElement; +/** + * @deprecated + * metadata に対して duration と seekhead を追加した metadata を返す + * @param metadata - 変更前の webm における ファイル先頭から 最初の Cluster 要素までの 要素 + * @param duration - Duration (TimecodeScale) + * @param cues - cue points for clusters + * @deprecated @param clusterPtrs - 変更前の webm における SeekHead に追加する Cluster 要素 への start pointer + * @deprecated @param cueInfos - please use cues. + */ +function putRefinedMetaData(metadata, info) { + if (Array.isArray(info.cueInfos) && !Array.isArray(info.cues)) { + console.warn("putRefinedMetaData: info.cueInfos property is deprecated. please use info.cues"); + info.cues = info.cueInfos; + } + var ebml = []; + var payload = []; + for (var i_1 = 0; i_1 < metadata.length; i_1++) { + var elm = metadata[i_1]; + if (elm.type === "m" && elm.name === "Segment") { + ebml = metadata.slice(0, i_1); + payload = metadata.slice(i_1); + if (elm.unknownSize) { + payload.shift(); // remove segment tag + break; + } + throw new Error("this metadata is not streaming webm file"); + } + } + // *0 *4 *5 *36 *40 *48=segmentOffset *185=originalPayloadOffsetEnd + // | | | | | | | + // [EBML][size]....[Segment][size][Info][size][Duration][size]...[Cluster] + // | | |^inf | | + // | +segmentSiz(12)+ | + // +-ebmlSize(36)--+ | +-payloadSize(137)-------------+offsetEndDiff+ + // | | +-newPayloadSize(??)-------------------------+ + // | | | | + // [Segment][size][Info][size][Duration][size]....[size][value][Cluster] + // ^ | + // | *??=newPayloadOffsetEnd + // inf + if (!(payload[payload.length - 1].dataEnd > 0)) { + throw new Error("metadata dataEnd has wrong number"); + } + var originalPayloadOffsetEnd = payload[payload.length - 1].dataEnd; // = first cluster ptr + var ebmlSize = ebml[ebml.length - 1].dataEnd; // = first segment ptr + var refinedEBMLSize = new EBMLEncoder_1.default().encode(ebml).byteLength; + var offsetDiff = refinedEBMLSize - ebmlSize; + var payloadSize = originalPayloadOffsetEnd - payload[0].tagStart; + var segmentSize = payload[0].tagStart - ebmlSize; + var segmentOffset = payload[0].tagStart; + var segmentTagBuf = new exports.Buffer([0x18, 0x53, 0x80, 0x67]); // Segment + var segmentSizeBuf = new exports.Buffer('01ffffffffffffff', 'hex'); // Segmentの最後の位置は無数の Cluster 依存なので。 writeVint(newPayloadSize).byteLength ではなく、 infinity. + var _segmentSize = segmentTagBuf.byteLength + segmentSizeBuf.byteLength; // == segmentSize + var newPayloadSize = payloadSize; + // We need the size to be stable between two refinements in order for our offsets to be correct + // Bound the number of possible refinements so we can't go infinate if something goes wrong + var i; + for (i = 1; i < 20; i++) { + var newPayloadOffsetEnd = ebmlSize + _segmentSize + newPayloadSize; + var offsetEndDiff = newPayloadOffsetEnd - originalPayloadOffsetEnd; + var sizeDiff = offsetDiff + offsetEndDiff; + var refined = refineMetadata(payload, sizeDiff, info); + var newNewRefinedSize = new EBMLEncoder_1.default().encode(refined).byteLength; // 一旦 seekhead を作って自身のサイズを調べる + if (newNewRefinedSize === newPayloadSize) { + // Size is stable + return new EBMLEncoder_1.default().encode([].concat(ebml, [{ type: "m", name: "Segment", isEnd: false, unknownSize: true }], refined)); + } + newPayloadSize = newNewRefinedSize; + } + throw new Error("unable to refine metadata, stable size could not be found in " + i + " iterations!"); +} +exports.putRefinedMetaData = putRefinedMetaData; +// Given a list of EBMLElementBuffers, returns their encoded size in bytes +function encodedSizeOfEbml(refinedMetaData) { + var encorder = new EBMLEncoder_1.default(); + return refinedMetaData.reduce(function (lst, elm) { return lst.concat(encorder.encode([elm])); }, []).reduce(function (o, buf) { return o + buf.byteLength; }, 0); +} +function refineMetadata(mesetadata, sizeDiff, info) { + var duration = info.duration, clusterPtrs = info.clusterPtrs, cues = info.cues; + var _metadata = mesetadata.slice(0); + if (typeof duration === "number") { + // duration を追加する + var overwrited_1 = false; + _metadata.forEach(function (elm) { + if (elm.type === "f" && elm.name === "Duration") { + overwrited_1 = true; + elm.data = createFloatBuffer(duration, 8); + } + }); + if (!overwrited_1) { + insertTag(_metadata, "Info", [{ name: "Duration", type: "f", data: createFloatBuffer(duration, 8) }]); + } + } + if (Array.isArray(cues)) { + insertTag(_metadata, "Cues", create_cue(cues, sizeDiff)); + } + var seekhead_children = []; + if (Array.isArray(clusterPtrs)) { + console.warn("append cluster pointers to seekhead is deprecated. please use cues"); + seekhead_children = create_seek_from_clusters(clusterPtrs, sizeDiff); + } + // remove seek info + /* + _metadata = _metadata.filter((elm)=> !( + elm.name === "Seek" || + elm.name === "SeekID" || + elm.name === "SeekPosition") ); + */ + // working on progress + //seekhead_children = seekhead_children.concat(create_seekhead(_metadata)); + insertTag(_metadata, "SeekHead", seekhead_children, true); + return _metadata; +} +function create_seekhead(metadata, sizeDiff) { + var seeks = []; + ["Info", "Tracks", "Cues"].forEach(function (tagName) { + var tagStarts = metadata.filter(function (elm) { return elm.type === "m" && elm.name === tagName && elm.isEnd === false; }).map(function (elm) { return elm["tagStart"]; }); + var tagStart = tagStarts[0]; + if (typeof tagStart !== "number") { + return; + } + seeks.push({ name: "Seek", type: "m", isEnd: false }); + switch (tagName) { + case "Info": + seeks.push({ name: "SeekID", type: "b", data: new exports.Buffer([0x15, 0x49, 0xA9, 0x66]) }); + break; + case "Tracks": + seeks.push({ name: "SeekID", type: "b", data: new exports.Buffer([0x16, 0x54, 0xAE, 0x6B]) }); + break; + case "Cues": + seeks.push({ name: "SeekID", type: "b", data: new exports.Buffer([0x1C, 0x53, 0xBB, 0x6B]) }); + break; + } + seeks.push({ name: "SeekPosition", type: "u", data: createUIntBuffer(tagStart + sizeDiff) }); + seeks.push({ name: "Seek", type: "m", isEnd: true }); + }); + return seeks; +} +function create_seek_from_clusters(clusterPtrs, sizeDiff) { + var seeks = []; + clusterPtrs.forEach(function (start) { + seeks.push({ name: "Seek", type: "m", isEnd: false }); + // [0x1F, 0x43, 0xB6, 0x75] で Cluster 意 + seeks.push({ name: "SeekID", type: "b", data: new exports.Buffer([0x1F, 0x43, 0xB6, 0x75]) }); + seeks.push({ name: "SeekPosition", type: "u", data: createUIntBuffer(start + sizeDiff) }); + seeks.push({ name: "Seek", type: "m", isEnd: true }); + }); + return seeks; +} +function create_cue(cueInfos, sizeDiff) { + var cues = []; + cueInfos.forEach(function (_a) { + var CueTrack = _a.CueTrack, CueClusterPosition = _a.CueClusterPosition, CueTime = _a.CueTime; + cues.push({ name: "CuePoint", type: "m", isEnd: false }); + cues.push({ name: "CueTime", type: "u", data: createUIntBuffer(CueTime) }); + cues.push({ name: "CueTrackPositions", type: "m", isEnd: false }); + cues.push({ name: "CueTrack", type: "u", data: createUIntBuffer(CueTrack) }); // video track + cues.push({ name: "CueClusterPosition", type: "u", data: createUIntBuffer(CueClusterPosition + sizeDiff) }); + cues.push({ name: "CueTrackPositions", type: "m", isEnd: true }); + cues.push({ name: "CuePoint", type: "m", isEnd: true }); + }); + return cues; +} +function insertTag(_metadata, tagName, children, insertHead) { + if (insertHead === void 0) { insertHead = false; } + // find the tagname from _metadata + var idx = -1; + for (var i = 0; i < _metadata.length; i++) { + var elm = _metadata[i]; + if (elm.type === "m" && elm.name === tagName && elm.isEnd === false) { + idx = i; + break; + } + } + if (idx >= 0) { + // insert [] to + Array.prototype.splice.apply(_metadata, [idx + 1, 0].concat(children)); + } + else if (insertHead) { + [].concat([{ name: tagName, type: "m", isEnd: false }], children, [{ name: tagName, type: "m", isEnd: true }]).reverse().forEach(function (elm) { _metadata.unshift(elm); }); + } + else { + // metadata 末尾に を追加 + // insert + _metadata.push({ name: tagName, type: "m", isEnd: false }); + children.forEach(function (elm) { _metadata.push(elm); }); + _metadata.push({ name: tagName, type: "m", isEnd: true }); + } +} +function concat(list) { + return exports.Buffer.concat(list); +} +exports.concat = concat; +function encodeValueToBuffer(elm) { + var data = new exports.Buffer(0); + if (elm.type === "m") { + return elm; + } + switch (elm.type) { + case "u": + data = createUIntBuffer(elm.value); + break; + case "i": + data = createIntBuffer(elm.value); + break; + case "f": + data = createFloatBuffer(elm.value); + break; + case "s": + data = new exports.Buffer(elm.value, 'ascii'); + break; + case "8": + data = new exports.Buffer(elm.value, 'utf8'); + break; + case "b": + data = elm.value; + break; + case "d": + data = new int64_buffer_1.Int64BE(elm.value.getTime().toString()).toBuffer(); + break; + } + return Object.assign({}, elm, { data: data }); +} +exports.encodeValueToBuffer = encodeValueToBuffer; +function createUIntBuffer(value) { + // Big-endian, any size from 1 to 8 + // but js number is float64, so max 6 bit octets + var bytes = 1; + for (; value >= Math.pow(2, 8 * bytes); bytes++) { } + if (bytes >= 7) { + console.warn("7bit or more bigger uint not supported."); + return new int64_buffer_1.Uint64BE(value).toBuffer(); + } + var data = new exports.Buffer(bytes); + data.writeUIntBE(value, 0, bytes); + return data; +} +exports.createUIntBuffer = createUIntBuffer; +function createIntBuffer(value) { + // Big-endian, any size from 1 to 8 octets + // but js number is float64, so max 6 bit + var bytes = 1; + for (; value >= Math.pow(2, 8 * bytes); bytes++) { } + if (bytes >= 7) { + console.warn("7bit or more bigger uint not supported."); + return new int64_buffer_1.Int64BE(value).toBuffer(); + } + var data = new exports.Buffer(bytes); + data.writeIntBE(value, 0, bytes); + return data; +} +exports.createIntBuffer = createIntBuffer; +function createFloatBuffer(value, bytes) { + if (bytes === void 0) { bytes = 8; } + // Big-endian, defined for 4 and 8 octets (32, 64 bits) + // js number is float64 so 8 bytes. + if (bytes === 8) { + // 64bit + var data = new exports.Buffer(8); + data.writeDoubleBE(value, 0); + return data; + } + else if (bytes === 4) { + // 32bit + var data = new exports.Buffer(4); + data.writeFloatBE(value, 0); + return data; + } + else { + throw new Error("float type bits must 4bytes or 8bytes"); + } +} +exports.createFloatBuffer = createFloatBuffer; +function convertEBMLDateToJSDate(int64str) { + if (int64str instanceof Date) { + return int64str; + } + return new Date(new Date("2001-01-01T00:00:00.000Z").getTime() + (Number(int64str) / 1000 / 1000)); +} +exports.convertEBMLDateToJSDate = convertEBMLDateToJSDate; diff --git a/lib/index.d.ts b/lib/index.d.ts new file mode 100644 index 0000000..266e8df --- /dev/null +++ b/lib/index.d.ts @@ -0,0 +1,8 @@ +/** + * based on ts-ebml and support large file,optimize memory usage during repair + * + * @param blob the blob you need to fix + * @returns the blob that has been fixed + * + */ +export default function fixWebmDuration(blob: Blob): Promise; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..f39cd8b --- /dev/null +++ b/lib/index.js @@ -0,0 +1,85 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var ebml_1 = require("./ebml"); +/** + * based on ts-ebml and support large file,optimize memory usage during repair + * + * @param blob the blob you need to fix + * @returns the blob that has been fixed + * + */ +function fixWebmDuration(blob) { + return __awaiter(this, void 0, void 0, function () { + var decoder, reader, readstream, readerBlob, _a, done, value, refinedMetadataBuf, refinedMetadataBlob, firstPartBlobWithoutMetadata, finalBlob; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!blob) { + throw Error('call to fixWebmDuration requires a blob'); + } + decoder = new ebml_1.Decoder(); + reader = new ebml_1.Reader(); + readstream = blob.stream(); + readerBlob = readstream.getReader(); + _b.label = 1; + case 1: + if (!true) return [3 /*break*/, 3]; + return [4 /*yield*/, readerBlob.read()]; + case 2: + _a = _b.sent(), done = _a.done, value = _a.value; + if (done) { + reader.stop(); + return [3 /*break*/, 3]; + } + decoder.decode(value).forEach(function (elm) { + reader.read(elm); + }); + value = null; + return [3 /*break*/, 1]; + case 3: + refinedMetadataBuf = ebml_1.tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues); + refinedMetadataBlob = new Blob([refinedMetadataBuf], { type: blob.type }); + firstPartBlobWithoutMetadata = blob.slice(reader.metadataSize); + finalBlob = new Blob([refinedMetadataBlob, firstPartBlobWithoutMetadata], { type: blob.type }); + return [2 /*return*/, finalBlob]; + } + }); + }); +} +exports.default = fixWebmDuration; diff --git a/package.json b/package.json index 2d1c4e2..d7724a2 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,18 @@ { "name": "webm-duration-fix", - "version": "1.0.2", + "version": "1.0.3", "description": "based on ts-ebml and support large file(than 2GB) and optimize memory usage during repair", - "main": "webm-duration-fix.js", - "type": "module", + "scripts": { + "init": "npm run update; npm run mkdir; npm run build", + "update": "npm run reset; npm update", + "reset": "rm -rf node_modules", + "mkdir": "mkdir lib dist 2>/dev/null", + "clean": "rm -rf lib/* dist/* test/*.js; mkdir -p dist", + "build": "npm run clean && tsc -p .; npm run browserify", + "browserify": "browserify lib/index.js --standalone EBML -o dist/EBML.js", + "lint": "tslint -c ./tslint.json --project ./tsconfig.json --type-check" + }, + "main": "./lib/index.js", "repository": { "type": "git", "url": "git+https://github.com/buynao/webm-duration-fix.git" @@ -12,7 +21,9 @@ "typescript", "fix-webm", "webm-duration", - "MediaRecorder" + "MediaRecorder", + "webm", + "ebml" ], "author": "buynao", "license": "ISC", @@ -21,6 +32,15 @@ }, "homepage": "https://github.com/buynao/webm-duration-fix#readme", "dependencies": { - "ts-ebml": "^2.0.2" + "buffer": "^6.0.3", + "ebml-block": "^1.1.2", + "events": "^3.3.0", + "int64-buffer": "^1.0.1" + }, + "devDependencies": { + "@types/node": "^17.0.8", + "browserify": "^17.0.0", + "tslint": "^6.1.3", + "typescript": "^4.5.4" } } diff --git a/src/ebml/EBML.ts b/src/ebml/EBML.ts new file mode 100644 index 0000000..ddf2370 --- /dev/null +++ b/src/ebml/EBML.ts @@ -0,0 +1,108 @@ +export type ChildElementsValue = NumberElement | StringElement | BinaryElement | DateElement; +export type EBMLElementValue = MasterElement | ChildElementsValue; +export type ChildElementBuffer = ChildElement & {data: Buffer}; +export type EBMLElementBuffer = MasterElement | ChildElementBuffer; +export type EBMLElementBufferValue = MasterElement | (ChildElementsValue & {data: Buffer}); +export type EBMLElementDetail = (MasterElement | (ChildElementsValue & {data: Buffer}) ) & ElementDetail; + +export interface IElement { + name: string; + type: "m"|"u"|"i"|"f"|"s"|"8"|"b"|"d"; +} + +export interface ChildElement extends IElement { + type: "u"|"i"|"f"|"s"|"8"|"b"|"d"; +} + +export interface MasterElement extends IElement { + type: "m"; + isEnd: boolean; + unknownSize?: boolean; +} + +export interface ChildElementValue extends ChildElement { + value: any; +} + +export interface NumberElement extends ChildElementValue { + type: "u" | "i" | "f"; + value: number; +} + +export interface StringElement extends ChildElementValue { + type: "s" | "8"; + value: string; +} + +export interface BinaryElement extends ChildElementValue { + type: "b"; + value: Buffer; +} + +export interface DateElement extends ChildElementValue { + type: "d"; + /** + * Date - signed 8 octets integer in nanoseconds with 0 indicating the precise + * beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC) + */ + value: Date; +} + + +export interface ElementDetail { + schema: Schema; + /** + * hex EBML ID + */ + EBML_ID: string; + /** + * The level within an EBML tree that the element may occur at. + * + is for a recursive level (can be its own child). + * g: global element (can be found at any level) + */ + level: number; + /** + * このタグのバッファ全体における開始オフセット位置 + */ + tagStart: number; + /** + * このタグのバッファ全体における終了オフセット位置 + */ + tagEnd: number; + /** + * size vint start + */ + sizeStart: number; + /** + * size vint end + */ + sizeEnd: number; + /** + * 要素の中身の開始位置 + */ + dataStart: number; + /** + * 要素の中身の終了位置 + */ + dataEnd: number; + /** + * dataEnd - dataStart + */ + dataSize: number; +} + +export interface SimpleBlock { + discardable: boolean; + frames: Buffer[]; + invisible: boolean; + keyframe: boolean; + timecode: number; + trackNumber: number; +} + +export interface Schema { + name: string; + level: number; + type: string; + description: string; +} diff --git a/src/ebml/EBMLDecoder.ts b/src/ebml/EBMLDecoder.ts new file mode 100644 index 0000000..9d75399 --- /dev/null +++ b/src/ebml/EBMLDecoder.ts @@ -0,0 +1,258 @@ + +import {Int64BE} from "int64-buffer"; +import {Buffer, readVint, convertEBMLDateToJSDate} from "./tools"; +import * as EBML from "./EBML"; +import * as tools from "./tools"; +import { byEbmlID } from "./ebmlID"; + +enum State { + STATE_TAG = 1, + STATE_SIZE = 2, + STATE_CONTENT = 3 +} + +export default class EBMLDecoder { + + private _buffer: Buffer; + private _tag_stack: EBML.EBMLElementDetail[]; + private _state: State; + /** + * _buffer の先頭からの位置 + */ + private _cursor: number; + /** + * 全体におけるポインタ + */ + private _total: number; + private _schema: typeof byEbmlID; + private _result: EBML.EBMLElementDetail[]; + + constructor() { + this._buffer = new Buffer(0); + this._tag_stack = []; + this._state = State.STATE_TAG; + this._cursor = 0; + this._total = 0; + this._schema = byEbmlID; + this._result = []; + } + + decode(chunk: ArrayBuffer): EBML.EBMLElementDetail[] { + this.readChunk(chunk); + const diff = this._result; + this._result = []; + return diff; + } + + private readChunk(chunk: ArrayBuffer): void { + // 読みかけの(読めなかった) this._buffer と 新しい chunk を合わせて読み直す + this._buffer = tools.concat([this._buffer, new Buffer(chunk)]); + while (this._cursor < this._buffer.length) { + // console.log(this._cursor, this._total, this._tag_stack); + if(this._state === State.STATE_TAG && !this.readTag()) { break; } + if(this._state === State.STATE_SIZE && !this.readSize()) { break; } + if(this._state === State.STATE_CONTENT && !this.readContent()) { break; } + } + } + + private getSchemaInfo(tagNum: number): EBML.Schema { + return this._schema[tagNum] || { + name: "unknown", + level: -1, + type: "unknown", + description: "unknown" + }; + } + + /** + * vint された parsing tag + * @return - return false when waiting for more data + */ + private readTag(): boolean { + // tag.length が buffer の外にある + if(this._cursor >= this._buffer.length) { return false; } + + // read ebml id vint without first byte + const tag = readVint(this._buffer, this._cursor); + + // tag が読めなかった + if (tag == null) { return false; } + + // >>>>>>>>> + // tag 識別子 + //const tagStr = this._buffer.toString("hex", this._cursor, this._cursor + tag.length); + //const tagNum = parseInt(tagStr, 16); + // 上と等価 + const buf = this._buffer.slice(this._cursor, this._cursor + tag.length); + const tagNum = buf.reduce((o, v, i, arr)=> o + v * Math.pow(16, 2*(arr.length-1-i)), 0); + + const schema = this.getSchemaInfo(tagNum); + + const tagObj: EBML.EBMLElementDetail = { + EBML_ID: tagNum.toString(16), + schema, + type: schema.type, + name: schema.name, + level: schema.level, + tagStart: this._total, + tagEnd: this._total + tag.length, + sizeStart: this._total + tag.length, + sizeEnd: null, + dataStart: null, + dataEnd: null, + dataSize: null, + data: null + }; + // | tag: vint | size: vint | data: Buffer(size) | + + this._tag_stack.push(tagObj); + // <<<<<<<< + + // ポインタを進める + this._cursor += tag.length; + this._total += tag.length; + + // 読み込み状態変更 + this._state = State.STATE_SIZE; + + return true; + } + + /** + * vint された現在のタグの内容の大きさを読み込む + * @return - return false when waiting for more data + */ + private readSize(): boolean { + // tag.length が buffer の外にある + if (this._cursor >= this._buffer.length) { return false; } + + // read ebml datasize vint without first byte + const size = readVint(this._buffer, this._cursor); + + // まだ読めない + if (size == null) { return false; } + + // >>>>>>>>> + // current tag の data size 決定 + const tagObj = this._tag_stack[this._tag_stack.length - 1]; + + tagObj.sizeEnd = tagObj.sizeStart + size.length; + + tagObj.dataStart = tagObj.sizeEnd; + + tagObj.dataSize = size.value; + + if (size.value === -1) { + // unknown size + tagObj.dataEnd = -1; + if(tagObj.type === "m"){ + tagObj.unknownSize = true; + } + } else { + tagObj.dataEnd = tagObj.sizeEnd + size.value; + } + + // <<<<<<<< + + // ポインタを進める + this._cursor += size.length; + this._total += size.length; + + this._state = State.STATE_CONTENT; + + return true; + } + + /** + * データ読み込み + */ + private readContent(): boolean { + + const tagObj = this._tag_stack[this._tag_stack.length - 1]; + + // master element は子要素を持つので生データはない + if (tagObj.type === 'm') { + // console.log('content should be tags'); + tagObj.isEnd = false; + this._result.push(tagObj); + this._state = State.STATE_TAG; + // この Mastert Element は空要素か + if(tagObj.dataSize === 0){ + // 即座に終了タグを追加 + const elm = Object.assign({}, tagObj, {isEnd: true}); + this._result.push(elm); + this._tag_stack.pop(); // スタックからこのタグを捨てる + } + return true; + } + + // waiting for more data + if (this._buffer.length < this._cursor + tagObj.dataSize) { return false; } + + // タグの中身の生データ + const data = this._buffer.slice(this._cursor, this._cursor + tagObj.dataSize); + + // 読み終わったバッファを捨てて読み込んでいる部分のバッファのみ残す + this._buffer = this._buffer.slice(this._cursor + tagObj.dataSize); + + tagObj.data = data; + + // >>>>>>>>> + switch(tagObj.type){ + //case "m": break; + // Master-Element - contains other EBML sub-elements of the next lower level + case "u": tagObj.value = data.readUIntBE(0, data.length); break; + // Unsigned Integer - Big-endian, any size from 1 to 8 octets + case "i": tagObj.value = data.readIntBE(0, data.length); break; + // Signed Integer - Big-endian, any size from 1 to 8 octets + case "f": tagObj.value = tagObj.dataSize === 4 ? data.readFloatBE(0) : + tagObj.dataSize === 8 ? data.readDoubleBE(0) : + (console.warn(`cannot read ${tagObj.dataSize} octets float. failback to 0`), 0); break; + // Float - Big-endian, defined for 4 and 8 octets (32, 64 bits) + case "s": tagObj.value = data.toString("ascii"); break; // ascii + // Printable ASCII (0x20 to 0x7E), zero-padded when needed + case "8": tagObj.value = data.toString("utf8"); break; + // Unicode string, zero padded when needed (RFC 2279) + case "b": tagObj.value = data; break; + // Binary - not interpreted by the parser + case "d": tagObj.value = convertEBMLDateToJSDate(new Int64BE(data).toNumber()); break; + // nano second; Date.UTC(2001,1,1,0,0,0,0) === 980985600000 + // Date - signed 8 octets integer in nanoseconds with 0 indicating + // the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC) + } + if(tagObj.value === null){ + throw new Error("unknown tag type:" + tagObj.type); + } + this._result.push(tagObj); + + // <<<<<<<< + + // ポインタを進める + this._total += tagObj.dataSize; + + // タグ待ちモードに変更 + this._state = State.STATE_TAG; + this._cursor = 0; + this._tag_stack.pop(); // remove the object from the stack + + while (this._tag_stack.length > 0) { + const topEle = this._tag_stack[this._tag_stack.length - 1]; + // 親が不定長サイズなので閉じタグは期待できない + if(topEle.dataEnd < 0){ + this._tag_stack.pop(); // 親タグを捨てる + return true; + } + // 閉じタグの来るべき場所まで来たかどうか + if (this._total < topEle.dataEnd) { + break; + } + // 閉じタグを挿入すべきタイミングが来た + if(topEle.type !== "m"){ throw new Error("parent element is not master element"); } + const elm = Object.assign({}, topEle, {isEnd: true}); + this._result.push(elm); + this._tag_stack.pop(); + } + + return true; + } +} diff --git a/src/ebml/EBMLEncoder.ts b/src/ebml/EBMLEncoder.ts new file mode 100644 index 0000000..b5f757a --- /dev/null +++ b/src/ebml/EBMLEncoder.ts @@ -0,0 +1,146 @@ +import * as EBML from "./EBML"; +import * as tools from "./tools"; +import {Buffer} from "./tools"; +import { byEbmlID } from "./ebmlID"; + + + +interface DataTree { + tagId: Buffer; + elm: EBML.EBMLElementBuffer; + children: DataTree[]; + data: Buffer | null; +} + +export default class EBMLEncoder { + private _buffers: Buffer[]; + private _stack: DataTree[]; + private _schema: typeof byEbmlID; + + constructor(){ + this._schema = byEbmlID; + this._buffers = []; + this._stack = []; + } + + encode(elms: EBML.EBMLElementBuffer[]): ArrayBuffer { + return tools.concat( + elms.reduce((lst, elm)=> + lst.concat(this.encodeChunk(elm)), [])).buffer; + } + + private encodeChunk(elm: EBML.EBMLElementBuffer): Buffer[] { + if(elm.type === "m"){ + if(!elm.isEnd){ + this.startTag(elm); + }else{ + this.endTag(elm); + } + }else{ + // ensure that we are working with an internal `Buffer` instance + elm.data = Buffer.from(elm.data); + this.writeTag(elm); + } + return this.flush(); + } + + private flush(): Buffer[] { + const ret = this._buffers; + this._buffers = []; + return ret; + } + + private getSchemaInfo(tagName: string): Buffer | null { + const tagNums = Object.keys(this._schema).map(Number); + for (let i = 0; i < tagNums.length; i++) { + let tagNum = tagNums[i]; + if (this._schema[tagNum].name === tagName) { + return new Buffer(tagNum.toString(16), 'hex'); + } + } + return null; + } + + private writeTag(elm: EBML.ChildElementBuffer) { + const tagName = elm.name; + const tagId = this.getSchemaInfo(tagName); + + const tagData = elm.data; + + if (tagId == null) { + throw new Error('No schema entry found for ' + tagName); + } + + const data = tools.encodeTag(tagId, tagData); + /** + * 親要素が閉じタグあり(isEnd)なら閉じタグが来るまで待つ(children queに入る) + */ + if(this._stack.length > 0) { + const last = this._stack[this._stack.length - 1]; + last.children.push({ + tagId, + elm, + children: [], + data + }); + return; + } + this._buffers = this._buffers.concat(data); + return; + } + + private startTag(elm: EBML.MasterElement){ + const tagName = elm.name; + const tagId = this.getSchemaInfo(tagName); + if (tagId == null) { + throw new Error('No schema entry found for ' + tagName); + } + + /** + * 閉じタグ不定長の場合はスタックに積まずに即時バッファに書き込む + */ + if(elm.unknownSize){ + const data = tools.encodeTag(tagId, new Buffer(0), elm.unknownSize); + this._buffers = this._buffers.concat(data); + return; + } + + const tag: DataTree = { + tagId, + elm, + children: [], + data: null + }; + + if(this._stack.length > 0) { + this._stack[this._stack.length - 1].children.push(tag); + } + this._stack.push(tag); + } + + private endTag(elm: EBML.MasterElement){ + const tagName = elm.name; + const tag = this._stack.pop(); + if(tag == null){ throw new Error("EBML structure is broken"); } + if(tag.elm.name !== elm.name){ throw new Error("EBML structure is broken"); } + + const childTagDataBuffers = tag.children.reduce((lst, child)=>{ + if(child.data === null){ throw new Error("EBML structure is broken"); } + return lst.concat(child.data); + }, []); + const childTagDataBuffer = tools.concat(childTagDataBuffers); + if(tag.elm.type === "m"){ + tag.data = tools.encodeTag(tag.tagId, childTagDataBuffer, tag.elm.unknownSize); + }else{ + tag.data = tools.encodeTag(tag.tagId, childTagDataBuffer); + } + + if (this._stack.length < 1) { + this._buffers = this._buffers.concat(tag.data); + } + } +} + + + + diff --git a/src/ebml/EBMLReader.ts b/src/ebml/EBMLReader.ts new file mode 100644 index 0000000..3007101 --- /dev/null +++ b/src/ebml/EBMLReader.ts @@ -0,0 +1,377 @@ +import {EventEmitter} from "events"; +import * as EBML from './EBML'; +import * as tools from './tools'; + +/** + * This is an informal code for reference. + * EBMLReader is a class for getting information to enable seeking Webm recorded by MediaRecorder. + * So please do not use for regular WebM files. + */ + +export default class EBMLReader extends EventEmitter { + private metadataloaded: boolean; + + private stack: (EBML.MasterElement & EBML.ElementDetail)[]; + private chunks: EBML.EBMLElementDetail[]; + + private segmentOffset: number; + private last2SimpleBlockVideoTrackTimecode: [number, number]; + private last2SimpleBlockAudioTrackTimecode: [number, number]; + private lastClusterTimecode: number; + private lastClusterPosition: number; + private firstVideoBlockRead: boolean; + private firstAudioBlockRead: boolean; + timecodeScale: number; + metadataSize: number; + metadatas: EBML.EBMLElementDetail[]; + + private currentTrack: {TrackNumber: number, TrackType: number, DefaultDuration: (number | null), CodecDelay: (number | null) }; + private trackTypes: number[]; // equals { [trackID: number]: number }; + private trackDefaultDuration: (number | null)[]; + private trackCodecDelay: (number | null)[]; + + private first_video_simpleblock_of_cluster_is_loaded: boolean; + + private ended: boolean; + trackInfo: { type: "video" | "audio" | "both"; trackNumber: number; } | { type: "nothing" }; + /** + * usefull for thumbnail creation. + */ + use_webp: boolean; + use_duration_every_simpleblock: boolean; // heavy + logging: boolean; + logGroup: string = ""; + private hasLoggingStarted: boolean = false; + /** + * usefull for recording chunks. + */ + use_segment_info: boolean; + /** see: https://bugs.chromium.org/p/chromium/issues/detail?id=606000#c22 */ + drop_default_duration: boolean; + + cues: {CueTrack: number; CueClusterPosition: number; CueTime: number; }[]; + + constructor(){ + super(); + this.metadataloaded = false; + this.chunks = []; + + this.stack = []; + this.segmentOffset = 0; + this.last2SimpleBlockVideoTrackTimecode = [0, 0]; + this.last2SimpleBlockAudioTrackTimecode = [0, 0]; + this.lastClusterTimecode = 0; + this.lastClusterPosition = 0; + this.timecodeScale = 1000000; // webm default TimecodeScale is 1ms + this.metadataSize = 0; + this.metadatas = []; + this.cues = []; + this.firstVideoBlockRead = false; + this.firstAudioBlockRead = false; + + this.currentTrack = {TrackNumber: -1, TrackType: -1, DefaultDuration: null, CodecDelay: null}; + this.trackTypes = []; + this.trackDefaultDuration = []; + this.trackCodecDelay = []; + this.trackInfo = { type: "nothing" }; + this.ended = false; + + this.logging = false; + + this.use_duration_every_simpleblock = false; + this.use_webp = false; + this.use_segment_info = true; + this.drop_default_duration = true; + } + + /** + * emit final state. + */ + stop() { + this.ended = true; + this.emit_segment_info(); + + // clean up any unclosed Master Elements at the end of the stream. + while (this.stack.length) { + this.stack.pop(); + if (this.logging) { + console.groupEnd(); + } + } + + // close main group if set, logging is enabled, and has actually logged anything. + if (this.logging && this.hasLoggingStarted && this.logGroup) { + console.groupEnd(); + } + } + + /** + * emit chunk info + */ + private emit_segment_info(){ + const data = this.chunks; + this.chunks = []; + if(!this.metadataloaded){ + this.metadataloaded = true; + this.metadatas = data; + const videoTrackNum = this.trackTypes.indexOf(1); // find first video track + const audioTrackNum = this.trackTypes.indexOf(2); // find first audio track + this.trackInfo = videoTrackNum >= 0 && audioTrackNum >= 0 ? {type: "both", trackNumber: videoTrackNum } + : videoTrackNum >= 0 ? {type: "video", trackNumber: videoTrackNum } + : audioTrackNum >= 0 ? {type: "audio", trackNumber: audioTrackNum } + : {type: "nothing" }; + if(!this.use_segment_info){ return; } + this.emit("metadata", {data, metadataSize: this.metadataSize}); + }else{ + if(!this.use_segment_info){ return; } + const timecode = this.lastClusterTimecode; + const duration = this.duration; + const timecodeScale = this.timecodeScale; + this.emit("cluster", {timecode, data}); + this.emit("duration", {timecodeScale, duration}); + } + } + read(elm: EBML.EBMLElementDetail){ + let drop = false; + if(this.ended){ + // reader is finished + return; + } + if(elm.type === "m"){ + // 閉じタグの自動挿入 + if(elm.isEnd){ + this.stack.pop(); + }else{ + const parent = this.stack[this.stack.length-1]; + if(parent != null && parent.level >= elm.level){ + // 閉じタグなしでレベルが下がったら閉じタグを挿入 + this.stack.pop(); + + // From http://w3c.github.io/media-source/webm-byte-stream-format.html#webm-media-segments + // This fixes logging for webm streams with Cluster of unknown length and no Cluster closing elements. + if (this.logging){ + console.groupEnd(); + } + + parent.dataEnd = elm.dataEnd; + parent.dataSize = elm.dataEnd - parent.dataStart; + parent.unknownSize = false; + const o = Object.assign({}, parent, {name: parent.name, type: parent.type, isEnd: true}); + this.chunks.push(o); + } + this.stack.push(elm); + } + } + if(elm.type === "m" && elm.name == "Segment"){ + if(this.segmentOffset != 0) { + console.warn("Multiple segments detected!"); + } + this.segmentOffset = elm.dataStart; + this.emit("segment_offset", this.segmentOffset); + }else if(elm.type === "b" && elm.name === "SimpleBlock"){ + const {timecode, trackNumber, frames} = tools.ebmlBlock(elm.data); + if(this.trackTypes[trackNumber] === 1){ // trackType === 1 => video track + if(!this.firstVideoBlockRead){ + this.firstVideoBlockRead = true; + if(this.trackInfo.type === "both" || this.trackInfo.type === "video"){ + const CueTime = this.lastClusterTimecode + timecode; + this.cues.push({CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime}); + this.emit("cue_info", {CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: this.lastClusterTimecode}); + this.emit("cue", {CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime}); + } + } + this.last2SimpleBlockVideoTrackTimecode = [this.last2SimpleBlockVideoTrackTimecode[1], timecode]; + }else if(this.trackTypes[trackNumber] === 2){ // trackType === 2 => audio track + if(!this.firstAudioBlockRead){ + this.firstAudioBlockRead = true; + if(this.trackInfo.type === "audio"){ + const CueTime = this.lastClusterTimecode + timecode; + this.cues.push({CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime}); + this.emit("cue_info", {CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime: this.lastClusterTimecode}); + this.emit("cue", {CueTrack: trackNumber, CueClusterPosition: this.lastClusterPosition, CueTime}); + } + } + this.last2SimpleBlockAudioTrackTimecode = [this.last2SimpleBlockAudioTrackTimecode[1], timecode]; + } + if(this.use_duration_every_simpleblock){ + this.emit("duration", {timecodeScale: this.timecodeScale, duration: this.duration}); + } + if(this.use_webp){ + frames.forEach((frame)=>{ + const startcode = frame.slice(3, 6).toString("hex"); + if(startcode !== "9d012a"){ return; }; // VP8 の場合 + const webpBuf = tools.VP8BitStreamToRiffWebPBuffer(frame); + const webp = new Blob([webpBuf], {type: "image/webp"}); + const currentTime = this.duration; + this.emit("webp", {currentTime, webp}); + }); + } + }else if(elm.type === "m" && elm.name === "Cluster" && elm.isEnd === false){ + this.firstVideoBlockRead = false; + this.firstAudioBlockRead = false; + this.emit_segment_info(); + this.emit("cluster_ptr", elm.tagStart); + this.lastClusterPosition = elm.tagStart; + }else if(elm.type === "u" && elm.name === "Timecode"){ + this.lastClusterTimecode = elm.value; + }else if(elm.type === "u" && elm.name === "TimecodeScale"){ + this.timecodeScale = elm.value; + }else if(elm.type === "m" && elm.name === "TrackEntry"){ + if(elm.isEnd){ + this.trackTypes[this.currentTrack.TrackNumber] = this.currentTrack.TrackType; + this.trackDefaultDuration[this.currentTrack.TrackNumber] = this.currentTrack.DefaultDuration; + this.trackCodecDelay[this.currentTrack.TrackNumber] = this.currentTrack.CodecDelay; + }else{ + this.currentTrack = {TrackNumber: -1, TrackType: -1, DefaultDuration: null, CodecDelay: null }; + } + }else if(elm.type === "u" && elm.name === "TrackType"){ + this.currentTrack.TrackType = elm.value; + }else if(elm.type === "u" && elm.name === "TrackNumber"){ + this.currentTrack.TrackNumber = elm.value; + }else if(elm.type === "u" && elm.name === "CodecDelay"){ + this.currentTrack.CodecDelay = elm.value; + }else if(elm.type === "u" && elm.name === "DefaultDuration"){ + // media source api は DefaultDuration を計算するとバグる。 + // https://bugs.chromium.org/p/chromium/issues/detail?id=606000#c22 + // chrome 58 ではこれを回避するために DefaultDuration 要素を抜き取った。 + // chrome 58 以前でもこのタグを抜き取ることで回避できる + if(this.drop_default_duration){ + console.warn("DefaultDuration detected!, remove it"); + drop = true; + }else{ + this.currentTrack.DefaultDuration = elm.value; + } + }else if(elm.name === "unknown"){ + console.warn(elm); + } + if(!this.metadataloaded && elm.dataEnd > 0){ + this.metadataSize = elm.dataEnd; + } + if(!drop){ this.chunks.push(elm); } + if(this.logging){ this.put(elm); } + } + /** + * DefaultDuration が定義されている場合は最後のフレームのdurationも考慮する + * 単位 timecodeScale + * + * !!! if you need duration with seconds !!! + * ```js + * const nanosec = reader.duration * reader.timecodeScale; + * const sec = nanosec / 1000 / 1000 / 1000; + * ``` + */ + get duration(){ + if(this.trackInfo.type === "nothing"){ + console.warn("no video, no audio track"); + return 0; + } + // defaultDuration は 生の nano sec + let defaultDuration = 0; + // nanoseconds + let codecDelay = 0; + let lastTimecode = 0; + + const _defaultDuration = this.trackDefaultDuration[this.trackInfo.trackNumber]; + if(typeof _defaultDuration === "number"){ + defaultDuration = _defaultDuration; + }else{ + // https://bugs.chromium.org/p/chromium/issues/detail?id=606000#c22 + // default duration がないときに使う delta + if(this.trackInfo.type === "both"){ + if(this.last2SimpleBlockAudioTrackTimecode[1] > this.last2SimpleBlockVideoTrackTimecode[1]){ + // audio diff + defaultDuration = (this.last2SimpleBlockAudioTrackTimecode[1] - this.last2SimpleBlockAudioTrackTimecode[0]) * this.timecodeScale; + // audio delay + const delay = this.trackCodecDelay[this.trackTypes.indexOf(2)]; // 2 => audio + if(typeof delay === "number"){ codecDelay = delay; } + // audio timecode + lastTimecode = this.last2SimpleBlockAudioTrackTimecode[1]; + }else{ + // video diff + defaultDuration = (this.last2SimpleBlockVideoTrackTimecode[1] - this.last2SimpleBlockVideoTrackTimecode[0]) * this.timecodeScale; + // video delay + const delay = this.trackCodecDelay[this.trackTypes.indexOf(1)]; // 1 => video + if(typeof delay === "number"){ codecDelay = delay; } + // video timecode + lastTimecode = this.last2SimpleBlockVideoTrackTimecode[1]; + } + }else if(this.trackInfo.type === "video"){ + defaultDuration = (this.last2SimpleBlockVideoTrackTimecode[1] - this.last2SimpleBlockVideoTrackTimecode[0]) * this.timecodeScale; + const delay = this.trackCodecDelay[this.trackInfo.trackNumber]; // 2 => audio + if(typeof delay === "number"){ codecDelay = delay; } + lastTimecode = this.last2SimpleBlockVideoTrackTimecode[1]; + }else if(this.trackInfo.type === "audio"){ + defaultDuration = (this.last2SimpleBlockAudioTrackTimecode[1] - this.last2SimpleBlockAudioTrackTimecode[0]) * this.timecodeScale; + const delay = this.trackCodecDelay[this.trackInfo.trackNumber]; // 1 => video + if(typeof delay === "number"){ codecDelay = delay; } + lastTimecode = this.last2SimpleBlockAudioTrackTimecode[1]; + }// else { not reached } + } + // convert to timecodescale + const duration_nanosec = ((this.lastClusterTimecode + lastTimecode) * this.timecodeScale) + defaultDuration - codecDelay; + const duration = duration_nanosec / this.timecodeScale; + return Math.floor(duration); + } + /** + * @deprecated + * emit on every segment + * https://www.matroska.org/technical/specs/notes.html#Position_References + */ + addListener(event: "segment_offset", listener: (ev: number )=> void): this; + /** + * @deprecated + * emit on every cluster element start. + * Offset byte from __file start__. It is not an offset from the Segment element. + */ + addListener(event: "cluster_ptr", listener: (ev: number )=> void): this; + /** @deprecated + * emit on every cue point for cluster to create seekable webm file from MediaRecorder + * */ + addListener(event: "cue_info", listener: (ev: CueInfo )=> void): this; + /** emit on every cue point for cluster to create seekable webm file from MediaRecorder */ + addListener(event: "cue", listener: (ev: CueInfo )=> void): this; + /** latest EBML > Info > TimecodeScale and EBML > Info > Duration to create seekable webm file from MediaRecorder */ + addListener(event: "duration", listener: (ev: DurationInfo )=> void): this; + /** EBML header without Cluster Element for recording metadata chunk */ + addListener(event: "metadata", listener: (ev: SegmentInfo & {metadataSize: number})=> void): this; + /** emit every Cluster Element and its children for recording chunk */ + addListener(event: "cluster", listener: (ev: SegmentInfo & {timecode: number})=> void): this; + /** for thumbnail */ + addListener(event: "webp", listener: (ev: ThumbnailInfo)=> void): this; + addListener(event: string, listener: (ev: any)=> void): this { + return super.addListener(event, listener); + } + + put(elm: EBML.EBMLElementDetail) { + if (!this.hasLoggingStarted) { + this.hasLoggingStarted = true; + if (this.logging && this.logGroup) { + console.groupCollapsed(this.logGroup); + } + } + if(elm.type === "m"){ + if(elm.isEnd){ + console.groupEnd(); + }else{ + console.group(elm.name+":"+elm.tagStart); + } + }else if(elm.type === "b"){ + // for debug + //if(elm.name === "SimpleBlock"){ + //const o = EBML.tools.ebmlBlock(elm.value); + //console.log(elm.name, elm.type, o.trackNumber, o.timecode); + //}else{ + console.log(elm.name, elm.type); + //} + }else{ + console.log(elm.name, elm.tagStart, elm.type, elm.value); + } + } +} +/** CueClusterPosition: Offset byte from __file start__. It is not an offset from the Segment element. */ +export interface CueInfo {CueTrack: number; CueClusterPosition: number; CueTime: number; }; +export interface SegmentInfo {data: EBML.EBMLElementDetail[];}; +export interface DurationInfo {duration: number; timecodeScale: number;}; +export interface ThumbnailInfo {webp: Blob; currentTime: number;}; + + diff --git a/src/ebml/ebmlID.ts b/src/ebml/ebmlID.ts new file mode 100644 index 0000000..53aa23f --- /dev/null +++ b/src/ebml/ebmlID.ts @@ -0,0 +1,2048 @@ +export const byEbmlID = { + 0x80: { + name: "ChapterDisplay", + level: 4, + type: "m", + multiple: true, + minver: 1, + webm: true, + description: "Contains all possible strings to use for the chapter display." + }, + 0x83: { + name: "TrackType", + level: 3, + type: "u", + mandatory: true, + minver: 1, + range: "1-254", + description: "A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control)." + }, + 0x85: { + name: "ChapString", + cppname: "ChapterString", + level: 5, + type: "8", + mandatory: true, + minver: 1, + webm: true, + description: "Contains the string to use as the chapter atom." + }, + 0x86: { + name: "CodecID", + level: 3, + type: "s", + mandatory: true, + minver: 1, + description: "An ID corresponding to the codec, see the codec page for more info." + }, + 0x88: { + name: "FlagDefault", + cppname: "TrackFlagDefault", + level: 3, + type: "u", + mandatory: true, + minver: 1, + "default": 1, + range: "0-1", + description: "Set if that track (audio, video or subs) SHOULD be active if no language found matches the user preference. (1 bit)" + }, + 0x89: { + name: "ChapterTrackNumber", + level: 5, + type: "u", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + range: "not 0", + description: "UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks." + }, + 0x91: { + name: "ChapterTimeStart", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: true, + description: "Timestamp of the start of Chapter (not scaled)." + }, + 0x92: { + name: "ChapterTimeEnd", + level: 4, + type: "u", + minver: 1, + webm: false, + description: "Timestamp of the end of Chapter (timestamp excluded, not scaled)." + }, + 0x96: { + name: "CueRefTime", + level: 5, + type: "u", + mandatory: true, + minver: 2, + webm: false, + description: "Timestamp of the referenced Block." + }, + 0x97: { + name: "CueRefCluster", + level: 5, + type: "u", + mandatory: true, + webm: false, + description: "The Position of the Cluster containing the referenced Block." + }, + 0x98: { + name: "ChapterFlagHidden", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks; see flag notes). (1 bit)" + }, + 0x4254: { + name: "ContentCompAlgo", + level: 6, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + // "br": [ "", "", "", "" ], + // "del": [ "1 - bzlib,", "2 - lzo1x" ], + description: "The compression algorithm used. Algorithms that have been specified so far are: 0 - zlib, 3 - Header Stripping" + }, + 0x4255: { + name: "ContentCompSettings", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track." + }, + 0x4282: { + name: "DocType", + level: 1, + type: "s", + mandatory: true, + "default": "matroska", + minver: 1, + description: "A string that describes the type of document that follows this EBML header. 'matroska' in our case or 'webm' for webm files." + }, + 0x4285: { + name: "DocTypeReadVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The minimum DocType version an interpreter has to support to read this file." + }, + 0x4286: { + name: "EBMLVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The version of EBML parser used to create the file." + }, + 0x4287: { + name: "DocTypeVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The version of DocType interpreter used to create the file." + }, + 0x4444: { + name: "SegmentFamily", + level: 2, + type: "b", + multiple: true, + minver: 1, + webm: false, + bytesize: 16, + description: "A randomly generated unique ID that all segments related to each other must use (128 bits)." + }, + 0x4461: { + name: "DateUTC", + level: 2, + type: "d", + minver: 1, + description: "Date of the origin of timestamp (value 0), i.e. production date." + }, + 0x4484: { + name: "TagDefault", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "0-1", + description: "Indication to know if this is the default/original language to use for the given tag. (1 bit)" + }, + 0x4485: { + name: "TagBinary", + level: 4, + type: "b", + minver: 1, + webm: false, + description: "The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString." + }, + 0x4487: { + name: "TagString", + level: 4, + type: "8", + minver: 1, + webm: false, + description: "The value of the Element." + }, + 0x4489: { + name: "Duration", + level: 2, + type: "f", + minver: 1, + range: "> 0", + description: "Duration of the segment (based on TimecodeScale)." + }, + 0x4598: { + name: "ChapterFlagEnabled", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "0-1", + description: "Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter (see flag notes). (1 bit)" + }, + 0x4660: { + name: "FileMimeType", + level: 3, + type: "s", + mandatory: true, + minver: 1, + webm: false, + description: "MIME type of the file." + }, + 0x4661: { + name: "FileUsedStartTime", + level: 3, + type: "u", + divx: true, + description: "DivX font extension" + }, + 0x4662: { + name: "FileUsedEndTime", + level: 3, + type: "u", + divx: true, + description: "DivX font extension" + }, + 0x4675: { + name: "FileReferral", + level: 3, + type: "b", + webm: false, + description: "A binary value that a track/codec can refer to when the attachment is needed." + }, + 0x5031: { + name: "ContentEncodingOrder", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment." + }, + 0x5032: { + name: "ContentEncodingScope", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "not 0", + // "br": [ "", "", "" ], + description: "A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values: 1 - all frame contents, 2 - the track's private data, 4 - the next ContentEncoding (next ContentEncodingOrder. Either the data inside ContentCompression and/or ContentEncryption)" + }, + 0x5033: { + name: "ContentEncodingType", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + // "br": [ "", "" ], + description: "A value describing what kind of transformation has been done. Possible values: 0 - compression, 1 - encryption" + }, + 0x5034: { + name: "ContentCompression", + level: 5, + type: "m", + minver: 1, + webm: false, + description: "Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking." + }, + 0x5035: { + name: "ContentEncryption", + level: 5, + type: "m", + minver: 1, + webm: false, + description: "Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise." + }, + 0x5378: { + name: "CueBlockNumber", + level: 4, + type: "u", + minver: 1, + "default": 1, + range: "not 0", + description: "Number of the Block in the specified Cluster." + }, + 0x5654: { + name: "ChapterStringUID", + level: 4, + type: "8", + mandatory: false, + minver: 3, + webm: true, + description: "A unique string ID to identify the Chapter. Use for WebVTT cue identifier storage." + }, + 0x5741: { + name: "WritingApp", + level: 2, + type: "8", + mandatory: true, + minver: 1, + description: "Writing application (\"mkvmerge-0.3.3\")." + }, + 0x5854: { + name: "SilentTracks", + cppname: "ClusterSilentTracks", + level: 2, + type: "m", + minver: 1, + webm: false, + description: "The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use." + }, + 0x6240: { + name: "ContentEncoding", + level: 4, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Settings for one content encoding like compression or encryption." + }, + 0x6264: { + name: "BitDepth", + cppname: "AudioBitDepth", + level: 4, + type: "u", + minver: 1, + range: "not 0", + description: "Bits per sample, mostly used for PCM." + }, + 0x6532: { + name: "SignedElement", + level: 3, + type: "b", + multiple: true, + webm: false, + description: "An element ID whose data will be used to compute the signature." + }, + 0x6624: { + name: "TrackTranslate", + level: 3, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "The track identification for the given Chapter Codec." + }, + 0x6911: { + name: "ChapProcessCommand", + cppname: "ChapterProcessCommand", + level: 5, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "Contains all the commands associated to the Atom." + }, + 0x6922: { + name: "ChapProcessTime", + cppname: "ChapterProcessTime", + level: 6, + type: "u", + mandatory: true, + minver: 1, + webm: false, + description: "Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter)." + }, + 0x6924: { + name: "ChapterTranslate", + level: 2, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "A tuple of corresponding ID used by chapter codecs to represent this segment." + }, + 0x6933: { + name: "ChapProcessData", + cppname: "ChapterProcessData", + level: 6, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands." + }, + 0x6944: { + name: "ChapProcess", + cppname: "ChapterProcess", + level: 4, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "Contains all the commands associated to the Atom." + }, + 0x6955: { + name: "ChapProcessCodecID", + cppname: "ChapterProcessCodecID", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later." + }, + 0x7373: { + name: "Tag", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Element containing elements specific to Tracks/Chapters." + }, + 0x7384: { + name: "SegmentFilename", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "A filename corresponding to this segment." + }, + 0x7446: { + name: "AttachmentLink", + cppname: "TrackAttachmentLink", + level: 3, + type: "u", + minver: 1, + webm: false, + range: "not 0", + description: "The UID of an attachment that is used by this codec." + }, + 0x258688: { + name: "CodecName", + level: 3, + type: "8", + minver: 1, + description: "A human-readable string specifying the codec." + }, + 0x18538067: { + name: "Segment", + level: "0", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment." + }, + 0x447a: { + name: "TagLanguage", + level: 4, + type: "s", + mandatory: true, + minver: 1, + webm: false, + "default": "und", + description: "Specifies the language of the tag specified, in the Matroska languages form." + }, + 0x45a3: { + name: "TagName", + level: 4, + type: "8", + mandatory: true, + minver: 1, + webm: false, + description: "The name of the Tag that is going to be stored." + }, + 0x67c8: { + name: "SimpleTag", + cppname: "TagSimple", + level: 3, + "recursive": "1", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Contains general information about the target." + }, + 0x63c6: { + name: "TagAttachmentUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment." + }, + 0x63c4: { + name: "TagChapterUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment." + }, + 0x63c9: { + name: "TagEditionUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment." + }, + 0x63c5: { + name: "TagTrackUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + "default": 0, + description: "A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment." + }, + 0x63ca: { + name: "TargetType", + cppname: "TagTargetType", + level: 4, + type: "s", + minver: 1, + webm: false, + "strong": "informational", + description: "An string that can be used to display the logical level of the target like \"ALBUM\", \"TRACK\", \"MOVIE\", \"CHAPTER\", etc (see TargetType)." + }, + 0x68ca: { + name: "TargetTypeValue", + cppname: "TagTargetTypeValue", + level: 4, + type: "u", + minver: 1, + webm: false, + "default": 50, + description: "A number to indicate the logical level of the target (see TargetType)." + }, + 0x63c0: { + name: "Targets", + cppname: "TagTargets", + level: 3, + type: "m", + mandatory: true, + minver: 1, + webm: false, + description: "Contain all UIDs where the specified meta data apply. It is empty to describe everything in the segment." + }, + 0x1254c367: { + name: "Tags", + level: 1, + type: "m", + multiple: true, + minver: 1, + webm: false, + description: "Element containing elements specific to Tracks/Chapters. A list of valid tags can be found here." + }, + 0x450d: { + name: "ChapProcessPrivate", + cppname: "ChapterProcessPrivate", + level: 5, + type: "b", + minver: 1, + webm: false, + description: "Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the \"DVD level\" equivalent." + }, + 0x437e: { + name: "ChapCountry", + cppname: "ChapterCountry", + level: 5, + type: "s", + multiple: true, + minver: 1, + webm: false, + description: "The countries corresponding to the string, same 2 octets as in Internet domains." + }, + 0x437c: { + name: "ChapLanguage", + cppname: "ChapterLanguage", + level: 5, + type: "s", + mandatory: true, + multiple: true, + minver: 1, + webm: true, + "default": "eng", + description: "The languages corresponding to the string, in the bibliographic ISO-639-2 form." + }, + 0x8f: { + name: "ChapterTrack", + level: 4, + type: "m", + minver: 1, + webm: false, + description: "List of tracks on which the chapter applies. If this element is not present, all tracks apply" + }, + 0x63c3: { + name: "ChapterPhysicalEquiv", + level: 4, + type: "u", + minver: 1, + webm: false, + description: "Specify the physical equivalent of this ChapterAtom like \"DVD\" (60) or \"SIDE\" (50), see complete list of values." + }, + 0x6ebc: { + name: "ChapterSegmentEditionUID", + level: 4, + type: "u", + minver: 1, + webm: false, + range: "not 0", + description: "The EditionUID to play from the segment linked in ChapterSegmentUID." + }, + 0x6e67: { + name: "ChapterSegmentUID", + level: 4, + type: "b", + minver: 1, + webm: false, + range: ">0", + bytesize: 16, + description: "A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used." + }, + 0x73c4: { + name: "ChapterUID", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: true, + range: "not 0", + description: "A unique ID to identify the Chapter." + }, + 0xb6: { + name: "ChapterAtom", + level: 3, + "recursive": "1", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: true, + description: "Contains the atom information to use as the chapter atom (apply to all tracks)." + }, + 0x45dd: { + name: "EditionFlagOrdered", + level: 3, + type: "u", + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "Specify if the chapters can be defined multiple times and the order to play them is enforced. (1 bit)" + }, + 0x45db: { + name: "EditionFlagDefault", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "If a flag is set (1) the edition should be used as the default one. (1 bit)" + }, + 0x45bd: { + name: "EditionFlagHidden", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + range: "0-1", + description: "If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks; see flag notes). (1 bit)" + }, + 0x45bc: { + name: "EditionUID", + level: 3, + type: "u", + minver: 1, + webm: false, + range: "not 0", + description: "A unique ID to identify the edition. It's useful for tagging an edition." + }, + 0x45b9: { + name: "EditionEntry", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: true, + description: "Contains all information about a segment edition." + }, + 0x1043a770: { + name: "Chapters", + level: 1, + type: "m", + minver: 1, + webm: true, + description: "A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation." + }, + 0x46ae: { + name: "FileUID", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + range: "not 0", + description: "Unique ID representing the file, as random as possible." + }, + 0x465c: { + name: "FileData", + level: 3, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "The data of the file." + }, + 0x466e: { + name: "FileName", + level: 3, + type: "8", + mandatory: true, + minver: 1, + webm: false, + description: "Filename of the attached file." + }, + 0x467e: { + name: "FileDescription", + level: 3, + type: "8", + minver: 1, + webm: false, + description: "A human-friendly name for the attached file." + }, + 0x61a7: { + name: "AttachedFile", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "An attached file." + }, + 0x1941a469: { + name: "Attachments", + level: 1, + type: "m", + minver: 1, + webm: false, + description: "Contain attached files." + }, + 0xeb: { + name: "CueRefCodecState", + level: 5, + type: "u", + webm: false, + "default": 0, + description: "The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry." + }, + 0x535f: { + name: "CueRefNumber", + level: 5, + type: "u", + webm: false, + "default": 1, + range: "not 0", + description: "Number of the referenced Block of Track X in the specified Cluster." + }, + 0xdb: { + name: "CueReference", + level: 4, + type: "m", + multiple: true, + minver: 2, + webm: false, + description: "The Clusters containing the required referenced Blocks." + }, + 0xea: { + name: "CueCodecState", + level: 4, + type: "u", + minver: 2, + webm: false, + "default": 0, + description: "The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry." + }, + 0xb2: { + name: "CueDuration", + level: 4, + type: "u", + mandatory: false, + minver: 4, + webm: false, + description: "The duration of the block according to the segment time base. If missing the track's DefaultDuration does not apply and no duration information is available in terms of the cues." + }, + 0xf0: { + name: "CueRelativePosition", + level: 4, + type: "u", + mandatory: false, + minver: 4, + webm: false, + description: "The relative position of the referenced block inside the cluster with 0 being the first possible position for an element inside that cluster.", + position: "clusterRelative" + }, + 0xf1: { + name: "CueClusterPosition", + level: 4, + type: "u", + mandatory: true, + minver: 1, + description: "The position of the Cluster containing the required Block.", + position: "segment", + }, + 0xf7: { + name: "CueTrack", + level: 4, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "The track for which a position is given." + }, + 0xb7: { + name: "CueTrackPositions", + level: 3, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contain positions for different tracks corresponding to the timestamp." + }, + 0xb3: { + name: "CueTime", + level: 3, + type: "u", + mandatory: true, + minver: 1, + description: "Absolute timestamp according to the segment time base." + }, + 0xbb: { + name: "CuePoint", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contains all information relative to a seek point in the segment." + }, + 0x1c53bb6b: { + name: "Cues", + level: 1, + type: "m", + minver: 1, + description: "A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non \"live\" streams." + }, + 0x47e6: { + name: "ContentSigHashAlgo", + level: 6, + type: "u", + minver: 1, + webm: false, + "default": 0, + // "br": [ "", "" ], + description: "The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values: 1 - SHA1-160 2 - MD5" + }, + 0x47e5: { + name: "ContentSigAlgo", + level: 6, + type: "u", + minver: 1, + webm: false, + "default": 0, + // "br": "", + description: "The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values: 1 - RSA" + }, + 0x47e4: { + name: "ContentSigKeyID", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "This is the ID of the private key the data was signed with." + }, + 0x47e3: { + name: "ContentSignature", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "A cryptographic signature of the contents." + }, + 0x47e2: { + name: "ContentEncKeyID", + level: 6, + type: "b", + minver: 1, + webm: false, + description: "For public key algorithms this is the ID of the public key the the data was encrypted with." + }, + 0x47e1: { + name: "ContentEncAlgo", + level: 6, + type: "u", + minver: 1, + webm: false, + "default": 0, + // "br": "", + description: "The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values: 1 - DES, 2 - 3DES, 3 - Twofish, 4 - Blowfish, 5 - AES" + }, + 0x6d80: { + name: "ContentEncodings", + level: 3, + type: "m", + minver: 1, + webm: false, + description: "Settings for several content encoding mechanisms like compression or encryption." + }, + 0xc4: { + name: "TrickMasterTrackSegmentUID", + level: 3, + type: "b", + divx: true, + bytesize: 16, + description: "DivX trick track extenstions" + }, + 0xc7: { + name: "TrickMasterTrackUID", + level: 3, + type: "u", + divx: true, + description: "DivX trick track extenstions" + }, + 0xc6: { + name: "TrickTrackFlag", + level: 3, + type: "u", + divx: true, + "default": 0, + description: "DivX trick track extenstions" + }, + 0xc1: { + name: "TrickTrackSegmentUID", + level: 3, + type: "b", + divx: true, + bytesize: 16, + description: "DivX trick track extenstions" + }, + 0xc0: { + name: "TrickTrackUID", + level: 3, + type: "u", + divx: true, + description: "DivX trick track extenstions" + }, + 0xed: { + name: "TrackJoinUID", + level: 5, + type: "u", + mandatory: true, + multiple: true, + minver: 3, + webm: false, + range: "not 0", + description: "The trackUID number of a track whose blocks are used to create this virtual track." + }, + 0xe9: { + name: "TrackJoinBlocks", + level: 4, + type: "m", + minver: 3, + webm: false, + description: "Contains the list of all tracks whose Blocks need to be combined to create this virtual track" + }, + 0xe6: { + name: "TrackPlaneType", + level: 6, + type: "u", + mandatory: true, + minver: 3, + webm: false, + description: "The kind of plane this track corresponds to (0: left eye, 1: right eye, 2: background)." + }, + 0xe5: { + name: "TrackPlaneUID", + level: 6, + type: "u", + mandatory: true, + minver: 3, + webm: false, + range: "not 0", + description: "The trackUID number of the track representing the plane." + }, + 0xe4: { + name: "TrackPlane", + level: 5, + type: "m", + mandatory: true, + multiple: true, + minver: 3, + webm: false, + description: "Contains a video plane track that need to be combined to create this 3D track" + }, + 0xe3: { + name: "TrackCombinePlanes", + level: 4, + type: "m", + minver: 3, + webm: false, + description: "Contains the list of all video plane tracks that need to be combined to create this 3D track" + }, + 0xe2: { + name: "TrackOperation", + level: 3, + type: "m", + minver: 3, + webm: false, + description: "Operation that needs to be applied on tracks to create this virtual track. For more details look at the Specification Notes on the subject." + }, + 0x7d7b: { + name: "ChannelPositions", + cppname: "AudioPosition", + level: 4, + type: "b", + webm: false, + description: "Table of horizontal angles for each successive channel, see appendix." + }, + 0x9f: { + name: "Channels", + cppname: "AudioChannels", + level: 4, + type: "u", + mandatory: true, + minver: 1, + "default": 1, + range: "not 0", + description: "Numbers of channels in the track." + }, + 0x78b5: { + name: "OutputSamplingFrequency", + cppname: "AudioOutputSamplingFreq", + level: 4, + type: "f", + minver: 1, + "default": "Sampling Frequency", + range: "> 0", + description: "Real output sampling frequency in Hz (used for SBR techniques)." + }, + 0xb5: { + name: "SamplingFrequency", + cppname: "AudioSamplingFreq", + level: 4, + type: "f", + mandatory: true, + minver: 1, + "default": 8000.0, + range: "> 0", + description: "Sampling frequency in Hz." + }, + 0xe1: { + name: "Audio", + cppname: "TrackAudio", + level: 3, + type: "m", + minver: 1, + description: "Audio settings." + }, + 0x2383e3: { + name: "FrameRate", + cppname: "VideoFrameRate", + level: 4, + type: "f", + range: "> 0", + "strong": "Informational", + description: "Number of frames per second. only." + }, + 0x2fb523: { + name: "GammaValue", + cppname: "VideoGamma", + level: 4, + type: "f", + webm: false, + range: "> 0", + description: "Gamma Value." + }, + 0x2eb524: { + name: "ColourSpace", + cppname: "VideoColourSpace", + level: 4, + type: "b", + minver: 1, + webm: false, + bytesize: 4, + description: "Same value as in AVI (32 bits)." + }, + 0x54b3: { + name: "AspectRatioType", + cppname: "VideoAspectRatio", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed)." + }, + 0x54b2: { + name: "DisplayUnit", + cppname: "VideoDisplayUnit", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "How DisplayWidth & DisplayHeight should be interpreted (0: pixels, 1: centimeters, 2: inches, 3: Display Aspect Ratio)." + }, + 0x54ba: { + name: "DisplayHeight", + cppname: "VideoDisplayHeight", + level: 4, + type: "u", + minver: 1, + "default": "PixelHeight", + range: "not 0", + description: "Height of the video frames to display. The default value is only valid when DisplayUnit is 0." + }, + 0x54b0: { + name: "DisplayWidth", + cppname: "VideoDisplayWidth", + level: 4, + type: "u", + minver: 1, + "default": "PixelWidth", + range: "not 0", + description: "Width of the video frames to display. The default value is only valid when DisplayUnit is 0." + }, + 0x54dd: { + name: "PixelCropRight", + cppname: "VideoPixelCropRight", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove on the right of the image." + }, + 0x54cc: { + name: "PixelCropLeft", + cppname: "VideoPixelCropLeft", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove on the left of the image." + }, + 0x54bb: { + name: "PixelCropTop", + cppname: "VideoPixelCropTop", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove at the top of the image." + }, + 0x54aa: { + name: "PixelCropBottom", + cppname: "VideoPixelCropBottom", + level: 4, + type: "u", + minver: 1, + "default": 0, + description: "The number of video pixels to remove at the bottom of the image (for HDTV content)." + }, + 0xba: { + name: "PixelHeight", + cppname: "VideoPixelHeight", + level: 4, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "Height of the encoded video frames in pixels." + }, + 0xb0: { + name: "PixelWidth", + cppname: "VideoPixelWidth", + level: 4, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "Width of the encoded video frames in pixels." + }, + 0x53b9: { + name: "OldStereoMode", + level: 4, + type: "u", + "maxver": "0", + webm: false, + divx: false, + description: "DEPRECATED, DO NOT USE. Bogus StereoMode value used in old versions of libmatroska. (0: mono, 1: right eye, 2: left eye, 3: both eyes)." + }, + 0x53c0: { + name: "AlphaMode", + cppname: "VideoAlphaMode", + level: 4, + type: "u", + minver: 3, + webm: true, + "default": 0, + description: "Alpha Video Mode. Presence of this element indicates that the BlockAdditional element could contain Alpha data." + }, + 0x53b8: { + name: "StereoMode", + cppname: "VideoStereoMode", + level: 4, + type: "u", + minver: 3, + webm: true, + "default": 0, + description: "Stereo-3D video mode (0: mono, 1: side by side (left eye is first), 2: top-bottom (right eye is first), 3: top-bottom (left eye is first), 4: checkboard (right is first), 5: checkboard (left is first), 6: row interleaved (right is first), 7: row interleaved (left is first), 8: column interleaved (right is first), 9: column interleaved (left is first), 10: anaglyph (cyan/red), 11: side by side (right eye is first), 12: anaglyph (green/magenta), 13 both eyes laced in one Block (left eye is first), 14 both eyes laced in one Block (right eye is first)) . There are some more details on 3D support in the Specification Notes." + }, + 0x9a: { + name: "FlagInterlaced", + cppname: "VideoFlagInterlaced", + level: 4, + type: "u", + mandatory: true, + minver: 2, + webm: true, + "default": 0, + range: "0-1", + description: "Set if the video is interlaced. (1 bit)" + }, + 0xe0: { + name: "Video", + cppname: "TrackVideo", + level: 3, + type: "m", + minver: 1, + description: "Video settings." + }, + 0x66a5: { + name: "TrackTranslateTrackID", + level: 4, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used." + }, + 0x66bf: { + name: "TrackTranslateCodec", + level: 4, + type: "u", + mandatory: true, + minver: 1, + webm: false, + description: "The chapter codec using this ID (0: Matroska Script, 1: DVD-menu)." + }, + 0x66fc: { + name: "TrackTranslateEditionUID", + level: 4, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment." + }, + 0x56bb: { + name: "SeekPreRoll", + level: 3, + type: "u", + mandatory: true, + multiple: false, + "default": 0, + minver: 4, + webm: true, + description: "After a discontinuity, SeekPreRoll is the duration in nanoseconds of the data the decoder must decode before the decoded data is valid." + }, + 0x56aa: { + name: "CodecDelay", + level: 3, + type: "u", + multiple: false, + "default": 0, + minver: 4, + webm: true, + description: "CodecDelay is The codec-built-in delay in nanoseconds. This value must be subtracted from each block timestamp in order to get the actual timestamp. The value should be small so the muxing of tracks with the same actual timestamp are in the same Cluster." + }, + 0x6fab: { + name: "TrackOverlay", + level: 3, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see SilentTracks) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc." + }, + 0xaa: { + name: "CodecDecodeAll", + level: 3, + type: "u", + mandatory: true, + minver: 2, + webm: false, + "default": 1, + range: "0-1", + description: "The codec can decode potentially damaged data (1 bit)." + }, + 0x26b240: { + name: "CodecDownloadURL", + level: 3, + type: "s", + multiple: true, + webm: false, + description: "A URL to download about the codec used." + }, + 0x3b4040: { + name: "CodecInfoURL", + level: 3, + type: "s", + multiple: true, + webm: false, + description: "A URL to find information about the codec used." + }, + 0x3a9697: { + name: "CodecSettings", + level: 3, + type: "8", + webm: false, + description: "A string describing the encoding setting used." + }, + 0x63a2: { + name: "CodecPrivate", + level: 3, + type: "b", + minver: 1, + description: "Private data only known to the codec." + }, + 0x22b59c: { + name: "Language", + cppname: "TrackLanguage", + level: 3, + type: "s", + minver: 1, + "default": "eng", + description: "Specifies the language of the track in the Matroska languages form." + }, + 0x536e: { + name: "Name", + cppname: "TrackName", + level: 3, + type: "8", + minver: 1, + description: "A human-readable track name." + }, + 0x55ee: { + name: "MaxBlockAdditionID", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "The maximum value of BlockAdditions for this track." + }, + 0x537f: { + name: "TrackOffset", + level: 3, + type: "i", + webm: false, + "default": 0, + description: "A value to add to the Block's Timestamp. This can be used to adjust the playback offset of a track." + }, + 0x23314f: { + name: "TrackTimecodeScale", + level: 3, + type: "f", + mandatory: true, + minver: 1, + "maxver": "3", + webm: false, + "default": 1.0, + range: "> 0", + description: "DEPRECATED, DO NOT USE. The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs)." + }, + 0x234e7a: { + name: "DefaultDecodedFieldDuration", + cppname: "TrackDefaultDecodedFieldDuration", + level: 3, + type: "u", + minver: 4, + range: "not 0", + description: "The period in nanoseconds (not scaled by TimcodeScale)\nbetween two successive fields at the output of the decoding process (see the notes)" + }, + 0x23e383: { + name: "DefaultDuration", + cppname: "TrackDefaultDuration", + level: 3, + type: "u", + minver: 1, + range: "not 0", + description: "Number of nanoseconds (not scaled via TimecodeScale) per frame ('frame' in the Matroska sense -- one element put into a (Simple)Block)." + }, + 0x6df8: { + name: "MaxCache", + cppname: "TrackMaxCache", + level: 3, + type: "u", + minver: 1, + webm: false, + description: "The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed." + }, + 0x6de7: { + name: "MinCache", + cppname: "TrackMinCache", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used." + }, + 0x9c: { + name: "FlagLacing", + cppname: "TrackFlagLacing", + level: 3, + type: "u", + mandatory: true, + minver: 1, + "default": 1, + range: "0-1", + description: "Set if the track may contain blocks using lacing. (1 bit)" + }, + 0x55aa: { + name: "FlagForced", + cppname: "TrackFlagForced", + level: 3, + type: "u", + mandatory: true, + minver: 1, + "default": 0, + range: "0-1", + description: "Set if that track MUST be active during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind. (1 bit)" + }, + 0xb9: { + name: "FlagEnabled", + cppname: "TrackFlagEnabled", + level: 3, + type: "u", + mandatory: true, + minver: 2, + webm: true, + "default": 1, + range: "0-1", + description: "Set if the track is usable. (1 bit)" + }, + 0x73c5: { + name: "TrackUID", + level: 3, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file." + }, + 0xd7: { + name: "TrackNumber", + level: 3, + type: "u", + mandatory: true, + minver: 1, + range: "not 0", + description: "The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number)." + }, + 0xae: { + name: "TrackEntry", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Describes a track with all elements." + }, + 0x1654ae6b: { + name: "Tracks", + level: 1, + type: "m", + multiple: true, + minver: 1, + description: "A top-level block of information with many tracks described." + }, + 0xaf: { + name: "EncryptedBlock", + level: 2, + type: "b", + multiple: true, + webm: false, + description: "Similar to EncryptedBlock Structure)" + }, + 0xca: { + name: "ReferenceTimeCode", + level: 4, + type: "u", + multiple: false, + mandatory: true, + minver: 0, + webm: false, + divx: true, + description: "DivX trick track extenstions" + }, + 0xc9: { + name: "ReferenceOffset", + level: 4, + type: "u", + multiple: false, + mandatory: true, + minver: 0, + webm: false, + divx: true, + description: "DivX trick track extenstions" + }, + 0xc8: { + name: "ReferenceFrame", + level: 3, + type: "m", + multiple: false, + minver: 0, + webm: false, + divx: true, + description: "DivX trick track extenstions" + }, + 0xcf: { + name: "SliceDuration", + level: 5, + type: "u", + "default": 0, + description: "The (scaled) duration to apply to the element." + }, + 0xce: { + name: "Delay", + cppname: "SliceDelay", + level: 5, + type: "u", + "default": 0, + description: "The (scaled) delay to apply to the element." + }, + 0xcb: { + name: "BlockAdditionID", + cppname: "SliceBlockAddID", + level: 5, + type: "u", + "default": 0, + description: "The ID of the BlockAdditional element (0 is the main Block)." + }, + 0xcd: { + name: "FrameNumber", + cppname: "SliceFrameNumber", + level: 5, + type: "u", + "default": 0, + description: "The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame)." + }, + 0xcc: { + name: "LaceNumber", + cppname: "SliceLaceNumber", + level: 5, + type: "u", + minver: 1, + "default": 0, + divx: false, + description: "The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback." + }, + 0xe8: { + name: "TimeSlice", + level: 4, + type: "m", + multiple: true, + minver: 1, + divx: false, + description: "Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback." + }, + 0x8e: { + name: "Slices", + level: 3, + type: "m", + minver: 1, + divx: false, + description: "Contains slices description." + }, + 0x75a2: { + name: "DiscardPadding", + level: 3, + type: "i", + minver: 4, + webm: true, + description: "Duration in nanoseconds of the silent data added to the Block (padding at the end of the Block for positive value, at the beginning of the Block for negative value). The duration of DiscardPadding is not calculated in the duration of the TrackEntry and should be discarded during playback." + }, + 0xa4: { + name: "CodecState", + level: 3, + type: "b", + minver: 2, + webm: false, + description: "The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry." + }, + 0xfd: { + name: "ReferenceVirtual", + level: 3, + type: "i", + webm: false, + description: "Relative position of the data that should be in position of the virtual block." + }, + 0xfb: { + name: "ReferenceBlock", + level: 3, + type: "i", + multiple: true, + minver: 1, + description: "Timestamp of another frame used as a reference (ie: B or P frame). The timestamp is relative to the block it's attached to." + }, + 0xfa: { + name: "ReferencePriority", + cppname: "FlagReferenced", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 0, + description: "This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced." + }, + 0x9b: { + name: "BlockDuration", + level: 3, + type: "u", + minver: 1, + "default": "TrackDuration", + description: "The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track (but can be omitted as other default values). When not written and with no DefaultDuration, the value is assumed to be the difference between the timestamp of this Block and the timestamp of the next Block in \"display\" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks. When set to 0 that means the frame is not a keyframe." + }, + 0xa5: { + name: "BlockAdditional", + level: 5, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "Interpreted by the codec as it wishes (using the BlockAddID)." + }, + 0xee: { + name: "BlockAddID", + level: 5, + type: "u", + mandatory: true, + minver: 1, + webm: false, + "default": 1, + range: "not 0", + description: "An ID to identify the BlockAdditional level." + }, + 0xa6: { + name: "BlockMore", + level: 4, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + webm: false, + description: "Contain the BlockAdditional and some parameters." + }, + 0x75a1: { + name: "BlockAdditions", + level: 3, + type: "m", + minver: 1, + webm: false, + description: "Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block structure could still see and use/skip these data." + }, + 0xa2: { + name: "BlockVirtual", + level: 3, + type: "b", + webm: false, + description: "A Block with no data. It must be stored in the stream at the place the real Block should be in display order. (see Block Virtual)" + }, + 0xa1: { + name: "Block", + level: 3, + type: "b", + mandatory: true, + minver: 1, + description: "Block containing the actual data to be rendered and a timestamp relative to the Cluster Timecode. (see Block Structure)" + }, + 0xa0: { + name: "BlockGroup", + level: 2, + type: "m", + multiple: true, + minver: 1, + description: "Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock." + }, + 0xa3: { + name: "SimpleBlock", + level: 2, + type: "b", + multiple: true, + minver: 2, + webm: true, + divx: true, + description: "Similar to SimpleBlock Structure" + }, + 0xab: { + name: "PrevSize", + cppname: "ClusterPrevSize", + level: 2, + type: "u", + minver: 1, + description: "Size of the previous Cluster, in octets. Can be useful for backward playing.", + position: "prevCluster" + }, + 0xa7: { + name: "Position", + cppname: "ClusterPosition", + level: 2, + type: "u", + minver: 1, + webm: false, + description: "The Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.", + position: "segment" + }, + 0x58d7: { + name: "SilentTrackNumber", + cppname: "ClusterSilentTrackNumber", + level: 3, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster." + }, + 0xe7: { + name: "Timecode", + cppname: "ClusterTimecode", + level: 2, + type: "u", + mandatory: true, + minver: 1, + description: "Absolute timestamp of the cluster (based on TimecodeScale)." + }, + 0x1f43b675: { + name: "Cluster", + level: 1, + type: "m", + multiple: true, + minver: 1, + description: "The lower level element containing the (monolithic) Block structure." + }, + 0x4d80: { + name: "MuxingApp", + level: 2, + type: "8", + mandatory: true, + minver: 1, + description: "Muxing application or library (\"libmatroska-0.4.3\")." + }, + 0x7ba9: { + name: "Title", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "General name of the segment." + }, + 0x2ad7b2: { + name: "TimecodeScaleDenominator", + level: 2, + type: "u", + mandatory: true, + minver: 4, + "default": "1000000000", + description: "Timestamp scale numerator, see TimecodeScale." + }, + 0x2ad7b1: { + name: "TimecodeScale", + level: 2, + type: "u", + mandatory: true, + minver: 1, + "default": "1000000", + description: "Timestamp scale in nanoseconds (1.000.000 means all timestamps in the segment are expressed in milliseconds)." + }, + 0x69a5: { + name: "ChapterTranslateID", + level: 3, + type: "b", + mandatory: true, + minver: 1, + webm: false, + description: "The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used." + }, + 0x69bf: { + name: "ChapterTranslateCodec", + level: 3, + type: "u", + mandatory: true, + minver: 1, + webm: false, + description: "The chapter codec using this ID (0: Matroska Script, 1: DVD-menu)." + }, + 0x69fc: { + name: "ChapterTranslateEditionUID", + level: 3, + type: "u", + multiple: true, + minver: 1, + webm: false, + description: "Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment." + }, + 0x3e83bb: { + name: "NextFilename", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "An escaped filename corresponding to the next segment." + }, + 0x3eb923: { + name: "NextUID", + level: 2, + type: "b", + minver: 1, + webm: false, + bytesize: 16, + description: "A unique ID to identify the next chained segment (128 bits)." + }, + 0x3c83ab: { + name: "PrevFilename", + level: 2, + type: "8", + minver: 1, + webm: false, + description: "An escaped filename corresponding to the previous segment." + }, + 0x3cb923: { + name: "PrevUID", + level: 2, + type: "b", + minver: 1, + webm: false, + bytesize: 16, + description: "A unique ID to identify the previous chained segment (128 bits)." + }, + 0x73a4: { + name: "SegmentUID", + level: 2, + type: "b", + minver: 1, + webm: false, + range: "not 0", + bytesize: 16, + description: "A randomly generated unique ID to identify the current segment between many others (128 bits)." + }, + 0x1549a966: { + name: "Info", + level: 1, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contains miscellaneous general information and statistics on the file." + }, + 0x53ac: { + name: "SeekPosition", + level: 3, + type: "u", + mandatory: true, + minver: 1, + description: "The position of the element in the segment in octets (0 = first level 1 element).", + position: "segment" + }, + 0x53ab: { + name: "SeekID", + level: 3, + type: "b", + mandatory: true, + minver: 1, + description: "The binary ID corresponding to the element name.", + type2: "ebmlID" + }, + 0x4dbb: { + name: "Seek", + cppname: "SeekPoint", + level: 2, + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Contains a single seek entry to an EBML element." + }, + 0x114d9b74: { + name: "SeekHead", + cppname: "SeekHeader", + level: 1, + type: "m", + multiple: true, + minver: 1, + description: "Contains the position of other level 1 elements." + }, + 0x7e7b: { + name: "SignatureElementList", + level: 2, + type: "m", + multiple: true, + webm: false, + i: "Cluster|Block|BlockAdditional", + description: "A list consists of a number of consecutive elements that represent one case where data is used in signature. Ex: means that the BlockAdditional of all Blocks in all Clusters is used for encryption." + }, + 0x7e5b: { + name: "SignatureElements", + level: 1, + type: "m", + webm: false, + description: "Contains elements that will be used to compute the signature." + }, + 0x7eb5: { + name: "Signature", + level: 1, + type: "b", + webm: false, + description: "The signature of the data (until a new." + }, + 0x7ea5: { + name: "SignaturePublicKey", + level: 1, + type: "b", + webm: false, + description: "The public key to use with the algorithm (in the case of a PKI-based signature)." + }, + 0x7e9a: { + name: "SignatureHash", + level: 1, + type: "u", + webm: false, + description: "Hash algorithm used (1=SHA1-160, 2=MD5)." + }, + 0x7e8a: { + name: "SignatureAlgo", + level: 1, + type: "u", + webm: false, + description: "Signature algorithm used (1=RSA, 2=elliptic)." + }, + 0x1b538667: { + name: "SignatureSlot", + level: -1, + type: "m", + multiple: true, + webm: false, + description: "Contain signature of some (coming) elements in the stream." + }, + 0xbf: { + name: "CRC-32", + level: -1, + type: "b", + minver: 1, + webm: false, + description: "The CRC is computed on all the data of the Master element it's in. The CRC element should be the first in it's parent master for easier reading. All level 1 elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian", + crc: true + }, + 0xec: { + name: "Void", + level: -1, + type: "b", + minver: 1, + description: "Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use." + }, + 0x42f3: { + name: "EBMLMaxSizeLength", + level: 1, + type: "u", + mandatory: true, + "default": 8, + minver: 1, + description: "The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid." + }, + 0x42f2: { + name: "EBMLMaxIDLength", + level: 1, + type: "u", + mandatory: true, + "default": 4, + minver: 1, + description: "The maximum length of the IDs you'll find in this file (4 or less in Matroska)." + }, + 0x42f7: { + name: "EBMLReadVersion", + level: 1, + type: "u", + mandatory: true, + "default": 1, + minver: 1, + description: "The minimum EBML version a parser has to support to read this file." + }, + 0x1a45dfa3: { + name: "EBML", + level: "0", + type: "m", + mandatory: true, + multiple: true, + minver: 1, + description: "Set the EBML characteristics of the data to follow. Each EBML document has to start with this." + } +}; diff --git a/src/ebml/index.ts b/src/ebml/index.ts new file mode 100644 index 0000000..88c2083 --- /dev/null +++ b/src/ebml/index.ts @@ -0,0 +1,12 @@ +export * from "./EBML"; +import Decoder from "./EBMLDecoder"; +import Encoder from "./EBMLEncoder"; +import Reader from "./EBMLReader"; +import * as tools from "./tools"; + +export { + Decoder, + Encoder, + Reader, + tools +}; diff --git a/src/ebml/tools-ebml.ts b/src/ebml/tools-ebml.ts new file mode 100644 index 0000000..f865d70 --- /dev/null +++ b/src/ebml/tools-ebml.ts @@ -0,0 +1,264 @@ +import { Buffer } from "./tools"; + +export default class Tools { + /** + * read variable length integer per + * https://www.matroska.org/technical/specs/index.html#EBML_ex + * @static + * @param {Buffer} buffer containing input + * @param {Number} [start=0] position in buffer + * @returns {{length: Number, value: number}} value / length object + */ + static readVint(buffer, start = 0) { + const length = 8 - Math.floor(Math.log2(buffer[start])); + if (length > 8) { + const number = Tools.readHexString(buffer, start, start + length); + throw new Error(`Unrepresentable length: ${length} ${number}`); + } + + if (start + length > buffer.length) { + return null; + } + + let value = buffer[start] & ((1 << (8 - length)) - 1); + for (let i = 1; i < length; i += 1) { + if (i === 7) { + if (value >= 2 ** 8 && buffer[start + 7] > 0) { + return { length, value: -1 }; + } + } + value *= 2 ** 8; + value += buffer[start + i]; + } + + return { length, value }; + } + + /** + * write variable length integer + * @static + * @param {Number} value to store into buffer + * @returns {Buffer} containing the value + */ + static writeVint(value) { + if (value < 0 || value > 2 ** 53) { + throw new Error(`Unrepresentable value: ${value}`); + } + + let length = 1; + for (length = 1; length <= 8; length += 1) { + if (value < 2 ** (7 * length) - 1) { + break; + } + } + + const buffer = Buffer.alloc(length); + let val = value; + for (let i = 1; i <= length; i += 1) { + const b = val & 0xff; + buffer[length - i] = b; + val -= b; + val /= 2 ** 8; + } + buffer[0] |= 1 << (8 - length); + + return buffer; + } + + /** + * * + * concatenate two arrays of bytes + * @static + * @param {Buffer} a1 First array + * @param {Buffer} a2 Second array + * @returns {Buffer} concatenated arrays + */ + static concatenate(a1, a2) { + // both null or undefined + if (!a1 && !a2) { + return Buffer.from([]); + } + if (!a1 || a1.byteLength === 0) { + return a2; + } + if (!a2 || a2.byteLength === 0) { + return a1; + } + + return Buffer.from([...a1, ...a2]); + } + + /** + * get a hex text string from Buff[start,end) + * @param {Buffer} buff from which to read the string + * @param {Number} [start=0] starting point (default 0) + * @param {Number} [end=buff.byteLength] ending point (default the whole buffer) + * @returns {string} the hex string + */ + static readHexString(buff, start = 0, end = buff.byteLength) { + return Array.from(buff.slice(start, end)) + .map(q => Number(q).toString(16)) + .reduce((acc, current) => `${acc}${current.padStart(2, '0')}`, ''); + } + + /** + * tries to read out a UTF-8 encoded string + * @param {Buffer} buff the buffer to attempt to read from + * @return {string|null} the decoded text, or null if unable to + */ + static readUtf8(buff) { + try { + return Buffer.from(buff).toString('utf8'); + } catch (exception) { + return null; + } + } + + /** + * get an unsigned number from a buffer + * @param {Buffer} buff from which to read variable-length unsigned number + * @returns {number|string} result (in hex for lengths > 6) + */ + static readUnsigned(buff) { + const b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 1: + return b.getUint8(0); + case 2: + return b.getUint16(0); + case 4: + return b.getUint32(0); + default: + break; + } + if (buff.byteLength <= 6) { + return buff.reduce((acc, current) => acc * 256 + current, 0); + } + + return Tools.readHexString(buff, 0, buff.byteLength); + } + + /** + * get an signed number from a buffer + * @static + * @param {Buffer} buff from which to read variable-length signed number + * @returns {number} result + */ + static readSigned(buff) { + const b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 1: + return b.getInt8(0); + case 2: + return b.getInt16(0); + case 4: + return b.getInt32(0); + default: + return NaN; + } + } + + /** + * get an floating-point number from a buffer + * @static + * @param {Buffer} buff from which to read variable-length floating-point number + * @returns {number} result + */ + static readFloat(buff) { + const b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 4: + return b.getFloat32(0); + case 8: + return b.getFloat64(0); + default: + return NaN; + } + } + + /** + * get a date from a buffer + * @static + * @param {Buffer} buff from which to read the date + * @return {Date} result + */ + static readDate(buff) { + const b = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); + switch (buff.byteLength) { + case 1: + return new Date(b.getUint8(0)); + case 2: + return new Date(b.getUint16(0)); + case 4: + return new Date(b.getUint32(0)); + case 8: + return new Date(Number.parseInt(Tools.readHexString(buff), 16)); + default: + return new Date(0); + } + } + + /** + * Reads the data from a tag + * @static + * @param {TagData} tagObj The tag object to be read + * @param {Buffer} data Data to be transformed + * @return {Tag} result + */ + static readDataFromTag(tagObj, data) { + const { type, name } = tagObj; + let { track } = tagObj; + let discardable = tagObj.discardable || false; + let keyframe = tagObj.keyframe || false; + let payload = null; + let value; + + switch (type) { + case 'u': + value = Tools.readUnsigned(data); + break; + case 'f': + value = Tools.readFloat(data); + break; + case 'i': + value = Tools.readSigned(data); + break; + case 's': + value = String.fromCharCode(...data); + break; + case '8': + value = Tools.readUtf8(data); + break; + case 'd': + value = Tools.readDate(data); + break; + default: + break; + } + + if (name === 'SimpleBlock' || name === 'Block') { + let p = 0; + const { length, value: trak } = Tools.readVint(data, p) as any; + p += length; + track = trak; + value = Tools.readSigned(data.subarray(p, p + 2)); + p += 2; + if (name === 'SimpleBlock') { + keyframe = Boolean(data[length + 2] & 0x80); + discardable = Boolean(data[length + 2] & 0x01); + } + p += 1; + payload = data.subarray(p); + } + + return { + ...tagObj, + data, + discardable, + keyframe, + payload, + track, + value, + }; + } +} diff --git a/src/ebml/tools.ts b/src/ebml/tools.ts new file mode 100644 index 0000000..c236889 --- /dev/null +++ b/src/ebml/tools.ts @@ -0,0 +1,703 @@ +/// +import {Int64BE, Uint64BE} from "int64-buffer"; +import * as EBML from "./EBML"; +import Encoder from "./EBMLEncoder"; +import * as _Buffer from "buffer"; +import _tools from "./tools-ebml"; +import * as _block from "ebml-block"; + +export const Buffer: typeof global.Buffer = _Buffer.Buffer; +export const readVint: (buffer: Buffer, start: number)=> null | ({length: number; value: number; }) = _tools.readVint; +export const writeVint: (val: number)=> Buffer = _tools.writeVint; + +export const ebmlBlock: (buf: Buffer)=> EBML.SimpleBlock = _block; +export function readBlock(buf: ArrayBuffer): EBML.SimpleBlock { + return ebmlBlock(new Buffer(buf)); +} + +/** + * @param end - if end === false then length is unknown + */ +export function encodeTag(tagId: Buffer, tagData: Buffer, unknownSize=false): Buffer { + return concat([ + tagId, + unknownSize ? + new Buffer('01ffffffffffffff', 'hex') : + writeVint(tagData.length), + tagData + ]); +} + +/** + * WebP ファイルにできる SimpleBlock の パスフィルタ + */ +export function WebPBlockFilter(elms: EBML.EBMLElementDetail[]): (EBML.BinaryElement & EBML.ElementDetail & {data: Buffer})[] { + return elms.reduce<(EBML.BinaryElement & EBML.ElementDetail & {data: Buffer})[]>((lst, elm)=>{ + if(elm.type !== "b"){ return lst; } + if(elm.name !== "SimpleBlock"){ return lst; } + const o = ebmlBlock(elm.data); + const hasWebP = o.frames.some((frame)=>{ + // https://tools.ietf.org/html/rfc6386#section-19.1 + const startcode = frame.slice(3, 6).toString("hex"); + return startcode === "9d012a"; + }); + if(!hasWebP){ return lst; } + return lst.concat(elm); + }, []); +} + +/** + * @param frame - VP8 BitStream のうち startcode をもつ frame + * @return - WebP ファイルの ArrayBuffer + */ +export function VP8BitStreamToRiffWebPBuffer(frame: Buffer): Buffer { + const VP8Chunk = createRIFFChunk("VP8 ", frame); + const WebPChunk = concat([ + new Buffer("WEBP", "ascii"), + VP8Chunk + ]); + return createRIFFChunk("RIFF", WebPChunk); +} + +/** + * RIFF データチャンクを作る + */ +export function createRIFFChunk(FourCC: string, chunk: Buffer): Buffer { + const chunkSize = new Buffer(4); + chunkSize.writeUInt32LE(chunk.byteLength , 0); + return concat([ + new Buffer(FourCC.substr(0, 4), "ascii"), + chunkSize, + chunk, + new Buffer(chunk.byteLength % 2 === 0 ? 0 : 1) // padding + ]); +} + + +/* Original Metadata + + m 0 EBML + u 1 EBMLVersion 1 + u 1 EBMLReadVersion 1 + u 1 EBMLMaxIDLength 4 + u 1 EBMLMaxSizeLength 8 + s 1 DocType webm + u 1 DocTypeVersion 4 + u 1 DocTypeReadVersion 2 + m 0 Segment + m 1 Info segmentContentStartPos, all CueClusterPositions provided in info.cues will be relative to here and will need adjusted + u 2 TimecodeScale 1000000 + 8 2 MuxingApp Chrome + 8 2 WritingApp Chrome + m 1 Tracks tracksStartPos + m 2 TrackEntry + u 3 TrackNumber 1 + u 3 TrackUID 31790271978391090 + u 3 TrackType 2 + s 3 CodecID A_OPUS + b 3 CodecPrivate + m 3 Audio + f 4 SamplingFrequency 48000 + u 4 Channels 1 + m 2 TrackEntry + u 3 TrackNumber 2 + u 3 TrackUID 24051277436254136 + u 3 TrackType 1 + s 3 CodecID V_VP8 + m 3 Video + u 4 PixelWidth 1024 + u 4 PixelHeight 576 + m 1 Cluster clusterStartPos + u 2 Timecode 0 + b 2 SimpleBlock track:2 timecode:0 keyframe:true invisible:false discardable:false lacing:1 +*/ + +/* Desired Metadata + + m 0 EBML + u 1 EBMLVersion 1 + u 1 EBMLReadVersion 1 + u 1 EBMLMaxIDLength 4 + u 1 EBMLMaxSizeLength 8 + s 1 DocType webm + u 1 DocTypeVersion 4 + u 1 DocTypeReadVersion 2 + m 0 Segment + m 1 SeekHead -> This is SeekPosition 0, so all SeekPositions can be calculated as (bytePos - segmentContentStartPos), which is 44 in this case + m 2 Seek + b 3 SeekID -> Buffer([0x15, 0x49, 0xA9, 0x66]) Info + u 3 SeekPosition -> infoStartPos = + m 2 Seek + b 3 SeekID -> Buffer([0x16, 0x54, 0xAE, 0x6B]) Tracks + u 3 SeekPosition { tracksStartPos } + m 2 Seek + b 3 SeekID -> Buffer([0x1C, 0x53, 0xBB, 0x6B]) Cues + u 3 SeekPosition { cuesStartPos } + m 1 Info + f 2 Duration 32480 -> overwrite, or insert if it doesn't exist + u 2 TimecodeScale 1000000 + 8 2 MuxingApp Chrome + 8 2 WritingApp Chrome + m 1 Tracks + m 2 TrackEntry + u 3 TrackNumber 1 + u 3 TrackUID 31790271978391090 + u 3 TrackType 2 + s 3 CodecID A_OPUS + b 3 CodecPrivate + m 3 Audio + f 4 SamplingFrequency 48000 + u 4 Channels 1 + m 2 TrackEntry + u 3 TrackNumber 2 + u 3 TrackUID 24051277436254136 + u 3 TrackType 1 + s 3 CodecID V_VP8 + m 3 Video + u 4 PixelWidth 1024 + u 4 PixelHeight 576 + m 1 Cues -> cuesStartPos + m 2 CuePoint + u 3 CueTime 0 + m 3 CueTrackPositions + u 4 CueTrack 1 + u 4 CueClusterPosition 3911 + m 2 CuePoint + u 3 CueTime 600 + m 3 CueTrackPositions + u 4 CueTrack 1 + u 4 CueClusterPosition 3911 + m 1 Cluster + u 2 Timecode 0 + b 2 SimpleBlock track:2 timecode:0 keyframe:true invisible:false discardable:false lacing:1 +*/ + +/** + * convert the metadata from a streaming webm bytestream to a seekable file by inserting Duration, Seekhead and Cues + * @param originalMetadata - orginal metadata (everything before the clusters start) from media recorder + * @param duration - Duration (TimecodeScale) + * @param cues - cue points for clusters + */ +export function makeMetadataSeekable( + originalMetadata: EBML.EBMLElementDetail[], + duration: number, + cuesInfo: {CueTrack: number; CueClusterPosition: number; CueTime: number; }[] ) + + : ArrayBuffer { + + // extract the header, we can reuse this as-is + let header = extractElement("EBML", originalMetadata); + let headerSize = encodedSizeOfEbml(header); + //console.error("Header size: " + headerSize); + //printElementIds(header); + + // After the header comes the Segment open tag, which in this implementation is always 12 bytes (4 byte id, 8 byte 'unknown length') + // After that the segment content starts. All SeekPositions and CueClusterPosition must be relative to segmentContentStartPos + let segmentContentStartPos = headerSize + 12; + //console.error("segmentContentStartPos: " + segmentContentStartPos); + + // find the original metadata size, and adjust it for header size and Segment start element so we can keep all positions relative to segmentContentStartPos + + let originalMetadataSize = originalMetadata[originalMetadata.length-1].dataEnd - segmentContentStartPos; + //console.error("Original Metadata size: " + originalMetadataSize); + //printElementIds(originalMetadata); + + // extract the segment info, remove the potentially existing Duration element, and add our own one. + let info : EBML.EBMLElementBuffer[] = extractElement("Info", originalMetadata); + removeElement("Duration", info); + info.splice(1,0, {name: "Duration", type: "f", data: createFloatBuffer(duration, 8) }) + let infoSize = encodedSizeOfEbml(info); + //console.error("Info size: " + infoSize); + //printElementIds(info); + + // extract the track info, we can re-use this as is + let tracks = extractElement("Tracks", originalMetadata) + let tracksSize = encodedSizeOfEbml(tracks); + //console.error("Tracks size: " + tracksSize); + //printElementIds(tracks); + + let seekHeadSize = 47; // Initial best guess, but could be slightly larger if the Cues element is huge. + let seekHead : EBML.EBMLElementBuffer[] = []; + + let cuesSize = 5 + cuesInfo.length * 15; // very rough initial approximation, depends a lot on file size and number of CuePoints + let cues : EBML.EBMLElementBuffer[] = []; + + let lastSizeDifference = -1; // + + // The size of SeekHead and Cues elements depends on how many bytes the offsets values can be encoded in. + // The actual offsets in CueClusterPosition depend on the final size of the SeekHead and Cues elements + // We need to iteratively converge to a stable solution. + + const maxIterations = 10; + for (let i = 0; i < maxIterations; i++) { + + // SeekHead starts at 0 + let infoStart = seekHeadSize; // Info comes directly after SeekHead + let tracksStart = infoStart + infoSize; // Tracks comes directly after Info + let cuesStart = tracksStart + tracksSize; // Cues starts directly after + let newMetadataSize = cuesStart + cuesSize; // total size of metadata + + // This is the offset all CueClusterPositions should be adjusted by due to the metadata size changing. + let sizeDifference = newMetadataSize - originalMetadataSize; + // console.error(`infoStart: ${infoStart}, infoSize: ${infoSize}`); + // console.error(`tracksStart: ${tracksStart}, tracksSize: ${tracksSize}`); + // console.error(`cuesStart: ${cuesStart}, cuesSize: ${cuesSize}`); + // console.error(`originalMetadataSize: ${originalMetadataSize}, newMetadataSize: ${newMetadataSize}, sizeDifference: ${sizeDifference}`); + + // create the SeekHead element + seekHead = []; + seekHead.push({name: "SeekHead", type: "m", isEnd: false}); + seekHead.push({name: "Seek", type: "m", isEnd: false}); + seekHead.push({name: "SeekID", type: "b", data: new Buffer([0x15, 0x49, 0xA9, 0x66])}); // Info + seekHead.push({name: "SeekPosition", type: "u", data: createUIntBuffer(infoStart)}); + seekHead.push({name: "Seek", type: "m", isEnd: true}); + seekHead.push({name: "Seek", type: "m", isEnd: false}); + seekHead.push({name: "SeekID", type: "b", data: new Buffer([0x16, 0x54, 0xAE, 0x6B])}); // Tracks + seekHead.push({name: "SeekPosition", type: "u", data: createUIntBuffer(tracksStart)}); + seekHead.push({name: "Seek", type: "m", isEnd: true}); + seekHead.push({name: "Seek", type: "m", isEnd: false}); + seekHead.push({name: "SeekID", type: "b", data: new Buffer([0x1C, 0x53, 0xBB, 0x6B])}); // Cues + seekHead.push({name: "SeekPosition", type: "u", data: createUIntBuffer(cuesStart)}); + seekHead.push({name: "Seek", type: "m", isEnd: true}); + seekHead.push({name: "SeekHead", type: "m", isEnd: true}); + + seekHeadSize = encodedSizeOfEbml(seekHead); + //console.error("SeekHead size: " + seekHeadSize); + //printElementIds(seekHead); + + // create the Cues element + cues = []; + cues.push({name: "Cues", type: "m", isEnd: false}) + cuesInfo.forEach(({ CueTrack, CueClusterPosition, CueTime }) => { + cues.push({ name: "CuePoint", type: "m", isEnd: false }); + cues.push({ name: "CueTime", type: "u", data: createUIntBuffer(CueTime) }); + cues.push({ name: "CueTrackPositions", type: "m", isEnd: false }); + cues.push({ name: "CueTrack", type: "u", data: createUIntBuffer(CueTrack) }); + //console.error(`CueClusterPosition: ${CueClusterPosition}, Corrected to: ${CueClusterPosition - segmentContentStartPos} , offset by ${sizeDifference} to become ${(CueClusterPosition - segmentContentStartPos) + sizeDifference - segmentContentStartPos}`); + // EBMLReader returns CueClusterPosition with absolute byte offsets. The Cues section expects them as offsets from the first level 1 element of the Segment, so we need to adjust it. + CueClusterPosition -= segmentContentStartPos; + // We also need to adjust to take into account the change in metadata size from when EBMLReader read the original metadata. + CueClusterPosition += sizeDifference; + cues.push({ name: "CueClusterPosition", type: "u", data: createUIntBuffer(CueClusterPosition) }); + cues.push({ name: "CueTrackPositions", type: "m", isEnd: true }); + cues.push({ name: "CuePoint", type: "m", isEnd: true }); + }); + cues.push({name: "Cues", type: "m", isEnd: true}) + + cuesSize = encodedSizeOfEbml(cues); + //console.error("Cues size: " + cuesSize); + //console.error("Cue count: " + cuesInfo.length); + //printElementIds(cues); + + // If the new MetadataSize is not the same as the previous iteration, we need to run once more. + if (lastSizeDifference !== sizeDifference){ + lastSizeDifference = sizeDifference; + + if (i === maxIterations-1) { + throw new Error("Failed to converge to a stable metadata size"); + } + + } else { + // We're done! Construct the new metadata from all individual components and return. + //console.error(`Final size: ${newMetadataSize}, difference: ${sizeDifference}`); + break; + } + } + + let finalMetadata: EBML.EBMLElementDetail[] = [].concat.apply([], + [ + header, + { name: "Segment", type: "m", isEnd: false, unknownSize: true }, + seekHead, + info, + tracks, + cues + ]); + + let result = new Encoder().encode(finalMetadata); + //printElementIds(finalMetadata); + //console.error(`Final metadata buffer size: ${result.byteLength}`); + //console.error(`Final metadata buffer size without header and segment: ${result.byteLength-segmentContentStartPos}`); + return result; +} + +/** + * print all element id names in a list + + * @param metadata - array of EBML elements to print + * +export function printElementIds(metadata: EBML.EBMLElementBuffer[]) { + + let result: EBML.EBMLElementBuffer[] = []; + let start: number = -1; + + for (let i = 0; i < metadata.length; i++) { + console.error("\t id: " + metadata[i].name); + } +} +*/ + +/** + * remove all occurances of an EBML element from an array of elements + * If it's a MasterElement you will also remove the content. (everything between start and end) + * @param idName - name of the EBML Element to remove. + * @param metadata - array of EBML elements to search + */ +export function removeElement(idName: string, metadata: EBML.EBMLElementBuffer[]) { + + let result: EBML.EBMLElementBuffer[] = []; + let start: number = -1; + + for (let i = 0; i < metadata.length; i++) { + + let element = metadata[i]; + + if (element.name === idName) { + + // if it's a Master element, extract the start and end element, and everything in between + if (element.type === "m") { + + if (!element.isEnd) { + start = i; + } else { + // we've reached the end, extract the whole thing + if (start == -1) + throw new Error(`Detected ${idName} closing element before finding the start`) + + metadata.splice(start, i - start + 1); + return; + } + + } else { + // not a Master element, so we've found what we're looking for. + metadata.splice(i, 1); + return; + } + } + } +} + +/** + * extract the first occurance of an EBML tag from a flattened array of EBML data. + * If it's a MasterElement you will also get the content. (everything between start and end) + * @param idName - name of the EBML Element to extract. + * @param metadata - array of EBML elements to search + */ +export function extractElement(idName: string, metadata: EBML.EBMLElementBuffer[]): EBML.EBMLElementBuffer[] { + + let result: EBML.EBMLElementBuffer[] = []; + let start: number = -1; + + for (let i = 0; i < metadata.length; i++) { + + let element = metadata[i]; + + if (element.name === idName) { + + // if it's a Master element, extract the start and end element, and everything in between + if (element.type === "m") { + + if (!element.isEnd) { + start = i; + } else { + // we've reached the end, extract the whole thing + if (start == -1) + throw new Error(`Detected ${idName} closing element before finding the start`) + + result = metadata.slice(start, i + 1); + break; + } + + } else { + // not a Master element, so we've found what we're looking for. + result.push(metadata[i]); + break; + } + } + } + + return result; +} + +/** + * @deprecated + * metadata に対して duration と seekhead を追加した metadata を返す + * @param metadata - 変更前の webm における ファイル先頭から 最初の Cluster 要素までの 要素 + * @param duration - Duration (TimecodeScale) + * @param cues - cue points for clusters + * @deprecated @param clusterPtrs - 変更前の webm における SeekHead に追加する Cluster 要素 への start pointer + * @deprecated @param cueInfos - please use cues. + */ +export function putRefinedMetaData( + metadata: EBML.EBMLElementDetail[], + info: { + duration?: number, + cues?: {CueTrack: number; CueClusterPosition: number; CueTime: number; }[], + clusterPtrs?: number[], + cueInfos?: {CueTrack: number; CueClusterPosition: number; CueTime: number; }[], + } +): ArrayBuffer { + if(Array.isArray(info.cueInfos) && !Array.isArray(info.cues)){ + console.warn("putRefinedMetaData: info.cueInfos property is deprecated. please use info.cues"); + info.cues = info.cueInfos; + } + let ebml: EBML.EBMLElementDetail[] = []; + let payload: EBML.EBMLElementDetail[] = []; + for(let i=0; i 0)){ throw new Error("metadata dataEnd has wrong number"); } + const originalPayloadOffsetEnd = payload[payload.length-1].dataEnd; // = first cluster ptr + const ebmlSize = ebml[ebml.length-1].dataEnd; // = first segment ptr + const refinedEBMLSize = new Encoder().encode(ebml).byteLength; + const offsetDiff = refinedEBMLSize - ebmlSize; + const payloadSize = originalPayloadOffsetEnd - payload[0].tagStart; + const segmentSize = payload[0].tagStart - ebmlSize; + const segmentOffset = payload[0].tagStart; + const segmentTagBuf = new Buffer([0x18, 0x53, 0x80, 0x67]); // Segment + const segmentSizeBuf = new Buffer('01ffffffffffffff', 'hex'); // Segmentの最後の位置は無数の Cluster 依存なので。 writeVint(newPayloadSize).byteLength ではなく、 infinity. + const _segmentSize = segmentTagBuf.byteLength + segmentSizeBuf.byteLength; // == segmentSize + let newPayloadSize = payloadSize; + // We need the size to be stable between two refinements in order for our offsets to be correct + // Bound the number of possible refinements so we can't go infinate if something goes wrong + let i; + for(i = 1; i < 20; i++) { + const newPayloadOffsetEnd = ebmlSize + _segmentSize + newPayloadSize; + const offsetEndDiff = newPayloadOffsetEnd - originalPayloadOffsetEnd; + const sizeDiff = offsetDiff + offsetEndDiff; + const refined = refineMetadata(payload, sizeDiff, info); + const newNewRefinedSize = new Encoder().encode(refined).byteLength; // 一旦 seekhead を作って自身のサイズを調べる + if(newNewRefinedSize === newPayloadSize) { + // Size is stable + return new Encoder().encode( + ([]).concat( + ebml, + [{type: "m", name: "Segment", isEnd: false, unknownSize: true}], + refined, + ) + ); + } + newPayloadSize = newNewRefinedSize; + } + throw new Error("unable to refine metadata, stable size could not be found in " + i + " iterations!"); +} +// Given a list of EBMLElementBuffers, returns their encoded size in bytes +function encodedSizeOfEbml(refinedMetaData: EBML.EBMLElementBuffer[]): number { + const encorder = new Encoder(); + return refinedMetaData.reduce((lst, elm)=> lst.concat(encorder.encode([elm])), []).reduce((o, buf)=> o + buf.byteLength, 0); +} + +function refineMetadata( + mesetadata: EBML.EBMLElementDetail[], + sizeDiff: number, + info: { + duration?: number, + clusterPtrs?: number[], + cues?: {CueTrack: number; CueClusterPosition: number; CueTime: number; }[] + } +): EBML.EBMLElementBuffer[] { + const {duration, clusterPtrs, cues} = info; + let _metadata: EBML.EBMLElementBuffer[] = mesetadata.slice(0); + if(typeof duration === "number"){ + // duration を追加する + let overwrited = false; + _metadata.forEach((elm)=>{ + if(elm.type === "f" && elm.name === "Duration"){ + overwrited = true; + elm.data = createFloatBuffer(duration, 8); + } + }); + if(!overwrited){ + insertTag(_metadata, "Info", [{name: "Duration", type: "f", data: createFloatBuffer(duration, 8) }]); + } + } + if(Array.isArray(cues)){ + insertTag(_metadata, "Cues", create_cue(cues, sizeDiff)); + } + + let seekhead_children: EBML.EBMLElementBuffer[] = []; + if(Array.isArray(clusterPtrs)){ + console.warn("append cluster pointers to seekhead is deprecated. please use cues"); + seekhead_children = create_seek_from_clusters(clusterPtrs, sizeDiff); + } + // remove seek info + /* + _metadata = _metadata.filter((elm)=> !( + elm.name === "Seek" || + elm.name === "SeekID" || + elm.name === "SeekPosition") ); + */ + // working on progress + //seekhead_children = seekhead_children.concat(create_seekhead(_metadata)); + + + insertTag(_metadata, "SeekHead", seekhead_children, true); + + return _metadata; +} + +function create_seekhead(metadata: (EBML.EBMLElementDetail|EBML.EBMLElementBuffer)[], sizeDiff: number): EBML.EBMLElementBuffer[] { + const seeks: EBML.EBMLElementBuffer[] = []; + ["Info", "Tracks", "Cues"].forEach((tagName)=>{ + const tagStarts = metadata.filter((elm)=> elm.type === "m" && elm.name === tagName && elm.isEnd === false).map((elm)=> elm["tagStart"]); + const tagStart = tagStarts[0]; + if(typeof tagStart !== "number"){ return; } + seeks.push({name: "Seek", type: "m", isEnd: false}); + switch(tagName){ + case "Info": seeks.push({name: "SeekID", type: "b", data: new Buffer([0x15, 0x49, 0xA9, 0x66]) }); break; + case "Tracks": seeks.push({name: "SeekID", type: "b", data: new Buffer([0x16, 0x54, 0xAE, 0x6B]) }); break; + case "Cues": seeks.push({name: "SeekID", type: "b", data: new Buffer([0x1C, 0x53, 0xBB, 0x6B]) }); break; + } + seeks.push({name: "SeekPosition", type: "u", data: createUIntBuffer(tagStart + sizeDiff)}); + seeks.push({name: "Seek", type: "m", isEnd: true}); + }); + return seeks; +} + +function create_seek_from_clusters(clusterPtrs: number[], sizeDiff: number): EBML.EBMLElementBuffer[] { + const seeks: EBML.EBMLElementBuffer[] = []; + clusterPtrs.forEach((start)=>{ + seeks.push({name: "Seek", type: "m", isEnd: false}); + // [0x1F, 0x43, 0xB6, 0x75] で Cluster 意 + seeks.push({name: "SeekID", type: "b", data: new Buffer([0x1F, 0x43, 0xB6, 0x75]) }); + seeks.push({name: "SeekPosition", type: "u", data: createUIntBuffer(start + sizeDiff)}); + seeks.push({name: "Seek", type: "m", isEnd: true}); + }); + return seeks; +} + +function create_cue(cueInfos: {CueTrack: number; CueClusterPosition: number; CueTime: number; }[], sizeDiff: number): EBML.EBMLElementBuffer[] { + const cues: EBML.EBMLElementBuffer[] = []; + cueInfos.forEach(({CueTrack, CueClusterPosition, CueTime})=>{ + cues.push({name: "CuePoint", type: "m", isEnd: false}); + cues.push({name: "CueTime", type: "u", data: createUIntBuffer(CueTime) }); + cues.push({name: "CueTrackPositions", type: "m", isEnd: false}); + cues.push({name: "CueTrack", type: "u", data: createUIntBuffer(CueTrack) }); // video track + cues.push({name: "CueClusterPosition", type: "u", data: createUIntBuffer(CueClusterPosition + sizeDiff) }); + cues.push({name: "CueTrackPositions", type: "m", isEnd: true}); + cues.push({name: "CuePoint", type: "m", isEnd: true}); + }); + return cues; +} + +function insertTag(_metadata: EBML.EBMLElementBuffer[], tagName: string, children: EBML.EBMLElementBuffer[], insertHead: boolean = false): void { + // find the tagname from _metadata + let idx = -1; + for(let i=0; i<_metadata.length; i++){ + const elm = _metadata[i]; + if(elm.type === "m" && elm.name === tagName && elm.isEnd === false){ + idx = i; + break; + } + } + if(idx >= 0){ + // insert [] to + Array.prototype.splice.apply(_metadata, [idx+1, 0].concat(children)); + }else if(insertHead){ + ([]).concat( + [{name: tagName, type: "m", isEnd: false}], + children, + [{name: tagName, type: "m", isEnd: true}], + ).reverse().forEach((elm)=>{ _metadata.unshift(elm); }); + }else{ + // metadata 末尾に を追加 + // insert + _metadata.push({name: tagName, type: "m", isEnd: false}); + children.forEach((elm)=>{ _metadata.push(elm); }); + _metadata.push({name: tagName, type: "m", isEnd: true}); + } +} + + +export function concat(list: Buffer[]): Buffer { + return Buffer.concat(list); +} + +export function encodeValueToBuffer(elm: EBML.MasterElement): EBML.MasterElement; +export function encodeValueToBuffer(elm: EBML.ChildElementsValue): EBML.ChildElementBuffer; +export function encodeValueToBuffer(elm: EBML.EBMLElementValue): EBML.EBMLElementBuffer { + let data = new Buffer(0); + if(elm.type === "m"){ return elm; } + switch(elm.type){ + case "u": data = createUIntBuffer(elm.value); break; + case "i": data = createIntBuffer(elm.value); break; + case "f": data = createFloatBuffer(elm.value); break; + case "s": data = new Buffer(elm.value, 'ascii'); break; + case "8": data = new Buffer(elm.value, 'utf8'); break; + case "b": data = elm.value; break; + case "d": data = new Int64BE(elm.value.getTime().toString()).toBuffer(); break; + } + return Object.assign({}, elm, {data}); +} + +export function createUIntBuffer(value: number): Buffer { + // Big-endian, any size from 1 to 8 + // but js number is float64, so max 6 bit octets + let bytes: 1|2|3|4|5|6 = 1; + for(; value >= Math.pow(2, 8*bytes); bytes++){} + if(bytes >= 7){ + console.warn("7bit or more bigger uint not supported."); + return new Uint64BE(value).toBuffer(); + } + const data = new Buffer(bytes); + data.writeUIntBE(value, 0, bytes); + return data; +} + +export function createIntBuffer(value: number): Buffer { + // Big-endian, any size from 1 to 8 octets + // but js number is float64, so max 6 bit + let bytes: 1|2|3|4|5|6 = 1; + for(; value >= Math.pow(2, 8*bytes); bytes++){} + if(bytes >= 7){ + console.warn("7bit or more bigger uint not supported."); + return new Int64BE(value).toBuffer(); + } + const data = new Buffer(bytes); + data.writeIntBE(value, 0, bytes); + return data; +} + +export function createFloatBuffer(value: number, bytes: 4|8 = 8): Buffer { + // Big-endian, defined for 4 and 8 octets (32, 64 bits) + // js number is float64 so 8 bytes. + if(bytes === 8){ + // 64bit + const data = new Buffer(8); + data.writeDoubleBE(value, 0); + return data; + }else if(bytes === 4){ + // 32bit + const data = new Buffer(4); + data.writeFloatBE(value, 0); + return data; + }else{ + throw new Error("float type bits must 4bytes or 8bytes"); + } +} + +export function convertEBMLDateToJSDate(int64str: number | string | Date): Date { + if(int64str instanceof Date){ return int64str; } + return new Date(new Date("2001-01-01T00:00:00.000Z").getTime() + (Number(int64str)/1000/1000)); +} diff --git a/webm-duration-fix.js b/src/index.ts similarity index 85% rename from webm-duration-fix.js rename to src/index.ts index fbf1fda..e7ca29e 100644 --- a/webm-duration-fix.js +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { tools, Decoder, Reader } from 'ts-ebml'; +import { tools, Decoder, Reader } from './ebml'; /** * based on ts-ebml and support large file,optimize memory usage during repair @@ -7,13 +7,13 @@ import { tools, Decoder, Reader } from 'ts-ebml'; * @returns the blob that has been fixed * */ -export default async function fixWebmDuration(blob) { +export default async function fixWebmDuration(blob: Blob): Promise { if (!blob) { throw Error('call to fixWebmDuration requires a blob'); } const decoder = new Decoder(); const reader = new Reader(); - const readstream = blob.stream(); + const readstream = blob.stream() as any; const readerBlob = readstream.getReader(); while (true) { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..54ed922 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,74 @@ +{ + "version": "2.0.0", + "compilerOptions": { + "allowJs": false, + "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "charset": "utf8", + "declaration": true, + "diagnostics": true, + "emitBOM": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": false, + "inlineSourceMap": false, + "inlineSources": false, + "isolatedModules": false, + //"jsx": "react", + "lib": ["dom", "es5", "es2015.core", "es2015.promise", "es2015.iterable", "es2015.symbol"], + "types" : [], + "listFiles": true, + "locale": "en-us", + //"mapRoot": null, + "module": "commonjs", + //"moduleResolution": "classic", + "newLine": "LF", + "noEmit": false, + "noEmitHelpers": false, + "noEmitOnError": false, + "noFallthroughCasesInSwitch": false, + "noImplicitAny": false, + "noImplicitReturns": true, + //"noImplicitUseStrict": false, + "noLib": false, + "noResolve": false, + "outDir": "lib", + //"outFile": null, + "preserveConstEnums": true, + "pretty": true, + //"reactNamespace": "React", + "removeComments": false, + "rootDir": "src", + "skipDefaultLibCheck": false, + //"inlineSourceMap": fals, + //"sourceMap": false, + //"sourceRoot": "./src", + "strictNullChecks": true, + "stripInternal": false, + "suppressExcessPropertyErrors": true, + "suppressImplicitAnyIndexErrors": true, + "target": "es5" + //"traceResolution": false + }, + "formatCodeOptions": { + "indentSize": 2, + "tabSize": 2, + "newLineCharacter": "\n", + "convertTabsToSpaces": true, + "insertSpaceAfterCommaDelimiter": true, + "insertSpaceAfterSemicolonInForStatements": true, + "insertSpaceBeforeAndAfterBinaryOperators": true, + "insertSpaceAfterKeywordsInControlFlowStatements": true, + "insertSpaceAfterFunctionKeywordForAnonymousFunctions": true, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false, + "placeOpenBraceOnNewLineForFunctions": true, + "placeOpenBraceOnNewLineForControlBlocks": true + }, + "files": [ + "src/index.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/webm-duration-fix.d.ts b/webm-duration-fix.d.ts deleted file mode 100644 index 4e8b215..0000000 --- a/webm-duration-fix.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface FixWebmDurationFunction { - (blob: Blob): Promise; -} - -declare const fixWebmDuration: FixWebmDurationFunction; -export default fixWebmDuration; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..c0ee076 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1490 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/highlight@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" + integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@types/node@^17.0.8": + version "17.0.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b" + integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg== + +JSONStream@^1.0.3: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.6.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== + dependencies: + acorn "^7.0.0" + acorn-walk "^7.0.0" + xtend "^4.0.2" + +acorn-walk@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.0.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +assert@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.0.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-pack@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774" + integrity sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA== + dependencies: + JSONStream "^1.0.3" + combine-source-map "~0.8.0" + defined "^1.0.0" + safe-buffer "^5.1.1" + through2 "^2.0.0" + umd "^3.0.0" + +browser-resolve@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-2.0.0.tgz#99b7304cb392f8d73dba741bb2d7da28c6d7842b" + integrity sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ== + dependencies: + resolve "^1.17.0" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserify@^17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-17.0.0.tgz#4c48fed6c02bfa2b51fd3b670fddb805723cdc22" + integrity sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w== + dependencies: + JSONStream "^1.0.3" + assert "^1.4.0" + browser-pack "^6.0.1" + browser-resolve "^2.0.0" + browserify-zlib "~0.2.0" + buffer "~5.2.1" + cached-path-relative "^1.0.0" + concat-stream "^1.6.0" + console-browserify "^1.1.0" + constants-browserify "~1.0.0" + crypto-browserify "^3.0.0" + defined "^1.0.0" + deps-sort "^2.0.1" + domain-browser "^1.2.0" + duplexer2 "~0.1.2" + events "^3.0.0" + glob "^7.1.0" + has "^1.0.0" + htmlescape "^1.1.0" + https-browserify "^1.0.0" + inherits "~2.0.1" + insert-module-globals "^7.2.1" + labeled-stream-splicer "^2.0.0" + mkdirp-classic "^0.5.2" + module-deps "^6.2.3" + os-browserify "~0.3.0" + parents "^1.0.1" + path-browserify "^1.0.0" + process "~0.11.0" + punycode "^1.3.2" + querystring-es3 "~0.2.0" + read-only-stream "^2.0.0" + readable-stream "^2.0.2" + resolve "^1.1.4" + shasum-object "^1.0.0" + shell-quote "^1.6.1" + stream-browserify "^3.0.0" + stream-http "^3.0.0" + string_decoder "^1.1.1" + subarg "^1.0.0" + syntax-error "^1.1.1" + through2 "^2.0.0" + timers-browserify "^1.0.1" + tty-browserify "0.0.1" + url "~0.11.0" + util "~0.12.0" + vm-browserify "^1.0.0" + xtend "^4.0.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buffer@~5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +cached-path-relative@^1.0.0, cached-path-relative@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db" + integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg== + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +chalk@^2.0.0, chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +combine-source-map@^0.8.0, combine-source-map@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" + integrity sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos= + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" + +commander@^2.12.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@~1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +constants-browserify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +convert-source-map@~1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" + integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA= + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +crypto-browserify@^3.0.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +dash-ast@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" + integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA== + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +deps-sort@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.1.tgz#9dfdc876d2bcec3386b6829ac52162cda9fa208d" + integrity sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw== + dependencies: + JSONStream "^1.0.3" + shasum-object "^1.0.0" + subarg "^1.0.0" + through2 "^2.0.0" + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +detective@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" + integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== + dependencies: + acorn-node "^1.6.1" + defined "^1.0.0" + minimist "^1.1.1" + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +domain-browser@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= + dependencies: + readable-stream "^2.0.2" + +ebml-block@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ebml-block/-/ebml-block-1.1.2.tgz#fd49951b0faf5a3049bdd61c851a76b5e679c290" + integrity sha512-HgNlIsRFP6D9VKU5atCeHRJY7XkJP8bOe8yEhd8NB7B3b4++VWTyauz6g650iiPmLfPLGlVpoJmGSgMfXDYusg== + +elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +es-abstract@^1.18.5: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +events@^3.0.0, events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +fast-safe-stringify@^2.0.7: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-assigned-identifiers@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" + integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +glob@^7.1.0, glob@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.0, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +htmlescape@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" + integrity sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E= + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +ieee754@^1.1.4, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inline-source-map@~0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" + integrity sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU= + dependencies: + source-map "~0.5.3" + +insert-module-globals@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.1.tgz#d5e33185181a4e1f33b15f7bf100ee91890d5cb3" + integrity sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg== + dependencies: + JSONStream "^1.0.3" + acorn-node "^1.5.2" + combine-source-map "^0.8.0" + concat-stream "^1.6.1" + is-buffer "^1.1.0" + path-is-absolute "^1.0.1" + process "~0.11.0" + through2 "^2.0.0" + undeclared-identifiers "^1.1.2" + xtend "^4.0.0" + +int64-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-1.0.1.tgz#c78d841b444cadf036cd04f8683696c740f15dca" + integrity sha512-+3azY4pXrjAupJHU1V9uGERWlhoqNswJNji6aD/02xac7oxol508AsMC5lxKhEqyZeDFy3enq5OGWXF4u75hiw== + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-negative-zero@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3, is-typed-array@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" + integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + +labeled-stream-splicer@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz#42a41a16abcd46fd046306cf4f2c3576fffb1c21" + integrity sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw== + dependencies: + inherits "^2.0.1" + stream-splicer "^2.0.0" + +lodash.memoize@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" + integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^0.5.3: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +module-deps@^6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.3.tgz#15490bc02af4b56cf62299c7c17cba32d71a96ee" + integrity sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA== + dependencies: + JSONStream "^1.0.3" + browser-resolve "^2.0.0" + cached-path-relative "^1.0.2" + concat-stream "~1.6.0" + defined "^1.0.0" + detective "^5.2.0" + duplexer2 "^0.1.2" + inherits "^2.0.1" + parents "^1.0.0" + readable-stream "^2.0.2" + resolve "^1.4.0" + stream-combiner2 "^1.1.1" + subarg "^1.0.0" + through2 "^2.0.0" + xtend "^4.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +os-browserify@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parents@^1.0.0, parents@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" + integrity sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E= + dependencies: + path-platform "~0.11.15" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-platform@~0.11.15: + version "0.11.15" + resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" + integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= + +pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@~0.11.0: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +querystring-es3@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +read-only-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" + integrity sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A= + dependencies: + readable-stream "^2.0.2" + +readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.5.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +resolve@^1.1.4, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.4.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" + integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== + dependencies: + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safer-buffer@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shasum-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shasum-object/-/shasum-object-1.0.0.tgz#0b7b74ff5b66ecf9035475522fa05090ac47e29e" + integrity sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg== + dependencies: + fast-safe-stringify "^2.0.7" + +shell-quote@^1.6.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +source-map@~0.5.3: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +stream-combiner2@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" + integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= + dependencies: + duplexer2 "~0.1.0" + readable-stream "^2.0.2" + +stream-http@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" + integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" + +stream-splicer@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.1.tgz#0b13b7ee2b5ac7e0609a7463d83899589a363fcd" + integrity sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.2" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +subarg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" + integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= + dependencies: + minimist "^1.1.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +syntax-error@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" + integrity sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w== + dependencies: + acorn-node "^1.2.0" + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timers-browserify@^1.0.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + integrity sha1-ycWLV1voQHN1y14kYtrO50NZ9B0= + dependencies: + process "~0.11.0" + +tslib@^1.13.0, tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslint@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" + integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.3" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.13.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" + integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== + +umd@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" + integrity sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +undeclared-identifiers@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz#9254c1d37bdac0ac2b52de4b6722792d2a91e30f" + integrity sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw== + dependencies: + acorn-node "^1.3.0" + dash-ast "^1.0.0" + get-assigned-identifiers "^1.2.0" + simple-concat "^1.0.0" + xtend "^4.0.1" + +url@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@~0.12.0: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +vm-browserify@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.2: + version "1.1.7" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" + integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.7" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==