From cc00a79881d45fa8f0ad21ef8fb7b9d358cc924f Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sun, 26 Mar 2023 16:04:51 -0700 Subject: [PATCH 01/10] Initial progress on WebCodecs. I'm on a plane and it's $18 to get Wifi for an hour. --- player/src/audio/index.ts | 3 + player/src/index.html | 3 +- player/src/index.ts | 45 +-- player/src/{ => mp4}/mp4.ts | 2 +- player/src/{ => mp4}/mp4box.all.js | 0 player/src/player.ts | 374 --------------------- player/src/{ => player}/init.ts | 2 +- player/src/segment.ts | 150 --------- player/src/source.ts | 147 -------- player/src/{stream.ts => stream/reader.ts} | 143 ++------ player/src/stream/writer.ts | 100 ++++++ player/src/track.ts | 124 ------- player/src/transport/index.ts | 135 ++++++++ player/src/{ => transport}/message.ts | 9 +- player/src/util.ts | 4 - player/src/video/decoder.ts | 123 +++++++ player/src/video/index.ts | 19 ++ player/src/video/message.ts | 15 + player/src/video/renderer.ts | 72 ++++ player/src/video/track.ts | 58 ++++ player/src/video/worker.ts | 22 ++ player/tsconfig.json | 2 +- 22 files changed, 588 insertions(+), 964 deletions(-) create mode 100644 player/src/audio/index.ts rename player/src/{ => mp4}/mp4.ts (99%) rename player/src/{ => mp4}/mp4box.all.js (100%) delete mode 100644 player/src/player.ts rename player/src/{ => player}/init.ts (94%) delete mode 100644 player/src/segment.ts delete mode 100644 player/src/source.ts rename player/src/{stream.ts => stream/reader.ts} (56%) create mode 100644 player/src/stream/writer.ts delete mode 100644 player/src/track.ts create mode 100644 player/src/transport/index.ts rename player/src/{ => transport}/message.ts (60%) delete mode 100644 player/src/util.ts create mode 100644 player/src/video/decoder.ts create mode 100644 player/src/video/index.ts create mode 100644 player/src/video/message.ts create mode 100644 player/src/video/renderer.ts create mode 100644 player/src/video/track.ts create mode 100644 player/src/video/worker.ts diff --git a/player/src/audio/index.ts b/player/src/audio/index.ts new file mode 100644 index 0000000..ebbc720 --- /dev/null +++ b/player/src/audio/index.ts @@ -0,0 +1,3 @@ +self.addEventListener('message', (e: Event) => { + +}) \ No newline at end of file diff --git a/player/src/index.html b/player/src/index.html index 92e0bdd..7ec7729 100644 --- a/player/src/index.html +++ b/player/src/index.html @@ -11,8 +11,7 @@
-
click to play
- +
diff --git a/player/src/index.ts b/player/src/index.ts index 40dbb7a..39e074a 100644 --- a/player/src/index.ts +++ b/player/src/index.ts @@ -1,48 +1,11 @@ -import { Player } from "./player" - -// This is so ghetto but I'm too lazy to improve it right now -const videoRef = document.querySelector("video#vid")!; -const liveRef = document.querySelector("#live")!; -const throttleRef = document.querySelector("#throttle")!; -const statsRef = document.querySelector("#stats")!; -const playRef = document.querySelector("#play")!; +import { Player } from "./transport/index" const params = new URLSearchParams(window.location.search) const url = params.get("url") || "https://localhost:4443/watch" +const canvas = document.querySelector("canvas#video")!; const player = new Player({ url: url, - videoRef: videoRef, - statsRef: statsRef, - throttleRef: throttleRef, -}) - -liveRef.addEventListener("click", (e) => { - e.preventDefault() - player.goLive() -}) - -throttleRef.addEventListener("click", (e) => { - e.preventDefault() - player.throttle() -}) - -playRef.addEventListener('click', (e) => { - videoRef.play() - e.preventDefault() -}) - -function playFunc(e: Event) { - playRef.style.display = "none" - //player.goLive() - - // Only fire once to restore pause/play functionality - videoRef.removeEventListener('play', playFunc) -} - -videoRef.addEventListener('play', playFunc) -videoRef.volume = 0.5 - -// Try to autoplay but ignore errors on mobile; they need to click -//vidRef.play().catch((e) => console.warn(e)) \ No newline at end of file + canvas: canvas, +}) \ No newline at end of file diff --git a/player/src/mp4.ts b/player/src/mp4/mp4.ts similarity index 99% rename from player/src/mp4.ts rename to player/src/mp4/mp4.ts index c150364..fdf3841 100644 --- a/player/src/mp4.ts +++ b/player/src/mp4/mp4.ts @@ -1,6 +1,6 @@ // Wrapper around MP4Box to play nicely with MP4Box. // I tried getting a mp4box.all.d.ts file to work but just couldn't figure it out -import { createFile, ISOFile, DataStream, BoxParser } from "./mp4box.all" +import { createFile, ISOFile, DataStream, BoxParser } from "./mp4box.all.js" // Rename some stuff so it's on brand. export { createFile as MP4New, ISOFile as MP4File, DataStream as MP4Stream, BoxParser as MP4Parser } diff --git a/player/src/mp4box.all.js b/player/src/mp4/mp4box.all.js similarity index 100% rename from player/src/mp4box.all.js rename to player/src/mp4/mp4box.all.js diff --git a/player/src/player.ts b/player/src/player.ts deleted file mode 100644 index 46487b9..0000000 --- a/player/src/player.ts +++ /dev/null @@ -1,374 +0,0 @@ -import { Source } from "./source" -import { StreamReader, StreamWriter } from "./stream" -import { InitParser } from "./init" -import { Segment } from "./segment" -import { Track } from "./track" -import { Message, MessageInit, MessageSegment } from "./message" - -/// - -export interface PlayerInit { - url: string; - - videoRef: HTMLVideoElement; - statsRef: HTMLElement; - throttleRef: HTMLElement; -} - -/* -*/ - - -export class Player { - mediaSource: MediaSource; - - init: Map; - audio: Track; - video: Track; - - quic: Promise; - api: Promise; - - // References to elements in the DOM - vidRef: HTMLVideoElement; // The video element itself - statsRef: HTMLElement; // The stats div - throttleRef: HTMLElement; // The throttle button - throttleCount: number; // number of times we've clicked the button in a row - - interval: number; - - timeRef?: DOMHighResTimeStamp; - - constructor(props: PlayerInit) { - this.vidRef = props.videoRef - this.statsRef = props.statsRef - this.throttleRef = props.throttleRef - this.throttleCount = 0 - - this.mediaSource = new MediaSource() - this.vidRef.src = URL.createObjectURL(this.mediaSource) - - this.init = new Map() - this.audio = new Track(new Source(this.mediaSource)); - this.video = new Track(new Source(this.mediaSource)); - - this.interval = setInterval(this.tick.bind(this), 100) - this.vidRef.addEventListener("waiting", this.tick.bind(this)) - - this.quic = this.connect(props.url) - - // Create a unidirectional stream for all of our messages - this.api = this.quic.then((q) => { - return q.createUnidirectionalStream() - }) - - // async functions - this.receiveStreams() - - // Limit to 4Mb/s - this.sendThrottle() - } - - async close() { - clearInterval(this.interval); - (await this.quic).close() - } - - async connect(url: string): Promise { - // TODO remove this when WebTransport supports the system CA pool - const fingerprintURL = new URL(url); - fingerprintURL.pathname = "/fingerprint" - - const response = await fetch(fingerprintURL) - if (!response.ok) { - throw new Error('failed to get server fingerprint'); - } - - const hex = await response.text() - - // Convert the hex to binary. - let fingerprint = []; - for (let c = 0; c < hex.length; c += 2) { - fingerprint.push(parseInt(hex.substring(c, c+2), 16)); - } - - //const fingerprint = Uint8Array.from(atob(hex), c => c.charCodeAt(0)) - - const quic = new WebTransport(url, { - "serverCertificateHashes": [{ - "algorithm": "sha-256", - "value": new Uint8Array(fingerprint), - }] - }) - - await quic.ready - - return quic - } - - async sendMessage(msg: any) { - const payload = JSON.stringify(msg) - const size = payload.length + 8 - - const stream = await this.api - - const writer = new StreamWriter(stream) - await writer.uint32(size) - await writer.string("warp") - await writer.string(payload) - writer.release() - } - - throttle() { - // Throttle is incremented each time we click the throttle button - this.throttleCount += 1 - this.sendThrottle() - - // After 5 seconds disable the throttling - setTimeout(() => { - this.throttleCount -= 1 - this.sendThrottle() - }, 5000) - } - - sendThrottle() { - let rate = 0; - - if (this.throttleCount > 0) { - // TODO detect the incoming bitrate instead of hard-coding - // Right shift by throttle to divide by 2,4,8,16,etc each time - const bitrate = 4 * 1024 * 1024 // 4Mb/s - - rate = bitrate >> (this.throttleCount-1) - - const str = formatBits(rate) + "/s" - this.throttleRef.textContent = `Throttle: ${ str }`; - } else { - this.throttleRef.textContent = "Throttle: none"; - } - - // Send the server a message to fake network congestion. - this.sendMessage({ - "debug": { - max_bitrate: rate, - }, - }) - } - - tick() { - // Try skipping ahead if there's no data in the current buffer. - this.trySeek() - - // Try skipping video if it would fix any desync. - this.trySkip() - - // Update the stats at the end - this.updateStats() - } - - goLive() { - const ranges = this.vidRef.buffered - if (!ranges.length) { - return - } - - this.vidRef.currentTime = ranges.end(ranges.length-1); - this.vidRef.play(); - } - - // Try seeking ahead to the next buffered range if there's a gap - trySeek() { - if (this.vidRef.readyState > 2) { // HAVE_CURRENT_DATA - // No need to seek - return - } - - const ranges = this.vidRef.buffered - if (!ranges.length) { - // Video has not started yet - return - } - - for (let i = 0; i < ranges.length; i += 1) { - const pos = ranges.start(i) - - if (this.vidRef.currentTime >= pos) { - // This would involve seeking backwards - continue - } - - console.warn("seeking forward", pos - this.vidRef.currentTime) - - this.vidRef.currentTime = pos - return - } - } - - // Try dropping video frames if there is future data available. - trySkip() { - let playhead: number | undefined - - if (this.vidRef.readyState > 2) { - // If we're not buffering, only skip video if it's before the current playhead - playhead = this.vidRef.currentTime - } - - this.video.advance(playhead) - } - - async receiveStreams() { - const q = await this.quic - const streams = q.incomingUnidirectionalStreams.getReader() - - while (true) { - const result = await streams.read() - if (result.done) break - - const stream = result.value - this.handleStream(stream) // don't await - } - } - - async handleStream(stream: ReadableStream) { - let r = new StreamReader(stream.getReader()) - - while (!await r.done()) { - const size = await r.uint32(); - const typ = new TextDecoder('utf-8').decode(await r.bytes(4)); - - if (typ != "warp") throw "expected warp atom" - if (size < 8) throw "atom too small" - - const payload = new TextDecoder('utf-8').decode(await r.bytes(size - 8)); - const msg = JSON.parse(payload) as Message - - if (msg.init) { - return this.handleInit(r, msg.init) - } else if (msg.segment) { - return this.handleSegment(r, msg.segment) - } - } - } - - async handleInit(stream: StreamReader, msg: MessageInit) { - let init = this.init.get(msg.id); - if (!init) { - init = new InitParser() - this.init.set(msg.id, init) - } - - while (1) { - const data = await stream.read() - if (!data) break - - init.push(data) - } - } - - async handleSegment(stream: StreamReader, msg: MessageSegment) { - let pending = this.init.get(msg.init); - if (!pending) { - pending = new InitParser() - this.init.set(msg.init, pending) - } - - // Wait for the init segment to be fully received and parsed - const init = await pending.ready; - - let track: Track; - if (init.info.videoTracks.length) { - track = this.video - } else { - track = this.audio - } - - const segment = new Segment(track.source, init, msg.timestamp) - - // The track is responsible for flushing the segments in order - track.add(segment) - - /* TODO I'm not actually sure why this code doesn't work; something trips up the MP4 parser - while (1) { - const data = await stream.read() - if (!data) break - - segment.push(data) - track.flush() // Flushes if the active segment has samples - } - */ - - // One day I'll figure it out; until then read one top-level atom at a time - while (!await stream.done()) { - const raw = await stream.peek(4) - const size = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint32(0) - const atom = await stream.bytes(size) - - segment.push(atom) - track.flush() // Flushes if the active segment has new samples - } - - segment.finish() - } - - updateStats() { - for (const child of this.statsRef.children) { - if (child.className == "audio buffer") { - const ranges: any = (this.audio) ? this.audio.buffered() : { length: 0 } - this.visualizeBuffer(child as HTMLElement, ranges) - } else if (child.className == "video buffer") { - const ranges: any = (this.video) ? this.video.buffered() : { length: 0 } - this.visualizeBuffer(child as HTMLElement, ranges) - } - } - } - - visualizeBuffer(element: HTMLElement, ranges: TimeRanges) { - const children = element.children - const max = 5 - - let index = 0 - let prev = 0 - - for (let i = 0; i < ranges.length; i += 1) { - let start = ranges.start(i) - this.vidRef.currentTime - let end = ranges.end(i) - this.vidRef.currentTime - - if (end < 0 || start > max) { - continue - } - - let fill: HTMLElement; - - if (index < children.length) { - fill = children[index] as HTMLElement; - } else { - fill = document.createElement("div") - element.appendChild(fill) - } - - fill.className = "fill" - fill.innerHTML = end.toFixed(2) - fill.setAttribute('style', "left: " + (100 * Math.max(start, 0) / max) + "%; right: " + (100 - 100 * Math.min(end, max) / max) + "%") - index += 1 - - prev = end - } - - for (let i = index; i < children.length; i += 1) { - element.removeChild(children[i]) - } - } -} - -// https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-in-javascript -function formatBits(bits: number, decimals: number = 1) { - if (bits === 0) return '0 bits'; - - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb']; - - const i = Math.floor(Math.log(bits) / Math.log(k)); - - return parseFloat((bits / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; -} diff --git a/player/src/init.ts b/player/src/player/init.ts similarity index 94% rename from player/src/init.ts rename to player/src/player/init.ts index 6d258a7..11594fa 100644 --- a/player/src/init.ts +++ b/player/src/player/init.ts @@ -1,4 +1,4 @@ -import { MP4New, MP4File, MP4ArrayBuffer, MP4Info } from "./mp4" +import { MP4New, MP4File, MP4ArrayBuffer, MP4Info } from "../mp4/mp4" export class InitParser { mp4box: MP4File; diff --git a/player/src/segment.ts b/player/src/segment.ts deleted file mode 100644 index 09f7649..0000000 --- a/player/src/segment.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { Source } from "./source" -import { Init } from "./init" -import { MP4New, MP4File, MP4Sample, MP4Stream, MP4Parser, MP4ArrayBuffer } from "./mp4" - -// Manage a segment download, keeping a buffer of a single sample to potentially rewrite the duration. -export class Segment { - source: Source; // The SourceBuffer used to decode media. - offset: number; // The byte offset in the received file so far - samples: MP4Sample[]; // The samples ready to be flushed to the source. - timestamp: number; // The expected timestamp of the first sample in milliseconds - init: Init; - - dts?: number; // The parsed DTS of the first sample - timescale?: number; // The parsed timescale of the segment - - input: MP4File; // MP4Box file used to parse the incoming atoms. - output: MP4File; // MP4Box file used to write the outgoing atoms after modification. - - done: boolean; // The segment has been completed - - constructor(source: Source, init: Init, timestamp: number) { - this.source = source - this.offset = 0 - this.done = false - this.timestamp = timestamp - this.init = init - - this.input = MP4New(); - this.output = MP4New(); - this.samples = []; - - this.input.onReady = (info: any) => { - this.input.setExtractionOptions(info.tracks[0].id, {}, { nbSamples: 1 }); - - this.input.onSamples = this.onSamples.bind(this) - this.input.start(); - } - - // We have to reparse the init segment to work with mp4box - for (let i = 0; i < init.raw.length; i += 1) { - this.offset = this.input.appendBuffer(init.raw[i]) - - // Also populate the output with our init segment so it knows about tracks - this.output.appendBuffer(init.raw[i]) - } - - this.input.flush() - this.output.flush() - } - - push(data: Uint8Array) { - if (this.done) return; // ignore new data after marked done - - // Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately - let box = new Uint8Array(data.byteLength); - box.set(data) - - // and for some reason we need to modify the underlying ArrayBuffer with offset - let buffer = box.buffer as MP4ArrayBuffer - buffer.fileStart = this.offset - - // Parse the data - this.offset = this.input.appendBuffer(buffer) - this.input.flush() - } - - onSamples(id: number, user: any, samples: MP4Sample[]) { - if (!samples.length) return; - - if (this.dts === undefined) { - this.dts = samples[0].dts; - this.timescale = samples[0].timescale; - } - - // Add the samples to a queue - this.samples.push(...samples) - } - - // Flushes any pending samples, returning true if the stream has finished. - flush(): boolean { - let stream = new MP4Stream(new ArrayBuffer(0), 0, false); // big-endian - - while (this.samples.length) { - // Keep a single sample if we're not done yet - if (!this.done && this.samples.length < 2) break; - - const sample = this.samples.shift() - if (!sample) break; - - let moof = this.output.createSingleSampleMoof(sample); - moof.write(stream); - - // adjusting the data_offset now that the moof size is known - moof.trafs[0].truns[0].data_offset = moof.size+8; //8 is mdat header - stream.adjustUint32(moof.trafs[0].truns[0].data_offset_position, moof.trafs[0].truns[0].data_offset); - - // @ts-ignore - var mdat = new MP4Parser.mdatBox(); - mdat.data = sample.data; - mdat.write(stream); - } - - this.source.initialize(this.init) - this.source.append(stream.buffer as ArrayBuffer) - - return this.done - } - - // The segment has completed - finish() { - this.done = true - this.flush() - - // Trim the buffer to 30s long after each segment. - this.source.trim(30) - } - - // Extend the last sample so it reaches the provided timestamp - skipTo(pts: number) { - if (this.samples.length == 0) return - let last = this.samples[this.samples.length-1] - - const skip = pts - (last.dts + last.duration); - - if (skip == 0) return; - if (skip < 0) throw "can't skip backwards" - - last.duration += skip - - if (this.timescale) { - console.warn("skipping video", skip / this.timescale) - } - } - - buffered() { - // Ignore if we have a single sample - if (this.samples.length <= 1) return undefined; - if (!this.timescale) return undefined; - - const first = this.samples[0]; - const last = this.samples[this.samples.length-1] - - - return { - length: 1, - start: first.dts / this.timescale, - end: (last.dts + last.duration) / this.timescale, - } - } -} diff --git a/player/src/source.ts b/player/src/source.ts deleted file mode 100644 index dc299a7..0000000 --- a/player/src/source.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { Init } from "./init" - -// Create a SourceBuffer with convenience methods -export class Source { - sourceBuffer?: SourceBuffer; - mediaSource: MediaSource; - queue: Array; - init?: Init; - - constructor(mediaSource: MediaSource) { - this.mediaSource = mediaSource; - this.queue = []; - } - - // (re)initialize the source using the provided init segment. - initialize(init: Init) { - // Check if the init segment is already in the queue. - for (let i = this.queue.length - 1; i >= 0; i--) { - if ((this.queue[i] as SourceInit).init == init) { - // Already queued up. - return - } - } - - // Check if the init segment has already been applied. - if (this.init == init) { - return - } - - // Add the init segment to the queue so we call addSourceBuffer or changeType - this.queue.push({ - kind: "init", - init: init, - }) - - for (let i = 0; i < init.raw.length; i += 1) { - this.queue.push({ - kind: "data", - data: init.raw[i], - }) - } - - this.flush() - } - - // Append the segment data to the buffer. - append(data: Uint8Array | ArrayBuffer) { - this.queue.push({ - kind: "data", - data: data, - }) - - this.flush() - } - - // Return the buffered range. - buffered() { - if (!this.sourceBuffer) { - return { length: 0 } - } - - return this.sourceBuffer.buffered - } - - // Delete any media older than x seconds from the buffer. - trim(duration: number) { - this.queue.push({ - kind: "trim", - trim: duration, - }) - - this.flush() - } - - // Flush any queued instructions - flush() { - while (1) { - // Check if the buffer is currently busy. - if (this.sourceBuffer && this.sourceBuffer.updating) { - break; - } - - // Process the next item in the queue. - const next = this.queue.shift() - if (!next) { - break; - } - - switch (next.kind) { - case "init": - this.init = next.init; - - if (!this.sourceBuffer) { - // Create a new source buffer. - this.sourceBuffer = this.mediaSource.addSourceBuffer(this.init.info.mime) - - // Call flush automatically after each update finishes. - this.sourceBuffer.addEventListener('updateend', this.flush.bind(this)) - } else { - this.sourceBuffer.changeType(next.init.info.mime) - } - - break; - case "data": - if (!this.sourceBuffer) { - throw "failed to call initailize before append" - } - - this.sourceBuffer.appendBuffer(next.data) - - break; - case "trim": - if (!this.sourceBuffer) { - throw "failed to call initailize before trim" - } - - const end = this.sourceBuffer.buffered.end(this.sourceBuffer.buffered.length - 1) - next.trim; - const start = this.sourceBuffer.buffered.start(0) - - if (end > start) { - this.sourceBuffer.remove(start, end) - } - - break; - default: - throw "impossible; unknown SourceItem" - } - } - } -} - -interface SourceItem {} - -class SourceInit implements SourceItem { - kind!: "init"; - init!: Init; -} - -class SourceData implements SourceItem { - kind!: "data"; - data!: Uint8Array | ArrayBuffer; -} - -class SourceTrim implements SourceItem { - kind!: "trim"; - trim!: number; -} \ No newline at end of file diff --git a/player/src/stream.ts b/player/src/stream/reader.ts similarity index 56% rename from player/src/stream.ts rename to player/src/stream/reader.ts index 7dd7120..edab7d0 100644 --- a/player/src/stream.ts +++ b/player/src/stream/reader.ts @@ -1,18 +1,13 @@ // Reader wraps a stream and provides convience methods for reading pieces from a stream -export class StreamReader { - reader: ReadableStreamDefaultReader; // TODO make a separate class without promises when null +export default class Reader { + reader: ReadableStream; buffer: Uint8Array; - constructor(reader: ReadableStreamDefaultReader, buffer: Uint8Array = new Uint8Array(0)) { + constructor(reader: ReadableStream, buffer: Uint8Array = new Uint8Array(0)) { this.reader = reader this.buffer = buffer } - // TODO implementing pipeTo seems more reasonable than releasing the lock - release() { - this.reader.releaseLock() - } - // Returns any number of bytes async read(): Promise { if (this.buffer.byteLength) { @@ -21,13 +16,38 @@ export class StreamReader { return buffer } - const result = await this.reader.read() + const result = await this.reader.getReader().read() return result.value } + async readAll(): Promise { + while (1) { + const result = await this.reader.getReader().read() + if (result.done) { + break + } + + const buffer = new Uint8Array(result.value) + + if (this.buffer.byteLength == 0) { + this.buffer = buffer + } else { + const temp = new Uint8Array(this.buffer.byteLength + buffer.byteLength) + temp.set(this.buffer) + temp.set(buffer, this.buffer.byteLength) + this.buffer = temp + } + } + + const result = this.buffer + this.buffer = new Uint8Array() + + return result + } + async bytes(size: number): Promise { while (this.buffer.byteLength < size) { - const result = await this.reader.read() + const result = await this.reader.getReader().read() if (result.done) { throw "short buffer" } @@ -52,7 +72,7 @@ export class StreamReader { async peek(size: number): Promise { while (this.buffer.byteLength < size) { - const result = await this.reader.read() + const result = await this.reader.getReader().read() if (result.done) { throw "short buffer" } @@ -151,104 +171,3 @@ export class StreamReader { } } } - -// StreamWriter wraps a stream and writes chunks of data -export class StreamWriter { - buffer: ArrayBuffer; - writer: WritableStreamDefaultWriter; - - constructor(stream: WritableStream) { - this.buffer = new ArrayBuffer(8) - this.writer = stream.getWriter() - } - - release() { - this.writer.releaseLock() - } - - async close() { - return this.writer.close() - } - - async uint8(v: number) { - const view = new DataView(this.buffer, 0, 1) - view.setUint8(0, v) - return this.writer.write(view) - } - - async uint16(v: number) { - const view = new DataView(this.buffer, 0, 2) - view.setUint16(0, v) - return this.writer.write(view) - } - - async uint24(v: number) { - const v1 = (v >> 16) & 0xff - const v2 = (v >> 8) & 0xff - const v3 = (v) & 0xff - - const view = new DataView(this.buffer, 0, 3) - view.setUint8(0, v1) - view.setUint8(1, v2) - view.setUint8(2, v3) - - return this.writer.write(view) - } - - async uint32(v: number) { - const view = new DataView(this.buffer, 0, 4) - view.setUint32(0, v) - return this.writer.write(view) - } - - async uint52(v: number) { - if (v > Number.MAX_SAFE_INTEGER) { - throw "value too large" - } - - this.uint64(BigInt(v)) - } - - async vint52(v: number) { - if (v > Number.MAX_SAFE_INTEGER) { - throw "value too large" - } - - if (v < (1 << 6)) { - return this.uint8(v) - } else if (v < (1 << 14)) { - return this.uint16(v|0x4000) - } else if (v < (1 << 30)) { - return this.uint32(v|0x80000000) - } else { - return this.uint64(BigInt(v) | 0xc000000000000000n) - } - } - - async uint64(v: bigint) { - const view = new DataView(this.buffer, 0, 8) - view.setBigUint64(0, v) - return this.writer.write(view) - } - - async vint64(v: bigint) { - if (v < (1 << 6)) { - return this.uint8(Number(v)) - } else if (v < (1 << 14)) { - return this.uint16(Number(v)|0x4000) - } else if (v < (1 << 30)) { - return this.uint32(Number(v)|0x80000000) - } else { - return this.uint64(v | 0xc000000000000000n) - } - } - - async bytes(buffer: ArrayBuffer) { - return this.writer.write(buffer) - } - - async string(str: string) { - const data = new TextEncoder().encode(str) - return this.writer.write(data) - } -} diff --git a/player/src/stream/writer.ts b/player/src/stream/writer.ts new file mode 100644 index 0000000..201a426 --- /dev/null +++ b/player/src/stream/writer.ts @@ -0,0 +1,100 @@ +// Writer wraps a stream and writes chunks of data +export default class Writer { + buffer: ArrayBuffer; + writer: WritableStreamDefaultWriter; + + constructor(stream: WritableStream) { + this.buffer = new ArrayBuffer(8) + this.writer = stream.getWriter() + } + + release() { + this.writer.releaseLock() + } + + async close() { + return this.writer.close() + } + + async uint8(v: number) { + const view = new DataView(this.buffer, 0, 1) + view.setUint8(0, v) + return this.writer.write(view) + } + + async uint16(v: number) { + const view = new DataView(this.buffer, 0, 2) + view.setUint16(0, v) + return this.writer.write(view) + } + + async uint24(v: number) { + const v1 = (v >> 16) & 0xff + const v2 = (v >> 8) & 0xff + const v3 = (v) & 0xff + + const view = new DataView(this.buffer, 0, 3) + view.setUint8(0, v1) + view.setUint8(1, v2) + view.setUint8(2, v3) + + return this.writer.write(view) + } + + async uint32(v: number) { + const view = new DataView(this.buffer, 0, 4) + view.setUint32(0, v) + return this.writer.write(view) + } + + async uint52(v: number) { + if (v > Number.MAX_SAFE_INTEGER) { + throw "value too large" + } + + this.uint64(BigInt(v)) + } + + async vint52(v: number) { + if (v > Number.MAX_SAFE_INTEGER) { + throw "value too large" + } + + if (v < (1 << 6)) { + return this.uint8(v) + } else if (v < (1 << 14)) { + return this.uint16(v|0x4000) + } else if (v < (1 << 30)) { + return this.uint32(v|0x80000000) + } else { + return this.uint64(BigInt(v) | 0xc000000000000000n) + } + } + + async uint64(v: bigint) { + const view = new DataView(this.buffer, 0, 8) + view.setBigUint64(0, v) + return this.writer.write(view) + } + + async vint64(v: bigint) { + if (v < (1 << 6)) { + return this.uint8(Number(v)) + } else if (v < (1 << 14)) { + return this.uint16(Number(v)|0x4000) + } else if (v < (1 << 30)) { + return this.uint32(Number(v)|0x80000000) + } else { + return this.uint64(v | 0xc000000000000000n) + } + } + + async bytes(buffer: ArrayBuffer) { + return this.writer.write(buffer) + } + + async string(str: string) { + const data = new TextEncoder().encode(str) + return this.writer.write(data) + } +} diff --git a/player/src/track.ts b/player/src/track.ts deleted file mode 100644 index b31cd7f..0000000 --- a/player/src/track.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { Source } from "./source" -import { Segment } from "./segment" -import { TimeRange } from "./util" - -// An audio or video track that consists of multiple sequential segments. -// -// Instead of buffering, we want to drop video while audio plays uninterupted. -// Chrome actually plays up to 3s of audio without video before buffering when in low latency mode. -// Unforuntately, this does not recover correctly when there are gaps (pls fix). -// Our solution is to flush segments in decode order, buffering a single additional frame. -// We extend the duration of the buffered frame and flush it to cover any gaps. -export class Track { - source: Source; - segments: Segment[]; - - constructor(source: Source) { - this.source = source; - this.segments = []; - } - - add(segment: Segment) { - // TODO don't add if the segment is out of date already - this.segments.push(segment) - - // Sort by timestamp ascending - // NOTE: The timestamp is in milliseconds, and we need to parse the media to get the accurate PTS/DTS. - this.segments.sort((a: Segment, b: Segment): number => { - return a.timestamp - b.timestamp - }) - } - - buffered(): TimeRanges { - let ranges: TimeRange[] = [] - - const buffered = this.source.buffered() as TimeRanges - for (let i = 0; i < buffered.length; i += 1) { - // Convert the TimeRanges into an oject we can modify - ranges.push({ - start: buffered.start(i), - end: buffered.end(i) - }) - } - - // Loop over segments and add in their ranges, merging if possible. - for (let segment of this.segments) { - const buffered = segment.buffered() - if (!buffered) continue; - - if (ranges.length) { - // Try to merge with an existing range - const last = ranges[ranges.length-1]; - if (buffered.start < last.start) { - // Network buffer is old; ignore it - continue - } - - // Extend the end of the last range instead of pushing - if (buffered.start <= last.end && buffered.end > last.end) { - last.end = buffered.end - continue - } - } - - ranges.push(buffered) - } - - // TODO typescript - return { - length: ranges.length, - start: (x) => { return ranges[x].start }, - end: (x) => { return ranges[x].end }, - } - } - - flush() { - while (1) { - if (!this.segments.length) break - - const first = this.segments[0] - const done = first.flush() - if (!done) break - - this.segments.shift() - } - } - - // Given the current playhead, determine if we should drop any segments - // If playhead is undefined, it means we're buffering so skip to anything now. - advance(playhead: number | undefined) { - if (this.segments.length < 2) return - - while (this.segments.length > 1) { - const current = this.segments[0]; - const next = this.segments[1]; - - if (next.dts === undefined || next.timescale == undefined) { - // No samples have been parsed for the next segment yet. - break - } - - if (current.dts === undefined) { - // No samples have been parsed for the current segment yet. - // We can't cover the gap by extending the sample so we have to seek. - // TODO I don't think this can happen, but I guess we have to seek past the gap. - break - } - - if (playhead !== undefined) { - // Check if the next segment has playable media now. - // Otherwise give the current segment more time to catch up. - if ((next.dts / next.timescale) > playhead) { - return - } - } - - current.skipTo(next.dts || 0) // tell typescript that it's not undefined; we already checked - current.finish() - - // TODO cancel the QUIC stream to save bandwidth - - this.segments.shift() - } - } -} diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts new file mode 100644 index 0000000..142cafb --- /dev/null +++ b/player/src/transport/index.ts @@ -0,0 +1,135 @@ +import * as Message from "./message" +import Reader from "../stream/reader" +import Writer from "../stream/writer" +import Video from "../video/index" + +/// + +export interface PlayerInit { + url: string; + canvas: HTMLCanvasElement; +} + +export class Player { + quic: Promise; + api: Promise; + + //audio: Worker; + video: Video; + + constructor(props: PlayerInit) { + //this.audio = new Worker("../audio") + this.video = new Video({ + canvas: props.canvas.transferControlToOffscreen(), + }) + + this.quic = this.connect(props.url) + + // Create a unidirectional stream for all of our messages + this.api = this.quic.then((q) => { + return q.createUnidirectionalStream() + }) + + // async functions + this.receiveStreams() + } + + async close() { + (await this.quic).close() + } + + async connect(url: string): Promise { + // TODO remove this when WebTransport supports the system CA pool + const fingerprintURL = new URL(url); + fingerprintURL.pathname = "/fingerprint" + + const response = await fetch(fingerprintURL) + if (!response.ok) { + throw new Error('failed to get server fingerprint'); + } + + const hex = await response.text() + + // Convert the hex to binary. + let fingerprint = []; + for (let c = 0; c < hex.length; c += 2) { + fingerprint.push(parseInt(hex.substring(c, c+2), 16)); + } + + //const fingerprint = Uint8Array.from(atob(hex), c => c.charCodeAt(0)) + + const quic = new WebTransport(url, { + "serverCertificateHashes": [{ + "algorithm": "sha-256", + "value": new Uint8Array(fingerprint), + }] + }) + + await quic.ready + + return quic + } + + async sendMessage(msg: any) { + const payload = JSON.stringify(msg) + const size = payload.length + 8 + + const stream = await this.api + + const writer = new Writer(stream) + await writer.uint32(size) + await writer.string("warp") + await writer.string(payload) + writer.release() + } + + async receiveStreams() { + const q = await this.quic + const streams = q.incomingUnidirectionalStreams.getReader() + + while (true) { + const result = await streams.read() + if (result.done) break + + const stream = result.value + this.handleStream(stream) // don't await + } + } + + async handleStream(stream: ReadableStream) { + let r = new Reader(stream.getReader()) + + while (!await r.done()) { + const size = await r.uint32(); + const typ = new TextDecoder('utf-8').decode(await r.bytes(4)); + + if (typ != "warp") throw "expected warp atom" + if (size < 8) throw "atom too small" + + const payload = new TextDecoder('utf-8').decode(await r.bytes(size - 8)); + const msg = JSON.parse(payload) + + if (msg.init) { + return this.handleInit(r, msg.init as Message.Init) + } else if (msg.segment) { + return this.handleSegment(r, msg.segment as Message.Segment) + } + } + } + + async handleInit(stream: Reader, msg: Message.Init) { + // TODO properly determine if audio or video + this.video.init({ + track: msg.id, + stream: stream, + }) + } + + async handleSegment(stream: Reader, msg: Message.Segment) { + // TODO properly determine if audio or video + this.video.segment({ + track: msg.init, + stream: stream, + }) + } +} \ No newline at end of file diff --git a/player/src/message.ts b/player/src/transport/message.ts similarity index 60% rename from player/src/message.ts rename to player/src/transport/message.ts index 026ebee..d151538 100644 --- a/player/src/message.ts +++ b/player/src/transport/message.ts @@ -1,13 +1,8 @@ -export interface Message { - init?: MessageInit - segment?: MessageSegment -} - -export interface MessageInit { +export interface Init { id: string } -export interface MessageSegment { +export interface Segment { init: string // id of the init segment timestamp: number // presentation timestamp in milliseconds of the first sample // TODO track would be nice diff --git a/player/src/util.ts b/player/src/util.ts deleted file mode 100644 index c3b6bda..0000000 --- a/player/src/util.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface TimeRange { - start: number; - end: number; -} diff --git a/player/src/video/decoder.ts b/player/src/video/decoder.ts new file mode 100644 index 0000000..4b8839a --- /dev/null +++ b/player/src/video/decoder.ts @@ -0,0 +1,123 @@ +import * as Message from "./message"; +import { Track } from "./track"; +import { Renderer } from "./renderer" +import { MP4New, MP4Sample, MP4ArrayBuffer } from "../mp4/mp4" + +export class Decoder { + tracks: Map; + renderer: Renderer; + + constructor(renderer: Renderer) { + this.tracks = new Map(); + this.renderer = renderer; + } + + async init(msg: Message.Init) { + let track = this.tracks.get(msg.track); + if (!track) { + track = new Track() + this.tracks.set(msg.track, track) + } + + while (1) { + const data = await msg.stream.read() + if (!data) break + + track.init(data) + } + + // TODO this will hang on incomplete data + const init = await track.ready; + const info = init.info; + + if (info.audioTracks.length + info.videoTracks.length != 1) { + throw new Error("expected a single track") + } + } + + async decode(msg: Message.Segment) { + let track = this.tracks.get(msg.track); + if (!track) { + track = new Track() + this.tracks.set(msg.track, track) + } + + // Wait for the init segment to be fully received and parsed + const init = await track.ready; + const info = init.info; + const video = info.videoTracks[0] + + const decoder = new VideoDecoder({ + output: (frame: VideoFrame) => { + this.renderer.push(frame) + }, + error: (err: Error) => { + console.warn(err) + } + }); + + decoder.configure({ + codec: info.mime, + codedHeight: video.track_height, + codedWidth: video.track_width, + // optimizeForLatency: true + }) + + const input = MP4New(); + + input.onSamples = (id: number, user: any, samples: MP4Sample[]) => { + for (let sample of samples) { + const timestamp = sample.dts / (1000 / info.timescale) // milliseconds + + decoder.decode(new EncodedVideoChunk({ + data: sample.data, + duration: sample.duration, + timestamp: timestamp, + type: sample.is_sync ? "key" : "delta", + })) + } + } + + input.onReady = (info: any) => { + input.setExtractionOptions(info.tracks[0].id, {}, { nbSamples: 1 }); + input.start(); + } + + let offset = 0 + + // MP4box requires us to reparse the init segment unfortunately + for (let raw of track.raw) { + offset = input.appendBuffer(raw) + } + + /* TODO I'm not actually sure why this code doesn't work; something trips up the MP4 parser + while (1) { + const data = await stream.read() + if (!data) break + + input.appendBuffer(data) + input.flush() + } + */ + + // One day I'll figure it out; until then read one top-level atom at a time + while (!await msg.stream.done()) { + const raw = await msg.stream.peek(4) + const size = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint32(0) + const atom = await msg.stream.bytes(size) + + // Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately + let box = new Uint8Array(atom.byteLength); + box.set(atom) + + // and for some reason we need to modify the underlying ArrayBuffer with offset + let buffer = box.buffer as MP4ArrayBuffer + buffer.fileStart = offset + + // Parse the data + offset = input.appendBuffer(buffer) + input.flush() + } + + } +} \ No newline at end of file diff --git a/player/src/video/index.ts b/player/src/video/index.ts new file mode 100644 index 0000000..8ea58f4 --- /dev/null +++ b/player/src/video/index.ts @@ -0,0 +1,19 @@ +import * as Message from "./message" + +// Wrapper around the WebWorker API +export default class Video { + worker: Worker; + + constructor(config: Message.Config) { + this.worker = new Worker(new URL('worker.ts', import.meta.url), { type: "module" }) + this.worker.postMessage({ config }, [ config.canvas ]) + } + + init(init: Message.Init) { + this.worker.postMessage({ init }, [ init.stream.buffer, init.stream.reader ]) + } + + segment(segment: Message.Segment) { + this.worker.postMessage({ segment }, [ segment.stream.buffer, segment.stream.reader ]) + } +} \ No newline at end of file diff --git a/player/src/video/message.ts b/player/src/video/message.ts new file mode 100644 index 0000000..9845708 --- /dev/null +++ b/player/src/video/message.ts @@ -0,0 +1,15 @@ +import Reader from "../stream/reader"; + +export interface Config { + canvas: OffscreenCanvas; +} + +export interface Init { + track: string; + stream: Reader; +} + +export interface Segment { + track: string; + stream: Reader; +} \ No newline at end of file diff --git a/player/src/video/renderer.ts b/player/src/video/renderer.ts new file mode 100644 index 0000000..c5b40cf --- /dev/null +++ b/player/src/video/renderer.ts @@ -0,0 +1,72 @@ +import * as Message from "./message"; + +export class Renderer { + canvas: OffscreenCanvas; + queue: Array; + render: number; // non-zero if requestAnimationFrame has been called + sync: DOMHighResTimeStamp; // the wall clock value for timestamp 0 + last?: number; // the timestamp of the last rendered frame + + constructor(config: Message.Config) { + this.canvas = config.canvas; + this.queue = []; + this.render = 0; + this.sync = 0; + } + + push(frame: VideoFrame) { + if (!this.sync) { + // Save the frame as the sync point + this.sync = performance.now() - frame.timestamp + } + + // Drop any old frames + if (this.last && frame.timestamp <= this.last) { + frame.close() + return + } + + // Insert the frame into the queue sorted by timestamp. + // TODO loop backwards for better performance + let index = this.queue.findIndex(other => { + return frame.timestamp < other.timestamp; + }) + + // Insert into the queue. + this.queue.splice(index, 0, frame) + + if (!this.render) { + this.render = self.requestAnimationFrame(this.draw.bind(this)) + } + } + + draw(now: DOMHighResTimeStamp) { + // Determine the target timestamp. + const target = now - this.sync + + let frame = this.queue[0] + if (frame.timestamp > target) { + // nothing to render yet + return + } + + this.queue.shift() + + // Check if we should skip some frames + while (this.queue.length) { + const next = this.queue[0] + if (next.timestamp > target) { + break + } + + this.queue.shift() + frame = next + } + + const ctx = this.canvas.getContext("2d"); + ctx?.drawImage(frame, 0, 0) // TODO aspect ratio + + this.last = frame.timestamp; + frame.close() + } +} \ No newline at end of file diff --git a/player/src/video/track.ts b/player/src/video/track.ts new file mode 100644 index 0000000..60aaec0 --- /dev/null +++ b/player/src/video/track.ts @@ -0,0 +1,58 @@ +import { MP4New, MP4File, MP4ArrayBuffer, MP4Info } from "../mp4/mp4" + +export interface Init { + raw: MP4ArrayBuffer[]; + info: MP4Info; +} + +export class Track { + mp4box: MP4File; + offset: number; + + raw: MP4ArrayBuffer[]; + ready: Promise; + + constructor() { + this.mp4box = MP4New() + this.raw = [] + this.offset = 0 + + // Create a promise that gets resolved once the init segment has been parsed. + this.ready = new Promise((resolve, reject) => { + this.mp4box.onError = reject + + // https://github.com/gpac/mp4box.js#onreadyinfo + this.mp4box.onReady = (info: MP4Info) => { + if (!info.isFragmented) { + reject("expected a fragmented mp4") + } + + if (info.tracks.length != 1) { + reject("expected a single track") + } + + resolve({ + raw: this.raw, + info, + }) + } + }) + } + + init(data: Uint8Array) { + // Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately + let box = new Uint8Array(data.byteLength); + box.set(data) + + // and for some reason we need to modify the underlying ArrayBuffer with fileStart + let buffer = box.buffer as MP4ArrayBuffer + buffer.fileStart = this.offset + + // Parse the data + this.offset = this.mp4box.appendBuffer(buffer) + this.mp4box.flush() + + // Add the box to our queue of chunks + this.raw.push(buffer) + } +} \ No newline at end of file diff --git a/player/src/video/worker.ts b/player/src/video/worker.ts new file mode 100644 index 0000000..b8ede50 --- /dev/null +++ b/player/src/video/worker.ts @@ -0,0 +1,22 @@ +import { Renderer } from "./renderer" +import { Decoder } from "./decoder" +import * as Message from "./message" + +let decoder: Decoder; +let renderer: Renderer; + +self.addEventListener('message', async (e: MessageEvent) => { + if (e.data.config) { + const config = e.data.config as Message.Config + + renderer = new Renderer(config) + decoder = new Decoder(renderer) + } + + if (e.data.segment) { + const segment = e.data.segment as Message.Segment + + await decoder.decode(segment) + } +}) + diff --git a/player/tsconfig.json b/player/tsconfig.json index 9762983..d778306 100644 --- a/player/tsconfig.json +++ b/player/tsconfig.json @@ -3,7 +3,7 @@ "src/**/*" ], "compilerOptions": { - "target": "es2021", + "target": "es2022", "strict": true, "typeRoots": [ "src/types" From 805f6ca392baad676866fddc7defacd2ded63c12 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 28 Mar 2023 07:54:41 +0900 Subject: [PATCH 02/10] Making good progress on WebCodecs. --- player/package-lock.json | 4235 --------------------------- player/package.json | 5 +- player/src/audio/decoder.ts | 120 + player/src/audio/index.ts | 20 +- player/src/audio/message.ts | 15 + player/src/audio/renderer.ts | 12 + player/src/audio/worker.ts | 21 + player/src/mp4/{mp4.ts => index.ts} | 30 +- player/src/{player => mp4}/init.ts | 34 +- player/src/stream/index.ts | 2 + player/src/stream/reader.ts | 29 +- player/src/transport/index.ts | 69 +- player/src/util/deferred.ts | 16 + player/src/util/index.ts | 1 + player/src/video/decoder.ts | 65 +- player/src/video/index.ts | 4 +- player/src/video/message.ts | 8 +- player/src/video/track.ts | 58 - player/src/video/worker.ts | 8 +- player/tsconfig.json | 2 + player/yarn.lock | 1515 +++++----- server/internal/warp/session.go | 10 + 22 files changed, 1159 insertions(+), 5120 deletions(-) delete mode 100644 player/package-lock.json create mode 100644 player/src/audio/decoder.ts create mode 100644 player/src/audio/message.ts create mode 100644 player/src/audio/renderer.ts create mode 100644 player/src/audio/worker.ts rename player/src/mp4/{mp4.ts => index.ts} (69%) rename player/src/{player => mp4}/init.ts (69%) create mode 100644 player/src/stream/index.ts create mode 100644 player/src/util/deferred.ts create mode 100644 player/src/util/index.ts delete mode 100644 player/src/video/track.ts diff --git a/player/package-lock.json b/player/package-lock.json deleted file mode 100644 index a53106f..0000000 --- a/player/package-lock.json +++ /dev/null @@ -1,4235 +0,0 @@ -{ - "name": "client", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "datastream-js": "^1.0.7" - }, - "devDependencies": { - "@parcel/validator-typescript": "^2.6.0", - "parcel": "^2.6.0", - "typescript": ">=3.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.17.12", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", - "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", - "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", - "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@lezer/common": { - "version": "0.15.12", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", - "integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==", - "dev": true, - "license": "MIT" - }, - "node_modules/@lezer/lr": { - "version": "0.15.8", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz", - "integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@lezer/common": "^0.15.0" - } - }, - "node_modules/@mischnic/json-sourcemap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz", - "integrity": "sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@lezer/common": "^0.15.7", - "@lezer/lr": "^0.15.4", - "json5": "^2.2.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.0.2.tgz", - "integrity": "sha512-DznYtF3lHuZDSRaIOYeif4JgO0NtO2Xf8DsngAugMx/bUdTFbg86jDTmkVJBNmV+cxszz6OjGvinnS8AbJ342g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@parcel/bundler-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.6.0.tgz", - "integrity": "sha512-AplEdGm/odV7yGmoeOnglxnY31WlNB5EqGLFGxkgs7uwDaTWoTX/9SWPG6xfvirhjDpms8sLSiVuBdFRCCLtNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/cache": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.6.0.tgz", - "integrity": "sha512-4vbD5uSuf+kRnrFesKhpn0AKnOw8u2UlvCyrplYmp1g9bNAkIooC/nDGdmkb/9SviPEbni9PEanQEHDU2+slpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/fs": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/utils": "2.6.0", - "lmdb": "2.3.10" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.6.0" - } - }, - "node_modules/@parcel/codeframe": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.6.0.tgz", - "integrity": "sha512-yXXxrO9yyedHKpTwC+Af0+vPmQm+A9xeEhkt4f0yVg1n4t4yUIxYlTedzbM8ygZEEBtkXU9jJ+PkgXbfMf0dqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/compressor-raw": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.6.0.tgz", - "integrity": "sha512-rtMU2mGl88bic6Xbq1u5L49bMK4s5185b0k7h3JRdS6/0rR+Xp4k/o9Wog+hHjK/s82z1eF9WmET779ZpIDIQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/config-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.6.0.tgz", - "integrity": "sha512-DXovFPhZITmTvFaSEdC8RRqROs9FLIJ4u8yFSU6FUyq2wpvtYVRXXoDrvXgClh2csXmK7JTJTp5JF7r0rd2UaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/bundler-default": "2.6.0", - "@parcel/compressor-raw": "2.6.0", - "@parcel/namer-default": "2.6.0", - "@parcel/optimizer-css": "2.6.0", - "@parcel/optimizer-htmlnano": "2.6.0", - "@parcel/optimizer-image": "2.6.0", - "@parcel/optimizer-svgo": "2.6.0", - "@parcel/optimizer-terser": "2.6.0", - "@parcel/packager-css": "2.6.0", - "@parcel/packager-html": "2.6.0", - "@parcel/packager-js": "2.6.0", - "@parcel/packager-raw": "2.6.0", - "@parcel/packager-svg": "2.6.0", - "@parcel/reporter-dev-server": "2.6.0", - "@parcel/resolver-default": "2.6.0", - "@parcel/runtime-browser-hmr": "2.6.0", - "@parcel/runtime-js": "2.6.0", - "@parcel/runtime-react-refresh": "2.6.0", - "@parcel/runtime-service-worker": "2.6.0", - "@parcel/transformer-babel": "2.6.0", - "@parcel/transformer-css": "2.6.0", - "@parcel/transformer-html": "2.6.0", - "@parcel/transformer-image": "2.6.0", - "@parcel/transformer-js": "2.6.0", - "@parcel/transformer-json": "2.6.0", - "@parcel/transformer-postcss": "2.6.0", - "@parcel/transformer-posthtml": "2.6.0", - "@parcel/transformer-raw": "2.6.0", - "@parcel/transformer-react-refresh-wrap": "2.6.0", - "@parcel/transformer-svg": "2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.6.0" - } - }, - "node_modules/@parcel/core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.6.0.tgz", - "integrity": "sha512-8OOWbPuxpFydpwNyKoz6d3e3O4DmxNYmMw4DXwrPSj/jyg7oa+SDtMT0/VXEhujE0HYkQPCHt4npRajkSuf99A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mischnic/json-sourcemap": "^0.1.0", - "@parcel/cache": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/events": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/graph": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/package-manager": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "abortcontroller-polyfill": "^1.1.9", - "base-x": "^3.0.8", - "browserslist": "^4.6.6", - "clone": "^2.1.1", - "dotenv": "^7.0.0", - "dotenv-expand": "^5.1.0", - "json5": "^2.2.0", - "msgpackr": "^1.5.4", - "nullthrows": "^1.1.1", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/css": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@parcel/css/-/css-1.9.0.tgz", - "integrity": "sha512-egCetUQ1H6pgYxOIxVQ8X/YT5e8G0R8eq6aVaUHrqnZ7A8cc6FYgknl9XRmoy2Xxo9h1htrbzdaEShQ5gROwvw==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/css-darwin-arm64": "1.9.0", - "@parcel/css-darwin-x64": "1.9.0", - "@parcel/css-linux-arm-gnueabihf": "1.9.0", - "@parcel/css-linux-arm64-gnu": "1.9.0", - "@parcel/css-linux-arm64-musl": "1.9.0", - "@parcel/css-linux-x64-gnu": "1.9.0", - "@parcel/css-linux-x64-musl": "1.9.0", - "@parcel/css-win32-x64-msvc": "1.9.0" - } - }, - "node_modules/@parcel/css-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@parcel/css-darwin-x64/-/css-darwin-x64-1.9.0.tgz", - "integrity": "sha512-4SpuwiM/4ayOgKflqSLd87XT7YwyC3wd2QuzOOkasjbe38UU+tot/87l2lQYEB538YinLdfwFQuFLDY0x9MxgA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/diagnostic": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.6.0.tgz", - "integrity": "sha512-+p8gC2FKxSI2veD7SoaNlP572v4kw+nafCQEPDtJuzYYRqywYUGncch25dkpgNApB4W4cXVkZu3ZbtIpCAmjQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mischnic/json-sourcemap": "^0.1.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/events": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.6.0.tgz", - "integrity": "sha512-2WaKtBs4iYwS88j4zRdyTJTgh8iuY4E32FMmjzzbheqETs6I05gWuPReGukJYxk8vc0Ir7tbzp12oAfpgo0Y+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/fs": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.6.0.tgz", - "integrity": "sha512-6vxtx5Zy6MvDvH1EPx9JxjKGF03bR7VE1dUf4HLeX2D8YmpL5hkHJnlRCFdcH08rzOVwaJLzg1QNtblWJXQ9CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/fs-search": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/watcher": "^2.0.0", - "@parcel/workers": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.6.0" - } - }, - "node_modules/@parcel/fs-search": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.6.0.tgz", - "integrity": "sha512-1nXzM3H/cA4kzLKvDBvwmNisKCdRqlgkLXh+OR1Zu28Kn4W34KuJMcHWW8cC+WIuuKqDh5oo2WPsC5y65GXBKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/graph": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.6.0.tgz", - "integrity": "sha512-rxrAzWm6rwbCRPbu0Z+zwMscpG8omffODniVWPlX2G0jgQGpjKsutBQ6RMfFIcfaQ4MzL3pIQOTf8bkjQOPsbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/hash": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.6.0.tgz", - "integrity": "sha512-YugWqhLxqK80Lo++3B3Kr5UPCHOdS8iI2zJ1jkzUeH9v6WUzbwWOnmPf6lN2S5m1BrIFFJd8Jc+CbEXWi8zoJA==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^1.0.3", - "xxhash-wasm": "^0.4.2" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/logger": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.6.0.tgz", - "integrity": "sha512-J1/7kPfSGBvMKSZdi0WCNuN0fIeiWxifnDGn7W/K8KhD422YwFJA8N046ps8nkDOPIXf1osnIECNp4GIR9oSYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/events": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/markdown-ansi": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.6.0.tgz", - "integrity": "sha512-fyjkrJQQSfKTUFTTasdZ6WrAkDoQ2+DYDjj+3p+RncYyrIa9zArKx4IiRiipsvNdtMvP0/hTdK8F3BOJ3KSU/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/namer-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.6.0.tgz", - "integrity": "sha512-r8O12r7ozJBctnFxVdXbf/fK97GIdNj3hiiUNWlXEmED9sw6ZPcChaLcfot0/443g8i87JDmSTKJ8js2tuz5XA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/node-resolver-core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.6.0.tgz", - "integrity": "sha512-AJDj5DZbB58plv0li8bdVSD+zpnkHE36Om3TYyNn1jgXXwgBM64Er/9p8yQn356jBqTQMh7zlJqvbdIyOiMeMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-css": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.6.0.tgz", - "integrity": "sha512-VMJknUwfKCw6Woov0lnPGdsGZewcI4ghW8WKmNZzC5uKCetk1XetV55QHBc1RNjGfsjfSTZiSa3guATj2zFJkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/css": "^1.9.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "browserslist": "^4.6.6", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-htmlnano": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.6.0.tgz", - "integrity": "sha512-HmvcUoYpfdx8ZfID4WOj/SE8N78NEBmzAffZ8f827mYMr4ZrbKzAgg6OG3tBbfF0zxH0bIjZcwqwZYk4SdbG7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "htmlnano": "^2.0.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "svgo": "^2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-image": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.6.0.tgz", - "integrity": "sha512-FDNr3LJ8SWR9rrtdCrZOlYF1hE9G5pxUWawGxUasbvqwcY5lEQwr2KRmfGZeg+KwOnzlImlY6dP2LGox1NFddQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-svgo": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.6.0.tgz", - "integrity": "sha512-LMTDVMd7T/IfLG59yLWl8Uw2HYGbj2C3jIwkMqH9MBUT5KILK66T3t0yV86SoZJnxZ6xBIJ+kCcCRssCzhvanw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "svgo": "^2.4.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/optimizer-terser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.6.0.tgz", - "integrity": "sha512-oezRt6Lz/QqcVDXyMfFjzQc7n0ThJowLJ4Lyhu8rMh0ZJYzc4UCFCw/19d4nRnzE+Qg0vj3mQCpdkA9/64E44g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1", - "terser": "^5.2.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/package-manager": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.6.0.tgz", - "integrity": "sha512-AqFfdkbOw51q/3ia2mIsFTmrpYEyUb3k+2uYC5GsLMz3go6OGn7/Crz0lZLSclv5EtwpRg3TWr9yL7RekVN/Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.6.0" - } - }, - "node_modules/@parcel/packager-css": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.6.0.tgz", - "integrity": "sha512-iXUttSe+wtnIM2PKCyFC2I4+Szv+8qHpC3wXeJlXlzd8wljm42y+6Fs4FZ0zihTccRxI2UUhFnKu90ag+5AmjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-html": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.6.0.tgz", - "integrity": "sha512-HsiXMkU9AJr3LLjsP2Kteho2jCVpabTwcU/fauwbwirhg0xNlRsKxYZRCllRhPkb0FWAnkjzwjOj01MHD6NJCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.6.0.tgz", - "integrity": "sha512-Uz3pqIFchFfKszWnNGDgIwM1uwHHJp7Dts6VzS9lf/2RbRgZT0fmce+NPgnVO5MMKBHzdvm32ShT6gFAABF5Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "globals": "^13.2.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-raw": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.6.0.tgz", - "integrity": "sha512-ktT6Qc/GgCq8H1+6y+AXufVzQj1s6KRoKf83qswCD0iY3MwCbJoEfc3IsB4K64FpHIL5Eu0z54IId+INvGbOYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/packager-svg": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.6.0.tgz", - "integrity": "sha512-OF2RShyspXu7H4Dn2PmchfMMYPx+kWjOXiYVQ6OkOI0MZmOydx7p8nrcG5+y7vCJTPlta828BSwva0GdKfn46A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "posthtml": "^0.16.4" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/plugin": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.6.0.tgz", - "integrity": "sha512-LzOaiK8R6eFEoov1cb3/W+o0XvXdI/VbDhMDl0L0II+/56M0UeayYtFP5QGTDn/fZqVlYfzPCtt3EMwdG7/dow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/types": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/reporter-cli": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.6.0.tgz", - "integrity": "sha512-QFG957NXx3L0D8Zw0+B2j7IHy8f/UzOVu6VvKE3rMkhq/iR2qLrPohQ+uvxlee+CLC0cG2qRSgJ7Ve/rjQPoJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "chalk": "^4.1.0", - "term-size": "^2.2.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/reporter-dev-server": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.6.0.tgz", - "integrity": "sha512-VvygsCA+uzWyijIV8zqU1gFyhAWknuaY4KIWhV4kCT8afRJwsLSwt/tpdaKDPuPU45h3tTsUdXH1wjaIk+dGeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/resolver-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.6.0.tgz", - "integrity": "sha512-ATk9wXvy5GOHAqyHbnCnU11fUPTtf8dLjpgVqL5XylwugZnyBXbynoTWX4w8h6mffkVtdfmzTJx/o4Lresz9sA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/node-resolver-core": "2.6.0", - "@parcel/plugin": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-browser-hmr": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.6.0.tgz", - "integrity": "sha512-90xvv/10cFML5dAhClBEJZ/ExiBQVPqQsZcvRmVZmc5mpZVJMKattWCQrd7pAf7FDYl4JAcvsK3DTwvRT/oLNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.6.0.tgz", - "integrity": "sha512-R4tJAIT/SX7VBQ+f7WmeekREQzzLsmgP1j486uKhQNyYrpvsN0HnRbg5aqvZjEjkEmSeJR0mOlWtMK5/m+0yTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-react-refresh": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.6.0.tgz", - "integrity": "sha512-2sRd13gc2EbMV/O5n2NPVGGhKBasb1fDTXGEY8y7qi9xDKc+ewok/D83T+w243FhCPS9Pf3ur5GkbPlrJGcenQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "react-error-overlay": "6.0.9", - "react-refresh": "^0.9.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/runtime-service-worker": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.6.0.tgz", - "integrity": "sha512-nVlknGw5J5Bkd1Wr1TbyWHhUd9CmVVebaRg/lpfVKYhAuE/2r+3N0+J8qbEIgtTRcHaSV7wTNpg4weSWq46VeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/source-map": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.0.5.tgz", - "integrity": "sha512-DRVlCFKLpqBSIbMxUoVlHgfiv12HTW/U7nnhzw52YgzDVXUX9OA41dXS1PU0pJ1si+D1k8msATUC+AoldN43mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": "^12.18.3 || >=14" - } - }, - "node_modules/@parcel/transformer-babel": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.6.0.tgz", - "integrity": "sha512-qTDzhLoaTpRJoppCNqaAlcUYxcDEvJffem1h3SAQiwvCLUBQowLyeaBy8sUxu54AU6eHFJyBld5ZocENyHTBCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "browserslist": "^4.6.6", - "json5": "^2.2.0", - "nullthrows": "^1.1.1", - "semver": "^5.7.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-css": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.6.0.tgz", - "integrity": "sha512-Ei9NPE5Rl9V+MGd8qddfZD0Fsqbvky8J62RwYsqLkptFl9FkhgwOu8Cmokz7IIc4GJ2qzfnG5y54K/Bi7Moq4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/css": "^1.9.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "browserslist": "^4.6.6", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-html": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.6.0.tgz", - "integrity": "sha512-YQh5WzNFjPhgV09P+zVS++albTCTvbPYAJXp5zUJ4HavzcpV2IB3HAPRk9x+iXUeRBQYYiO5SMMRkdy9a4CzQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-image": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.6.0.tgz", - "integrity": "sha512-Zkh1i6nWNOTOReKlZD+bLJCHA16dPLO6Or7ETAHtSF3iRzMNFcVFp+851Awj3l4zeJ6CoCWlyxsR4CEdioRgiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/workers": "2.6.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "peerDependencies": { - "@parcel/core": "^2.6.0" - } - }, - "node_modules/@parcel/transformer-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.6.0.tgz", - "integrity": "sha512-4v2r3EVdMKowBziVBW9HZqvAv88HaeiezkWyMX4wAfplo9jBtWEp99KEQINzSEdbXROR81M9oJjlGF5+yoVr/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "@swc/helpers": "^0.3.15", - "browserslist": "^4.6.6", - "detect-libc": "^1.0.3", - "nullthrows": "^1.1.1", - "regenerator-runtime": "^0.13.7", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.6.0" - } - }, - "node_modules/@parcel/transformer-json": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.6.0.tgz", - "integrity": "sha512-zb+TQAdHWdXijKcFhLe+5KN1O0IzXwW1gJhPr8DJEA3qhPaCsncsw5RCVjQlP3a7NXr1mMm1eMtO6bhIMqbXeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "json5": "^2.2.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-postcss": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.6.0.tgz", - "integrity": "sha512-czmh2mOPJLwYbtnPTFlxKYcaQHH6huIlpfNX1XgdsaEYS+yFs8ZXpzqjxI1wu6rMW0R0q5aon72yB3PJewvqNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "clone": "^2.1.1", - "nullthrows": "^1.1.1", - "postcss-value-parser": "^4.2.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-posthtml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.6.0.tgz", - "integrity": "sha512-R1FmPMZ0pgrbPZkDppa2pE+6KDK3Wxof6uQo7juHLB2ELGOTaYofsG3nrRdk+chyAHaVv4qWLqXbfZK6pGepEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-raw": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.6.0.tgz", - "integrity": "sha512-QDirlWCS/qy0DQ3WvDIAnFP52n1TJW/uWH+4PGMNnX4/M3/2UchY8xp9CN0tx4NQ4g09S8o3gLlHvNxQqZxFrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-react-refresh-wrap": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.6.0.tgz", - "integrity": "sha512-G34orfvLDUTumuerqNmA8T8NUHk+R0jwUjbVPO7gpB6VCVQ5ocTABdE9vN9Uu/cUsHij40TUFwqK4R9TFEBIEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "react-refresh": "^0.9.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-svg": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.6.0.tgz", - "integrity": "sha512-e7yrb7775A7tEGRsAHQSMhXe+u4yisH5W0PuIzAQQy/a2IwBjaSxNnvyelN7tNX0FYq0BK6An5wRbhK4YmM+xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/ts-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/ts-utils/-/ts-utils-2.6.0.tgz", - "integrity": "sha512-U2Spr/vdOnxLzztXP6WpMO7JZTsaYO1G6F/cUTG5fReTQ0imM952FAc/WswpZWAPZqXqWCnvC/Z91JIkMDuYrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "typescript": ">=3.0.0" - } - }, - "node_modules/@parcel/types": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.6.0.tgz", - "integrity": "sha512-lAMYvOBfNEJMsPJ+plbB50305o0TwNrY1xX5RRIWBqwOa6bYmbW1ZljUk1tQvnkpIE4eAHQwnPR5Z2XWg18wGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/cache": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/package-manager": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/workers": "2.6.0", - "utility-types": "^3.10.0" - } - }, - "node_modules/@parcel/utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.6.0.tgz", - "integrity": "sha512-ElXz+QHtT1JQIucbQJBk7SzAGoOlBp4yodEQVvTKS7GA+hEGrSP/cmibl6qm29Rjtd0zgQsdd+2XmP3xvP2gQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/codeframe": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/markdown-ansi": "2.6.0", - "@parcel/source-map": "^2.0.0", - "chalk": "^4.1.0" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/validator-typescript": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/validator-typescript/-/validator-typescript-2.6.0.tgz", - "integrity": "sha512-NMroc+QPoTo436COHsqEQsn+Qd+7HE1s1X6he1Bqb+RMB4rZsvOZI22MgFj1eU5MpfYuM4zTID0Uz221hiS59w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/ts-utils": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "typescript": ">=3.0.0" - } - }, - "node_modules/@parcel/watcher": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.5.tgz", - "integrity": "sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/workers": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.6.0.tgz", - "integrity": "sha512-3tcI2LF5fd/WZtSnSjyWdDE+G+FitdNrRgSObzSp+axHKMAM23sO0z7KY8s2SYCF40msdYbFUW8eI6JlYNJoWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/diagnostic": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "chrome-trace-event": "^1.0.2", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "peerDependencies": { - "@parcel/core": "^2.6.0" - } - }, - "node_modules/@swc/helpers": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", - "integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true, - "license": "MIT" - }, - "node_modules/abortcontroller-polyfill": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", - "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, - "node_modules/browserslist": { - "version": "4.20.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz", - "integrity": "sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001349", - "electron-to-chromium": "^1.4.147", - "escalade": "^3.1.1", - "node-releases": "^2.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001352", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz", - "integrity": "sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-tree": "^1.1.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/datastream-js": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/datastream-js/-/datastream-js-1.0.7.tgz", - "integrity": "sha512-rW7N3QkEx01reZ6/BF1s3sGnh1JdFpektwSqgUz8bmmvfmD+NNGTPhbTePZjs0B3VSizFX26Kr8qNtNJDz0NAQ==", - "dependencies": { - "text-encoding": "^0.6.4" - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", - "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=6" - } - }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.150", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz", - "integrity": "sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA==", - "dev": true, - "license": "ISC" - }, - "node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/get-port": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", - "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/htmlnano": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.2.tgz", - "integrity": "sha512-+ZrQFS4Ub+zd+/fWwfvoYCEGNEa0/zrpys6CyXxvZDwtL7Pl+pOtRkiujyvBQ7Lmfp7/iEPxtOFgxWA16Gkj3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cosmiconfig": "^7.0.1", - "posthtml": "^0.16.5", - "timsort": "^0.3.0" - }, - "peerDependencies": { - "cssnano": "^5.0.11", - "postcss": "^8.3.11", - "purgecss": "^4.0.3", - "relateurl": "^0.2.7", - "srcset": "^5.0.0", - "svgo": "^2.8.0", - "terser": "^5.10.0", - "uncss": "^0.17.3" - }, - "peerDependenciesMeta": { - "cssnano": { - "optional": true - }, - "postcss": { - "optional": true - }, - "purgecss": { - "optional": true - }, - "relateurl": { - "optional": true - }, - "srcset": { - "optional": true - }, - "svgo": { - "optional": true - }, - "terser": { - "optional": true - }, - "uncss": { - "optional": true - } - } - }, - "node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-json": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", - "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lmdb": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.3.10.tgz", - "integrity": "sha512-GtH+nStn9V59CfYeQ5ddx6YTfuFCmu86UJojIjJAweG+/Fm0PDknuk3ovgYDtY/foMeMdZa8/P7oSljW/d5UPw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "msgpackr": "^1.5.4", - "nan": "^2.14.2", - "node-addon-api": "^4.3.0", - "node-gyp-build-optional-packages": "^4.3.2", - "ordered-binary": "^1.2.4", - "weak-lru-cache": "^1.2.2" - }, - "optionalDependencies": { - "lmdb-darwin-arm64": "2.3.10", - "lmdb-darwin-x64": "2.3.10", - "lmdb-linux-arm": "2.3.10", - "lmdb-linux-arm64": "2.3.10", - "lmdb-linux-x64": "2.3.10", - "lmdb-win32-x64": "2.3.10" - } - }, - "node_modules/lmdb-darwin-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/lmdb-darwin-x64/-/lmdb-darwin-x64-2.3.10.tgz", - "integrity": "sha512-gAc/1b/FZOb9yVOT+o0huA+hdW82oxLo5r22dFTLoRUFG1JMzxdTjmnW6ONVOHdqC9a5bt3vBCEY3jmXNqV26A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lmdb/node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lmdb/node_modules/node-gyp-build-optional-packages": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-4.3.5.tgz", - "integrity": "sha512-5ke7D8SiQsTQL7CkHpfR1tLwfqtKc0KYEmlnkwd40jHCASskZeS98qoZ1qDUns2aUQWikcjidRUs6PM/3iyN/w==", - "dev": true, - "license": "MIT", - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, - "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/msgpackr": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.6.1.tgz", - "integrity": "sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "msgpackr-extract": "^2.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-2.0.2.tgz", - "integrity": "sha512-coskCeJG2KDny23zWeu+6tNy7BLnAiOGgiwzlgdm4oeSsTpqEJJPguHIuKZcCdB7tzhZbXNYSg6jZAXkZErkJA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.0.2" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm": "2.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-linux-x64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-win32-x64": "2.0.2" - } - }, - "node_modules/nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-gyp-build": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", - "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", - "dev": true, - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.2.tgz", - "integrity": "sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g==", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "node-gyp-build-optional": "optional.js", - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-releases": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", - "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "dev": true, - "license": "MIT" - }, - "node_modules/ordered-binary": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.2.5.tgz", - "integrity": "sha512-djRmZoEpOGvIRW7ufsCDHtvcUa18UC9TxnPbHhSVFZHsoyg0dtut1bWtBZ/fmxdPN62oWXrV6adM7NoWU+CneA==", - "dev": true, - "license": "MIT" - }, - "node_modules/parcel": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.6.0.tgz", - "integrity": "sha512-pSTJ7wC6uTl16PKLXQV7RfL9FGoIDA1iVpNvaav47n6UkUdKqfx0spcVPpw35kWdRcHJF61YAvkPjP2hTwHQ+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@parcel/config-default": "2.6.0", - "@parcel/core": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/events": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/package-manager": "2.6.0", - "@parcel/reporter-cli": "2.6.0", - "@parcel/reporter-dev-server": "2.6.0", - "@parcel/utils": "2.6.0", - "chalk": "^4.1.0", - "commander": "^7.0.0", - "get-port": "^4.2.0", - "v8-compile-cache": "^2.0.0" - }, - "bin": { - "parcel": "lib/bin.js" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/posthtml": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", - "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "posthtml-parser": "^0.11.0", - "posthtml-render": "^3.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/posthtml-parser": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", - "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/posthtml-render": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", - "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-json": "^2.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/posthtml/node_modules/posthtml-parser": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", - "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", - "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==", - "dev": true, - "license": "MIT" - }, - "node_modules/react-refresh": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", - "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true, - "license": "MIT" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz", - "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "deprecated": "no longer maintained" - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true, - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true, - "license": "0BSD" - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true, - "license": "MIT" - }, - "node_modules/weak-lru-cache": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", - "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/xxhash-wasm": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", - "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==", - "dev": true, - "license": "MIT" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.17.12", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", - "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", - "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", - "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@lezer/common": { - "version": "0.15.12", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", - "integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==", - "dev": true - }, - "@lezer/lr": { - "version": "0.15.8", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz", - "integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==", - "dev": true, - "requires": { - "@lezer/common": "^0.15.0" - } - }, - "@mischnic/json-sourcemap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz", - "integrity": "sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==", - "dev": true, - "requires": { - "@lezer/common": "^0.15.7", - "@lezer/lr": "^0.15.4", - "json5": "^2.2.1" - } - }, - "@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.0.2.tgz", - "integrity": "sha512-DznYtF3lHuZDSRaIOYeif4JgO0NtO2Xf8DsngAugMx/bUdTFbg86jDTmkVJBNmV+cxszz6OjGvinnS8AbJ342g==", - "dev": true, - "optional": true - }, - "@parcel/bundler-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.6.0.tgz", - "integrity": "sha512-AplEdGm/odV7yGmoeOnglxnY31WlNB5EqGLFGxkgs7uwDaTWoTX/9SWPG6xfvirhjDpms8sLSiVuBdFRCCLtNA==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/cache": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.6.0.tgz", - "integrity": "sha512-4vbD5uSuf+kRnrFesKhpn0AKnOw8u2UlvCyrplYmp1g9bNAkIooC/nDGdmkb/9SviPEbni9PEanQEHDU2+slpA==", - "dev": true, - "requires": { - "@parcel/fs": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/utils": "2.6.0", - "lmdb": "2.3.10" - } - }, - "@parcel/codeframe": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.6.0.tgz", - "integrity": "sha512-yXXxrO9yyedHKpTwC+Af0+vPmQm+A9xeEhkt4f0yVg1n4t4yUIxYlTedzbM8ygZEEBtkXU9jJ+PkgXbfMf0dqw==", - "dev": true, - "requires": { - "chalk": "^4.1.0" - } - }, - "@parcel/compressor-raw": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.6.0.tgz", - "integrity": "sha512-rtMU2mGl88bic6Xbq1u5L49bMK4s5185b0k7h3JRdS6/0rR+Xp4k/o9Wog+hHjK/s82z1eF9WmET779ZpIDIQQ==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0" - } - }, - "@parcel/config-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.6.0.tgz", - "integrity": "sha512-DXovFPhZITmTvFaSEdC8RRqROs9FLIJ4u8yFSU6FUyq2wpvtYVRXXoDrvXgClh2csXmK7JTJTp5JF7r0rd2UaA==", - "dev": true, - "requires": { - "@parcel/bundler-default": "2.6.0", - "@parcel/compressor-raw": "2.6.0", - "@parcel/namer-default": "2.6.0", - "@parcel/optimizer-css": "2.6.0", - "@parcel/optimizer-htmlnano": "2.6.0", - "@parcel/optimizer-image": "2.6.0", - "@parcel/optimizer-svgo": "2.6.0", - "@parcel/optimizer-terser": "2.6.0", - "@parcel/packager-css": "2.6.0", - "@parcel/packager-html": "2.6.0", - "@parcel/packager-js": "2.6.0", - "@parcel/packager-raw": "2.6.0", - "@parcel/packager-svg": "2.6.0", - "@parcel/reporter-dev-server": "2.6.0", - "@parcel/resolver-default": "2.6.0", - "@parcel/runtime-browser-hmr": "2.6.0", - "@parcel/runtime-js": "2.6.0", - "@parcel/runtime-react-refresh": "2.6.0", - "@parcel/runtime-service-worker": "2.6.0", - "@parcel/transformer-babel": "2.6.0", - "@parcel/transformer-css": "2.6.0", - "@parcel/transformer-html": "2.6.0", - "@parcel/transformer-image": "2.6.0", - "@parcel/transformer-js": "2.6.0", - "@parcel/transformer-json": "2.6.0", - "@parcel/transformer-postcss": "2.6.0", - "@parcel/transformer-posthtml": "2.6.0", - "@parcel/transformer-raw": "2.6.0", - "@parcel/transformer-react-refresh-wrap": "2.6.0", - "@parcel/transformer-svg": "2.6.0" - } - }, - "@parcel/core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.6.0.tgz", - "integrity": "sha512-8OOWbPuxpFydpwNyKoz6d3e3O4DmxNYmMw4DXwrPSj/jyg7oa+SDtMT0/VXEhujE0HYkQPCHt4npRajkSuf99A==", - "dev": true, - "requires": { - "@mischnic/json-sourcemap": "^0.1.0", - "@parcel/cache": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/events": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/graph": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/package-manager": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "abortcontroller-polyfill": "^1.1.9", - "base-x": "^3.0.8", - "browserslist": "^4.6.6", - "clone": "^2.1.1", - "dotenv": "^7.0.0", - "dotenv-expand": "^5.1.0", - "json5": "^2.2.0", - "msgpackr": "^1.5.4", - "nullthrows": "^1.1.1", - "semver": "^5.7.1" - } - }, - "@parcel/css": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@parcel/css/-/css-1.9.0.tgz", - "integrity": "sha512-egCetUQ1H6pgYxOIxVQ8X/YT5e8G0R8eq6aVaUHrqnZ7A8cc6FYgknl9XRmoy2Xxo9h1htrbzdaEShQ5gROwvw==", - "dev": true, - "requires": { - "@parcel/css-darwin-arm64": "1.9.0", - "@parcel/css-darwin-x64": "1.9.0", - "@parcel/css-linux-arm-gnueabihf": "1.9.0", - "@parcel/css-linux-arm64-gnu": "1.9.0", - "@parcel/css-linux-arm64-musl": "1.9.0", - "@parcel/css-linux-x64-gnu": "1.9.0", - "@parcel/css-linux-x64-musl": "1.9.0", - "@parcel/css-win32-x64-msvc": "1.9.0", - "detect-libc": "^1.0.3" - } - }, - "@parcel/css-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@parcel/css-darwin-x64/-/css-darwin-x64-1.9.0.tgz", - "integrity": "sha512-4SpuwiM/4ayOgKflqSLd87XT7YwyC3wd2QuzOOkasjbe38UU+tot/87l2lQYEB538YinLdfwFQuFLDY0x9MxgA==", - "dev": true, - "optional": true - }, - "@parcel/diagnostic": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.6.0.tgz", - "integrity": "sha512-+p8gC2FKxSI2veD7SoaNlP572v4kw+nafCQEPDtJuzYYRqywYUGncch25dkpgNApB4W4cXVkZu3ZbtIpCAmjQQ==", - "dev": true, - "requires": { - "@mischnic/json-sourcemap": "^0.1.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/events": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.6.0.tgz", - "integrity": "sha512-2WaKtBs4iYwS88j4zRdyTJTgh8iuY4E32FMmjzzbheqETs6I05gWuPReGukJYxk8vc0Ir7tbzp12oAfpgo0Y+g==", - "dev": true - }, - "@parcel/fs": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.6.0.tgz", - "integrity": "sha512-6vxtx5Zy6MvDvH1EPx9JxjKGF03bR7VE1dUf4HLeX2D8YmpL5hkHJnlRCFdcH08rzOVwaJLzg1QNtblWJXQ9CA==", - "dev": true, - "requires": { - "@parcel/fs-search": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/watcher": "^2.0.0", - "@parcel/workers": "2.6.0" - } - }, - "@parcel/fs-search": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.6.0.tgz", - "integrity": "sha512-1nXzM3H/cA4kzLKvDBvwmNisKCdRqlgkLXh+OR1Zu28Kn4W34KuJMcHWW8cC+WIuuKqDh5oo2WPsC5y65GXBKQ==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3" - } - }, - "@parcel/graph": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.6.0.tgz", - "integrity": "sha512-rxrAzWm6rwbCRPbu0Z+zwMscpG8omffODniVWPlX2G0jgQGpjKsutBQ6RMfFIcfaQ4MzL3pIQOTf8bkjQOPsbg==", - "dev": true, - "requires": { - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/hash": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.6.0.tgz", - "integrity": "sha512-YugWqhLxqK80Lo++3B3Kr5UPCHOdS8iI2zJ1jkzUeH9v6WUzbwWOnmPf6lN2S5m1BrIFFJd8Jc+CbEXWi8zoJA==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "xxhash-wasm": "^0.4.2" - } - }, - "@parcel/logger": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.6.0.tgz", - "integrity": "sha512-J1/7kPfSGBvMKSZdi0WCNuN0fIeiWxifnDGn7W/K8KhD422YwFJA8N046ps8nkDOPIXf1osnIECNp4GIR9oSYw==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/events": "2.6.0" - } - }, - "@parcel/markdown-ansi": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.6.0.tgz", - "integrity": "sha512-fyjkrJQQSfKTUFTTasdZ6WrAkDoQ2+DYDjj+3p+RncYyrIa9zArKx4IiRiipsvNdtMvP0/hTdK8F3BOJ3KSU/g==", - "dev": true, - "requires": { - "chalk": "^4.1.0" - } - }, - "@parcel/namer-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.6.0.tgz", - "integrity": "sha512-r8O12r7ozJBctnFxVdXbf/fK97GIdNj3hiiUNWlXEmED9sw6ZPcChaLcfot0/443g8i87JDmSTKJ8js2tuz5XA==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/node-resolver-core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.6.0.tgz", - "integrity": "sha512-AJDj5DZbB58plv0li8bdVSD+zpnkHE36Om3TYyNn1jgXXwgBM64Er/9p8yQn356jBqTQMh7zlJqvbdIyOiMeMg==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/optimizer-css": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.6.0.tgz", - "integrity": "sha512-VMJknUwfKCw6Woov0lnPGdsGZewcI4ghW8WKmNZzC5uKCetk1XetV55QHBc1RNjGfsjfSTZiSa3guATj2zFJkQ==", - "dev": true, - "requires": { - "@parcel/css": "^1.9.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "browserslist": "^4.6.6", - "nullthrows": "^1.1.1" - } - }, - "@parcel/optimizer-htmlnano": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.6.0.tgz", - "integrity": "sha512-HmvcUoYpfdx8ZfID4WOj/SE8N78NEBmzAffZ8f827mYMr4ZrbKzAgg6OG3tBbfF0zxH0bIjZcwqwZYk4SdbG7g==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "htmlnano": "^2.0.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "svgo": "^2.4.0" - } - }, - "@parcel/optimizer-image": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.6.0.tgz", - "integrity": "sha512-FDNr3LJ8SWR9rrtdCrZOlYF1hE9G5pxUWawGxUasbvqwcY5lEQwr2KRmfGZeg+KwOnzlImlY6dP2LGox1NFddQ==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "detect-libc": "^1.0.3" - } - }, - "@parcel/optimizer-svgo": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.6.0.tgz", - "integrity": "sha512-LMTDVMd7T/IfLG59yLWl8Uw2HYGbj2C3jIwkMqH9MBUT5KILK66T3t0yV86SoZJnxZ6xBIJ+kCcCRssCzhvanw==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "svgo": "^2.4.0" - } - }, - "@parcel/optimizer-terser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.6.0.tgz", - "integrity": "sha512-oezRt6Lz/QqcVDXyMfFjzQc7n0ThJowLJ4Lyhu8rMh0ZJYzc4UCFCw/19d4nRnzE+Qg0vj3mQCpdkA9/64E44g==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1", - "terser": "^5.2.0" - } - }, - "@parcel/package-manager": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.6.0.tgz", - "integrity": "sha512-AqFfdkbOw51q/3ia2mIsFTmrpYEyUb3k+2uYC5GsLMz3go6OGn7/Crz0lZLSclv5EtwpRg3TWr9yL7RekVN/Uw==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "semver": "^5.7.1" - } - }, - "@parcel/packager-css": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.6.0.tgz", - "integrity": "sha512-iXUttSe+wtnIM2PKCyFC2I4+Szv+8qHpC3wXeJlXlzd8wljm42y+6Fs4FZ0zihTccRxI2UUhFnKu90ag+5AmjA==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/packager-html": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.6.0.tgz", - "integrity": "sha512-HsiXMkU9AJr3LLjsP2Kteho2jCVpabTwcU/fauwbwirhg0xNlRsKxYZRCllRhPkb0FWAnkjzwjOj01MHD6NJCg==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5" - } - }, - "@parcel/packager-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.6.0.tgz", - "integrity": "sha512-Uz3pqIFchFfKszWnNGDgIwM1uwHHJp7Dts6VzS9lf/2RbRgZT0fmce+NPgnVO5MMKBHzdvm32ShT6gFAABF5Vw==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "globals": "^13.2.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/packager-raw": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.6.0.tgz", - "integrity": "sha512-ktT6Qc/GgCq8H1+6y+AXufVzQj1s6KRoKf83qswCD0iY3MwCbJoEfc3IsB4K64FpHIL5Eu0z54IId+INvGbOYA==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0" - } - }, - "@parcel/packager-svg": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.6.0.tgz", - "integrity": "sha512-OF2RShyspXu7H4Dn2PmchfMMYPx+kWjOXiYVQ6OkOI0MZmOydx7p8nrcG5+y7vCJTPlta828BSwva0GdKfn46A==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "posthtml": "^0.16.4" - } - }, - "@parcel/plugin": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.6.0.tgz", - "integrity": "sha512-LzOaiK8R6eFEoov1cb3/W+o0XvXdI/VbDhMDl0L0II+/56M0UeayYtFP5QGTDn/fZqVlYfzPCtt3EMwdG7/dow==", - "dev": true, - "requires": { - "@parcel/types": "2.6.0" - } - }, - "@parcel/reporter-cli": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.6.0.tgz", - "integrity": "sha512-QFG957NXx3L0D8Zw0+B2j7IHy8f/UzOVu6VvKE3rMkhq/iR2qLrPohQ+uvxlee+CLC0cG2qRSgJ7Ve/rjQPoJg==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "chalk": "^4.1.0", - "term-size": "^2.2.1" - } - }, - "@parcel/reporter-dev-server": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.6.0.tgz", - "integrity": "sha512-VvygsCA+uzWyijIV8zqU1gFyhAWknuaY4KIWhV4kCT8afRJwsLSwt/tpdaKDPuPU45h3tTsUdXH1wjaIk+dGeQ==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0" - } - }, - "@parcel/resolver-default": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.6.0.tgz", - "integrity": "sha512-ATk9wXvy5GOHAqyHbnCnU11fUPTtf8dLjpgVqL5XylwugZnyBXbynoTWX4w8h6mffkVtdfmzTJx/o4Lresz9sA==", - "dev": true, - "requires": { - "@parcel/node-resolver-core": "2.6.0", - "@parcel/plugin": "2.6.0" - } - }, - "@parcel/runtime-browser-hmr": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.6.0.tgz", - "integrity": "sha512-90xvv/10cFML5dAhClBEJZ/ExiBQVPqQsZcvRmVZmc5mpZVJMKattWCQrd7pAf7FDYl4JAcvsK3DTwvRT/oLNA==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0" - } - }, - "@parcel/runtime-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.6.0.tgz", - "integrity": "sha512-R4tJAIT/SX7VBQ+f7WmeekREQzzLsmgP1j486uKhQNyYrpvsN0HnRbg5aqvZjEjkEmSeJR0mOlWtMK5/m+0yTA==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/runtime-react-refresh": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.6.0.tgz", - "integrity": "sha512-2sRd13gc2EbMV/O5n2NPVGGhKBasb1fDTXGEY8y7qi9xDKc+ewok/D83T+w243FhCPS9Pf3ur5GkbPlrJGcenQ==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "react-error-overlay": "6.0.9", - "react-refresh": "^0.9.0" - } - }, - "@parcel/runtime-service-worker": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.6.0.tgz", - "integrity": "sha512-nVlknGw5J5Bkd1Wr1TbyWHhUd9CmVVebaRg/lpfVKYhAuE/2r+3N0+J8qbEIgtTRcHaSV7wTNpg4weSWq46VeA==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/source-map": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.0.5.tgz", - "integrity": "sha512-DRVlCFKLpqBSIbMxUoVlHgfiv12HTW/U7nnhzw52YgzDVXUX9OA41dXS1PU0pJ1si+D1k8msATUC+AoldN43mg==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3" - } - }, - "@parcel/transformer-babel": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.6.0.tgz", - "integrity": "sha512-qTDzhLoaTpRJoppCNqaAlcUYxcDEvJffem1h3SAQiwvCLUBQowLyeaBy8sUxu54AU6eHFJyBld5ZocENyHTBCA==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "browserslist": "^4.6.6", - "json5": "^2.2.0", - "nullthrows": "^1.1.1", - "semver": "^5.7.0" - } - }, - "@parcel/transformer-css": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.6.0.tgz", - "integrity": "sha512-Ei9NPE5Rl9V+MGd8qddfZD0Fsqbvky8J62RwYsqLkptFl9FkhgwOu8Cmokz7IIc4GJ2qzfnG5y54K/Bi7Moq4Q==", - "dev": true, - "requires": { - "@parcel/css": "^1.9.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "browserslist": "^4.6.6", - "nullthrows": "^1.1.1" - } - }, - "@parcel/transformer-html": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.6.0.tgz", - "integrity": "sha512-YQh5WzNFjPhgV09P+zVS++albTCTvbPYAJXp5zUJ4HavzcpV2IB3HAPRk9x+iXUeRBQYYiO5SMMRkdy9a4CzQQ==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - } - }, - "@parcel/transformer-image": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.6.0.tgz", - "integrity": "sha512-Zkh1i6nWNOTOReKlZD+bLJCHA16dPLO6Or7ETAHtSF3iRzMNFcVFp+851Awj3l4zeJ6CoCWlyxsR4CEdioRgiQ==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/workers": "2.6.0", - "nullthrows": "^1.1.1" - } - }, - "@parcel/transformer-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.6.0.tgz", - "integrity": "sha512-4v2r3EVdMKowBziVBW9HZqvAv88HaeiezkWyMX4wAfplo9jBtWEp99KEQINzSEdbXROR81M9oJjlGF5+yoVr/w==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/utils": "2.6.0", - "@parcel/workers": "2.6.0", - "@swc/helpers": "^0.3.15", - "browserslist": "^4.6.6", - "detect-libc": "^1.0.3", - "nullthrows": "^1.1.1", - "regenerator-runtime": "^0.13.7", - "semver": "^5.7.1" - } - }, - "@parcel/transformer-json": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.6.0.tgz", - "integrity": "sha512-zb+TQAdHWdXijKcFhLe+5KN1O0IzXwW1gJhPr8DJEA3qhPaCsncsw5RCVjQlP3a7NXr1mMm1eMtO6bhIMqbXeA==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "json5": "^2.2.0" - } - }, - "@parcel/transformer-postcss": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.6.0.tgz", - "integrity": "sha512-czmh2mOPJLwYbtnPTFlxKYcaQHH6huIlpfNX1XgdsaEYS+yFs8ZXpzqjxI1wu6rMW0R0q5aon72yB3PJewvqNQ==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "clone": "^2.1.1", - "nullthrows": "^1.1.1", - "postcss-value-parser": "^4.2.0", - "semver": "^5.7.1" - } - }, - "@parcel/transformer-posthtml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.6.0.tgz", - "integrity": "sha512-R1FmPMZ0pgrbPZkDppa2pE+6KDK3Wxof6uQo7juHLB2ELGOTaYofsG3nrRdk+chyAHaVv4qWLqXbfZK6pGepEg==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - } - }, - "@parcel/transformer-raw": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.6.0.tgz", - "integrity": "sha512-QDirlWCS/qy0DQ3WvDIAnFP52n1TJW/uWH+4PGMNnX4/M3/2UchY8xp9CN0tx4NQ4g09S8o3gLlHvNxQqZxFrQ==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0" - } - }, - "@parcel/transformer-react-refresh-wrap": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.6.0.tgz", - "integrity": "sha512-G34orfvLDUTumuerqNmA8T8NUHk+R0jwUjbVPO7gpB6VCVQ5ocTABdE9vN9Uu/cUsHij40TUFwqK4R9TFEBIEQ==", - "dev": true, - "requires": { - "@parcel/plugin": "2.6.0", - "@parcel/utils": "2.6.0", - "react-refresh": "^0.9.0" - } - }, - "@parcel/transformer-svg": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.6.0.tgz", - "integrity": "sha512-e7yrb7775A7tEGRsAHQSMhXe+u4yisH5W0PuIzAQQy/a2IwBjaSxNnvyelN7tNX0FYq0BK6An5wRbhK4YmM+xw==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/plugin": "2.6.0", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - } - }, - "@parcel/ts-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/ts-utils/-/ts-utils-2.6.0.tgz", - "integrity": "sha512-U2Spr/vdOnxLzztXP6WpMO7JZTsaYO1G6F/cUTG5fReTQ0imM952FAc/WswpZWAPZqXqWCnvC/Z91JIkMDuYrA==", - "dev": true, - "requires": { - "nullthrows": "^1.1.1" - } - }, - "@parcel/types": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.6.0.tgz", - "integrity": "sha512-lAMYvOBfNEJMsPJ+plbB50305o0TwNrY1xX5RRIWBqwOa6bYmbW1ZljUk1tQvnkpIE4eAHQwnPR5Z2XWg18wGQ==", - "dev": true, - "requires": { - "@parcel/cache": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/package-manager": "2.6.0", - "@parcel/source-map": "^2.0.0", - "@parcel/workers": "2.6.0", - "utility-types": "^3.10.0" - } - }, - "@parcel/utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.6.0.tgz", - "integrity": "sha512-ElXz+QHtT1JQIucbQJBk7SzAGoOlBp4yodEQVvTKS7GA+hEGrSP/cmibl6qm29Rjtd0zgQsdd+2XmP3xvP2gQQ==", - "dev": true, - "requires": { - "@parcel/codeframe": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/hash": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/markdown-ansi": "2.6.0", - "@parcel/source-map": "^2.0.0", - "chalk": "^4.1.0" - } - }, - "@parcel/validator-typescript": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/validator-typescript/-/validator-typescript-2.6.0.tgz", - "integrity": "sha512-NMroc+QPoTo436COHsqEQsn+Qd+7HE1s1X6he1Bqb+RMB4rZsvOZI22MgFj1eU5MpfYuM4zTID0Uz221hiS59w==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/plugin": "2.6.0", - "@parcel/ts-utils": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0" - } - }, - "@parcel/watcher": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.5.tgz", - "integrity": "sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw==", - "dev": true, - "requires": { - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" - } - }, - "@parcel/workers": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.6.0.tgz", - "integrity": "sha512-3tcI2LF5fd/WZtSnSjyWdDE+G+FitdNrRgSObzSp+axHKMAM23sO0z7KY8s2SYCF40msdYbFUW8eI6JlYNJoWQ==", - "dev": true, - "requires": { - "@parcel/diagnostic": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/types": "2.6.0", - "@parcel/utils": "2.6.0", - "chrome-trace-event": "^1.0.2", - "nullthrows": "^1.1.1" - } - }, - "@swc/helpers": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", - "integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==", - "dev": true, - "requires": { - "tslib": "^2.4.0" - } - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "abortcontroller-polyfill": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", - "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==", - "dev": true - }, - "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "browserslist": { - "version": "4.20.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz", - "integrity": "sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001349", - "electron-to-chromium": "^1.4.147", - "escalade": "^3.1.1", - "node-releases": "^2.0.5", - "picocolors": "^1.0.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001352", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz", - "integrity": "sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dev": true, - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true - }, - "csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dev": true, - "requires": { - "css-tree": "^1.1.2" - } - }, - "datastream-js": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/datastream-js/-/datastream-js-1.0.7.tgz", - "integrity": "sha512-rW7N3QkEx01reZ6/BF1s3sGnh1JdFpektwSqgUz8bmmvfmD+NNGTPhbTePZjs0B3VSizFX26Kr8qNtNJDz0NAQ==", - "requires": { - "text-encoding": "^0.6.4" - } - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true - }, - "dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dotenv": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", - "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", - "dev": true - }, - "dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.150", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz", - "integrity": "sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA==", - "dev": true - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "get-port": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", - "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", - "dev": true - }, - "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "htmlnano": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.2.tgz", - "integrity": "sha512-+ZrQFS4Ub+zd+/fWwfvoYCEGNEa0/zrpys6CyXxvZDwtL7Pl+pOtRkiujyvBQ7Lmfp7/iEPxtOFgxWA16Gkj3w==", - "dev": true, - "requires": { - "cosmiconfig": "^7.0.1", - "posthtml": "^0.16.5", - "timsort": "^0.3.0" - } - }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-json": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", - "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "lmdb": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.3.10.tgz", - "integrity": "sha512-GtH+nStn9V59CfYeQ5ddx6YTfuFCmu86UJojIjJAweG+/Fm0PDknuk3ovgYDtY/foMeMdZa8/P7oSljW/d5UPw==", - "dev": true, - "requires": { - "lmdb-darwin-arm64": "2.3.10", - "lmdb-darwin-x64": "2.3.10", - "lmdb-linux-arm": "2.3.10", - "lmdb-linux-arm64": "2.3.10", - "lmdb-linux-x64": "2.3.10", - "lmdb-win32-x64": "2.3.10", - "msgpackr": "^1.5.4", - "nan": "^2.14.2", - "node-addon-api": "^4.3.0", - "node-gyp-build-optional-packages": "^4.3.2", - "ordered-binary": "^1.2.4", - "weak-lru-cache": "^1.2.2" - }, - "dependencies": { - "node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "dev": true - }, - "node-gyp-build-optional-packages": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-4.3.5.tgz", - "integrity": "sha512-5ke7D8SiQsTQL7CkHpfR1tLwfqtKc0KYEmlnkwd40jHCASskZeS98qoZ1qDUns2aUQWikcjidRUs6PM/3iyN/w==", - "dev": true - } - } - }, - "lmdb-darwin-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/lmdb-darwin-x64/-/lmdb-darwin-x64-2.3.10.tgz", - "integrity": "sha512-gAc/1b/FZOb9yVOT+o0huA+hdW82oxLo5r22dFTLoRUFG1JMzxdTjmnW6ONVOHdqC9a5bt3vBCEY3jmXNqV26A==", - "dev": true, - "optional": true - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "dev": true - }, - "msgpackr": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.6.1.tgz", - "integrity": "sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA==", - "dev": true, - "requires": { - "msgpackr-extract": "^2.0.2" - } - }, - "msgpackr-extract": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-2.0.2.tgz", - "integrity": "sha512-coskCeJG2KDny23zWeu+6tNy7BLnAiOGgiwzlgdm4oeSsTpqEJJPguHIuKZcCdB7tzhZbXNYSg6jZAXkZErkJA==", - "dev": true, - "optional": true, - "requires": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm": "2.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-linux-x64": "2.0.2", - "@msgpackr-extract/msgpackr-extract-win32-x64": "2.0.2", - "node-gyp-build-optional-packages": "5.0.2" - } - }, - "nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", - "dev": true - }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true - }, - "node-gyp-build": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", - "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", - "dev": true - }, - "node-gyp-build-optional-packages": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.2.tgz", - "integrity": "sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g==", - "dev": true, - "optional": true - }, - "node-releases": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", - "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", - "dev": true - }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "dev": true - }, - "ordered-binary": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.2.5.tgz", - "integrity": "sha512-djRmZoEpOGvIRW7ufsCDHtvcUa18UC9TxnPbHhSVFZHsoyg0dtut1bWtBZ/fmxdPN62oWXrV6adM7NoWU+CneA==", - "dev": true - }, - "parcel": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.6.0.tgz", - "integrity": "sha512-pSTJ7wC6uTl16PKLXQV7RfL9FGoIDA1iVpNvaav47n6UkUdKqfx0spcVPpw35kWdRcHJF61YAvkPjP2hTwHQ+Q==", - "dev": true, - "requires": { - "@parcel/config-default": "2.6.0", - "@parcel/core": "2.6.0", - "@parcel/diagnostic": "2.6.0", - "@parcel/events": "2.6.0", - "@parcel/fs": "2.6.0", - "@parcel/logger": "2.6.0", - "@parcel/package-manager": "2.6.0", - "@parcel/reporter-cli": "2.6.0", - "@parcel/reporter-dev-server": "2.6.0", - "@parcel/utils": "2.6.0", - "chalk": "^4.1.0", - "commander": "^7.0.0", - "get-port": "^4.2.0", - "v8-compile-cache": "^2.0.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "posthtml": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", - "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", - "dev": true, - "requires": { - "posthtml-parser": "^0.11.0", - "posthtml-render": "^3.0.0" - }, - "dependencies": { - "posthtml-parser": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", - "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", - "dev": true, - "requires": { - "htmlparser2": "^7.1.1" - } - } - } - }, - "posthtml-parser": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", - "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", - "dev": true, - "requires": { - "htmlparser2": "^7.1.1" - } - }, - "posthtml-render": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", - "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", - "dev": true, - "requires": { - "is-json": "^2.0.1" - } - }, - "react-error-overlay": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", - "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==", - "dev": true - }, - "react-refresh": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", - "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "dev": true, - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - } - }, - "term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true - }, - "terser": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz", - "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } - } - }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=" - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", - "dev": true - }, - "utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "weak-lru-cache": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", - "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", - "dev": true - }, - "xxhash-wasm": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", - "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==", - "dev": true - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - } - } -} diff --git a/player/package.json b/player/package.json index 56ac7ae..5ebb925 100644 --- a/player/package.json +++ b/player/package.json @@ -7,7 +7,8 @@ }, "devDependencies": { "@parcel/validator-typescript": "^2.6.0", - "parcel": "^2.6.0", - "typescript": ">=3.0.0" + "parcel": "^2.8.0", + "typescript": ">=3.0.0", + "@types/dom-webcodecs": "^0.1.6" } } diff --git a/player/src/audio/decoder.ts b/player/src/audio/decoder.ts new file mode 100644 index 0000000..c1b813f --- /dev/null +++ b/player/src/audio/decoder.ts @@ -0,0 +1,120 @@ +import * as Message from "./message"; +import { InitParser } from "../mp4/init"; +import { Renderer } from "./renderer" +import { MP4New, MP4Sample, MP4ArrayBuffer } from "../mp4/index" + +export class Decoder { + tracks: Map; + renderer: Renderer; + + constructor(renderer: Renderer) { + this.tracks = new Map(); + this.renderer = renderer; + } + + async init(msg: Message.Init) { + let track = this.tracks.get(msg.track); + if (!track) { + track = new InitParser() + this.tracks.set(msg.track, track) + } + + while (1) { + const data = await msg.stream.read() + if (!data) break + + track.init(data) + } + + // TODO this will hang on incomplete data + const init = await track.ready; + const info = init.info; + + if (info.audioTracks.length != 1 || info.videoTracks.length != 0) { + throw new Error("expected a single audio track") + } + } + + async decode(msg: Message.Segment) { + let track = this.tracks.get(msg.track); + if (!track) { + track = new InitParser() + this.tracks.set(msg.track, track) + } + + // Wait for the init segment to be fully received and parsed + const init = await track.ready; + const info = init.info; + const video = info.videoTracks[0] + + const decoder = new AudioDecoder({ + output: (frame: AudioFrame) => { + this.renderer.push(frame) + }, + error: (err: Error) => { + console.warn(err) + } + }); + + decoder.configure({ + codec: info.mime, + // TODO what else? + // optimizeForLatency: true + }) + + const input = MP4New(); + + input.onSamples = (id: number, user: any, samples: MP4Sample[]) => { + for (let sample of samples) { + const timestamp = sample.dts / (1000 / info.timescale) // milliseconds + + decoder.decode(new EncodedAudioChunk({ + data: sample.data, + duration: sample.duration, + timestamp: timestamp, + })) + } + } + + input.onReady = (info: any) => { + input.setExtractionOptions(info.tracks[0].id, {}, { nbSamples: 1 }); + input.start(); + } + + let offset = 0 + + // MP4box requires us to reparse the init segment unfortunately + for (let raw of track.raw) { + offset = input.appendBuffer(raw) + } + + /* TODO I'm not actually sure why this code doesn't work; something trips up the MP4 parser + while (1) { + const data = await stream.read() + if (!data) break + + input.appendBuffer(data) + input.flush() + } + */ + + // One day I'll figure it out; until then read one top-level atom at a time + while (!await msg.stream.done()) { + const raw = await msg.stream.peek(4) + const size = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint32(0) + const atom = await msg.stream.bytes(size) + + // Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately + let box = new Uint8Array(atom.byteLength); + box.set(atom) + + // and for some reason we need to modify the underlying ArrayBuffer with offset + let buffer = box.buffer as MP4ArrayBuffer + buffer.fileStart = offset + + // Parse the data + offset = input.appendBuffer(buffer) + input.flush() + } + } +} \ No newline at end of file diff --git a/player/src/audio/index.ts b/player/src/audio/index.ts index ebbc720..517a3f4 100644 --- a/player/src/audio/index.ts +++ b/player/src/audio/index.ts @@ -1,3 +1,19 @@ -self.addEventListener('message', (e: Event) => { +import * as Message from "./message" -}) \ No newline at end of file +// Wrapper around the WebWorker API +export default class Audio { + worker: Worker; + + constructor(config: Message.Config) { + this.worker = new Worker(new URL('worker.ts', import.meta.url), { type: "module" }) + this.worker.postMessage({ config }, [ ]) + } + + init(init: Message.Init) { + this.worker.postMessage({ init }, [ init.stream.buffer, init.stream.reader ]) + } + + segment(segment: Message.Segment) { + this.worker.postMessage({ segment }, [ segment.stream.buffer, segment.stream.reader ]) + } +} \ No newline at end of file diff --git a/player/src/audio/message.ts b/player/src/audio/message.ts new file mode 100644 index 0000000..9845708 --- /dev/null +++ b/player/src/audio/message.ts @@ -0,0 +1,15 @@ +import Reader from "../stream/reader"; + +export interface Config { + canvas: OffscreenCanvas; +} + +export interface Init { + track: string; + stream: Reader; +} + +export interface Segment { + track: string; + stream: Reader; +} \ No newline at end of file diff --git a/player/src/audio/renderer.ts b/player/src/audio/renderer.ts new file mode 100644 index 0000000..774e11b --- /dev/null +++ b/player/src/audio/renderer.ts @@ -0,0 +1,12 @@ +import * as Message from "./message"; + +export class Renderer { + render: number; // non-zero if requestAnimationFrame has been called + sync: DOMHighResTimeStamp; // the wall clock value for timestamp 0 + last?: number; // the timestamp of the last rendered frame + + constructor(config: Message.Config) { + this.render = 0; + this.sync = 0; + } +} \ No newline at end of file diff --git a/player/src/audio/worker.ts b/player/src/audio/worker.ts new file mode 100644 index 0000000..dec1c3f --- /dev/null +++ b/player/src/audio/worker.ts @@ -0,0 +1,21 @@ +import { Renderer } from "./renderer" +import { Decoder } from "./decoder" +import * as Message from "./message" + +let decoder: Decoder; +let renderer: Renderer; + +self.addEventListener('message', async (e: MessageEvent) => { + if (e.data.config) { + const config = e.data.config as Message.Config + + renderer = new Renderer(config) + decoder = new Decoder(renderer) + } + + if (e.data.segment) { + const segment = e.data.segment as Message.Segment + + await decoder.decode(segment) + } +}) diff --git a/player/src/mp4/mp4.ts b/player/src/mp4/index.ts similarity index 69% rename from player/src/mp4/mp4.ts rename to player/src/mp4/index.ts index fdf3841..7868f32 100644 --- a/player/src/mp4/mp4.ts +++ b/player/src/mp4/index.ts @@ -1,13 +1,13 @@ // Wrapper around MP4Box to play nicely with MP4Box. // I tried getting a mp4box.all.d.ts file to work but just couldn't figure it out -import { createFile, ISOFile, DataStream, BoxParser } from "./mp4box.all.js" +import { createFile, ISOFile, DataStream, BoxParser } from "./mp4box.all" // Rename some stuff so it's on brand. -export { createFile as MP4New, ISOFile as MP4File, DataStream as MP4Stream, BoxParser as MP4Parser } +export { createFile as New, ISOFile as File, DataStream as Stream, BoxParser as Parser } -export type MP4ArrayBuffer = ArrayBuffer & {fileStart: number}; +export type ArrayBufferOffset = ArrayBuffer & {fileStart: number}; -export interface MP4MediaTrack { +export interface MediaTrack { id: number; created: Date; modified: Date; @@ -25,13 +25,13 @@ export interface MP4MediaTrack { nb_samples: number; } -export interface MP4VideoData { +export interface VideoData { width: number; height: number; } -export interface MP4VideoTrack extends MP4MediaTrack { - video: MP4VideoData; +export interface VideoTrack extends MediaTrack { + video: VideoData; } export interface MP4AudioData { @@ -40,13 +40,13 @@ export interface MP4AudioData { sample_size: number; } -export interface MP4AudioTrack extends MP4MediaTrack { +export interface AudioTrack extends MediaTrack { audio: MP4AudioData; } -export type MP4Track = MP4VideoTrack | MP4AudioTrack; +export type Track = VideoTrack | AudioTrack; -export interface MP4Info { +export interface Info { duration: number; timescale: number; fragment_duration: number; @@ -56,13 +56,13 @@ export interface MP4Info { brands: string[]; created: Date; modified: Date; - tracks: MP4Track[]; + tracks: Track[]; mime: string; - videoTracks: MP4Track[]; - audioTracks: MP4Track[]; + videoTracks: Track[]; + audioTracks: Track[]; } -export interface MP4Sample { +export interface Sample { number: number; track_id: number; timescale: number; @@ -83,3 +83,5 @@ export interface MP4Sample { offset: number; subsamples: any; } + +export { Init, InitParser } from "./init" \ No newline at end of file diff --git a/player/src/player/init.ts b/player/src/mp4/init.ts similarity index 69% rename from player/src/player/init.ts rename to player/src/mp4/init.ts index 11594fa..0c94c7b 100644 --- a/player/src/player/init.ts +++ b/player/src/mp4/init.ts @@ -1,24 +1,28 @@ -import { MP4New, MP4File, MP4ArrayBuffer, MP4Info } from "../mp4/mp4" +import * as MP4 from "./index" + +export interface Init { + raw: MP4.ArrayBufferOffset; + info: MP4.Info; +} export class InitParser { - mp4box: MP4File; + mp4box: MP4.File; offset: number; - raw: MP4ArrayBuffer[]; - ready: Promise; + raw: MP4.ArrayBufferOffset[]; + info: Promise; constructor() { - this.mp4box = MP4New() - + this.mp4box = MP4.New() this.raw = [] this.offset = 0 // Create a promise that gets resolved once the init segment has been parsed. - this.ready = new Promise((resolve, reject) => { + this.info = new Promise((resolve, reject) => { this.mp4box.onError = reject // https://github.com/gpac/mp4box.js#onreadyinfo - this.mp4box.onReady = (info: MP4Info) => { + this.mp4box.onReady = (info: MP4.Info) => { if (!info.isFragmented) { reject("expected a fragmented mp4") } @@ -27,10 +31,7 @@ export class InitParser { reject("expected a single track") } - resolve({ - info: info, - raw: this.raw, - }) + resolve(info) } }) } @@ -41,7 +42,7 @@ export class InitParser { box.set(data) // and for some reason we need to modify the underlying ArrayBuffer with fileStart - let buffer = box.buffer as MP4ArrayBuffer + let buffer = box.buffer as MP4.ArrayBufferOffset buffer.fileStart = this.offset // Parse the data @@ -51,9 +52,4 @@ export class InitParser { // Add the box to our queue of chunks this.raw.push(buffer) } -} - -export interface Init { - raw: MP4ArrayBuffer[]; - info: MP4Info; -} +} \ No newline at end of file diff --git a/player/src/stream/index.ts b/player/src/stream/index.ts new file mode 100644 index 0000000..fcd9226 --- /dev/null +++ b/player/src/stream/index.ts @@ -0,0 +1,2 @@ +export { default as Reader } from "./reader" +export { default as Writer } from "./writer" \ No newline at end of file diff --git a/player/src/stream/reader.ts b/player/src/stream/reader.ts index edab7d0..c1b1e48 100644 --- a/player/src/stream/reader.ts +++ b/player/src/stream/reader.ts @@ -10,19 +10,26 @@ export default class Reader { // Returns any number of bytes async read(): Promise { + if (this.buffer.byteLength) { const buffer = this.buffer; this.buffer = new Uint8Array() return buffer } - const result = await this.reader.getReader().read() + const r = this.reader.getReader() + const result = await r.read() + + r.releaseLock() + return result.value } async readAll(): Promise { + const r = this.reader.getReader() + while (1) { - const result = await this.reader.getReader().read() + const result = await r.read() if (result.done) { break } @@ -42,12 +49,16 @@ export default class Reader { const result = this.buffer this.buffer = new Uint8Array() + r.releaseLock() + return result } async bytes(size: number): Promise { + const r = this.reader.getReader() + while (this.buffer.byteLength < size) { - const result = await this.reader.getReader().read() + const result = await r.read() if (result.done) { throw "short buffer" } @@ -67,12 +78,16 @@ export default class Reader { const result = new Uint8Array(this.buffer.buffer, this.buffer.byteOffset, size) this.buffer = new Uint8Array(this.buffer.buffer, this.buffer.byteOffset + size) + r.releaseLock() + return result } async peek(size: number): Promise { + const r = this.reader.getReader() + while (this.buffer.byteLength < size) { - const result = await this.reader.getReader().read() + const result = await r.read() if (result.done) { throw "short buffer" } @@ -89,7 +104,11 @@ export default class Reader { } } - return new Uint8Array(this.buffer.buffer, this.buffer.byteOffset, size) + const result = new Uint8Array(this.buffer.buffer, this.buffer.byteOffset, size) + + r.releaseLock() + + return result } async view(size: number): Promise { diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index 142cafb..e42da4e 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -1,6 +1,7 @@ import * as Message from "./message" -import Reader from "../stream/reader" -import Writer from "../stream/writer" +import * as Stream from "../stream" +import * as MP4 from "../mp4" + import Video from "../video/index" /// @@ -13,11 +14,14 @@ export interface PlayerInit { export class Player { quic: Promise; api: Promise; + tracks: Map //audio: Worker; video: Video; constructor(props: PlayerInit) { + this.tracks = new Map(); + //this.audio = new Worker("../audio") this.video = new Video({ canvas: props.canvas.transferControlToOffscreen(), @@ -76,7 +80,7 @@ export class Player { const stream = await this.api - const writer = new Writer(stream) + const writer = new Stream.Writer(stream) await writer.uint32(size) await writer.string("warp") await writer.string(payload) @@ -97,7 +101,7 @@ export class Player { } async handleStream(stream: ReadableStream) { - let r = new Reader(stream.getReader()) + let r = new Stream.Reader(stream) while (!await r.done()) { const size = await r.uint32(); @@ -117,19 +121,52 @@ export class Player { } } - async handleInit(stream: Reader, msg: Message.Init) { - // TODO properly determine if audio or video - this.video.init({ - track: msg.id, - stream: stream, - }) + async handleInit(stream: Stream.Reader, msg: Message.Init) { + let track = this.tracks.get(msg.id); + if (!track) { + track = new MP4.InitParser() + this.tracks.set(msg.id, track) + } + + while (1) { + const data = await stream.read() + if (!data) break + + track.push(data) + } + + const info = await track.info + + if (info.audioTracks.length + info.videoTracks.length != 1) { + throw new Error("expected a single track") + } + + if (info.videoTracks.length) { + this.video.init({ + track: msg.id, + info: info, + raw: track.raw, + }) + } } - async handleSegment(stream: Reader, msg: Message.Segment) { - // TODO properly determine if audio or video - this.video.segment({ - track: msg.init, - stream: stream, - }) + async handleSegment(stream: Stream.Reader, msg: Message.Segment) { + let track = this.tracks.get(msg.init); + if (!track) { + track = new MP4.InitParser() + this.tracks.set(msg.init, track) + } + + const info = await track.info + + // Wait until we learn if this is an audio or video track + + if (info.videoTracks.length) { + this.video.segment({ + track: msg.init, + buffer: stream.buffer, + reader: stream.reader, + }) + } } } \ No newline at end of file diff --git a/player/src/util/deferred.ts b/player/src/util/deferred.ts new file mode 100644 index 0000000..eee54f8 --- /dev/null +++ b/player/src/util/deferred.ts @@ -0,0 +1,16 @@ +export default class Deferred { + promise: Promise + resolve: (value: T | PromiseLike) => void + reject: (value: T | PromiseLike) => void + + constructor() { + // Set initial values so TS stops being annoying. + this.resolve = (value: T | PromiseLike) => {}; + this.reject = (value: T | PromiseLike) => {}; + + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve + this.reject = reject + }) + } + } \ No newline at end of file diff --git a/player/src/util/index.ts b/player/src/util/index.ts new file mode 100644 index 0000000..adcfc49 --- /dev/null +++ b/player/src/util/index.ts @@ -0,0 +1 @@ +export { default as Deferred } from "./deferred" diff --git a/player/src/video/decoder.ts b/player/src/video/decoder.ts index 4b8839a..33b77b8 100644 --- a/player/src/video/decoder.ts +++ b/player/src/video/decoder.ts @@ -1,10 +1,13 @@ import * as Message from "./message"; -import { Track } from "./track"; +import * as MP4 from "../mp4" +import * as Stream from "../stream" +import * as Util from "../util" + import { Renderer } from "./renderer" -import { MP4New, MP4Sample, MP4ArrayBuffer } from "../mp4/mp4" export class Decoder { - tracks: Map; + // Store the init message for each track + tracks: Map> renderer: Renderer; constructor(renderer: Renderer) { @@ -13,37 +16,24 @@ export class Decoder { } async init(msg: Message.Init) { - let track = this.tracks.get(msg.track); - if (!track) { - track = new Track() - this.tracks.set(msg.track, track) - } + let track = this.tracks.get(msg.track); + if (!track) { + track = new Util.Deferred() + this.tracks.set(msg.track, track) + } - while (1) { - const data = await msg.stream.read() - if (!data) break - - track.init(data) - } - - // TODO this will hang on incomplete data - const init = await track.ready; - const info = init.info; - - if (info.audioTracks.length + info.videoTracks.length != 1) { - throw new Error("expected a single track") - } + track.resolve(msg) } async decode(msg: Message.Segment) { let track = this.tracks.get(msg.track); if (!track) { - track = new Track() + track = new Util.Deferred() this.tracks.set(msg.track, track) } // Wait for the init segment to be fully received and parsed - const init = await track.ready; + const init = await track.promise; const info = init.info; const video = info.videoTracks[0] @@ -57,17 +47,18 @@ export class Decoder { }); decoder.configure({ - codec: info.mime, + codec: video.codec, codedHeight: video.track_height, codedWidth: video.track_width, // optimizeForLatency: true }) - const input = MP4New(); + const input = MP4.New(); - input.onSamples = (id: number, user: any, samples: MP4Sample[]) => { + input.onSamples = (id: number, user: any, samples: MP4.Sample[]) => { for (let sample of samples) { const timestamp = sample.dts / (1000 / info.timescale) // milliseconds + console.log(sample) decoder.decode(new EncodedVideoChunk({ data: sample.data, @@ -83,13 +74,16 @@ export class Decoder { input.start(); } - let offset = 0 - // MP4box requires us to reparse the init segment unfortunately - for (let raw of track.raw) { - offset = input.appendBuffer(raw) + let offset = 0; + + for (let raw of init.raw) { + raw.fileStart = offset + input.appendBuffer(raw) } + const stream = new Stream.Reader(msg.reader, msg.buffer) + /* TODO I'm not actually sure why this code doesn't work; something trips up the MP4 parser while (1) { const data = await stream.read() @@ -101,23 +95,22 @@ export class Decoder { */ // One day I'll figure it out; until then read one top-level atom at a time - while (!await msg.stream.done()) { - const raw = await msg.stream.peek(4) + while (!await stream.done()) { + const raw = await stream.peek(4) const size = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint32(0) - const atom = await msg.stream.bytes(size) + const atom = await stream.bytes(size) // Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately let box = new Uint8Array(atom.byteLength); box.set(atom) // and for some reason we need to modify the underlying ArrayBuffer with offset - let buffer = box.buffer as MP4ArrayBuffer + let buffer = box.buffer as MP4.ArrayBufferOffset buffer.fileStart = offset // Parse the data offset = input.appendBuffer(buffer) input.flush() } - } } \ No newline at end of file diff --git a/player/src/video/index.ts b/player/src/video/index.ts index 8ea58f4..d314ccd 100644 --- a/player/src/video/index.ts +++ b/player/src/video/index.ts @@ -10,10 +10,10 @@ export default class Video { } init(init: Message.Init) { - this.worker.postMessage({ init }, [ init.stream.buffer, init.stream.reader ]) + this.worker.postMessage({ init }) // note: we copy the raw init bytes each time } segment(segment: Message.Segment) { - this.worker.postMessage({ segment }, [ segment.stream.buffer, segment.stream.reader ]) + this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ]) } } \ No newline at end of file diff --git a/player/src/video/message.ts b/player/src/video/message.ts index 9845708..c9f7a01 100644 --- a/player/src/video/message.ts +++ b/player/src/video/message.ts @@ -1,4 +1,4 @@ -import Reader from "../stream/reader"; +import * as MP4 from "../mp4" export interface Config { canvas: OffscreenCanvas; @@ -6,10 +6,12 @@ export interface Config { export interface Init { track: string; - stream: Reader; + info: MP4.Info; + raw: MP4.ArrayBufferOffset[]; } export interface Segment { track: string; - stream: Reader; + buffer: Uint8Array; // unread buffered data + reader: ReadableStream; // unread unbuffered data } \ No newline at end of file diff --git a/player/src/video/track.ts b/player/src/video/track.ts deleted file mode 100644 index 60aaec0..0000000 --- a/player/src/video/track.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MP4New, MP4File, MP4ArrayBuffer, MP4Info } from "../mp4/mp4" - -export interface Init { - raw: MP4ArrayBuffer[]; - info: MP4Info; -} - -export class Track { - mp4box: MP4File; - offset: number; - - raw: MP4ArrayBuffer[]; - ready: Promise; - - constructor() { - this.mp4box = MP4New() - this.raw = [] - this.offset = 0 - - // Create a promise that gets resolved once the init segment has been parsed. - this.ready = new Promise((resolve, reject) => { - this.mp4box.onError = reject - - // https://github.com/gpac/mp4box.js#onreadyinfo - this.mp4box.onReady = (info: MP4Info) => { - if (!info.isFragmented) { - reject("expected a fragmented mp4") - } - - if (info.tracks.length != 1) { - reject("expected a single track") - } - - resolve({ - raw: this.raw, - info, - }) - } - }) - } - - init(data: Uint8Array) { - // Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately - let box = new Uint8Array(data.byteLength); - box.set(data) - - // and for some reason we need to modify the underlying ArrayBuffer with fileStart - let buffer = box.buffer as MP4ArrayBuffer - buffer.fileStart = this.offset - - // Parse the data - this.offset = this.mp4box.appendBuffer(buffer) - this.mp4box.flush() - - // Add the box to our queue of chunks - this.raw.push(buffer) - } -} \ No newline at end of file diff --git a/player/src/video/worker.ts b/player/src/video/worker.ts index b8ede50..2ee48e6 100644 --- a/player/src/video/worker.ts +++ b/player/src/video/worker.ts @@ -11,11 +11,11 @@ self.addEventListener('message', async (e: MessageEvent) => { renderer = new Renderer(config) decoder = new Decoder(renderer) - } - - if (e.data.segment) { + } else if (e.data.init) { + const init = e.data.init as Message.Init + await decoder.init(init) + } else if (e.data.segment) { const segment = e.data.segment as Message.Segment - await decoder.decode(segment) } }) diff --git a/player/tsconfig.json b/player/tsconfig.json index d778306..5fce1da 100644 --- a/player/tsconfig.json +++ b/player/tsconfig.json @@ -4,6 +4,8 @@ ], "compilerOptions": { "target": "es2022", + "module": "es2022", + "moduleResolution": "node", "strict": true, "typeRoots": [ "src/types" diff --git a/player/yarn.lock b/player/yarn.lock index d9845f4..056dc27 100644 --- a/player/yarn.lock +++ b/player/yarn.lock @@ -3,207 +3,238 @@ "@babel/code-frame@^7.0.0": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.18.6": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/highlight@^7.16.7": - version "7.17.12" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz" - integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" "@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== dependencies: - "@jridgewell/set-array" "^1.0.0" + "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0": - version "1.1.1" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== "@jridgewell/source-map@^0.3.2": version "0.3.2" - resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== "@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" "@lezer/common@^0.15.0", "@lezer/common@^0.15.7": version "0.15.12" - resolved "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz" + resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9" integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig== "@lezer/lr@^0.15.4": version "0.15.8" - resolved "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz" + resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21" integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg== dependencies: "@lezer/common" "^0.15.0" +"@lmdb/lmdb-darwin-arm64@2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe" + integrity sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A== + +"@lmdb/lmdb-darwin-x64@2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz#89d8390041bce6bab24a82a20392be22faf54ffc" + integrity sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA== + +"@lmdb/lmdb-linux-arm64@2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz#14fe4c96c2bb1285f93797f45915fa35ee047268" + integrity sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ== + +"@lmdb/lmdb-linux-arm@2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz#05bde4573ab10cf21827339fe687148f2590cfa1" + integrity sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw== + +"@lmdb/lmdb-linux-x64@2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz#d2f85afd857d2c33d2caa5b057944574edafcfee" + integrity sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q== + +"@lmdb/lmdb-win32-x64@2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5" + integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA== + "@mischnic/json-sourcemap@^0.1.0": version "0.1.0" - resolved "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507" integrity sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA== dependencies: "@lezer/common" "^0.15.7" "@lezer/lr" "^0.15.4" json5 "^2.2.1" -"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.0.2.tgz#01e3669b8b2dc01f6353f2c87e1ec94faf52c587" - integrity sha512-FMX5i7a+ojIguHpWbzh5MCsCouJkwf4z4ejdUY/fsgB9Vkdak4ZnoIEskOyOUMMB4lctiZFGszFQJXUeFL8tRg== +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" + integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== -"@msgpackr-extract/msgpackr-extract-darwin-x64@2.0.2": - version "2.0.2" - resolved "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.0.2.tgz" - integrity sha512-DznYtF3lHuZDSRaIOYeif4JgO0NtO2Xf8DsngAugMx/bUdTFbg86jDTmkVJBNmV+cxszz6OjGvinnS8AbJ342g== +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" + integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== -"@msgpackr-extract/msgpackr-extract-linux-arm64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.0.2.tgz#ff629f94379981bf476dffb1439a7c1d3dba2d72" - integrity sha512-b0jMEo566YdM2K+BurSed7bswjo3a6bcdw5ETqoIfSuxKuRLPfAiOjVbZyZBgx3J/TAM/QrvEQ/VN89A0ZAxSg== +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" + integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== -"@msgpackr-extract/msgpackr-extract-linux-arm@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.0.2.tgz#5f6fd30d266c4a90cf989049c7f2e50e5d4fcd4c" - integrity sha512-Gy9+c3Wj+rUlD3YvCZTi92gs+cRX7ZQogtwq0IhRenloTTlsbpezNgk6OCkt59V4ATEWSic9rbU92H/l7XsRvA== +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" + integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== -"@msgpackr-extract/msgpackr-extract-linux-x64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.0.2.tgz#167faa553b9dbffac8b03bf27de9b6f846f0e1bc" - integrity sha512-zrBHaePwcv4cQXxzYgNj0+A8I1uVN97E7/3LmkRocYZ+rMwUsnPpp4RuTAHSRoKlTQV3nSdCQW4Qdt4MXw/iHw== +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" + integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== -"@msgpackr-extract/msgpackr-extract-win32-x64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.0.2.tgz#baea7764b1adf201ce4a792fe971fd7211dad2e4" - integrity sha512-fpnI00dt+yO1cKx9qBXelKhPBdEgvc8ZPav1+0r09j0woYQU2N79w/jcGawSY5UGlgQ3vjaJsFHnGbGvvqdLzg== +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" + integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== -"@parcel/bundler-default@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.6.0.tgz" - integrity sha512-AplEdGm/odV7yGmoeOnglxnY31WlNB5EqGLFGxkgs7uwDaTWoTX/9SWPG6xfvirhjDpms8sLSiVuBdFRCCLtNA== +"@parcel/bundler-default@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.8.3.tgz#d64739dbc2dbd59d6629861bf77a8083aced5229" + integrity sha512-yJvRsNWWu5fVydsWk3O2L4yIy3UZiKWO2cPDukGOIWMgp/Vbpp+2Ct5IygVRtE22bnseW/E/oe0PV3d2IkEJGg== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/graph" "2.8.3" + "@parcel/hash" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" -"@parcel/cache@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/cache/-/cache-2.6.0.tgz" - integrity sha512-4vbD5uSuf+kRnrFesKhpn0AKnOw8u2UlvCyrplYmp1g9bNAkIooC/nDGdmkb/9SviPEbni9PEanQEHDU2+slpA== +"@parcel/cache@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.8.3.tgz#169e130cf59913c0ed9fadce1a450e68f710e16f" + integrity sha512-k7xv5vSQrJLdXuglo+Hv3yF4BCSs1tQ/8Vbd6CHTkOhf7LcGg6CPtLw053R/KdMpd/4GPn0QrAsOLdATm1ELtQ== dependencies: - "@parcel/fs" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/utils" "2.6.0" - lmdb "2.3.10" + "@parcel/fs" "2.8.3" + "@parcel/logger" "2.8.3" + "@parcel/utils" "2.8.3" + lmdb "2.5.2" -"@parcel/codeframe@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.6.0.tgz" - integrity sha512-yXXxrO9yyedHKpTwC+Af0+vPmQm+A9xeEhkt4f0yVg1n4t4yUIxYlTedzbM8ygZEEBtkXU9jJ+PkgXbfMf0dqw== +"@parcel/codeframe@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.8.3.tgz#84fb529ef70def7f5bc64f6c59b18d24826f5fcc" + integrity sha512-FE7sY53D6n/+2Pgg6M9iuEC6F5fvmyBkRE4d9VdnOoxhTXtkEqpqYgX7RJ12FAQwNlxKq4suBJQMgQHMF2Kjeg== dependencies: chalk "^4.1.0" -"@parcel/compressor-raw@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.6.0.tgz" - integrity sha512-rtMU2mGl88bic6Xbq1u5L49bMK4s5185b0k7h3JRdS6/0rR+Xp4k/o9Wog+hHjK/s82z1eF9WmET779ZpIDIQQ== +"@parcel/compressor-raw@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz#301753df8c6de967553149639e8a4179b88f0c95" + integrity sha512-bVDsqleBUxRdKMakWSlWC9ZjOcqDKE60BE+Gh3JSN6WJrycJ02P5wxjTVF4CStNP/G7X17U+nkENxSlMG77ySg== dependencies: - "@parcel/plugin" "2.6.0" + "@parcel/plugin" "2.8.3" -"@parcel/config-default@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.6.0.tgz" - integrity sha512-DXovFPhZITmTvFaSEdC8RRqROs9FLIJ4u8yFSU6FUyq2wpvtYVRXXoDrvXgClh2csXmK7JTJTp5JF7r0rd2UaA== +"@parcel/config-default@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/config-default/-/config-default-2.8.3.tgz#9a43486e7c702e96c68052c37b79098d7240e35b" + integrity sha512-o/A/mbrO6X/BfGS65Sib8d6SSG45NYrNooNBkH/o7zbOBSRQxwyTlysleK1/3Wa35YpvFyLOwgfakqCtbGy4fw== dependencies: - "@parcel/bundler-default" "2.6.0" - "@parcel/compressor-raw" "2.6.0" - "@parcel/namer-default" "2.6.0" - "@parcel/optimizer-css" "2.6.0" - "@parcel/optimizer-htmlnano" "2.6.0" - "@parcel/optimizer-image" "2.6.0" - "@parcel/optimizer-svgo" "2.6.0" - "@parcel/optimizer-terser" "2.6.0" - "@parcel/packager-css" "2.6.0" - "@parcel/packager-html" "2.6.0" - "@parcel/packager-js" "2.6.0" - "@parcel/packager-raw" "2.6.0" - "@parcel/packager-svg" "2.6.0" - "@parcel/reporter-dev-server" "2.6.0" - "@parcel/resolver-default" "2.6.0" - "@parcel/runtime-browser-hmr" "2.6.0" - "@parcel/runtime-js" "2.6.0" - "@parcel/runtime-react-refresh" "2.6.0" - "@parcel/runtime-service-worker" "2.6.0" - "@parcel/transformer-babel" "2.6.0" - "@parcel/transformer-css" "2.6.0" - "@parcel/transformer-html" "2.6.0" - "@parcel/transformer-image" "2.6.0" - "@parcel/transformer-js" "2.6.0" - "@parcel/transformer-json" "2.6.0" - "@parcel/transformer-postcss" "2.6.0" - "@parcel/transformer-posthtml" "2.6.0" - "@parcel/transformer-raw" "2.6.0" - "@parcel/transformer-react-refresh-wrap" "2.6.0" - "@parcel/transformer-svg" "2.6.0" + "@parcel/bundler-default" "2.8.3" + "@parcel/compressor-raw" "2.8.3" + "@parcel/namer-default" "2.8.3" + "@parcel/optimizer-css" "2.8.3" + "@parcel/optimizer-htmlnano" "2.8.3" + "@parcel/optimizer-image" "2.8.3" + "@parcel/optimizer-svgo" "2.8.3" + "@parcel/optimizer-terser" "2.8.3" + "@parcel/packager-css" "2.8.3" + "@parcel/packager-html" "2.8.3" + "@parcel/packager-js" "2.8.3" + "@parcel/packager-raw" "2.8.3" + "@parcel/packager-svg" "2.8.3" + "@parcel/reporter-dev-server" "2.8.3" + "@parcel/resolver-default" "2.8.3" + "@parcel/runtime-browser-hmr" "2.8.3" + "@parcel/runtime-js" "2.8.3" + "@parcel/runtime-react-refresh" "2.8.3" + "@parcel/runtime-service-worker" "2.8.3" + "@parcel/transformer-babel" "2.8.3" + "@parcel/transformer-css" "2.8.3" + "@parcel/transformer-html" "2.8.3" + "@parcel/transformer-image" "2.8.3" + "@parcel/transformer-js" "2.8.3" + "@parcel/transformer-json" "2.8.3" + "@parcel/transformer-postcss" "2.8.3" + "@parcel/transformer-posthtml" "2.8.3" + "@parcel/transformer-raw" "2.8.3" + "@parcel/transformer-react-refresh-wrap" "2.8.3" + "@parcel/transformer-svg" "2.8.3" -"@parcel/core@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/core/-/core-2.6.0.tgz" - integrity sha512-8OOWbPuxpFydpwNyKoz6d3e3O4DmxNYmMw4DXwrPSj/jyg7oa+SDtMT0/VXEhujE0HYkQPCHt4npRajkSuf99A== +"@parcel/core@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/core/-/core-2.8.3.tgz#22a69f36095d53736ab10bf42697d9aa5f4e382b" + integrity sha512-Euf/un4ZAiClnlUXqPB9phQlKbveU+2CotZv7m7i+qkgvFn5nAGnrV4h1OzQU42j9dpgOxWi7AttUDMrvkbhCQ== dependencies: "@mischnic/json-sourcemap" "^0.1.0" - "@parcel/cache" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/events" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/graph" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/package-manager" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" + "@parcel/cache" "2.8.3" + "@parcel/diagnostic" "2.8.3" + "@parcel/events" "2.8.3" + "@parcel/fs" "2.8.3" + "@parcel/graph" "2.8.3" + "@parcel/hash" "2.8.3" + "@parcel/logger" "2.8.3" + "@parcel/package-manager" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" + "@parcel/workers" "2.8.3" abortcontroller-polyfill "^1.1.9" base-x "^3.0.8" browserslist "^4.6.6" @@ -215,618 +246,577 @@ nullthrows "^1.1.1" semver "^5.7.1" -"@parcel/css-darwin-arm64@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@parcel/css-darwin-arm64/-/css-darwin-arm64-1.9.0.tgz#5a020c604249180afcf69ce0f6978b807e2011b3" - integrity sha512-f/guZseS2tNKtKw94LgpNTItZqdVA0mnznqPsmQaR5lSB+cM3IPrSV8cgOOpAS7Vwo9ggxuJartToxBBN+dWSw== - -"@parcel/css-darwin-x64@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@parcel/css-darwin-x64/-/css-darwin-x64-1.9.0.tgz" - integrity sha512-4SpuwiM/4ayOgKflqSLd87XT7YwyC3wd2QuzOOkasjbe38UU+tot/87l2lQYEB538YinLdfwFQuFLDY0x9MxgA== - -"@parcel/css-linux-arm-gnueabihf@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm-gnueabihf/-/css-linux-arm-gnueabihf-1.9.0.tgz#d97457f821867a4937453baf15caac3aa83704b1" - integrity sha512-KxCyX5fFvX5636Y8LSXwCxXMtIncgP7Lkw8nLsqd24C5YqMokmuOtAcHb/vQ9zQG6YiUWTv0MybqDuL7dBDfVw== - -"@parcel/css-linux-arm64-gnu@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm64-gnu/-/css-linux-arm64-gnu-1.9.0.tgz#7dd9c5d8d91354cce6f70e1a59bc3bbe2fa86296" - integrity sha512-wZ6Gsn6l+lSuvRdfWoyr7TdY24l29eGCD8QhXcqA1ALnFI7+KOTMBJ6aV3tjWUjMw3sg5qkosMHVqlWZzvrgXw== - -"@parcel/css-linux-arm64-musl@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm64-musl/-/css-linux-arm64-musl-1.9.0.tgz#5c26ae07c372d615c67a53b9664d5c9ba6196eb7" - integrity sha512-N6n5HhMzcNR5oXWr0Md91gKYtuDhqDlp+aGDb3VT21uSCNLOvijOUz248v/VaPoRno1BPFYlMxn0fYYTTReB3A== - -"@parcel/css-linux-x64-gnu@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-x64-gnu/-/css-linux-x64-gnu-1.9.0.tgz#029e4588956c5151da9d5746364e11381d09abed" - integrity sha512-QufawDkaiOjsh6jcZk/dgDBPMqBtIs+LGTOgcJDM6XL4mcbDNxO6VkDANssRUgPnbG66YYy419CUWFta9aeVOg== - -"@parcel/css-linux-x64-musl@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-x64-musl/-/css-linux-x64-musl-1.9.0.tgz#bf8a21a3cdd17e3d21f00021a57e67b020037843" - integrity sha512-s528buicSd83/5M5DN31JqwefZ8tqx4Jm97srkLDVBCZg+XEe9P0bO7q1Ngz5ZVFqfwvv8OYLPOtAtBmEppG3g== - -"@parcel/css-win32-x64-msvc@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@parcel/css-win32-x64-msvc/-/css-win32-x64-msvc-1.9.0.tgz#e0d729c510b0f41a526b32843348a995c41c030d" - integrity sha512-L4s84iK4PXnO/SzZyTsazAuzadtEYLGHgi1dyKYxMMGCjToCDjuwsn5K8bykeewZxjoL7RaunQGqCBRt5dfB5Q== - -"@parcel/css@^1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@parcel/css/-/css-1.9.0.tgz" - integrity sha512-egCetUQ1H6pgYxOIxVQ8X/YT5e8G0R8eq6aVaUHrqnZ7A8cc6FYgknl9XRmoy2Xxo9h1htrbzdaEShQ5gROwvw== - dependencies: - detect-libc "^1.0.3" - optionalDependencies: - "@parcel/css-darwin-arm64" "1.9.0" - "@parcel/css-darwin-x64" "1.9.0" - "@parcel/css-linux-arm-gnueabihf" "1.9.0" - "@parcel/css-linux-arm64-gnu" "1.9.0" - "@parcel/css-linux-arm64-musl" "1.9.0" - "@parcel/css-linux-x64-gnu" "1.9.0" - "@parcel/css-linux-x64-musl" "1.9.0" - "@parcel/css-win32-x64-msvc" "1.9.0" - -"@parcel/diagnostic@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.6.0.tgz" - integrity sha512-+p8gC2FKxSI2veD7SoaNlP572v4kw+nafCQEPDtJuzYYRqywYUGncch25dkpgNApB4W4cXVkZu3ZbtIpCAmjQQ== +"@parcel/diagnostic@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.8.3.tgz#d560276d5d2804b48beafa1feaf3fc6b2ac5e39d" + integrity sha512-u7wSzuMhLGWZjVNYJZq/SOViS3uFG0xwIcqXw12w54Uozd6BH8JlhVtVyAsq9kqnn7YFkw6pXHqAo5Tzh4FqsQ== dependencies: "@mischnic/json-sourcemap" "^0.1.0" nullthrows "^1.1.1" -"@parcel/events@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/events/-/events-2.6.0.tgz" - integrity sha512-2WaKtBs4iYwS88j4zRdyTJTgh8iuY4E32FMmjzzbheqETs6I05gWuPReGukJYxk8vc0Ir7tbzp12oAfpgo0Y+g== +"@parcel/events@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.8.3.tgz#205f8d874e6ecc2cbdb941bf8d54bae669e571af" + integrity sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w== -"@parcel/fs-search@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.6.0.tgz" - integrity sha512-1nXzM3H/cA4kzLKvDBvwmNisKCdRqlgkLXh+OR1Zu28Kn4W34KuJMcHWW8cC+WIuuKqDh5oo2WPsC5y65GXBKQ== +"@parcel/fs-search@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.8.3.tgz#1c7d812c110b808758f44c56e61dfffdb09e9451" + integrity sha512-DJBT2N8knfN7Na6PP2mett3spQLTqxFrvl0gv+TJRp61T8Ljc4VuUTb0hqBj+belaASIp3Q+e8+SgaFQu7wLiQ== dependencies: detect-libc "^1.0.3" -"@parcel/fs@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/fs/-/fs-2.6.0.tgz" - integrity sha512-6vxtx5Zy6MvDvH1EPx9JxjKGF03bR7VE1dUf4HLeX2D8YmpL5hkHJnlRCFdcH08rzOVwaJLzg1QNtblWJXQ9CA== +"@parcel/fs@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.8.3.tgz#80536afe877fc8a2bd26be5576b9ba27bb4c5754" + integrity sha512-y+i+oXbT7lP0e0pJZi/YSm1vg0LDsbycFuHZIL80pNwdEppUAtibfJZCp606B7HOjMAlNZOBo48e3hPG3d8jgQ== dependencies: - "@parcel/fs-search" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/watcher" "^2.0.0" - "@parcel/workers" "2.6.0" + "@parcel/fs-search" "2.8.3" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" + "@parcel/watcher" "^2.0.7" + "@parcel/workers" "2.8.3" -"@parcel/graph@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/graph/-/graph-2.6.0.tgz" - integrity sha512-rxrAzWm6rwbCRPbu0Z+zwMscpG8omffODniVWPlX2G0jgQGpjKsutBQ6RMfFIcfaQ4MzL3pIQOTf8bkjQOPsbg== +"@parcel/graph@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.8.3.tgz#00ffe8ec032e74fee57199e54529f1da7322571d" + integrity sha512-26GL8fYZPdsRhSXCZ0ZWliloK6DHlMJPWh6Z+3VVZ5mnDSbYg/rRKWmrkhnr99ZWmL9rJsv4G74ZwvDEXTMPBg== dependencies: - "@parcel/utils" "2.6.0" nullthrows "^1.1.1" -"@parcel/hash@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/hash/-/hash-2.6.0.tgz" - integrity sha512-YugWqhLxqK80Lo++3B3Kr5UPCHOdS8iI2zJ1jkzUeH9v6WUzbwWOnmPf6lN2S5m1BrIFFJd8Jc+CbEXWi8zoJA== +"@parcel/hash@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.8.3.tgz#bc2499a27395169616cad2a99e19e69b9098f6e9" + integrity sha512-FVItqzjWmnyP4ZsVgX+G00+6U2IzOvqDtdwQIWisCcVoXJFCqZJDy6oa2qDDFz96xCCCynjRjPdQx2jYBCpfYw== dependencies: detect-libc "^1.0.3" xxhash-wasm "^0.4.2" -"@parcel/logger@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/logger/-/logger-2.6.0.tgz" - integrity sha512-J1/7kPfSGBvMKSZdi0WCNuN0fIeiWxifnDGn7W/K8KhD422YwFJA8N046ps8nkDOPIXf1osnIECNp4GIR9oSYw== +"@parcel/logger@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.8.3.tgz#e14e4debafb3ca9e87c07c06780f9afc38b2712c" + integrity sha512-Kpxd3O/Vs7nYJIzkdmB6Bvp3l/85ydIxaZaPfGSGTYOfaffSOTkhcW9l6WemsxUrlts4za6CaEWcc4DOvaMOPA== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/events" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/events" "2.8.3" -"@parcel/markdown-ansi@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.6.0.tgz" - integrity sha512-fyjkrJQQSfKTUFTTasdZ6WrAkDoQ2+DYDjj+3p+RncYyrIa9zArKx4IiRiipsvNdtMvP0/hTdK8F3BOJ3KSU/g== +"@parcel/markdown-ansi@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz#1337d421bb1133ad178f386a8e1b746631bba4a1" + integrity sha512-4v+pjyoh9f5zuU/gJlNvNFGEAb6J90sOBwpKJYJhdWXLZMNFCVzSigxrYO+vCsi8G4rl6/B2c0LcwIMjGPHmFQ== dependencies: chalk "^4.1.0" -"@parcel/namer-default@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.6.0.tgz" - integrity sha512-r8O12r7ozJBctnFxVdXbf/fK97GIdNj3hiiUNWlXEmED9sw6ZPcChaLcfot0/443g8i87JDmSTKJ8js2tuz5XA== +"@parcel/namer-default@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.8.3.tgz#5304bee74beb4b9c1880781bdbe35be0656372f4" + integrity sha512-tJ7JehZviS5QwnxbARd8Uh63rkikZdZs1QOyivUhEvhN+DddSAVEdQLHGPzkl3YRk0tjFhbqo+Jci7TpezuAMw== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" nullthrows "^1.1.1" -"@parcel/node-resolver-core@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.6.0.tgz" - integrity sha512-AJDj5DZbB58plv0li8bdVSD+zpnkHE36Om3TYyNn1jgXXwgBM64Er/9p8yQn356jBqTQMh7zlJqvbdIyOiMeMg== +"@parcel/node-resolver-core@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-2.8.3.tgz#581df074a27646400b3fed9da95297b616a7db8f" + integrity sha512-12YryWcA5Iw2WNoEVr/t2HDjYR1iEzbjEcxfh1vaVDdZ020PiGw67g5hyIE/tsnG7SRJ0xdRx1fQ2hDgED+0Ww== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" + semver "^5.7.1" -"@parcel/optimizer-css@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.6.0.tgz" - integrity sha512-VMJknUwfKCw6Woov0lnPGdsGZewcI4ghW8WKmNZzC5uKCetk1XetV55QHBc1RNjGfsjfSTZiSa3guATj2zFJkQ== +"@parcel/optimizer-css@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz#420a333f4b78f7ff15e69217dfed34421b1143ee" + integrity sha512-JotGAWo8JhuXsQDK0UkzeQB0UR5hDAKvAviXrjqB4KM9wZNLhLleeEAW4Hk8R9smCeQFP6Xg/N/NkLDpqMwT3g== dependencies: - "@parcel/css" "^1.9.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/utils" "2.8.3" browserslist "^4.6.6" + lightningcss "^1.16.1" nullthrows "^1.1.1" -"@parcel/optimizer-htmlnano@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.6.0.tgz" - integrity sha512-HmvcUoYpfdx8ZfID4WOj/SE8N78NEBmzAffZ8f827mYMr4ZrbKzAgg6OG3tBbfF0zxH0bIjZcwqwZYk4SdbG7g== +"@parcel/optimizer-htmlnano@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.3.tgz#a71ab6f0f24160ef9f573266064438eff65e96d0" + integrity sha512-L8/fHbEy8Id2a2E0fwR5eKGlv9VYDjrH9PwdJE9Za9v1O/vEsfl/0T/79/x129l5O0yB6EFQkFa20MiK3b+vOg== dependencies: - "@parcel/plugin" "2.6.0" + "@parcel/plugin" "2.8.3" htmlnano "^2.0.0" nullthrows "^1.1.1" posthtml "^0.16.5" svgo "^2.4.0" -"@parcel/optimizer-image@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.6.0.tgz" - integrity sha512-FDNr3LJ8SWR9rrtdCrZOlYF1hE9G5pxUWawGxUasbvqwcY5lEQwr2KRmfGZeg+KwOnzlImlY6dP2LGox1NFddQ== +"@parcel/optimizer-image@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/optimizer-image/-/optimizer-image-2.8.3.tgz#ea49b4245b4f7d60b38c7585c6311fb21d341baa" + integrity sha512-SD71sSH27SkCDNUNx9A3jizqB/WIJr3dsfp+JZGZC42tpD/Siim6Rqy9M4To/BpMMQIIiEXa5ofwS+DgTEiEHQ== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" + "@parcel/workers" "2.8.3" detect-libc "^1.0.3" -"@parcel/optimizer-svgo@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.6.0.tgz" - integrity sha512-LMTDVMd7T/IfLG59yLWl8Uw2HYGbj2C3jIwkMqH9MBUT5KILK66T3t0yV86SoZJnxZ6xBIJ+kCcCRssCzhvanw== +"@parcel/optimizer-svgo@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.3.tgz#04da4efec6b623679539a84961bff6998034ba8a" + integrity sha512-9KQed99NZnQw3/W4qBYVQ7212rzA9EqrQG019TIWJzkA9tjGBMIm2c/nXpK1tc3hQ3e7KkXkFCQ3C+ibVUnHNA== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" svgo "^2.4.0" -"@parcel/optimizer-terser@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.6.0.tgz" - integrity sha512-oezRt6Lz/QqcVDXyMfFjzQc7n0ThJowLJ4Lyhu8rMh0ZJYzc4UCFCw/19d4nRnzE+Qg0vj3mQCpdkA9/64E44g== +"@parcel/optimizer-terser@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/optimizer-terser/-/optimizer-terser-2.8.3.tgz#3a06d98d09386a1a0ae1be85376a8739bfba9618" + integrity sha512-9EeQlN6zIeUWwzrzu6Q2pQSaYsYGah8MtiQ/hog9KEPlYTP60hBv/+utDyYEHSQhL7y5ym08tPX5GzBvwAD/dA== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" terser "^5.2.0" -"@parcel/package-manager@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.6.0.tgz" - integrity sha512-AqFfdkbOw51q/3ia2mIsFTmrpYEyUb3k+2uYC5GsLMz3go6OGn7/Crz0lZLSclv5EtwpRg3TWr9yL7RekVN/Uw== +"@parcel/package-manager@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.8.3.tgz#ddd0d62feae3cf0fb6cc0537791b3a16296ad458" + integrity sha512-tIpY5pD2lH53p9hpi++GsODy6V3khSTX4pLEGuMpeSYbHthnOViobqIlFLsjni+QA1pfc8NNNIQwSNdGjYflVA== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/fs" "2.8.3" + "@parcel/logger" "2.8.3" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" + "@parcel/workers" "2.8.3" semver "^5.7.1" -"@parcel/packager-css@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.6.0.tgz" - integrity sha512-iXUttSe+wtnIM2PKCyFC2I4+Szv+8qHpC3wXeJlXlzd8wljm42y+6Fs4FZ0zihTccRxI2UUhFnKu90ag+5AmjA== +"@parcel/packager-css@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.8.3.tgz#0eff34268cb4f5dfb53c1bbca85f5567aeb1835a" + integrity sha512-WyvkMmsurlHG8d8oUVm7S+D+cC/T3qGeqogb7sTI52gB6uiywU7lRCizLNqGFyFGIxcVTVHWnSHqItBcLN76lA== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" -"@parcel/packager-html@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.6.0.tgz" - integrity sha512-HsiXMkU9AJr3LLjsP2Kteho2jCVpabTwcU/fauwbwirhg0xNlRsKxYZRCllRhPkb0FWAnkjzwjOj01MHD6NJCg== +"@parcel/packager-html@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/packager-html/-/packager-html-2.8.3.tgz#f9263b891aa4dd46c6e2fa2b07025a482132fff1" + integrity sha512-OhPu1Hx1RRKJodpiu86ZqL8el2Aa4uhBHF6RAL1Pcrh2EhRRlPf70Sk0tC22zUpYL7es+iNKZ/n0Rl+OWSHWEw== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" posthtml "^0.16.5" -"@parcel/packager-js@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.6.0.tgz" - integrity sha512-Uz3pqIFchFfKszWnNGDgIwM1uwHHJp7Dts6VzS9lf/2RbRgZT0fmce+NPgnVO5MMKBHzdvm32ShT6gFAABF5Vw== +"@parcel/packager-js@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/packager-js/-/packager-js-2.8.3.tgz#3ed11565915d73d12192b6901c75a6b820e4a83a" + integrity sha512-0pGKC3Ax5vFuxuZCRB+nBucRfFRz4ioie19BbDxYnvBxrd4M3FIu45njf6zbBYsI9eXqaDnL1b3DcZJfYqtIzw== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/hash" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/utils" "2.8.3" globals "^13.2.0" nullthrows "^1.1.1" -"@parcel/packager-raw@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.6.0.tgz" - integrity sha512-ktT6Qc/GgCq8H1+6y+AXufVzQj1s6KRoKf83qswCD0iY3MwCbJoEfc3IsB4K64FpHIL5Eu0z54IId+INvGbOYA== +"@parcel/packager-raw@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/packager-raw/-/packager-raw-2.8.3.tgz#bdec826df991e186cb58691cc45d12ad5c06676e" + integrity sha512-BA6enNQo1RCnco9MhkxGrjOk59O71IZ9DPKu3lCtqqYEVd823tXff2clDKHK25i6cChmeHu6oB1Rb73hlPqhUA== dependencies: - "@parcel/plugin" "2.6.0" + "@parcel/plugin" "2.8.3" -"@parcel/packager-svg@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.6.0.tgz" - integrity sha512-OF2RShyspXu7H4Dn2PmchfMMYPx+kWjOXiYVQ6OkOI0MZmOydx7p8nrcG5+y7vCJTPlta828BSwva0GdKfn46A== +"@parcel/packager-svg@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/packager-svg/-/packager-svg-2.8.3.tgz#7233315296001c531cb55ca96b5f2ef672343630" + integrity sha512-mvIoHpmv5yzl36OjrklTDFShLUfPFTwrmp1eIwiszGdEBuQaX7JVI3Oo2jbVQgcN4W7J6SENzGQ3Q5hPTW3pMw== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" posthtml "^0.16.4" -"@parcel/plugin@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.6.0.tgz" - integrity sha512-LzOaiK8R6eFEoov1cb3/W+o0XvXdI/VbDhMDl0L0II+/56M0UeayYtFP5QGTDn/fZqVlYfzPCtt3EMwdG7/dow== +"@parcel/plugin@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.8.3.tgz#7bb30a5775eaa6473c27f002a0a3ee7308d6d669" + integrity sha512-jZ6mnsS4D9X9GaNnvrixDQwlUQJCohDX2hGyM0U0bY2NWU8Km97SjtoCpWjq+XBCx/gpC4g58+fk9VQeZq2vlw== dependencies: - "@parcel/types" "2.6.0" + "@parcel/types" "2.8.3" -"@parcel/reporter-cli@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.6.0.tgz" - integrity sha512-QFG957NXx3L0D8Zw0+B2j7IHy8f/UzOVu6VvKE3rMkhq/iR2qLrPohQ+uvxlee+CLC0cG2qRSgJ7Ve/rjQPoJg== +"@parcel/reporter-cli@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz#12a4743b51b8fe6837f53c20e01bbf1f7336e8e4" + integrity sha512-3sJkS6tFFzgIOz3u3IpD/RsmRxvOKKiQHOTkiiqRt1l44mMDGKS7zANRnJYsQzdCsgwc9SOP30XFgJwtoVlMbw== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" chalk "^4.1.0" term-size "^2.2.1" -"@parcel/reporter-dev-server@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.6.0.tgz" - integrity sha512-VvygsCA+uzWyijIV8zqU1gFyhAWknuaY4KIWhV4kCT8afRJwsLSwt/tpdaKDPuPU45h3tTsUdXH1wjaIk+dGeQ== +"@parcel/reporter-dev-server@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.3.tgz#a0daa5cc015642684cea561f4e0e7116bbffdc1c" + integrity sha512-Y8C8hzgzTd13IoWTj+COYXEyCkXfmVJs3//GDBsH22pbtSFMuzAZd+8J9qsCo0EWpiDow7V9f1LischvEh3FbQ== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" -"@parcel/resolver-default@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.6.0.tgz" - integrity sha512-ATk9wXvy5GOHAqyHbnCnU11fUPTtf8dLjpgVqL5XylwugZnyBXbynoTWX4w8h6mffkVtdfmzTJx/o4Lresz9sA== +"@parcel/resolver-default@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/resolver-default/-/resolver-default-2.8.3.tgz#5ae41e537ae4a793c1abb47f094482b9e2ac3535" + integrity sha512-k0B5M/PJ+3rFbNj4xZSBr6d6HVIe6DH/P3dClLcgBYSXAvElNDfXgtIimbjCyItFkW9/BfcgOVKEEIZOeySH/A== dependencies: - "@parcel/node-resolver-core" "2.6.0" - "@parcel/plugin" "2.6.0" + "@parcel/node-resolver-core" "2.8.3" + "@parcel/plugin" "2.8.3" -"@parcel/runtime-browser-hmr@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.6.0.tgz" - integrity sha512-90xvv/10cFML5dAhClBEJZ/ExiBQVPqQsZcvRmVZmc5mpZVJMKattWCQrd7pAf7FDYl4JAcvsK3DTwvRT/oLNA== +"@parcel/runtime-browser-hmr@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.3.tgz#1fa74e1fbd1030b0a920c58afa3a9eb7dc4bcd1e" + integrity sha512-2O1PYi2j/Q0lTyGNV3JdBYwg4rKo6TEVFlYGdd5wCYU9ZIN9RRuoCnWWH2qCPj3pjIVtBeppYxzfVjPEHINWVg== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" -"@parcel/runtime-js@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.6.0.tgz" - integrity sha512-R4tJAIT/SX7VBQ+f7WmeekREQzzLsmgP1j486uKhQNyYrpvsN0HnRbg5aqvZjEjkEmSeJR0mOlWtMK5/m+0yTA== +"@parcel/runtime-js@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/runtime-js/-/runtime-js-2.8.3.tgz#0baa4c8fbf77eabce05d01ccc186614968ffc0cd" + integrity sha512-IRja0vNKwvMtPgIqkBQh0QtRn0XcxNC8HU1jrgWGRckzu10qJWO+5ULgtOeR4pv9krffmMPqywGXw6l/gvJKYQ== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" -"@parcel/runtime-react-refresh@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.6.0.tgz" - integrity sha512-2sRd13gc2EbMV/O5n2NPVGGhKBasb1fDTXGEY8y7qi9xDKc+ewok/D83T+w243FhCPS9Pf3ur5GkbPlrJGcenQ== +"@parcel/runtime-react-refresh@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.3.tgz#381a942fb81e8f5ac6c7e0ee1b91dbf34763c3f8" + integrity sha512-2v/qFKp00MfG0234OdOgQNAo6TLENpFYZMbVbAsPMY9ITiqG73MrEsrGXVoGbYiGTMB/Toer/lSWlJxtacOCuA== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" react-error-overlay "6.0.9" react-refresh "^0.9.0" -"@parcel/runtime-service-worker@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.6.0.tgz" - integrity sha512-nVlknGw5J5Bkd1Wr1TbyWHhUd9CmVVebaRg/lpfVKYhAuE/2r+3N0+J8qbEIgtTRcHaSV7wTNpg4weSWq46VeA== +"@parcel/runtime-service-worker@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.3.tgz#54d92da9ff1dfbd27db0e84164a22fa59e99b348" + integrity sha512-/Skkw+EeRiwzOJso5fQtK8c9b452uWLNhQH1ISTodbmlcyB4YalAiSsyHCtMYD0c3/t5Sx4ZS7vxBAtQd0RvOw== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" -"@parcel/source-map@^2.0.0": - version "2.0.5" - resolved "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.0.5.tgz" - integrity sha512-DRVlCFKLpqBSIbMxUoVlHgfiv12HTW/U7nnhzw52YgzDVXUX9OA41dXS1PU0pJ1si+D1k8msATUC+AoldN43mg== +"@parcel/source-map@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.1.1.tgz#fb193b82dba6dd62cc7a76b326f57bb35000a782" + integrity sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew== dependencies: detect-libc "^1.0.3" -"@parcel/transformer-babel@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.6.0.tgz" - integrity sha512-qTDzhLoaTpRJoppCNqaAlcUYxcDEvJffem1h3SAQiwvCLUBQowLyeaBy8sUxu54AU6eHFJyBld5ZocENyHTBCA== +"@parcel/transformer-babel@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-babel/-/transformer-babel-2.8.3.tgz#286bc6cb9afe4c0259f0b28e0f2f47322a24b130" + integrity sha512-L6lExfpvvC7T/g3pxf3CIJRouQl+sgrSzuWQ0fD4PemUDHvHchSP4SNUVnd6gOytF3Y1KpnEZIunQGi5xVqQCQ== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/utils" "2.8.3" browserslist "^4.6.6" json5 "^2.2.0" nullthrows "^1.1.1" semver "^5.7.0" -"@parcel/transformer-css@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.6.0.tgz" - integrity sha512-Ei9NPE5Rl9V+MGd8qddfZD0Fsqbvky8J62RwYsqLkptFl9FkhgwOu8Cmokz7IIc4GJ2qzfnG5y54K/Bi7Moq4Q== +"@parcel/transformer-css@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-css/-/transformer-css-2.8.3.tgz#d6c44100204e73841ad8e0f90472172ea8b9120c" + integrity sha512-xTqFwlSXtnaYen9ivAgz+xPW7yRl/u4QxtnDyDpz5dr8gSeOpQYRcjkd4RsYzKsWzZcGtB5EofEk8ayUbWKEUg== dependencies: - "@parcel/css" "^1.9.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/utils" "2.8.3" browserslist "^4.6.6" + lightningcss "^1.16.1" nullthrows "^1.1.1" -"@parcel/transformer-html@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.6.0.tgz" - integrity sha512-YQh5WzNFjPhgV09P+zVS++albTCTvbPYAJXp5zUJ4HavzcpV2IB3HAPRk9x+iXUeRBQYYiO5SMMRkdy9a4CzQQ== +"@parcel/transformer-html@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-html/-/transformer-html-2.8.3.tgz#5c68b28ee6b8c7a13b8aee87f7957ad3227bd83f" + integrity sha512-kIZO3qsMYTbSnSpl9cnZog+SwL517ffWH54JeB410OSAYF1ouf4n5v9qBnALZbuCCmPwJRGs4jUtE452hxwN4g== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/hash" "2.8.3" + "@parcel/plugin" "2.8.3" nullthrows "^1.1.1" posthtml "^0.16.5" posthtml-parser "^0.10.1" posthtml-render "^3.0.0" semver "^5.7.1" + srcset "4" -"@parcel/transformer-image@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.6.0.tgz" - integrity sha512-Zkh1i6nWNOTOReKlZD+bLJCHA16dPLO6Or7ETAHtSF3iRzMNFcVFp+851Awj3l4zeJ6CoCWlyxsR4CEdioRgiQ== +"@parcel/transformer-image@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-image/-/transformer-image-2.8.3.tgz#73805b2bfc3c8919d7737544e5f8be39e3f303fe" + integrity sha512-cO4uptcCGTi5H6bvTrAWEFUsTNhA4kCo8BSvRSCHA2sf/4C5tGQPHt3JhdO0GQLPwZRCh/R41EkJs5HZ8A8DAg== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/workers" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" + "@parcel/workers" "2.8.3" nullthrows "^1.1.1" -"@parcel/transformer-js@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.6.0.tgz" - integrity sha512-4v2r3EVdMKowBziVBW9HZqvAv88HaeiezkWyMX4wAfplo9jBtWEp99KEQINzSEdbXROR81M9oJjlGF5+yoVr/w== +"@parcel/transformer-js@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-js/-/transformer-js-2.8.3.tgz#fe400df428394d1e7fe5afb6dea5c7c858e44f03" + integrity sha512-9Qd6bib+sWRcpovvzvxwy/PdFrLUXGfmSW9XcVVG8pvgXsZPFaNjnNT8stzGQj1pQiougCoxMY4aTM5p1lGHEQ== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" - "@swc/helpers" "^0.3.15" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/utils" "2.8.3" + "@parcel/workers" "2.8.3" + "@swc/helpers" "^0.4.12" browserslist "^4.6.6" detect-libc "^1.0.3" nullthrows "^1.1.1" regenerator-runtime "^0.13.7" semver "^5.7.1" -"@parcel/transformer-json@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.6.0.tgz" - integrity sha512-zb+TQAdHWdXijKcFhLe+5KN1O0IzXwW1gJhPr8DJEA3qhPaCsncsw5RCVjQlP3a7NXr1mMm1eMtO6bhIMqbXeA== +"@parcel/transformer-json@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-json/-/transformer-json-2.8.3.tgz#25deb3a5138cc70a83269fc5d39d564609354d36" + integrity sha512-B7LmVq5Q7bZO4ERb6NHtRuUKWGysEeaj9H4zelnyBv+wLgpo4f5FCxSE1/rTNmP9u1qHvQ3scGdK6EdSSokGPg== dependencies: - "@parcel/plugin" "2.6.0" + "@parcel/plugin" "2.8.3" json5 "^2.2.0" -"@parcel/transformer-postcss@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.6.0.tgz" - integrity sha512-czmh2mOPJLwYbtnPTFlxKYcaQHH6huIlpfNX1XgdsaEYS+yFs8ZXpzqjxI1wu6rMW0R0q5aon72yB3PJewvqNQ== +"@parcel/transformer-postcss@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-postcss/-/transformer-postcss-2.8.3.tgz#df4fdc1c90893823445f2a8eb8e2bdd0349ccc58" + integrity sha512-e8luB/poIlz6jBsD1Izms+6ElbyzuoFVa4lFVLZnTAChI3UxPdt9p/uTsIO46HyBps/Bk8ocvt3J4YF84jzmvg== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/hash" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" clone "^2.1.1" nullthrows "^1.1.1" postcss-value-parser "^4.2.0" semver "^5.7.1" -"@parcel/transformer-posthtml@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.6.0.tgz" - integrity sha512-R1FmPMZ0pgrbPZkDppa2pE+6KDK3Wxof6uQo7juHLB2ELGOTaYofsG3nrRdk+chyAHaVv4qWLqXbfZK6pGepEg== +"@parcel/transformer-posthtml@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.3.tgz#7c3912a5a631cb26485f6464e0d6eeabb6f1e718" + integrity sha512-pkzf9Smyeaw4uaRLsT41RGrPLT5Aip8ZPcntawAfIo+KivBQUV0erY1IvHYjyfFzq1ld/Fo2Ith9He6mxpPifA== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" nullthrows "^1.1.1" posthtml "^0.16.5" posthtml-parser "^0.10.1" posthtml-render "^3.0.0" semver "^5.7.1" -"@parcel/transformer-raw@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.6.0.tgz" - integrity sha512-QDirlWCS/qy0DQ3WvDIAnFP52n1TJW/uWH+4PGMNnX4/M3/2UchY8xp9CN0tx4NQ4g09S8o3gLlHvNxQqZxFrQ== +"@parcel/transformer-raw@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-raw/-/transformer-raw-2.8.3.tgz#3a22213fe18a5f83fd78889cb49f06e059cfead7" + integrity sha512-G+5cXnd2/1O3nV/pgRxVKZY/HcGSseuhAe71gQdSQftb8uJEURyUHoQ9Eh0JUD3MgWh9V+nIKoyFEZdf9T0sUQ== dependencies: - "@parcel/plugin" "2.6.0" + "@parcel/plugin" "2.8.3" -"@parcel/transformer-react-refresh-wrap@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.6.0.tgz" - integrity sha512-G34orfvLDUTumuerqNmA8T8NUHk+R0jwUjbVPO7gpB6VCVQ5ocTABdE9vN9Uu/cUsHij40TUFwqK4R9TFEBIEQ== +"@parcel/transformer-react-refresh-wrap@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.3.tgz#8b0392638405dd470a886002229f7889d5464822" + integrity sha512-q8AAoEvBnCf/nPvgOwFwKZfEl/thwq7c2duxXkhl+tTLDRN2vGmyz4355IxCkavSX+pLWSQ5MexklSEeMkgthg== dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/plugin" "2.8.3" + "@parcel/utils" "2.8.3" react-refresh "^0.9.0" -"@parcel/transformer-svg@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.6.0.tgz" - integrity sha512-e7yrb7775A7tEGRsAHQSMhXe+u4yisH5W0PuIzAQQy/a2IwBjaSxNnvyelN7tNX0FYq0BK6An5wRbhK4YmM+xw== +"@parcel/transformer-svg@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-svg/-/transformer-svg-2.8.3.tgz#4df959cba4ebf45d7aaddd540f752e6e84df38b2" + integrity sha512-3Zr/gBzxi1ZH1fftH/+KsZU7w5GqkmxlB0ZM8ovS5E/Pl1lq1t0xvGJue9m2VuQqP8Mxfpl5qLFmsKlhaZdMIQ== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/hash" "2.8.3" + "@parcel/plugin" "2.8.3" nullthrows "^1.1.1" posthtml "^0.16.5" posthtml-parser "^0.10.1" posthtml-render "^3.0.0" semver "^5.7.1" -"@parcel/ts-utils@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/ts-utils/-/ts-utils-2.6.0.tgz" - integrity sha512-U2Spr/vdOnxLzztXP6WpMO7JZTsaYO1G6F/cUTG5fReTQ0imM952FAc/WswpZWAPZqXqWCnvC/Z91JIkMDuYrA== +"@parcel/ts-utils@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/ts-utils/-/ts-utils-2.8.3.tgz#f3590ca033c061779dc35ff3d14af2860ed106ac" + integrity sha512-4HMt9B9LF2pDFvSKGImho48tlCvCUl7ly1ZMXvQdmEq2i0yoS81tDsmxX3yly/RVUVeUCGAj1JRuuy1lw5zw1A== dependencies: nullthrows "^1.1.1" -"@parcel/types@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/types/-/types-2.6.0.tgz" - integrity sha512-lAMYvOBfNEJMsPJ+plbB50305o0TwNrY1xX5RRIWBqwOa6bYmbW1ZljUk1tQvnkpIE4eAHQwnPR5Z2XWg18wGQ== +"@parcel/types@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.8.3.tgz#3306bc5391b6913bd619914894b8cd84a24b30fa" + integrity sha512-FECA1FB7+0UpITKU0D6TgGBpGxYpVSMNEENZbSJxFSajNy3wrko+zwBKQmFOLOiPcEtnGikxNs+jkFWbPlUAtw== dependencies: - "@parcel/cache" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/package-manager" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/workers" "2.6.0" + "@parcel/cache" "2.8.3" + "@parcel/diagnostic" "2.8.3" + "@parcel/fs" "2.8.3" + "@parcel/package-manager" "2.8.3" + "@parcel/source-map" "^2.1.1" + "@parcel/workers" "2.8.3" utility-types "^3.10.0" -"@parcel/utils@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/utils/-/utils-2.6.0.tgz" - integrity sha512-ElXz+QHtT1JQIucbQJBk7SzAGoOlBp4yodEQVvTKS7GA+hEGrSP/cmibl6qm29Rjtd0zgQsdd+2XmP3xvP2gQQ== +"@parcel/utils@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.8.3.tgz#0d56c9e8e22c119590a5e044a0e01031965da40e" + integrity sha512-IhVrmNiJ+LOKHcCivG5dnuLGjhPYxQ/IzbnF2DKNQXWBTsYlHkJZpmz7THoeLtLliGmSOZ3ZCsbR8/tJJKmxjA== dependencies: - "@parcel/codeframe" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/markdown-ansi" "2.6.0" - "@parcel/source-map" "^2.0.0" + "@parcel/codeframe" "2.8.3" + "@parcel/diagnostic" "2.8.3" + "@parcel/hash" "2.8.3" + "@parcel/logger" "2.8.3" + "@parcel/markdown-ansi" "2.8.3" + "@parcel/source-map" "^2.1.1" chalk "^4.1.0" "@parcel/validator-typescript@^2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/validator-typescript/-/validator-typescript-2.6.0.tgz" - integrity sha512-NMroc+QPoTo436COHsqEQsn+Qd+7HE1s1X6he1Bqb+RMB4rZsvOZI22MgFj1eU5MpfYuM4zTID0Uz221hiS59w== + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/validator-typescript/-/validator-typescript-2.8.3.tgz#6f9cb5b48df302b1d65e9b17dc1a20870e746976" + integrity sha512-2UYGCAwrxh7HIGcrXl8Vu9Sisd8vAu/6Jp/oJV5n9ZQuT5O9pQAlK2lZGSocYRucBtmb4WajII2S2GTzUZeEuQ== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/ts-utils" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/plugin" "2.8.3" + "@parcel/ts-utils" "2.8.3" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" -"@parcel/watcher@^2.0.0": - version "2.0.5" - resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.5.tgz" - integrity sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw== +"@parcel/watcher@^2.0.7": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545" + integrity sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw== dependencies: + is-glob "^4.0.3" + micromatch "^4.0.5" node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@parcel/workers@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@parcel/workers/-/workers-2.6.0.tgz" - integrity sha512-3tcI2LF5fd/WZtSnSjyWdDE+G+FitdNrRgSObzSp+axHKMAM23sO0z7KY8s2SYCF40msdYbFUW8eI6JlYNJoWQ== +"@parcel/workers@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.8.3.tgz#255450ccf4db234082407e4ddda5fd575f08c235" + integrity sha512-+AxBnKgjqVpUHBcHLWIHcjYgKIvHIpZjN33mG5LG9XXvrZiqdWvouEzqEXlVLq5VzzVbKIQQcmsvRy138YErkg== dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/diagnostic" "2.8.3" + "@parcel/logger" "2.8.3" + "@parcel/types" "2.8.3" + "@parcel/utils" "2.8.3" chrome-trace-event "^1.0.2" nullthrows "^1.1.1" -"@swc/helpers@^0.3.15": - version "0.3.17" - resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz" - integrity sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q== +"@swc/helpers@^0.4.12": + version "0.4.14" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" + integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== dependencies: tslib "^2.4.0" "@trysound/sax@0.2.0": version "0.2.0" - resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@types/dom-webcodecs@^0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.6.tgz#204f2c8c2e6286114e1ec2997630bc0fd2081ffa" + integrity sha512-m+Y2WRIKvLwFzyToNGA5XV+sfihtrfcOaMrXzsZULVdqYyKy4yn0XZ8lES9RRwZTzW7TKcz84xed3e5FBk3wCg== + "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== abortcontroller-polyfill@^1.1.9: - version "1.7.3" - resolved "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz" - integrity sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q== + version "1.7.5" + resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" + integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== acorn@^8.5.0: - version "8.7.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + 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" ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" base-x@^3.0.8: version "3.0.9" - resolved "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== dependencies: safe-buffer "^5.0.1" boolbase@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== -browserslist@^4.6.6: - version "4.20.4" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz" - integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw== +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: - caniuse-lite "^1.0.30001349" - electron-to-chromium "^1.4.147" - escalade "^3.1.1" - node-releases "^2.0.5" - picocolors "^1.0.0" + fill-range "^7.0.1" + +browserslist@^4.6.6: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caniuse-lite@^1.0.30001349: - version "1.0.30001352" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz" - integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA== +caniuse-lite@^1.0.30001449: + version "1.0.30001470" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001470.tgz#09c8e87c711f75ff5d39804db2613dd593feeb10" + integrity sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA== chalk@^2.0.0: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + 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" @@ -835,7 +825,7 @@ chalk@^2.0.0: chalk@^4.1.0: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -843,52 +833,52 @@ chalk@^4.1.0: chrome-trace-event@^1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== clone@^2.1.1: version "2.1.2" - resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + 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-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== commander@^2.20.0: version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^7.0.0, commander@^7.2.0: version "7.2.0" - resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== cosmiconfig@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -898,7 +888,7 @@ cosmiconfig@^7.0.1: css-select@^4.1.3: version "4.3.0" - resolved "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== dependencies: boolbase "^1.0.0" @@ -909,7 +899,7 @@ css-select@^4.1.3: css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: mdn-data "2.0.14" @@ -917,24 +907,24 @@ css-tree@^1.1.2, css-tree@^1.1.3: css-what@^6.0.1: version "6.1.0" - resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== csso@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== dependencies: css-tree "^1.1.2" detect-libc@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== dom-serializer@^1.0.1: version "1.4.1" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== dependencies: domelementtype "^2.0.1" @@ -943,19 +933,19 @@ dom-serializer@^1.0.1: domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1: version "4.3.1" - resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== dependencies: domelementtype "^2.2.0" domutils@^2.8.0: version "2.8.0" - resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== dependencies: dom-serializer "^1.0.1" @@ -964,72 +954,79 @@ domutils@^2.8.0: dotenv-expand@^5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== dotenv@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g== -electron-to-chromium@^1.4.147: - version "1.4.150" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz" - integrity sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA== +electron-to-chromium@^1.4.284: + version "1.4.340" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz#3a6d7414c1fc2dbf84b6f7af3ec24270606c85b8" + integrity sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg== entities@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== entities@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + get-port@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119" integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw== globals@^13.2.0: - version "13.15.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== htmlnano@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.2.tgz" - integrity sha512-+ZrQFS4Ub+zd+/fWwfvoYCEGNEa0/zrpys6CyXxvZDwtL7Pl+pOtRkiujyvBQ7Lmfp7/iEPxtOFgxWA16Gkj3w== + version "2.0.3" + resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-2.0.3.tgz#50ee639ed63357d4a6c01309f52a35892e4edc2e" + integrity sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A== dependencies: cosmiconfig "^7.0.1" posthtml "^0.16.5" @@ -1037,7 +1034,7 @@ htmlnano@^2.0.0: htmlparser2@^7.1.1: version "7.2.0" - resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5" integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog== dependencies: domelementtype "^2.0.1" @@ -1047,7 +1044,7 @@ htmlparser2@^7.1.1: import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -1055,176 +1052,221 @@ import-fresh@^3.2.1: is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-json@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-json/-/is-json-2.0.1.tgz#6be166d144828a131d686891b983df62c39491ff" integrity sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA== +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json5@^2.2.0, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +lightningcss-darwin-arm64@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.19.0.tgz#56ab071e932f845dbb7667f44f5b78441175a343" + integrity sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg== + +lightningcss-darwin-x64@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.19.0.tgz#c867308b88859ba61a2c46c82b1ca52ff73a1bd0" + integrity sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw== + +lightningcss-linux-arm-gnueabihf@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.19.0.tgz#0f921dc45f2e5c3aea70fab98844ac0e5f2f81be" + integrity sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig== + +lightningcss-linux-arm64-gnu@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.19.0.tgz#027f9df9c7f4ffa127c37a71726245a5794d7ba2" + integrity sha512-zwXRjWqpev8wqO0sv0M1aM1PpjHz6RVIsBcxKszIG83Befuh4yNysjgHVplF9RTU7eozGe3Ts7r6we1+Qkqsww== + +lightningcss-linux-arm64-musl@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.19.0.tgz#85ea987da868524eac6db94f8e1eaa23d0b688a3" + integrity sha512-vSCKO7SDnZaFN9zEloKSZM5/kC5gbzUjoJQ43BvUpyTFUX7ACs/mDfl2Eq6fdz2+uWhUh7vf92c4EaaP4udEtA== + +lightningcss-linux-x64-gnu@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.19.0.tgz#02bec89579ab4153dccc0def755d1fd9e3ee7f3c" + integrity sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ== + +lightningcss-linux-x64-musl@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.19.0.tgz#e36a5df8193ae961d22974635e4c100a1823bb8c" + integrity sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg== + +lightningcss-win32-x64-msvc@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.19.0.tgz#0854dbd153035eca1396e2227c708ad43655a61c" + integrity sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg== + +lightningcss@^1.16.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.19.0.tgz#fbbad0975de66252e38d96b5bdd2a62f2dd0ffbf" + integrity sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA== + dependencies: + detect-libc "^1.0.3" + optionalDependencies: + lightningcss-darwin-arm64 "1.19.0" + lightningcss-darwin-x64 "1.19.0" + lightningcss-linux-arm-gnueabihf "1.19.0" + lightningcss-linux-arm64-gnu "1.19.0" + lightningcss-linux-arm64-musl "1.19.0" + lightningcss-linux-x64-gnu "1.19.0" + lightningcss-linux-x64-musl "1.19.0" + lightningcss-win32-x64-msvc "1.19.0" lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -lmdb-darwin-arm64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.3.10.tgz#4e20f75770eeedc60af3d4630975fd105a89ffe8" - integrity sha512-LVXbH2MYu7/ZuQ8+P9rv+SwNyBKltxo7vHAGJS94HWyfwnCbKEYER9PImBvNBwzvgtaYk6x0RMX3oor6e6KdDQ== - -lmdb-darwin-x64@2.3.10: - version "2.3.10" - resolved "https://registry.npmjs.org/lmdb-darwin-x64/-/lmdb-darwin-x64-2.3.10.tgz" - integrity sha512-gAc/1b/FZOb9yVOT+o0huA+hdW82oxLo5r22dFTLoRUFG1JMzxdTjmnW6ONVOHdqC9a5bt3vBCEY3jmXNqV26A== - -lmdb-linux-arm64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-linux-arm64/-/lmdb-linux-arm64-2.3.10.tgz#ac7db8bdfe0e9dbf2be1cc3362d6f2b79e2a9722" - integrity sha512-Ihr8mdICTK3jA4GXHxrXGK2oekn0mY6zuDSXQDNtyRSH19j3D2Y04A7SEI9S0EP/t5sjKSudYgZbiHDxRCsI5A== - -lmdb-linux-arm@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-linux-arm/-/lmdb-linux-arm-2.3.10.tgz#74235418bbe7bf41e8ea5c9d52365c4ff5ca4b49" - integrity sha512-Rb8+4JjsThuEcJ7GLLwFkCFnoiwv/3hAAbELWITz70buQFF+dCZvCWWgEgmDTxwn5r+wIkdUjmFv4dqqiKQFmQ== - -lmdb-linux-x64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-linux-x64/-/lmdb-linux-x64-2.3.10.tgz#d790b95061d03c5c99a57b3ad5126f7723c60a2f" - integrity sha512-E3l3pDiCA9uvnLf+t3qkmBGRO01dp1EHD0x0g0iRnfpAhV7wYbayJGfG93BUt22Tj3fnq4HDo4dQ6ZWaDI1nuw== - -lmdb-win32-x64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-win32-x64/-/lmdb-win32-x64-2.3.10.tgz#bff73d12d94084343c569b16069d8d38626eb2d6" - integrity sha512-gspWk34tDANhjn+brdqZstJMptGiwj4qFNVg0Zey9ds+BUlif+Lgf5szrfOVzZ8gVRkk1Lgbz7i78+V7YK7SCA== - -lmdb@2.3.10: - version "2.3.10" - resolved "https://registry.npmjs.org/lmdb/-/lmdb-2.3.10.tgz" - integrity sha512-GtH+nStn9V59CfYeQ5ddx6YTfuFCmu86UJojIjJAweG+/Fm0PDknuk3ovgYDtY/foMeMdZa8/P7oSljW/d5UPw== +lmdb@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.5.2.tgz#37e28a9fb43405f4dc48c44cec0e13a14c4a6ff1" + integrity sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA== dependencies: msgpackr "^1.5.4" - nan "^2.14.2" node-addon-api "^4.3.0" - node-gyp-build-optional-packages "^4.3.2" + node-gyp-build-optional-packages "5.0.3" ordered-binary "^1.2.4" weak-lru-cache "^1.2.2" optionalDependencies: - lmdb-darwin-arm64 "2.3.10" - lmdb-darwin-x64 "2.3.10" - lmdb-linux-arm "2.3.10" - lmdb-linux-arm64 "2.3.10" - lmdb-linux-x64 "2.3.10" - lmdb-win32-x64 "2.3.10" + "@lmdb/lmdb-darwin-arm64" "2.5.2" + "@lmdb/lmdb-darwin-x64" "2.5.2" + "@lmdb/lmdb-linux-arm" "2.5.2" + "@lmdb/lmdb-linux-arm64" "2.5.2" + "@lmdb/lmdb-linux-x64" "2.5.2" + "@lmdb/lmdb-win32-x64" "2.5.2" mdn-data@2.0.14: version "2.0.14" - resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== -msgpackr-extract@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-2.0.2.tgz" - integrity sha512-coskCeJG2KDny23zWeu+6tNy7BLnAiOGgiwzlgdm4oeSsTpqEJJPguHIuKZcCdB7tzhZbXNYSg6jZAXkZErkJA== +micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - node-gyp-build-optional-packages "5.0.2" + braces "^3.0.2" + picomatch "^2.3.1" + +msgpackr-extract@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" + integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== + dependencies: + node-gyp-build-optional-packages "5.0.7" optionalDependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm" "2.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-linux-x64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-win32-x64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" msgpackr@^1.5.4: - version "1.6.1" - resolved "https://registry.npmjs.org/msgpackr/-/msgpackr-1.6.1.tgz" - integrity sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA== + version "1.8.5" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.5.tgz#8cadfb935357680648f33699d0e833c9179dbfeb" + integrity sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg== optionalDependencies: - msgpackr-extract "^2.0.2" - -nan@^2.14.2: - version "2.16.0" - resolved "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + msgpackr-extract "^3.0.1" node-addon-api@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== node-addon-api@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== -node-gyp-build-optional-packages@5.0.2: - version "5.0.2" - resolved "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.2.tgz" - integrity sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g== +node-gyp-build-optional-packages@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" + integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== -node-gyp-build-optional-packages@^4.3.2: - version "4.3.5" - resolved "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-4.3.5.tgz" - integrity sha512-5ke7D8SiQsTQL7CkHpfR1tLwfqtKc0KYEmlnkwd40jHCASskZeS98qoZ1qDUns2aUQWikcjidRUs6PM/3iyN/w== +node-gyp-build-optional-packages@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" + integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== node-gyp-build@^4.3.0: - version "4.4.0" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz" - integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== -node-releases@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz" - integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== nth-check@^2.0.1: version "2.1.1" - resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" nullthrows@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== ordered-binary@^1.2.4: - version "1.2.5" - resolved "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.2.5.tgz" - integrity sha512-djRmZoEpOGvIRW7ufsCDHtvcUa18UC9TxnPbHhSVFZHsoyg0dtut1bWtBZ/fmxdPN62oWXrV6adM7NoWU+CneA== + version "1.4.0" + resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389" + integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ== -parcel@^2.6.0: - version "2.6.0" - resolved "https://registry.npmjs.org/parcel/-/parcel-2.6.0.tgz" - integrity sha512-pSTJ7wC6uTl16PKLXQV7RfL9FGoIDA1iVpNvaav47n6UkUdKqfx0spcVPpw35kWdRcHJF61YAvkPjP2hTwHQ+Q== +parcel@^2.8.0: + version "2.8.3" + resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.8.3.tgz#1ff71d7317274fd367379bc7310a52c6b75d30c2" + integrity sha512-5rMBpbNE72g6jZvkdR5gS2nyhwIXaJy8i65osOqs/+5b7zgf3eMKgjSsDrv6bhz3gzifsba6MBJiZdBckl+vnA== dependencies: - "@parcel/config-default" "2.6.0" - "@parcel/core" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/events" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/package-manager" "2.6.0" - "@parcel/reporter-cli" "2.6.0" - "@parcel/reporter-dev-server" "2.6.0" - "@parcel/utils" "2.6.0" + "@parcel/config-default" "2.8.3" + "@parcel/core" "2.8.3" + "@parcel/diagnostic" "2.8.3" + "@parcel/events" "2.8.3" + "@parcel/fs" "2.8.3" + "@parcel/logger" "2.8.3" + "@parcel/package-manager" "2.8.3" + "@parcel/reporter-cli" "2.8.3" + "@parcel/reporter-dev-server" "2.8.3" + "@parcel/utils" "2.8.3" chalk "^4.1.0" commander "^7.0.0" get-port "^4.2.0" @@ -1232,14 +1274,14 @@ parcel@^2.6.0: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -1249,43 +1291,48 @@ parse-json@^5.0.0: path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== picocolors@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + postcss-value-parser@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== posthtml-parser@^0.10.1: version "0.10.2" - resolved "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz" + resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.2.tgz#df364d7b179f2a6bf0466b56be7b98fd4e97c573" integrity sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg== dependencies: htmlparser2 "^7.1.1" posthtml-parser@^0.11.0: version "0.11.0" - resolved "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz" + resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.11.0.tgz#25d1c7bf811ea83559bc4c21c189a29747a24b7a" integrity sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw== dependencies: htmlparser2 "^7.1.1" posthtml-render@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-3.0.0.tgz#97be44931496f495b4f07b99e903cc70ad6a3205" integrity sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA== dependencies: is-json "^2.0.1" posthtml@^0.16.4, posthtml@^0.16.5: version "0.16.6" - resolved "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz" + resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.16.6.tgz#e2fc407f67a64d2fa3567afe770409ffdadafe59" integrity sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ== dependencies: posthtml-parser "^0.11.0" @@ -1293,37 +1340,37 @@ posthtml@^0.16.4, posthtml@^0.16.5: react-error-overlay@6.0.9: version "6.0.9" - resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== react-refresh@^0.9.0: version "0.9.0" - resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf" integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ== regenerator-runtime@^0.13.7: - version "0.13.9" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== safe-buffer@^5.0.1: version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== semver@^5.7.0, semver@^5.7.1: version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== source-map-support@~0.5.20: version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" @@ -1331,31 +1378,36 @@ source-map-support@~0.5.20: source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +srcset@4: + version "4.0.0" + resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4" + integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw== + stable@^0.1.8: version "0.1.8" - resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + 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-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" svgo@^2.4.0: version "2.8.0" - resolved "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== dependencies: "@trysound/sax" "0.2.0" @@ -1368,13 +1420,13 @@ svgo@^2.4.0: term-size@^2.2.1: version "2.2.1" - resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== terser@^5.2.0: - version "5.14.0" - resolved "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz" - integrity sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g== + version "5.16.8" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.8.tgz#ccde583dabe71df3f4ed02b65eb6532e0fae15d5" + integrity sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -1383,45 +1435,60 @@ terser@^5.2.0: timsort@^0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== typescript@>=3.0.0: - version "4.7.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz" - integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA== + version "5.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.2.tgz#891e1a90c5189d8506af64b9ef929fca99ba1ee5" + integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw== + +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" utility-types@^3.10.0: version "3.10.0" - resolved "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== v8-compile-cache@^2.0.0: version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== weak-lru-cache@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19" integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw== xxhash-wasm@^0.4.2: version "0.4.2" - resolved "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz" + resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz#752398c131a4dd407b5132ba62ad372029be6f79" integrity sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA== yaml@^1.10.0: version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== diff --git a/server/internal/warp/session.go b/server/internal/warp/session.go index dac1cb1..d52c42f 100644 --- a/server/internal/warp/session.go +++ b/server/internal/warp/session.go @@ -200,6 +200,11 @@ func (s *Session) writeInit(ctx context.Context, init *MediaInit) (err error) { return fmt.Errorf("failed to write init data: %w", err) } + err = stream.Close() + if err != nil { + return fmt.Errorf("failed to close init stream: %w", err) + } + return nil } @@ -210,6 +215,11 @@ func (s *Session) writeSegment(ctx context.Context, segment *MediaSegment) (err return fmt.Errorf("failed to create stream: %w", err) } + if temp == nil { + // Not sure when this happens, perhaps when closing a connection? + return fmt.Errorf("received a nil stream from quic-go: %w", err) + } + // Wrap the stream in an object that buffers writes instead of blocking. stream := NewStream(temp) s.streams.Add(stream.Run) From 8e1c8d13e231e5fec478cfb43016075a474e6335 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 28 Mar 2023 10:30:35 +0900 Subject: [PATCH 03/10] Decoding and rendering works, with a few bugs of course. --- player/src/video/decoder.ts | 25 ++++++++++++++++--------- player/src/video/renderer.ts | 16 ++++++++++++++-- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/player/src/video/decoder.ts b/player/src/video/decoder.ts index 33b77b8..b4d8c4e 100644 --- a/player/src/video/decoder.ts +++ b/player/src/video/decoder.ts @@ -46,19 +46,26 @@ export class Decoder { } }); - decoder.configure({ - codec: video.codec, - codedHeight: video.track_height, - codedWidth: video.track_width, - // optimizeForLatency: true - }) - const input = MP4.New(); input.onSamples = (id: number, user: any, samples: MP4.Sample[]) => { for (let sample of samples) { - const timestamp = sample.dts / (1000 / info.timescale) // milliseconds - console.log(sample) + const timestamp = 1000 * sample.dts / sample.timescale // milliseconds + + if (sample.is_sync) { + // Configure the decoder using the AVC box for H.264 + const avcc = sample.description.avcC; + const description = new MP4.Stream(new Uint8Array(avcc.size), 0, false) + avcc.write(description) + + decoder.configure({ + codec: video.codec, + codedHeight: video.track_height, + codedWidth: video.track_width, + description: description.buffer?.slice(8), + // optimizeForLatency: true + }) + } decoder.decode(new EncodedVideoChunk({ data: sample.data, diff --git a/player/src/video/renderer.ts b/player/src/video/renderer.ts index c5b40cf..0ec28f5 100644 --- a/player/src/video/renderer.ts +++ b/player/src/video/renderer.ts @@ -46,7 +46,8 @@ export class Renderer { let frame = this.queue[0] if (frame.timestamp > target) { - // nothing to render yet + // nothing to render yet, wait for the next animation frame + this.render = self.requestAnimationFrame(this.draw.bind(this)) return } @@ -59,14 +60,25 @@ export class Renderer { break } + console.log("dropping frame") + + frame.close() + this.queue.shift() frame = next } const ctx = this.canvas.getContext("2d"); - ctx?.drawImage(frame, 0, 0) // TODO aspect ratio + ctx?.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height) // TODO aspect ratio this.last = frame.timestamp; frame.close() + + if (this.queue.length > 0) { + this.render = self.requestAnimationFrame(this.draw.bind(this)) + } else { + // Break the loop for now + this.render = 0 + } } } \ No newline at end of file From 17250fc1a5d5c16b7a6ee109d185bb8fdf3aadcb Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 28 Mar 2023 14:57:18 +0900 Subject: [PATCH 04/10] temp --- cert/generate | 3 --- player/fingerprint.sha256 | 1 + player/package.json | 5 +++-- player/src/transport/fingerprint.hex | 1 + player/src/transport/index.ts | 22 +++++---------------- player/src/video/renderer.ts | 2 -- player/yarn.lock | 7 +++++++ server/go.mod | 2 +- server/go.sum | 5 ++--- server/internal/warp/{warp.go => server.go} | 0 server/internal/warp/session.go | 7 ++++++- server/main.go | 2 ++ 12 files changed, 28 insertions(+), 29 deletions(-) create mode 100644 player/fingerprint.sha256 create mode 100644 player/src/transport/fingerprint.hex rename server/internal/warp/{warp.go => server.go} (100%) diff --git a/cert/generate b/cert/generate index 5103f25..4376f56 100755 --- a/cert/generate +++ b/cert/generate @@ -17,6 +17,3 @@ mkcert -ecdsa -cert-file "$CRT" -key-file "$KEY" localhost 127.0.0.1 ::1 # Reduce the expiration time of the certificate to 14 days; the WebTransport maximum. # TODO https://github.com/FiloSottile/mkcert/pull/513 openssl x509 -days 14 -in "$CRT" -signkey "$KEY" -out "$CRT" - -# Compute the sha256 fingerprint of the certificate for WebTransport -# openssl x509 -in "$CRT" -outform der | openssl dgst -sha256 diff --git a/player/fingerprint.sha256 b/player/fingerprint.sha256 new file mode 100644 index 0000000..3545bf9 --- /dev/null +++ b/player/fingerprint.sha256 @@ -0,0 +1 @@ +50e151a79d06af6b9289180af319ca974e227d9ec58c30362c9304e56df3e3ea diff --git a/player/package.json b/player/package.json index 5ebb925..0c4f4be 100644 --- a/player/package.json +++ b/player/package.json @@ -6,9 +6,10 @@ "check": "tsc --noEmit" }, "devDependencies": { + "@parcel/transformer-inline-string": "2.8.3", "@parcel/validator-typescript": "^2.6.0", + "@types/dom-webcodecs": "^0.1.6", "parcel": "^2.8.0", - "typescript": ">=3.0.0", - "@types/dom-webcodecs": "^0.1.6" + "typescript": ">=3.0.0" } } diff --git a/player/src/transport/fingerprint.hex b/player/src/transport/fingerprint.hex new file mode 100644 index 0000000..8e821ec --- /dev/null +++ b/player/src/transport/fingerprint.hex @@ -0,0 +1 @@ +642ab03bc58c48ed614e6523d93c2455d8223aedfbdcba7f502ca0ea08590921 diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index e42da4e..6c45ac9 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -3,6 +3,7 @@ import * as Stream from "../stream" import * as MP4 from "../mp4" import Video from "../video/index" +import fingerprint from 'bundle-text:./fingerprint.hex'; /// @@ -43,29 +44,16 @@ export class Player { } async connect(url: string): Promise { - // TODO remove this when WebTransport supports the system CA pool - const fingerprintURL = new URL(url); - fingerprintURL.pathname = "/fingerprint" - - const response = await fetch(fingerprintURL) - if (!response.ok) { - throw new Error('failed to get server fingerprint'); - } - - const hex = await response.text() - // Convert the hex to binary. - let fingerprint = []; - for (let c = 0; c < hex.length; c += 2) { - fingerprint.push(parseInt(hex.substring(c, c+2), 16)); + let hash = []; + for (let c = 0; c < fingerprint.length; c += 2) { + hash.push(parseInt(fingerprint.substring(c, c+2), 16)); } - //const fingerprint = Uint8Array.from(atob(hex), c => c.charCodeAt(0)) - const quic = new WebTransport(url, { "serverCertificateHashes": [{ "algorithm": "sha-256", - "value": new Uint8Array(fingerprint), + "value": new Uint8Array(hash), }] }) diff --git a/player/src/video/renderer.ts b/player/src/video/renderer.ts index 0ec28f5..95c5cd0 100644 --- a/player/src/video/renderer.ts +++ b/player/src/video/renderer.ts @@ -60,8 +60,6 @@ export class Renderer { break } - console.log("dropping frame") - frame.close() this.queue.shift() diff --git a/player/yarn.lock b/player/yarn.lock index 056dc27..24f0b4e 100644 --- a/player/yarn.lock +++ b/player/yarn.lock @@ -576,6 +576,13 @@ "@parcel/workers" "2.8.3" nullthrows "^1.1.1" +"@parcel/transformer-inline-string@2.8.3": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@parcel/transformer-inline-string/-/transformer-inline-string-2.8.3.tgz#693f13dd3d3f5f19c3af9389643328328496ace5" + integrity sha512-TBMk2H9nV8JMOsLztalhzS6HgthG5SCHKYkR2MaW7eSZuSGotbSP22aJip8HgQZ/lPMdOMb1lknHmd8WROxWHg== + dependencies: + "@parcel/plugin" "2.8.3" + "@parcel/transformer-js@2.8.3": version "2.8.3" resolved "https://registry.yarnpkg.com/@parcel/transformer-js/-/transformer-js-2.8.3.tgz#fe400df428394d1e7fe5afb6dea5c7c858e44f03" diff --git a/server/go.mod b/server/go.mod index 1b83d5f..7e5b4fb 100644 --- a/server/go.mod +++ b/server/go.mod @@ -20,7 +20,7 @@ require ( github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect - golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect + golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect diff --git a/server/go.sum b/server/go.sum index a709198..fed92be 100644 --- a/server/go.sum +++ b/server/go.sum @@ -77,7 +77,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucas-clemente/quic-go v0.31.0 h1:MfNp3fk0wjWRajw6quMFA3ap1AVtlU+2mtwmbVogB2M= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= @@ -153,8 +152,8 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= +golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= diff --git a/server/internal/warp/warp.go b/server/internal/warp/server.go similarity index 100% rename from server/internal/warp/warp.go rename to server/internal/warp/server.go diff --git a/server/internal/warp/session.go b/server/internal/warp/session.go index d52c42f..a267619 100644 --- a/server/internal/warp/session.go +++ b/server/internal/warp/session.go @@ -176,6 +176,11 @@ func (s *Session) writeInit(ctx context.Context, init *MediaInit) (err error) { return fmt.Errorf("failed to create stream: %w", err) } + if temp == nil { + // Not sure when this happens, perhaps when closing a connection? + return fmt.Errorf("received a nil stream from quic-go") + } + // Wrap the stream in an object that buffers writes instead of blocking. stream := NewStream(temp) s.streams.Add(stream.Run) @@ -217,7 +222,7 @@ func (s *Session) writeSegment(ctx context.Context, segment *MediaSegment) (err if temp == nil { // Not sure when this happens, perhaps when closing a connection? - return fmt.Errorf("received a nil stream from quic-go: %w", err) + return fmt.Errorf("received a nil stream from quic-go") } // Wrap the stream in an object that buffers writes instead of blocking. diff --git a/server/main.go b/server/main.go index a0b5f9b..9689c89 100644 --- a/server/main.go +++ b/server/main.go @@ -56,6 +56,8 @@ func run(ctx context.Context) (err error) { hash := sha256.Sum256(tlsCert.Certificate[0]) fingerprint := hex.EncodeToString(hash[:]) + fmt.Println(fingerprint) + webConfig := web.Config{ Addr: *addr, CertFile: *cert, From 0240af66cd693a3af496e5fe7ab9e759b3792ddb Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 30 Mar 2023 10:44:37 +0900 Subject: [PATCH 05/10] Fix the video renderer. --- player/fingerprint.sha256 | 1 - player/src/transport/.gitignore | 1 + player/src/transport/fingerprint.hex | 1 - player/src/transport/index.ts | 4 ++- player/src/video/renderer.ts | 37 +++++++++++++++++++++++----- server/main.go | 2 -- 6 files changed, 35 insertions(+), 11 deletions(-) delete mode 100644 player/fingerprint.sha256 create mode 100644 player/src/transport/.gitignore delete mode 100644 player/src/transport/fingerprint.hex diff --git a/player/fingerprint.sha256 b/player/fingerprint.sha256 deleted file mode 100644 index 3545bf9..0000000 --- a/player/fingerprint.sha256 +++ /dev/null @@ -1 +0,0 @@ -50e151a79d06af6b9289180af319ca974e227d9ec58c30362c9304e56df3e3ea diff --git a/player/src/transport/.gitignore b/player/src/transport/.gitignore new file mode 100644 index 0000000..628e634 --- /dev/null +++ b/player/src/transport/.gitignore @@ -0,0 +1 @@ +fingerprint.hex diff --git a/player/src/transport/fingerprint.hex b/player/src/transport/fingerprint.hex deleted file mode 100644 index 8e821ec..0000000 --- a/player/src/transport/fingerprint.hex +++ /dev/null @@ -1 +0,0 @@ -642ab03bc58c48ed614e6523d93c2455d8223aedfbdcba7f502ca0ea08590921 diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index 6c45ac9..677ac0d 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -3,6 +3,8 @@ import * as Stream from "../stream" import * as MP4 from "../mp4" import Video from "../video/index" + +// @ts-ignore bundler embeds data import fingerprint from 'bundle-text:./fingerprint.hex'; /// @@ -46,7 +48,7 @@ export class Player { async connect(url: string): Promise { // Convert the hex to binary. let hash = []; - for (let c = 0; c < fingerprint.length; c += 2) { + for (let c = 0; c < fingerprint.length-1; c += 2) { hash.push(parseInt(fingerprint.substring(c, c+2), 16)); } diff --git a/player/src/video/renderer.ts b/player/src/video/renderer.ts index 95c5cd0..98170ab 100644 --- a/player/src/video/renderer.ts +++ b/player/src/video/renderer.ts @@ -7,11 +7,14 @@ export class Renderer { sync: DOMHighResTimeStamp; // the wall clock value for timestamp 0 last?: number; // the timestamp of the last rendered frame + maxDuration: number; // the maximum duration allowed in the buffer + constructor(config: Message.Config) { this.canvas = config.canvas; this.queue = []; this.render = 0; this.sync = 0; + this.maxDuration = 10 * 1000 } push(frame: VideoFrame) { @@ -27,14 +30,36 @@ export class Renderer { } // Insert the frame into the queue sorted by timestamp. - // TODO loop backwards for better performance - let index = this.queue.findIndex(other => { - return frame.timestamp < other.timestamp; - }) + let low = 0 + let high = this.queue.length; - // Insert into the queue. - this.queue.splice(index, 0, frame) + // Fast path because we normally append to the end. + if (this.queue.length > 0 && this.queue[this.queue.length].timestamp <= frame.timestamp) { + this.queue.push(frame) + } else { + // Do a full binary search + while (low < high) { + var mid = (low + high) >>> 1; + if (this.queue[mid].timestamp < frame.timestamp) low = mid + 1; + else high = mid; + } + this.queue.splice(low, 0, frame) + } + + // Trim the max size of the buffer + const last = this.queue[this.queue.length-1].timestamp + while (1) { + const first = this.queue[0] + if (first.timestamp + this.maxDuration >= last) { + break + } + + first.close() + this.queue.shift() + } + + // Queue up to render the next frame. if (!this.render) { this.render = self.requestAnimationFrame(this.draw.bind(this)) } diff --git a/server/main.go b/server/main.go index 9689c89..a0b5f9b 100644 --- a/server/main.go +++ b/server/main.go @@ -56,8 +56,6 @@ func run(ctx context.Context) (err error) { hash := sha256.Sum256(tlsCert.Certificate[0]) fingerprint := hex.EncodeToString(hash[:]) - fmt.Println(fingerprint) - webConfig := web.Config{ Addr: *addr, CertFile: *cert, From 032c49ab50b1a934ac78760bd02695944138f373 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 1 Apr 2023 19:04:08 +0900 Subject: [PATCH 06/10] Initial work on audio while airbourne. --- player/src/audio/decoder.ts | 75 ++++++++--------- player/src/audio/index.ts | 6 +- player/src/audio/message.ts | 12 +-- player/src/audio/renderer.ts | 20 ++++- player/src/audio/source/index.ts | 64 ++++++++++++++ player/src/audio/source/message.ts | 11 +++ player/src/audio/source/ring.ts | 131 +++++++++++++++++++++++++++++ player/src/audio/source/worklet.ts | 68 +++++++++++++++ player/src/audio/worker.ts | 12 ++- player/src/transport/index.ts | 33 ++++++-- player/src/video/decoder.ts | 8 +- player/src/video/renderer.ts | 2 +- 12 files changed, 376 insertions(+), 66 deletions(-) create mode 100644 player/src/audio/source/index.ts create mode 100644 player/src/audio/source/message.ts create mode 100644 player/src/audio/source/ring.ts create mode 100644 player/src/audio/source/worklet.ts diff --git a/player/src/audio/decoder.ts b/player/src/audio/decoder.ts index c1b813f..5c1d654 100644 --- a/player/src/audio/decoder.ts +++ b/player/src/audio/decoder.ts @@ -1,10 +1,13 @@ import * as Message from "./message"; -import { InitParser } from "../mp4/init"; +import * as MP4 from "../mp4" +import * as Stream from "../stream" +import * as Util from "../util" + import { Renderer } from "./renderer" -import { MP4New, MP4Sample, MP4ArrayBuffer } from "../mp4/index" export class Decoder { - tracks: Map; + // Store the init message for each track + tracks: Map> renderer: Renderer; constructor(renderer: Renderer) { @@ -13,43 +16,34 @@ export class Decoder { } async init(msg: Message.Init) { - let track = this.tracks.get(msg.track); - if (!track) { - track = new InitParser() - this.tracks.set(msg.track, track) + let track = this.tracks.get(msg.track); + if (!track) { + track = new Util.Deferred() + this.tracks.set(msg.track, track) + } + + if (msg.info.audioTracks.length != 1 || msg.info.videoTracks.length != 0) { + throw new Error("Expected a single audio track") } - while (1) { - const data = await msg.stream.read() - if (!data) break - - track.init(data) - } - - // TODO this will hang on incomplete data - const init = await track.ready; - const info = init.info; - - if (info.audioTracks.length != 1 || info.videoTracks.length != 0) { - throw new Error("expected a single audio track") - } + track.resolve(msg) } async decode(msg: Message.Segment) { let track = this.tracks.get(msg.track); if (!track) { - track = new InitParser() + track = new Util.Deferred() this.tracks.set(msg.track, track) } // Wait for the init segment to be fully received and parsed - const init = await track.ready; + const init = await track.promise; const info = init.info; - const video = info.videoTracks[0] + const audio = info.audioTracks[0] const decoder = new AudioDecoder({ output: (frame: AudioFrame) => { - this.renderer.push(frame) + this.renderer.emit(frame) }, error: (err: Error) => { console.warn(err) @@ -57,21 +51,19 @@ export class Decoder { }); decoder.configure({ - codec: info.mime, - // TODO what else? + codec: audio.codec, // optimizeForLatency: true }) - const input = MP4New(); + const input = MP4.New(); - input.onSamples = (id: number, user: any, samples: MP4Sample[]) => { + input.onSamples = (id: number, user: any, samples: MP4.Sample[]) => { for (let sample of samples) { - const timestamp = sample.dts / (1000 / info.timescale) // milliseconds - + // TODO this assumes that timescale == sample rate decoder.decode(new EncodedAudioChunk({ data: sample.data, duration: sample.duration, - timestamp: timestamp, + timestamp: sample.dts, })) } } @@ -81,13 +73,16 @@ export class Decoder { input.start(); } - let offset = 0 - // MP4box requires us to reparse the init segment unfortunately - for (let raw of track.raw) { - offset = input.appendBuffer(raw) + let offset = 0; + + for (let raw of init.raw) { + raw.fileStart = offset + input.appendBuffer(raw) } + const stream = new Stream.Reader(msg.reader, msg.buffer) + /* TODO I'm not actually sure why this code doesn't work; something trips up the MP4 parser while (1) { const data = await stream.read() @@ -99,17 +94,17 @@ export class Decoder { */ // One day I'll figure it out; until then read one top-level atom at a time - while (!await msg.stream.done()) { - const raw = await msg.stream.peek(4) + while (!await stream.done()) { + const raw = await stream.peek(4) const size = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint32(0) - const atom = await msg.stream.bytes(size) + const atom = await stream.bytes(size) // Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately let box = new Uint8Array(atom.byteLength); box.set(atom) // and for some reason we need to modify the underlying ArrayBuffer with offset - let buffer = box.buffer as MP4ArrayBuffer + let buffer = box.buffer as MP4.ArrayBufferOffset buffer.fileStart = offset // Parse the data diff --git a/player/src/audio/index.ts b/player/src/audio/index.ts index 517a3f4..3c06976 100644 --- a/player/src/audio/index.ts +++ b/player/src/audio/index.ts @@ -6,14 +6,14 @@ export default class Audio { constructor(config: Message.Config) { this.worker = new Worker(new URL('worker.ts', import.meta.url), { type: "module" }) - this.worker.postMessage({ config }, [ ]) + this.worker.postMessage({ config }, []) } init(init: Message.Init) { - this.worker.postMessage({ init }, [ init.stream.buffer, init.stream.reader ]) + this.worker.postMessage({ init }) // note: we copy the raw init bytes each time } segment(segment: Message.Segment) { - this.worker.postMessage({ segment }, [ segment.stream.buffer, segment.stream.reader ]) + this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ]) } } \ No newline at end of file diff --git a/player/src/audio/message.ts b/player/src/audio/message.ts index 9845708..f9e39e5 100644 --- a/player/src/audio/message.ts +++ b/player/src/audio/message.ts @@ -1,15 +1,17 @@ -import Reader from "../stream/reader"; +import * as MP4 from "../mp4" export interface Config { - canvas: OffscreenCanvas; + // temporarily empty } export interface Init { track: string; - stream: Reader; + info: MP4.Info; + raw: MP4.ArrayBufferOffset[]; } export interface Segment { track: string; - stream: Reader; -} \ No newline at end of file + buffer: Uint8Array; // unread buffered data + reader: ReadableStream; // unread unbuffered data +} diff --git a/player/src/audio/renderer.ts b/player/src/audio/renderer.ts index 774e11b..5a704cd 100644 --- a/player/src/audio/renderer.ts +++ b/player/src/audio/renderer.ts @@ -1,12 +1,26 @@ import * as Message from "./message"; +import Source from "./source"; + export class Renderer { + ctx: AudioContext; + source: Source; + render: number; // non-zero if requestAnimationFrame has been called - sync: DOMHighResTimeStamp; // the wall clock value for timestamp 0 last?: number; // the timestamp of the last rendered frame - constructor(config: Message.Config) { + maxDuration: number; // the maximum duration allowed in the buffer + + constructor() { this.render = 0; - this.sync = 0; + this.maxDuration = 10 * 1000 + + // TODO evaluate { latencyHint: "interactive" } + this.ctx = new AudioContext() + this.source = new Source(this.ctx) + } + + emit(frame: AudioFrame) { + this.source.emit(frame) } } \ No newline at end of file diff --git a/player/src/audio/source/index.ts b/player/src/audio/source/index.ts new file mode 100644 index 0000000..c0f8421 --- /dev/null +++ b/player/src/audio/source/index.ts @@ -0,0 +1,64 @@ +import * as Message from "./message" +import Ring from "./ring" + +// Wrapper around the AudioWorklet API to make it easier to use. +export default class Source { + ctx: AudioContext; + worklet?: AudioWorkletNode; // async initialization + channels: Ring[]; + + ready: Promise; + + constructor(ctx: AudioContext) { + this.ctx = ctx + + // two channels, holding a maximum of 1s at 44khz + this.channels = [ + new Ring(44000), + new Ring(44000), + ] + + // Start loading the worklet + this.ready = this.setup() + } + + private async setup(): Promise { + // Load the worklet source code. + await this.ctx.audioWorklet.addModule('worklet.ts') + + // Create a worklet + this.worklet = new AudioWorkletNode(this.ctx, 'source'); + + this.worklet.port.onmessage = this.onMessage.bind(this) + + this.worklet.onprocessorerror = (e: Event) => { + console.error("Audio worklet error:", e); + }; + + const config: Message.Config = { + channels: this.channels, + } + + this.worklet.port.postMessage({ config }) + } + + private async onMessage(e: MessageEvent) { + if (e.data.configReply) { + const reply = e.data.configReply as Message.ConfigReply + + if (reply.error) { + throw reply.error + } + + // Start playback + this.worklet?.connect(this.ctx.destination); + } + } + + emit(frame: AudioFrame) { + for (let i = 0; i < frame.channels; i += 1) { + const ring = this.channels[i] + ring.set(frame, i) + } + } +} \ No newline at end of file diff --git a/player/src/audio/source/message.ts b/player/src/audio/source/message.ts new file mode 100644 index 0000000..9f21a71 --- /dev/null +++ b/player/src/audio/source/message.ts @@ -0,0 +1,11 @@ +import Ring from "./ring" + +// Sent to the worklet to share ring buffers. +export interface Config { + channels: Ring[]; +} + +// Reply from the worklet indicating when the configuration was suscessful. +export interface ConfigReply { + error?: Error; +} \ No newline at end of file diff --git a/player/src/audio/source/ring.ts b/player/src/audio/source/ring.ts new file mode 100644 index 0000000..dedb1d0 --- /dev/null +++ b/player/src/audio/source/ring.ts @@ -0,0 +1,131 @@ +// Ring buffer with audio samples. + +// TODO typescript enums when I have internet access +const STATE = { + START: 0, + END: 1, +} + +export default class Ring { + state: SharedArrayBuffer; + stateView: Int32Array; + + buffer: SharedArrayBuffer; + capacity: number; + + constructor(samples: number) { + this.state = new SharedArrayBuffer(Object.keys(STATE).length * Int32Array.BYTES_PER_ELEMENT) + this.stateView = new Int32Array(this.state) + + this.setStart(0) + this.setEnd(0) + + this.capacity = samples; + + // TODO better way to loop in modern Javascript? + this.buffer = new SharedArrayBuffer(samples * Float32Array.BYTES_PER_ELEMENT) + } + + setStart(start: number) { + return Atomics.store(this.stateView, STATE.START, start); + } + + getStart(): number { + return Atomics.load(this.stateView, STATE.START); + } + + setEnd(end: number) { + return Atomics.store(this.stateView, STATE.START, end); + } + + getEnd(): number { + return Atomics.load(this.stateView, STATE.END); + } + + set(frame: AudioFrame, channel: number) { + let start = this.getStart() + + // The number of samples to skip at the start. + let offset = start - frame.timestamp; + if (offset > 0) { + console.warn("dropping old samples", offset) + } else { + offset = 0 + } + + let count = frame.numberOfFrames - offset; + if (count <= 0) { + frame.close() + + // Skip the entire frame + return + } + + if (start + this.capacity < frame.timestamp + count) { + // The renderer has to buffer frames; we have a fixed capacity. + // TODO maybe it's better to buffer here instead. + throw new Error("exceeded capacity") + } + + let end = this.getEnd() + + const startIndex = start % this.capacity; + const endIndex = end % this.capacity; + + if (startIndex < endIndex) { + // One continuous range to copy. + const full = new Float32Array(this.buffer, startIndex, endIndex-startIndex) + + frame.copyTo(full, { + planeIndex: channel, + frameOffset: offset, + frameCount: count, + }) + } else { + // Wrapped around the ring buffer, so we have to copy twice. + const wrap = this.capacity - startIndex; + + const first = new Float32Array(this.buffer, startIndex) + const second = new Float32Array(this.buffer, 0, endIndex) + + frame.copyTo(first, { + planeIndex: channel, + frameOffset: offset, + frameCount: wrap, + }) + + frame.copyTo(second, { + planeIndex: channel, + frameOffset: offset + wrap, + frameCount: endIndex, + }) + } + + // TODO insert silence when index > end + if (frame.timestamp + count > end) { + end = frame.timestamp + count + this.setEnd(end) + } + } + + peek(count: number): Float32Array[] { + const start = this.getStart() + const end = this.getEnd() + + const startIndex = start % this.capacity; + const endIndex = end % this.capacity; + + if (startIndex < endIndex) { + const full = new Float32Array(this.buffer, startIndex, endIndex - startIndex) + return [ full ] + } else { + const first = new Float32Array(this.buffer, startIndex) + const second = new Float32Array(this.buffer, 0, endIndex) + return [ first, second ] + } + } + + advance(count: number) { + this.setStart(this.getStart() + count) + } +} \ No newline at end of file diff --git a/player/src/audio/source/worklet.ts b/player/src/audio/source/worklet.ts new file mode 100644 index 0000000..8ade137 --- /dev/null +++ b/player/src/audio/source/worklet.ts @@ -0,0 +1,68 @@ +// This is an AudioWorklet that acts as a media source. +// The renderer copies audio samples to a ring buffer read by this worklet. +// The worklet then outputs those samples to emit audio. + +import * as Message from "../message" +import * as Util from "../../util" + +import Ring from "./ring" + +class Source extends AudioWorkletProcessor { + channels?: Ring[]; + + constructor() { + // The super constructor call is required. + super(); + + this.port.onmessage = (e: MessageEvent) => { + if (e.data.config) { + this.config(e.data.config as Message.Config) + } + } + } + + static get parameterDescriptors() { + return []; + } + + config(config: Message.Config) { + this.channels = config.channels; + } + + // TODO correct types + process(inputs: any, outputs: any, parameters: any) { + if (!this.channels) { + return + } + + if (outputs.length != 1) { + throw new Error("only a single track is supported") + } + + const track = outputs[0]; + + for (let i = 0; i < track.length; i += 1) { + const input = this.channels[i] + const output = track[i]; + + const parts = input.peek(output.length) + + let offset = 0 + for (let i = 0; i < parts.length; i += 1) { + output.set(parts[i], offset) + offset += parts[i].length + } + + if (offset < output.length) { + // TODO render silence + } + + // Always advance the full amount. + input.advance(output.length) + } + + return true; + } +} + +self.registerProcessor("source", Source); \ No newline at end of file diff --git a/player/src/audio/worker.ts b/player/src/audio/worker.ts index dec1c3f..028fc65 100644 --- a/player/src/audio/worker.ts +++ b/player/src/audio/worker.ts @@ -2,20 +2,24 @@ import { Renderer } from "./renderer" import { Decoder } from "./decoder" import * as Message from "./message" -let decoder: Decoder; let renderer: Renderer; +let decoder: Decoder; self.addEventListener('message', async (e: MessageEvent) => { if (e.data.config) { - const config = e.data.config as Message.Config + const config = e.data.config as Message.Config; - renderer = new Renderer(config) + renderer = new Renderer() decoder = new Decoder(renderer) } + if (e.data.init) { + const init = e.data.init as Message.Init + await decoder.init(init) + } + if (e.data.segment) { const segment = e.data.segment as Message.Segment - await decoder.decode(segment) } }) diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index 677ac0d..f9df8ee 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -2,7 +2,8 @@ import * as Message from "./message" import * as Stream from "../stream" import * as MP4 from "../mp4" -import Video from "../video/index" +import Audio from "../audio" +import Video from "../video" // @ts-ignore bundler embeds data import fingerprint from 'bundle-text:./fingerprint.hex'; @@ -19,13 +20,14 @@ export class Player { api: Promise; tracks: Map - //audio: Worker; + audio: Audio; video: Video; constructor(props: PlayerInit) { this.tracks = new Map(); - //this.audio = new Worker("../audio") + // TODO move these to another class so this only deals with the transport. + this.audio = new Audio({}) this.video = new Video({ canvas: props.canvas.transferControlToOffscreen(), }) @@ -52,7 +54,7 @@ export class Player { hash.push(parseInt(fingerprint.substring(c, c+2), 16)); } - const quic = new WebTransport(url, { + const quic = new WebTransport(url, { "serverCertificateHashes": [{ "algorithm": "sha-256", "value": new Uint8Array(hash), @@ -131,12 +133,20 @@ export class Player { throw new Error("expected a single track") } - if (info.videoTracks.length) { + if (info.audioTracks) { + this.audio.init({ + track: msg.id, + info: info, + raw: track.raw, + }) + } else if (info.videoTracks) { this.video.init({ track: msg.id, info: info, raw: track.raw, }) + } else { + throw new Error("init is neither audio nor video") } } @@ -147,16 +157,23 @@ export class Player { this.tracks.set(msg.init, track) } + // Wait until we learn if this is an audio or video track const info = await track.info - // Wait until we learn if this is an audio or video track - - if (info.videoTracks.length) { + if (info.audioTracks) { + this.audio.segment({ + track: msg.init, + buffer: stream.buffer, + reader: stream.reader, + }) + } else if (info.videoTracks) { this.video.segment({ track: msg.init, buffer: stream.buffer, reader: stream.reader, }) + } else { + throw new Error("segment is neither audio nor video") } } } \ No newline at end of file diff --git a/player/src/video/decoder.ts b/player/src/video/decoder.ts index b4d8c4e..276e2ff 100644 --- a/player/src/video/decoder.ts +++ b/player/src/video/decoder.ts @@ -22,6 +22,10 @@ export class Decoder { this.tracks.set(msg.track, track) } + if (msg.info.videoTracks.length != 1 || msg.info.audioTracks.length != 0) { + throw new Error("Expected a single video track") + } + track.resolve(msg) } @@ -39,7 +43,7 @@ export class Decoder { const decoder = new VideoDecoder({ output: (frame: VideoFrame) => { - this.renderer.push(frame) + this.renderer.emit(frame) }, error: (err: Error) => { console.warn(err) @@ -83,7 +87,7 @@ export class Decoder { // MP4box requires us to reparse the init segment unfortunately let offset = 0; - + for (let raw of init.raw) { raw.fileStart = offset input.appendBuffer(raw) diff --git a/player/src/video/renderer.ts b/player/src/video/renderer.ts index 98170ab..e863746 100644 --- a/player/src/video/renderer.ts +++ b/player/src/video/renderer.ts @@ -17,7 +17,7 @@ export class Renderer { this.maxDuration = 10 * 1000 } - push(frame: VideoFrame) { + emit(frame: VideoFrame) { if (!this.sync) { // Save the frame as the sync point this.sync = performance.now() - frame.timestamp From c8c856d6b8ed7694a4e7699f1b8fc2d5056eab7e Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 1 Apr 2023 18:34:10 -0700 Subject: [PATCH 07/10] WIP --- player/.proxyrc.js | 7 +++++++ player/package.json | 1 + player/src/audio/decoder.ts | 9 +++++++-- player/src/audio/index.ts | 18 ++++++++++-------- player/src/audio/message.ts | 2 +- player/src/audio/renderer.ts | 8 +++----- player/src/audio/source/index.ts | 2 +- player/src/audio/worker.ts | 4 ++-- player/src/index.css | 6 ------ player/src/transport/index.ts | 12 +++++++----- player/yarn.lock | 5 +++++ 11 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 player/.proxyrc.js diff --git a/player/.proxyrc.js b/player/.proxyrc.js new file mode 100644 index 0000000..4d53c59 --- /dev/null +++ b/player/.proxyrc.js @@ -0,0 +1,7 @@ +module.exports = function (app) { + app.use((req, res, next) => { + res.setHeader('Cross-Origin-Opener-Policy', 'same-origin'); + res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp'); + next(); + }); +}; diff --git a/player/package.json b/player/package.json index 0c4f4be..8238f94 100644 --- a/player/package.json +++ b/player/package.json @@ -8,6 +8,7 @@ "devDependencies": { "@parcel/transformer-inline-string": "2.8.3", "@parcel/validator-typescript": "^2.6.0", + "@types/audioworklet": "^0.0.41", "@types/dom-webcodecs": "^0.1.6", "parcel": "^2.8.0", "typescript": ">=3.0.0" diff --git a/player/src/audio/decoder.ts b/player/src/audio/decoder.ts index 5c1d654..baf76dd 100644 --- a/player/src/audio/decoder.ts +++ b/player/src/audio/decoder.ts @@ -10,7 +10,7 @@ export class Decoder { tracks: Map> renderer: Renderer; - constructor(renderer: Renderer) { + constructor(config: Message.Config, renderer: Renderer) { this.tracks = new Map(); this.renderer = renderer; } @@ -22,6 +22,7 @@ export class Decoder { this.tracks.set(msg.track, track) } + console.log(msg.info) if (msg.info.audioTracks.length != 1 || msg.info.videoTracks.length != 0) { throw new Error("Expected a single audio track") } @@ -42,16 +43,19 @@ export class Decoder { const audio = info.audioTracks[0] const decoder = new AudioDecoder({ - output: (frame: AudioFrame) => { + output: (frame: AudioData) => { this.renderer.emit(frame) }, error: (err: Error) => { console.warn(err) } }); + console.log(audio) decoder.configure({ codec: audio.codec, + numberOfChannels: audio.audio.channel_count, + sampleRate: audio.audio.sample_rate, // optimizeForLatency: true }) @@ -61,6 +65,7 @@ export class Decoder { for (let sample of samples) { // TODO this assumes that timescale == sample rate decoder.decode(new EncodedAudioChunk({ + type: sample.is_sync ? "key" : "delta", data: sample.data, duration: sample.duration, timestamp: sample.dts, diff --git a/player/src/audio/index.ts b/player/src/audio/index.ts index 3c06976..670e874 100644 --- a/player/src/audio/index.ts +++ b/player/src/audio/index.ts @@ -1,19 +1,21 @@ import * as Message from "./message" +import { Renderer } from "./renderer" +import { Decoder } from "./decoder" -// Wrapper around the WebWorker API export default class Audio { - worker: Worker; + renderer: Renderer; + decoder: Decoder; constructor(config: Message.Config) { - this.worker = new Worker(new URL('worker.ts', import.meta.url), { type: "module" }) - this.worker.postMessage({ config }, []) + this.renderer = new Renderer(config) + this.decoder = new Decoder(config, this.renderer) } - init(init: Message.Init) { - this.worker.postMessage({ init }) // note: we copy the raw init bytes each time + async init(init: Message.Init) { + await this.decoder.init(init) } - segment(segment: Message.Segment) { - this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ]) + async segment(segment: Message.Segment) { + await this.decoder.decode(segment) } } \ No newline at end of file diff --git a/player/src/audio/message.ts b/player/src/audio/message.ts index f9e39e5..39bab14 100644 --- a/player/src/audio/message.ts +++ b/player/src/audio/message.ts @@ -1,7 +1,7 @@ import * as MP4 from "../mp4" export interface Config { - // temporarily empty + ctx: AudioContext; } export interface Init { diff --git a/player/src/audio/renderer.ts b/player/src/audio/renderer.ts index 5a704cd..a379685 100644 --- a/player/src/audio/renderer.ts +++ b/player/src/audio/renderer.ts @@ -3,7 +3,6 @@ import * as Message from "./message"; import Source from "./source"; export class Renderer { - ctx: AudioContext; source: Source; render: number; // non-zero if requestAnimationFrame has been called @@ -11,16 +10,15 @@ export class Renderer { maxDuration: number; // the maximum duration allowed in the buffer - constructor() { + constructor(config: Message.Config) { this.render = 0; this.maxDuration = 10 * 1000 // TODO evaluate { latencyHint: "interactive" } - this.ctx = new AudioContext() - this.source = new Source(this.ctx) + this.source = new Source(config.ctx) } - emit(frame: AudioFrame) { + emit(frame: AudioData) { this.source.emit(frame) } } \ No newline at end of file diff --git a/player/src/audio/source/index.ts b/player/src/audio/source/index.ts index c0f8421..1d70385 100644 --- a/player/src/audio/source/index.ts +++ b/player/src/audio/source/index.ts @@ -55,7 +55,7 @@ export default class Source { } } - emit(frame: AudioFrame) { + emit(frame: AudioData) { for (let i = 0; i < frame.channels; i += 1) { const ring = this.channels[i] ring.set(frame, i) diff --git a/player/src/audio/worker.ts b/player/src/audio/worker.ts index 028fc65..bd5ec76 100644 --- a/player/src/audio/worker.ts +++ b/player/src/audio/worker.ts @@ -9,8 +9,8 @@ self.addEventListener('message', async (e: MessageEvent) => { if (e.data.config) { const config = e.data.config as Message.Config; - renderer = new Renderer() - decoder = new Decoder(renderer) + renderer = new Renderer(config) + decoder = new Decoder(config, renderer) } if (e.data.init) { diff --git a/player/src/index.css b/player/src/index.css index 844f581..c406cf4 100644 --- a/player/src/index.css +++ b/player/src/index.css @@ -29,12 +29,6 @@ body { z-index: 1; } -#vid { - width: 100%; - height: 100%; - max-height: 100vh; -} - #controls { display: flex; flex-wrap: wrap; diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index f9df8ee..39243c0 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -27,7 +27,9 @@ export class Player { this.tracks = new Map(); // TODO move these to another class so this only deals with the transport. - this.audio = new Audio({}) + this.audio = new Audio({ + ctx: new AudioContext(), + }) this.video = new Video({ canvas: props.canvas.transferControlToOffscreen(), }) @@ -133,13 +135,13 @@ export class Player { throw new Error("expected a single track") } - if (info.audioTracks) { + if (info.audioTracks.length) { this.audio.init({ track: msg.id, info: info, raw: track.raw, }) - } else if (info.videoTracks) { + } else if (info.videoTracks.length) { this.video.init({ track: msg.id, info: info, @@ -160,13 +162,13 @@ export class Player { // Wait until we learn if this is an audio or video track const info = await track.info - if (info.audioTracks) { + if (info.audioTracks.length) { this.audio.segment({ track: msg.init, buffer: stream.buffer, reader: stream.reader, }) - } else if (info.videoTracks) { + } else if (info.videoTracks.length) { this.video.segment({ track: msg.init, buffer: stream.buffer, diff --git a/player/yarn.lock b/player/yarn.lock index 24f0b4e..c4a13b7 100644 --- a/player/yarn.lock +++ b/player/yarn.lock @@ -743,6 +743,11 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@types/audioworklet@^0.0.41": + version "0.0.41" + resolved "https://registry.yarnpkg.com/@types/audioworklet/-/audioworklet-0.0.41.tgz#ed2e67c7dcbe207974c54812e21819984c74a4f0" + integrity sha512-8BWffzGoSRz436IviQVPye75YYWfac4OKdcLgkZxb3APZxSmAOp2SMtsH1yuM1x57/z/J7bsm05Yq98Hzk1t/w== + "@types/dom-webcodecs@^0.1.6": version "0.1.6" resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.6.tgz#204f2c8c2e6286114e1ec2997630bc0fd2081ffa" From a5278e6e04e33bdf4e095c34db8bad77cf1f808d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 1 Apr 2023 22:46:11 -0700 Subject: [PATCH 08/10] Load MP4Box from npm with correct typescript definitions. --- player/package.json | 3 + player/src/audio/decoder.ts | 2 +- player/src/audio/source/index.ts | 2 +- player/src/mp4/index.ts | 92 +- player/src/mp4/init.ts | 6 +- player/src/mp4/mp4box.all.js | 8247 ----------------- player/src/mp4/mp4box.d.ts | 148 + player/src/transport/index.ts | 2 - .../{types => transport}/webtransport.d.ts | 0 player/tsconfig.json | 4 - player/yarn.lock | 5 + 11 files changed, 169 insertions(+), 8342 deletions(-) delete mode 100644 player/src/mp4/mp4box.all.js create mode 100644 player/src/mp4/mp4box.d.ts rename player/src/{types => transport}/webtransport.d.ts (100%) diff --git a/player/package.json b/player/package.json index 8238f94..0fe1770 100644 --- a/player/package.json +++ b/player/package.json @@ -12,5 +12,8 @@ "@types/dom-webcodecs": "^0.1.6", "parcel": "^2.8.0", "typescript": ">=3.0.0" + }, + "dependencies": { + "mp4box": "^0.5.2" } } diff --git a/player/src/audio/decoder.ts b/player/src/audio/decoder.ts index baf76dd..388d326 100644 --- a/player/src/audio/decoder.ts +++ b/player/src/audio/decoder.ts @@ -109,7 +109,7 @@ export class Decoder { box.set(atom) // and for some reason we need to modify the underlying ArrayBuffer with offset - let buffer = box.buffer as MP4.ArrayBufferOffset + let buffer = box.buffer as MP4.ArrayBuffer buffer.fileStart = offset // Parse the data diff --git a/player/src/audio/source/index.ts b/player/src/audio/source/index.ts index 1d70385..4f2ebd0 100644 --- a/player/src/audio/source/index.ts +++ b/player/src/audio/source/index.ts @@ -56,7 +56,7 @@ export default class Source { } emit(frame: AudioData) { - for (let i = 0; i < frame.channels; i += 1) { + for (let i = 0; i < frame.numberOfChannels; i += 1) { const ring = this.channels[i] ring.set(frame, i) } diff --git a/player/src/mp4/index.ts b/player/src/mp4/index.ts index 7868f32..e05fbce 100644 --- a/player/src/mp4/index.ts +++ b/player/src/mp4/index.ts @@ -1,87 +1,11 @@ -// Wrapper around MP4Box to play nicely with MP4Box. -// I tried getting a mp4box.all.d.ts file to work but just couldn't figure it out -import { createFile, ISOFile, DataStream, BoxParser } from "./mp4box.all" - // Rename some stuff so it's on brand. -export { createFile as New, ISOFile as File, DataStream as Stream, BoxParser as Parser } - -export type ArrayBufferOffset = ArrayBuffer & {fileStart: number}; - -export interface MediaTrack { - id: number; - created: Date; - modified: Date; - movie_duration: number; - layer: number; - alternate_group: number; - volume: number; - track_width: number; - track_height: number; - timescale: number; - duration: number; - bitrate: number; - codec: string; - language: string; - nb_samples: number; -} - -export interface VideoData { - width: number; - height: number; -} - -export interface VideoTrack extends MediaTrack { - video: VideoData; -} - -export interface MP4AudioData { - sample_rate: number; - channel_count: number; - sample_size: number; -} - -export interface AudioTrack extends MediaTrack { - audio: MP4AudioData; -} - -export type Track = VideoTrack | AudioTrack; - -export interface Info { - duration: number; - timescale: number; - fragment_duration: number; - isFragmented: boolean; - isProgressive: boolean; - hasIOD: boolean; - brands: string[]; - created: Date; - modified: Date; - tracks: Track[]; - mime: string; - videoTracks: Track[]; - audioTracks: Track[]; -} - -export interface Sample { - number: number; - track_id: number; - timescale: number; - description_index: number; - description: any; - data: ArrayBuffer; - size: number; - alreadyRead: number; - duration: number; - cts: number; - dts: number; - is_sync: boolean; - is_leading: number; - depends_on: number; - is_depended_on: number; - has_redundancy: number; - degration_priority: number; - offset: number; - subsamples: any; -} +export { + createFile as New, + MP4File as File, + MP4ArrayBuffer as ArrayBuffer, + MP4Info as Info, + DataStream as Stream, + Sample, +} from "mp4box" export { Init, InitParser } from "./init" \ No newline at end of file diff --git a/player/src/mp4/init.ts b/player/src/mp4/init.ts index 0c94c7b..d36c25b 100644 --- a/player/src/mp4/init.ts +++ b/player/src/mp4/init.ts @@ -1,7 +1,7 @@ import * as MP4 from "./index" export interface Init { - raw: MP4.ArrayBufferOffset; + raw: MP4.ArrayBuffer; info: MP4.Info; } @@ -9,7 +9,7 @@ export class InitParser { mp4box: MP4.File; offset: number; - raw: MP4.ArrayBufferOffset[]; + raw: MP4.ArrayBuffer[]; info: Promise; constructor() { @@ -42,7 +42,7 @@ export class InitParser { box.set(data) // and for some reason we need to modify the underlying ArrayBuffer with fileStart - let buffer = box.buffer as MP4.ArrayBufferOffset + let buffer = box.buffer as MP4.ArrayBuffer buffer.fileStart = this.offset // Parse the data diff --git a/player/src/mp4/mp4box.all.js b/player/src/mp4/mp4box.all.js deleted file mode 100644 index 4528833..0000000 --- a/player/src/mp4/mp4box.all.js +++ /dev/null @@ -1,8247 +0,0 @@ -// file:src/log.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var Log = (function (){ - var start = new Date(); - var LOG_LEVEL_ERROR = 4; - var LOG_LEVEL_WARNING = 3; - var LOG_LEVEL_INFO = 2; - var LOG_LEVEL_DEBUG = 1; - var log_level = LOG_LEVEL_ERROR; - var logObject = { - setLogLevel : function(level) { - if (level == this.debug) log_level = LOG_LEVEL_DEBUG; - else if (level == this.info) log_level = LOG_LEVEL_INFO; - else if (level == this.warn) log_level = LOG_LEVEL_WARNING; - else if (level == this.error) log_level = LOG_LEVEL_ERROR; - else log_level = LOG_LEVEL_ERROR; - }, - debug : function(module, msg) { - if (console.debug === undefined) { - console.debug = console.log; - } - if (LOG_LEVEL_DEBUG >= log_level) { - console.debug("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - }, - log : function(module, msg) { - this.debug(module.msg) - }, - info : function(module, msg) { - if (LOG_LEVEL_INFO >= log_level) { - console.info("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - }, - warn : function(module, msg) { - if (LOG_LEVEL_WARNING >= log_level) { - console.warn("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - }, - error : function(module, msg) { - if (LOG_LEVEL_ERROR >= log_level) { - console.error("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - } - }; - return logObject; - })(); - -/* Helper function to print a duration value in the form H:MM:SS.MS */ -Log.getDurationString = function(duration, _timescale) { - var neg; - /* Helper function to print a number on a fixed number of digits */ - function pad(number, length) { - var str = '' + number; - var a = str.split('.'); - while (a[0].length < length) { - a[0] = '0' + a[0]; - } - return a.join('.'); - } - if (duration < 0) { - neg = true; - duration = -duration; - } else { - neg = false; - } - var timescale = _timescale || 1; - var duration_sec = duration/timescale; - var hours = Math.floor(duration_sec/3600); - duration_sec -= hours * 3600; - var minutes = Math.floor(duration_sec/60); - duration_sec -= minutes * 60; - var msec = duration_sec*1000; - duration_sec = Math.floor(duration_sec); - msec -= duration_sec*1000; - msec = Math.floor(msec); - return (neg ? "-": "")+hours+":"+pad(minutes,2)+":"+pad(duration_sec,2)+"."+pad(msec,3); -} - -/* Helper function to stringify HTML5 TimeRanges objects */ -Log.printRanges = function(ranges) { - var length = ranges.length; - if (length > 0) { - var str = ""; - for (var i = 0; i < length; i++) { - if (i > 0) str += ","; - str += "["+Log.getDurationString(ranges.start(i))+ ","+Log.getDurationString(ranges.end(i))+"]"; - } - return str; - } else { - return "(empty)"; - } -} - -if (typeof exports !== 'undefined') { - exports.Log = Log; -} -// file:src/stream.js -var MP4BoxStream = function(arrayBuffer) { - if (arrayBuffer instanceof ArrayBuffer) { - this.buffer = arrayBuffer; - this.dataview = new DataView(arrayBuffer); - } else { - throw ("Needs an array buffer"); - } - this.position = 0; -}; - -/************************************************************************* - Common API between MultiBufferStream and SimpleStream - *************************************************************************/ -MP4BoxStream.prototype.getPosition = function() { - return this.position; -} - -MP4BoxStream.prototype.getEndPosition = function() { - return this.buffer.byteLength; -} - -MP4BoxStream.prototype.getLength = function() { - return this.buffer.byteLength; -} - -MP4BoxStream.prototype.seek = function (pos) { - var npos = Math.max(0, Math.min(this.buffer.byteLength, pos)); - this.position = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; - return true; -} - -MP4BoxStream.prototype.isEos = function () { - return this.getPosition() >= this.getEndPosition(); -} - -/************************************************************************* - Read methods, simimar to DataStream but simpler - *************************************************************************/ -MP4BoxStream.prototype.readAnyInt = function(size, signed) { - var res = 0; - if (this.position + size <= this.buffer.byteLength) { - switch (size) { - case 1: - if (signed) { - res = this.dataview.getInt8(this.position); - } else { - res = this.dataview.getUint8(this.position); - } - break; - case 2: - if (signed) { - res = this.dataview.getInt16(this.position); - } else { - res = this.dataview.getUint16(this.position); - } - break; - case 3: - if (signed) { - throw ("No method for reading signed 24 bits values"); - } else { - res = this.dataview.getUint8(this.position) << 16; - res |= this.dataview.getUint8(this.position+1) << 8; - res |= this.dataview.getUint8(this.position+2); - } - break; - case 4: - if (signed) { - res = this.dataview.getInt32(this.position); - } else { - res = this.dataview.getUint32(this.position); - } - break; - case 8: - if (signed) { - throw ("No method for reading signed 64 bits values"); - } else { - res = this.dataview.getUint32(this.position) << 32; - res |= this.dataview.getUint32(this.position+4); - } - break; - default: - throw ("readInt method not implemented for size: "+size); - } - this.position+= size; - return res; - } else { - throw ("Not enough bytes in buffer"); - } -} - -MP4BoxStream.prototype.readUint8 = function() { - return this.readAnyInt(1, false); -} - -MP4BoxStream.prototype.readUint16 = function() { - return this.readAnyInt(2, false); -} - -MP4BoxStream.prototype.readUint24 = function() { - return this.readAnyInt(3, false); -} - -MP4BoxStream.prototype.readUint32 = function() { - return this.readAnyInt(4, false); -} - -MP4BoxStream.prototype.readUint64 = function() { - return this.readAnyInt(8, false); -} - -MP4BoxStream.prototype.readString = function(length) { - if (this.position + length <= this.buffer.byteLength) { - var s = ""; - for (var i = 0; i < length; i++) { - s += String.fromCharCode(this.readUint8()); - } - return s; - } else { - throw ("Not enough bytes in buffer"); - } -} - -MP4BoxStream.prototype.readCString = function() { - var arr = []; - while(true) { - var b = this.readUint8(); - if (b !== 0) { - arr.push(b); - } else { - break; - } - } - return String.fromCharCode.apply(null, arr); -} - -MP4BoxStream.prototype.readInt8 = function() { - return this.readAnyInt(1, true); -} - -MP4BoxStream.prototype.readInt16 = function() { - return this.readAnyInt(2, true); -} - -MP4BoxStream.prototype.readInt32 = function() { - return this.readAnyInt(4, true); -} - -MP4BoxStream.prototype.readInt64 = function() { - return this.readAnyInt(8, false); -} - -MP4BoxStream.prototype.readUint8Array = function(length) { - var arr = new Uint8Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readUint8(); - } - return arr; -} - -MP4BoxStream.prototype.readInt16Array = function(length) { - var arr = new Int16Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readInt16(); - } - return arr; -} - -MP4BoxStream.prototype.readUint16Array = function(length) { - var arr = new Int16Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readUint16(); - } - return arr; -} - -MP4BoxStream.prototype.readUint32Array = function(length) { - var arr = new Uint32Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readUint32(); - } - return arr; -} - -MP4BoxStream.prototype.readInt32Array = function(length) { - var arr = new Int32Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readInt32(); - } - return arr; -} - -if (typeof exports !== 'undefined') { - exports.MP4BoxStream = MP4BoxStream; -}// file:src/DataStream.js -/** - DataStream reads scalars, arrays and structs of data from an ArrayBuffer. - It's like a file-like DataView on steroids. - - @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from. - @param {?Number} byteOffset Offset from arrayBuffer beginning for the DataStream. - @param {?Boolean} endianness DataStream.BIG_ENDIAN or DataStream.LITTLE_ENDIAN (the default). - */ -var DataStream = function(arrayBuffer, byteOffset, endianness) { - this._byteOffset = byteOffset || 0; - if (arrayBuffer instanceof ArrayBuffer) { - this.buffer = arrayBuffer; - } else if (typeof arrayBuffer == "object") { - this.dataView = arrayBuffer; - if (byteOffset) { - this._byteOffset += byteOffset; - } - } else { - this.buffer = new ArrayBuffer(arrayBuffer || 0); - } - this.position = 0; - this.endianness = endianness == null ? DataStream.LITTLE_ENDIAN : endianness; -}; -DataStream.prototype = {}; - -DataStream.prototype.getPosition = function() { - return this.position; -} - -/** - Internal function to resize the DataStream buffer when required. - @param {number} extra Number of bytes to add to the buffer allocation. - @return {null} - */ -DataStream.prototype._realloc = function(extra) { - if (!this._dynamicSize) { - return; - } - var req = this._byteOffset + this.position + extra; - var blen = this._buffer.byteLength; - if (req <= blen) { - if (req > this._byteLength) { - this._byteLength = req; - } - return; - } - if (blen < 1) { - blen = 1; - } - while (req > blen) { - blen *= 2; - } - var buf = new ArrayBuffer(blen); - var src = new Uint8Array(this._buffer); - var dst = new Uint8Array(buf, 0, src.length); - dst.set(src); - this.buffer = buf; - this._byteLength = req; -}; - -/** - Internal function to trim the DataStream buffer when required. - Used for stripping out the extra bytes from the backing buffer when - the virtual byteLength is smaller than the buffer byteLength (happens after - growing the buffer with writes and not filling the extra space completely). - - @return {null} - */ -DataStream.prototype._trimAlloc = function() { - if (this._byteLength == this._buffer.byteLength) { - return; - } - var buf = new ArrayBuffer(this._byteLength); - var dst = new Uint8Array(buf); - var src = new Uint8Array(this._buffer, 0, dst.length); - dst.set(src); - this.buffer = buf; -}; - - -/** - Big-endian const to use as default endianness. - @type {boolean} - */ -DataStream.BIG_ENDIAN = false; - -/** - Little-endian const to use as default endianness. - @type {boolean} - */ -DataStream.LITTLE_ENDIAN = true; - -/** - Virtual byte length of the DataStream backing buffer. - Updated to be max of original buffer size and last written size. - If dynamicSize is false is set to buffer size. - @type {number} - */ -DataStream.prototype._byteLength = 0; - -/** - Returns the byte length of the DataStream object. - @type {number} - */ -Object.defineProperty(DataStream.prototype, 'byteLength', - { get: function() { - return this._byteLength - this._byteOffset; - }}); - -/** - Set/get the backing ArrayBuffer of the DataStream object. - The setter updates the DataView to point to the new buffer. - @type {Object} - */ -Object.defineProperty(DataStream.prototype, 'buffer', - { get: function() { - this._trimAlloc(); - return this._buffer; - }, - set: function(v) { - this._buffer = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the byteOffset of the DataStream object. - The setter updates the DataView to point to the new byteOffset. - @type {number} - */ -Object.defineProperty(DataStream.prototype, 'byteOffset', - { get: function() { - return this._byteOffset; - }, - set: function(v) { - this._byteOffset = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the backing DataView of the DataStream object. - The setter updates the buffer and byteOffset to point to the DataView values. - @type {Object} - */ -Object.defineProperty(DataStream.prototype, 'dataView', - { get: function() { - return this._dataView; - }, - set: function(v) { - this._byteOffset = v.byteOffset; - this._buffer = v.buffer; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._byteOffset + v.byteLength; - } }); - -/** - Sets the DataStream read/write position to given position. - Clamps between 0 and DataStream length. - - @param {number} pos Position to seek to. - @return {null} - */ -DataStream.prototype.seek = function(pos) { - var npos = Math.max(0, Math.min(this.byteLength, pos)); - this.position = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; -}; - -/** - Returns true if the DataStream seek pointer is at the end of buffer and - there's no more data to read. - - @return {boolean} True if the seek pointer is at the end of the buffer. - */ -DataStream.prototype.isEof = function() { - return (this.position >= this._byteLength); -}; - - -/** - Maps a Uint8Array into the DataStream buffer. - - Nice for quickly reading in data. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Uint8Array to the DataStream backing buffer. - */ -DataStream.prototype.mapUint8Array = function(length) { - this._realloc(length * 1); - var arr = new Uint8Array(this._buffer, this.byteOffset+this.position, length); - this.position += length * 1; - return arr; -}; - - -/** - Reads an Int32Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Int32Array. - */ -DataStream.prototype.readInt32Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 4) : length; - var arr = new Int32Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads an Int16Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Int16Array. - */ -DataStream.prototype.readInt16Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 2) : length; - var arr = new Int16Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads an Int8Array of desired length from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Int8Array. - */ -DataStream.prototype.readInt8Array = function(length) { - length = length == null ? (this.byteLength-this.position) : length; - var arr = new Int8Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Uint32Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Uint32Array. - */ -DataStream.prototype.readUint32Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 4) : length; - var arr = new Uint32Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Uint16Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Uint16Array. - */ -DataStream.prototype.readUint16Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 2) : length; - var arr = new Uint16Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Uint8Array of desired length from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Uint8Array. - */ -DataStream.prototype.readUint8Array = function(length) { - length = length == null ? (this.byteLength-this.position) : length; - var arr = new Uint8Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Float64Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Float64Array. - */ -DataStream.prototype.readFloat64Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 8) : length; - var arr = new Float64Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Float32Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Float32Array. - */ -DataStream.prototype.readFloat32Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 4) : length; - var arr = new Float32Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - - -/** - Reads a 32-bit int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readInt32 = function(e) { - var v = this._dataView.getInt32(this.position, e == null ? this.endianness : e); - this.position += 4; - return v; -}; - -/** - Reads a 16-bit int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readInt16 = function(e) { - var v = this._dataView.getInt16(this.position, e == null ? this.endianness : e); - this.position += 2; - return v; -}; - -/** - Reads an 8-bit int from the DataStream. - - @return {number} The read number. - */ -DataStream.prototype.readInt8 = function() { - var v = this._dataView.getInt8(this.position); - this.position += 1; - return v; -}; - -/** - Reads a 32-bit unsigned int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readUint32 = function(e) { - var v = this._dataView.getUint32(this.position, e == null ? this.endianness : e); - this.position += 4; - return v; -}; - -/** - Reads a 16-bit unsigned int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readUint16 = function(e) { - var v = this._dataView.getUint16(this.position, e == null ? this.endianness : e); - this.position += 2; - return v; -}; - -/** - Reads an 8-bit unsigned int from the DataStream. - - @return {number} The read number. - */ -DataStream.prototype.readUint8 = function() { - var v = this._dataView.getUint8(this.position); - this.position += 1; - return v; -}; - -/** - Reads a 32-bit float from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readFloat32 = function(e) { - var v = this._dataView.getFloat32(this.position, e == null ? this.endianness : e); - this.position += 4; - return v; -}; - -/** - Reads a 64-bit float from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readFloat64 = function(e) { - var v = this._dataView.getFloat64(this.position, e == null ? this.endianness : e); - this.position += 8; - return v; -}; - -/** - Native endianness. Either DataStream.BIG_ENDIAN or DataStream.LITTLE_ENDIAN - depending on the platform endianness. - - @type {boolean} - */ -DataStream.endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; - -/** - Copies byteLength bytes from the src buffer at srcOffset to the - dst buffer at dstOffset. - - @param {Object} dst Destination ArrayBuffer to write to. - @param {number} dstOffset Offset to the destination ArrayBuffer. - @param {Object} src Source ArrayBuffer to read from. - @param {number} srcOffset Offset to the source ArrayBuffer. - @param {number} byteLength Number of bytes to copy. - */ -DataStream.memcpy = function(dst, dstOffset, src, srcOffset, byteLength) { - var dstU8 = new Uint8Array(dst, dstOffset, byteLength); - var srcU8 = new Uint8Array(src, srcOffset, byteLength); - dstU8.set(srcU8); -}; - -/** - Converts array to native endianness in-place. - - @param {Object} array Typed array to convert. - @param {boolean} arrayIsLittleEndian True if the data in the array is - little-endian. Set false for big-endian. - @return {Object} The converted typed array. - */ -DataStream.arrayToNative = function(array, arrayIsLittleEndian) { - if (arrayIsLittleEndian == this.endianness) { - return array; - } else { - return this.flipArrayEndianness(array); - } -}; - -/** - Converts native endianness array to desired endianness in-place. - - @param {Object} array Typed array to convert. - @param {boolean} littleEndian True if the converted array should be - little-endian. Set false for big-endian. - @return {Object} The converted typed array. - */ -DataStream.nativeToEndian = function(array, littleEndian) { - if (this.endianness == littleEndian) { - return array; - } else { - return this.flipArrayEndianness(array); - } -}; - -/** - Flips typed array endianness in-place. - - @param {Object} array Typed array to flip. - @return {Object} The converted typed array. - */ -DataStream.flipArrayEndianness = function(array) { - var u8 = new Uint8Array(array.buffer, array.byteOffset, array.byteLength); - for (var i=0; ik; j--, k++) { - var tmp = u8[k]; - u8[k] = u8[j]; - u8[j] = tmp; - } - } - return array; -}; - -/** - Seek position where DataStream#readStruct ran into a problem. - Useful for debugging struct parsing. - - @type {number} - */ -DataStream.prototype.failurePosition = 0; - -String.fromCharCodeUint8 = function(uint8arr) { - var arr = []; - for (var i = 0; i < uint8arr.length; i++) { - arr[i] = uint8arr[i]; - } - return String.fromCharCode.apply(null, arr); -} -/** - Read a string of desired length and encoding from the DataStream. - - @param {number} length The length of the string to read in bytes. - @param {?string} encoding The encoding of the string data in the DataStream. - Defaults to ASCII. - @return {string} The read string. - */ -DataStream.prototype.readString = function(length, encoding) { - if (encoding == null || encoding == "ASCII") { - return String.fromCharCodeUint8.apply(null, [this.mapUint8Array(length == null ? this.byteLength-this.position : length)]); - } else { - return (new TextDecoder(encoding)).decode(this.mapUint8Array(length)); - } -}; - -/** - Read null-terminated string of desired length from the DataStream. Truncates - the returned string so that the null byte is not a part of it. - - @param {?number} length The length of the string to read. - @return {string} The read string. - */ -DataStream.prototype.readCString = function(length) { - var blen = this.byteLength-this.position; - var u8 = new Uint8Array(this._buffer, this._byteOffset + this.position); - var len = blen; - if (length != null) { - len = Math.min(length, blen); - } - for (var i = 0; i < len && u8[i] !== 0; i++); // find first zero byte - var s = String.fromCharCodeUint8.apply(null, [this.mapUint8Array(i)]); - if (length != null) { - this.position += len-i; - } else if (i != blen) { - this.position += 1; // trailing zero if not at end of buffer - } - return s; -}; - -/* - TODO: fix endianness for 24/64-bit fields - TODO: check range/support for 64-bits numbers in JavaScript -*/ -var MAX_SIZE = Math.pow(2, 32); - -DataStream.prototype.readInt64 = function () { - return (this.readInt32()*MAX_SIZE)+this.readUint32(); -} -DataStream.prototype.readUint64 = function () { - return (this.readUint32()*MAX_SIZE)+this.readUint32(); -} - -DataStream.prototype.readInt64 = function () { - return (this.readUint32()*MAX_SIZE)+this.readUint32(); -} - -DataStream.prototype.readUint24 = function () { - return (this.readUint8()<<16)+(this.readUint8()<<8)+this.readUint8(); -} - -if (typeof exports !== 'undefined') { - exports.DataStream = DataStream; -} -// file:src/DataStream-write.js -/** - Saves the DataStream contents to the given filename. - Uses Chrome's anchor download property to initiate download. - - @param {string} filename Filename to save as. - @return {null} - */ -DataStream.prototype.save = function(filename) { - var blob = new Blob([this.buffer]); - if (window.URL && URL.createObjectURL) { - var url = window.URL.createObjectURL(blob); - var a = document.createElement('a'); - // Required in Firefox: - document.body.appendChild(a); - a.setAttribute('href', url); - a.setAttribute('download', filename); - // Required in Firefox: - a.setAttribute('target', '_self'); - a.click(); - window.URL.revokeObjectURL(url); - } else { - throw("DataStream.save: Can't create object URL."); - } -}; - -/** - Whether to extend DataStream buffer when trying to write beyond its size. - If set, the buffer is reallocated to twice its current size until the - requested write fits the buffer. - @type {boolean} - */ -DataStream.prototype._dynamicSize = true; -Object.defineProperty(DataStream.prototype, 'dynamicSize', - { get: function() { - return this._dynamicSize; - }, - set: function(v) { - if (!v) { - this._trimAlloc(); - } - this._dynamicSize = v; - } }); - -/** - Internal function to trim the DataStream buffer when required. - Used for stripping out the first bytes when not needed anymore. - - @return {null} - */ -DataStream.prototype.shift = function(offset) { - var buf = new ArrayBuffer(this._byteLength-offset); - var dst = new Uint8Array(buf); - var src = new Uint8Array(this._buffer, offset, dst.length); - dst.set(src); - this.buffer = buf; - this.position -= offset; -}; - -/** - Writes an Int32Array of specified endianness to the DataStream. - - @param {Object} arr The array to write. - @param {?boolean} e Endianness of the data to write. - */ -DataStream.prototype.writeInt32Array = function(arr, e) { - this._realloc(arr.length * 4); - if (arr instanceof Int32Array && - this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) { - DataStream.memcpy(this._buffer, this.byteOffset+this.position, - arr.buffer, 0, - arr.byteLength); - this.mapInt32Array(arr.length, e); - } else { - for (var i=0; i>16); - this.writeUint8((v & 0x0000FF00)>>8); - this.writeUint8((v & 0x000000FF)); -} - -DataStream.prototype.adjustUint32 = function(position, value) { - var pos = this.position; - this.seek(position); - this.writeUint32(value); - this.seek(pos); -} -// file:src/DataStream-map.js -/** - Maps an Int32Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Int32Array to the DataStream backing buffer. - */ -DataStream.prototype.mapInt32Array = function(length, e) { - this._realloc(length * 4); - var arr = new Int32Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 4; - return arr; -}; - -/** - Maps an Int16Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Int16Array to the DataStream backing buffer. - */ -DataStream.prototype.mapInt16Array = function(length, e) { - this._realloc(length * 2); - var arr = new Int16Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 2; - return arr; -}; - -/** - Maps an Int8Array into the DataStream buffer. - - Nice for quickly reading in data. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Int8Array to the DataStream backing buffer. - */ -DataStream.prototype.mapInt8Array = function(length) { - this._realloc(length * 1); - var arr = new Int8Array(this._buffer, this.byteOffset+this.position, length); - this.position += length * 1; - return arr; -}; - -/** - Maps a Uint32Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Uint32Array to the DataStream backing buffer. - */ -DataStream.prototype.mapUint32Array = function(length, e) { - this._realloc(length * 4); - var arr = new Uint32Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 4; - return arr; -}; - -/** - Maps a Uint16Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Uint16Array to the DataStream backing buffer. - */ -DataStream.prototype.mapUint16Array = function(length, e) { - this._realloc(length * 2); - var arr = new Uint16Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 2; - return arr; -}; - -/** - Maps a Float64Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Float64Array to the DataStream backing buffer. - */ -DataStream.prototype.mapFloat64Array = function(length, e) { - this._realloc(length * 8); - var arr = new Float64Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 8; - return arr; -}; - -/** - Maps a Float32Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Float32Array to the DataStream backing buffer. - */ -DataStream.prototype.mapFloat32Array = function(length, e) { - this._realloc(length * 4); - var arr = new Float32Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 4; - return arr; -}; -// file:src/buffer.js -/** - * MultiBufferStream is a class that acts as a SimpleStream for parsing - * It holds several, possibly non-contiguous ArrayBuffer objects, each with a fileStart property - * containing the offset for the buffer data in an original/virtual file - * - * It inherits also from DataStream for all read/write/alloc operations - */ - -/** - * Constructor - */ -var MultiBufferStream = function(buffer) { - /* List of ArrayBuffers, with a fileStart property, sorted in fileStart order and non overlapping */ - this.buffers = []; - this.bufferIndex = -1; - if (buffer) { - this.insertBuffer(buffer); - this.bufferIndex = 0; - } -} -MultiBufferStream.prototype = new DataStream(new ArrayBuffer(), 0, DataStream.BIG_ENDIAN); - -/************************************************************************************ - Methods for the managnement of the buffers (insertion, removal, concatenation, ...) - ***********************************************************************************/ - -MultiBufferStream.prototype.initialized = function() { - var firstBuffer; - if (this.bufferIndex > -1) { - return true; - } else if (this.buffers.length > 0) { - firstBuffer = this.buffers[0]; - if (firstBuffer.fileStart === 0) { - this.buffer = firstBuffer; - this.bufferIndex = 0; - Log.debug("MultiBufferStream", "Stream ready for parsing"); - return true; - } else { - Log.warn("MultiBufferStream", "The first buffer should have a fileStart of 0"); - this.logBufferLevel(); - return false; - } - } else { - Log.warn("MultiBufferStream", "No buffer to start parsing from"); - this.logBufferLevel(); - return false; - } -} - -/** - * helper functions to concatenate two ArrayBuffer objects - * @param {ArrayBuffer} buffer1 - * @param {ArrayBuffer} buffer2 - * @return {ArrayBuffer} the concatenation of buffer1 and buffer2 in that order - */ -ArrayBuffer.concat = function(buffer1, buffer2) { - Log.debug("ArrayBuffer", "Trying to create a new buffer of size: "+(buffer1.byteLength + buffer2.byteLength)); - var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); - tmp.set(new Uint8Array(buffer1), 0); - tmp.set(new Uint8Array(buffer2), buffer1.byteLength); - return tmp.buffer; -}; - -/** - * Reduces the size of a given buffer, but taking the part between offset and offset+newlength - * @param {ArrayBuffer} buffer - * @param {Number} offset the start of new buffer - * @param {Number} newLength the length of the new buffer - * @return {ArrayBuffer} the new buffer - */ -MultiBufferStream.prototype.reduceBuffer = function(buffer, offset, newLength) { - var smallB; - smallB = new Uint8Array(newLength); - smallB.set(new Uint8Array(buffer, offset, newLength)); - smallB.buffer.fileStart = buffer.fileStart+offset; - smallB.buffer.usedBytes = 0; - return smallB.buffer; -} - -/** - * Inserts the new buffer in the sorted list of buffers, - * making sure, it is not overlapping with existing ones (possibly reducing its size). - * if the new buffer overrides/replaces the 0-th buffer (for instance because it is bigger), - * updates the DataStream buffer for parsing -*/ -MultiBufferStream.prototype.insertBuffer = function(ab) { - var to_add = true; - /* TODO: improve insertion if many buffers */ - for (var i = 0; i < this.buffers.length; i++) { - var b = this.buffers[i]; - if (ab.fileStart <= b.fileStart) { - /* the insertion position is found */ - if (ab.fileStart === b.fileStart) { - /* The new buffer overlaps with an existing buffer */ - if (ab.byteLength > b.byteLength) { - /* the new buffer is bigger than the existing one - remove the existing buffer and try again to insert - the new buffer to check overlap with the next ones */ - this.buffers.splice(i, 1); - i--; - continue; - } else { - /* the new buffer is smaller than the existing one, just drop it */ - Log.warn("MultiBufferStream", "Buffer (fileStart: "+ab.fileStart+" - Length: "+ab.byteLength+") already appended, ignoring"); - } - } else { - /* The beginning of the new buffer is not overlapping with an existing buffer - let's check the end of it */ - if (ab.fileStart + ab.byteLength <= b.fileStart) { - /* no overlap, we can add it as is */ - } else { - /* There is some overlap, cut the new buffer short, and add it*/ - ab = this.reduceBuffer(ab, 0, b.fileStart - ab.fileStart); - } - Log.debug("MultiBufferStream", "Appending new buffer (fileStart: "+ab.fileStart+" - Length: "+ab.byteLength+")"); - this.buffers.splice(i, 0, ab); - /* if this new buffer is inserted in the first place in the list of the buffer, - and the DataStream is initialized, make it the buffer used for parsing */ - if (i === 0) { - this.buffer = ab; - } - } - to_add = false; - break; - } else if (ab.fileStart < b.fileStart + b.byteLength) { - /* the new buffer overlaps its beginning with the end of the current buffer */ - var offset = b.fileStart + b.byteLength - ab.fileStart; - var newLength = ab.byteLength - offset; - if (newLength > 0) { - /* the new buffer is bigger than the current overlap, drop the overlapping part and try again inserting the remaining buffer */ - ab = this.reduceBuffer(ab, offset, newLength); - } else { - /* the content of the new buffer is entirely contained in the existing buffer, drop it entirely */ - to_add = false; - break; - } - } - } - /* if the buffer has not been added, we can add it at the end */ - if (to_add) { - Log.debug("MultiBufferStream", "Appending new buffer (fileStart: "+ab.fileStart+" - Length: "+ab.byteLength+")"); - this.buffers.push(ab); - /* if this new buffer is inserted in the first place in the list of the buffer, - and the DataStream is initialized, make it the buffer used for parsing */ - if (i === 0) { - this.buffer = ab; - } - } -} - -/** - * Displays the status of the buffers (number and used bytes) - * @param {Object} info callback method for display - */ -MultiBufferStream.prototype.logBufferLevel = function(info) { - var i; - var buffer; - var used, total; - var ranges = []; - var range; - var bufferedString = ""; - used = 0; - total = 0; - for (i = 0; i < this.buffers.length; i++) { - buffer = this.buffers[i]; - if (i === 0) { - range = {}; - ranges.push(range); - range.start = buffer.fileStart; - range.end = buffer.fileStart+buffer.byteLength; - bufferedString += "["+range.start+"-"; - } else if (range.end === buffer.fileStart) { - range.end = buffer.fileStart+buffer.byteLength; - } else { - range = {}; - range.start = buffer.fileStart; - bufferedString += (ranges[ranges.length-1].end-1)+"], ["+range.start+"-"; - range.end = buffer.fileStart+buffer.byteLength; - ranges.push(range); - } - used += buffer.usedBytes; - total += buffer.byteLength; - } - if (ranges.length > 0) { - bufferedString += (range.end-1)+"]"; - } - var log = (info ? Log.info : Log.debug) - if (this.buffers.length === 0) { - log("MultiBufferStream", "No more buffer in memory"); - } else { - log("MultiBufferStream", ""+this.buffers.length+" stored buffer(s) ("+used+"/"+total+" bytes), continuous ranges: "+bufferedString); - } -} - -MultiBufferStream.prototype.cleanBuffers = function () { - var i; - var buffer; - for (i = 0; i < this.buffers.length; i++) { - buffer = this.buffers[i]; - if (buffer.usedBytes === buffer.byteLength) { - Log.debug("MultiBufferStream", "Removing buffer #"+i); - this.buffers.splice(i, 1); - i--; - } - } -} - -MultiBufferStream.prototype.mergeNextBuffer = function() { - var next_buffer; - if (this.bufferIndex+1 < this.buffers.length) { - next_buffer = this.buffers[this.bufferIndex+1]; - if (next_buffer.fileStart === this.buffer.fileStart + this.buffer.byteLength) { - var oldLength = this.buffer.byteLength; - var oldUsedBytes = this.buffer.usedBytes; - var oldFileStart = this.buffer.fileStart; - this.buffers[this.bufferIndex] = ArrayBuffer.concat(this.buffer, next_buffer); - this.buffer = this.buffers[this.bufferIndex]; - this.buffers.splice(this.bufferIndex+1, 1); - this.buffer.usedBytes = oldUsedBytes; /* TODO: should it be += ? */ - this.buffer.fileStart = oldFileStart; - Log.debug("ISOFile", "Concatenating buffer for box parsing (length: "+oldLength+"->"+this.buffer.byteLength+")"); - return true; - } else { - return false; - } - } else { - return false; - } -} - - -/************************************************************************* - Seek-related functions - *************************************************************************/ - -/** - * Finds the buffer that holds the given file position - * @param {Boolean} fromStart indicates if the search should start from the current buffer (false) - * or from the first buffer (true) - * @param {Number} filePosition position in the file to seek to - * @param {Boolean} markAsUsed indicates if the bytes in between the current position and the seek position - * should be marked as used for garbage collection - * @return {Number} the index of the buffer holding the seeked file position, -1 if not found. - */ -MultiBufferStream.prototype.findPosition = function(fromStart, filePosition, markAsUsed) { - var i; - var abuffer = null; - var index = -1; - - /* find the buffer with the largest position smaller than the given position */ - if (fromStart === true) { - /* the reposition can be in the past, we need to check from the beginning of the list of buffers */ - i = 0; - } else { - i = this.bufferIndex; - } - - while (i < this.buffers.length) { - abuffer = this.buffers[i]; - if (abuffer.fileStart <= filePosition) { - index = i; - if (markAsUsed) { - if (abuffer.fileStart + abuffer.byteLength <= filePosition) { - abuffer.usedBytes = abuffer.byteLength; - } else { - abuffer.usedBytes = filePosition - abuffer.fileStart; - } - this.logBufferLevel(); - } - } else { - break; - } - i++; - } - - if (index !== -1) { - abuffer = this.buffers[index]; - if (abuffer.fileStart + abuffer.byteLength >= filePosition) { - Log.debug("MultiBufferStream", "Found position in existing buffer #"+index); - return index; - } else { - return -1; - } - } else { - return -1; - } -} - -/** - * Finds the largest file position contained in a buffer or in the next buffers if they are contiguous (no gap) - * starting from the given buffer index or from the current buffer if the index is not given - * - * @param {Number} inputindex Index of the buffer to start from - * @return {Number} The largest file position found in the buffers - */ -MultiBufferStream.prototype.findEndContiguousBuf = function(inputindex) { - var i; - var currentBuf; - var nextBuf; - var index = (inputindex !== undefined ? inputindex : this.bufferIndex); - currentBuf = this.buffers[index]; - /* find the end of the contiguous range of data */ - if (this.buffers.length > index+1) { - for (i = index+1; i < this.buffers.length; i++) { - nextBuf = this.buffers[i]; - if (nextBuf.fileStart === currentBuf.fileStart + currentBuf.byteLength) { - currentBuf = nextBuf; - } else { - break; - } - } - } - /* return the position of last byte in the file that we have */ - return currentBuf.fileStart + currentBuf.byteLength; -} - -/** - * Returns the largest file position contained in the buffers, larger than the given position - * @param {Number} pos the file position to start from - * @return {Number} the largest position in the current buffer or in the buffer and the next contiguous - * buffer that holds the given position - */ -MultiBufferStream.prototype.getEndFilePositionAfter = function(pos) { - var index = this.findPosition(true, pos, false); - if (index !== -1) { - return this.findEndContiguousBuf(index); - } else { - return pos; - } -} - -/************************************************************************* - Garbage collection related functions - *************************************************************************/ - -/** - * Marks a given number of bytes as used in the current buffer for garbage collection - * @param {Number} nbBytes - */ -MultiBufferStream.prototype.addUsedBytes = function(nbBytes) { - this.buffer.usedBytes += nbBytes; - this.logBufferLevel(); -} - -/** - * Marks the entire current buffer as used, ready for garbage collection - */ -MultiBufferStream.prototype.setAllUsedBytes = function() { - this.buffer.usedBytes = this.buffer.byteLength; - this.logBufferLevel(); -} - -/************************************************************************* - Common API between MultiBufferStream and SimpleStream - *************************************************************************/ - -/** - * Tries to seek to a given file position - * if possible, repositions the parsing from there and returns true - * if not possible, does not change anything and returns false - * @param {Number} filePosition position in the file to seek to - * @param {Boolean} fromStart indicates if the search should start from the current buffer (false) - * or from the first buffer (true) - * @param {Boolean} markAsUsed indicates if the bytes in between the current position and the seek position - * should be marked as used for garbage collection - * @return {Boolean} true if the seek succeeded, false otherwise - */ -MultiBufferStream.prototype.seek = function(filePosition, fromStart, markAsUsed) { - var index; - index = this.findPosition(fromStart, filePosition, markAsUsed); - if (index !== -1) { - this.buffer = this.buffers[index]; - this.bufferIndex = index; - this.position = filePosition - this.buffer.fileStart; - Log.debug("MultiBufferStream", "Repositioning parser at buffer position: "+this.position); - return true; - } else { - Log.debug("MultiBufferStream", "Position "+filePosition+" not found in buffered data"); - return false; - } -} - -/** - * Returns the current position in the file - * @return {Number} the position in the file - */ -MultiBufferStream.prototype.getPosition = function() { - if (this.bufferIndex === -1 || this.buffers[this.bufferIndex] === null) { - throw "Error accessing position in the MultiBufferStream"; - } - return this.buffers[this.bufferIndex].fileStart+this.position; -} - -/** - * Returns the length of the current buffer - * @return {Number} the length of the current buffer - */ -MultiBufferStream.prototype.getLength = function() { - return this.byteLength; -} - -MultiBufferStream.prototype.getEndPosition = function() { - if (this.bufferIndex === -1 || this.buffers[this.bufferIndex] === null) { - throw "Error accessing position in the MultiBufferStream"; - } - return this.buffers[this.bufferIndex].fileStart+this.byteLength; -} - -if (typeof exports !== 'undefined') { - exports.MultiBufferStream = MultiBufferStream; -}// file:src/descriptor.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var MPEG4DescriptorParser = function () { - var ES_DescrTag = 0x03; - var DecoderConfigDescrTag = 0x04; - var DecSpecificInfoTag = 0x05; - var SLConfigDescrTag = 0x06; - - var descTagToName = []; - descTagToName[ES_DescrTag] = "ES_Descriptor"; - descTagToName[DecoderConfigDescrTag] = "DecoderConfigDescriptor"; - descTagToName[DecSpecificInfoTag] = "DecoderSpecificInfo"; - descTagToName[SLConfigDescrTag] = "SLConfigDescriptor"; - - this.getDescriptorName = function(tag) { - return descTagToName[tag]; - } - - var that = this; - var classes = {}; - - this.parseOneDescriptor = function (stream) { - var hdrSize = 0; - var size = 0; - var tag; - var desc; - var byteRead; - tag = stream.readUint8(); - hdrSize++; - byteRead = stream.readUint8(); - hdrSize++; - while (byteRead & 0x80) { - size = (byteRead & 0x7F)<<7; - byteRead = stream.readUint8(); - hdrSize++; - } - size += byteRead & 0x7F; - Log.debug("MPEG4DescriptorParser", "Found "+(descTagToName[tag] || "Descriptor "+tag)+", size "+size+" at position "+stream.getPosition()); - if (descTagToName[tag]) { - desc = new classes[descTagToName[tag]](size); - } else { - desc = new classes.Descriptor(size); - } - desc.parse(stream); - return desc; - } - - classes.Descriptor = function(_tag, _size) { - this.tag = _tag; - this.size = _size; - this.descs = []; - } - - classes.Descriptor.prototype.parse = function (stream) { - this.data = stream.readUint8Array(this.size); - } - - classes.Descriptor.prototype.findDescriptor = function (tag) { - for (var i = 0; i < this.descs.length; i++) { - if (this.descs[i].tag == tag) { - return this.descs[i]; - } - } - return null; - } - - classes.Descriptor.prototype.parseRemainingDescriptors = function (stream) { - var start = stream.position; - while (stream.position < start+this.size) { - var desc = that.parseOneDescriptor(stream); - this.descs.push(desc); - } - } - - classes.ES_Descriptor = function (size) { - classes.Descriptor.call(this, ES_DescrTag, size); - } - - classes.ES_Descriptor.prototype = new classes.Descriptor(); - - classes.ES_Descriptor.prototype.parse = function(stream) { - this.ES_ID = stream.readUint16(); - this.flags = stream.readUint8(); - this.size -= 3; - if (this.flags & 0x80) { - this.dependsOn_ES_ID = stream.readUint16(); - this.size -= 2; - } else { - this.dependsOn_ES_ID = 0; - } - if (this.flags & 0x40) { - var l = stream.readUint8(); - this.URL = stream.readString(l); - this.size -= l+1; - } else { - this.URL = ""; - } - if (this.flags & 0x20) { - this.OCR_ES_ID = stream.readUint16(); - this.size -= 2; - } else { - this.OCR_ES_ID = 0; - } - this.parseRemainingDescriptors(stream); - } - - classes.ES_Descriptor.prototype.getOTI = function(stream) { - var dcd = this.findDescriptor(DecoderConfigDescrTag); - if (dcd) { - return dcd.oti; - } else { - return 0; - } - } - - classes.ES_Descriptor.prototype.getAudioConfig = function(stream) { - var dcd = this.findDescriptor(DecoderConfigDescrTag); - if (!dcd) return null; - var dsi = dcd.findDescriptor(DecSpecificInfoTag); - if (dsi && dsi.data) { - var audioObjectType = (dsi.data[0]& 0xF8) >> 3; - if (audioObjectType === 31 && dsi.data.length >= 2) { - audioObjectType = 32 + ((dsi.data[0] & 0x7) << 3) + ((dsi.data[1] & 0xE0) >> 5); - } - return audioObjectType; - } else { - return null; - } - } - - classes.DecoderConfigDescriptor = function (size) { - classes.Descriptor.call(this, DecoderConfigDescrTag, size); - } - classes.DecoderConfigDescriptor.prototype = new classes.Descriptor(); - - classes.DecoderConfigDescriptor.prototype.parse = function(stream) { - this.oti = stream.readUint8(); - this.streamType = stream.readUint8(); - this.bufferSize = stream.readUint24(); - this.maxBitrate = stream.readUint32(); - this.avgBitrate = stream.readUint32(); - this.size -= 13; - this.parseRemainingDescriptors(stream); - } - - classes.DecoderSpecificInfo = function (size) { - classes.Descriptor.call(this, DecSpecificInfoTag, size); - } - classes.DecoderSpecificInfo.prototype = new classes.Descriptor(); - - classes.SLConfigDescriptor = function (size) { - classes.Descriptor.call(this, SLConfigDescrTag, size); - } - classes.SLConfigDescriptor.prototype = new classes.Descriptor(); - - return this; -} - -if (typeof exports !== 'undefined') { - exports.MPEG4DescriptorParser = MPEG4DescriptorParser; -}// file:src/box.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var BoxParser = { - ERR_INVALID_DATA : -1, - ERR_NOT_ENOUGH_DATA : 0, - OK : 1, - - // Boxes to be created with default parsing - BASIC_BOXES: [ "mdat", "idat", "free", "skip", "meco", "strk" ], - FULL_BOXES: [ "hmhd", "nmhd", "iods", "xml ", "bxml", "ipro", "mere" ], - CONTAINER_BOXES: [ - [ "moov", [ "trak", "pssh" ] ], - [ "trak" ], - [ "edts" ], - [ "mdia" ], - [ "minf" ], - [ "dinf" ], - [ "stbl", [ "sgpd", "sbgp" ] ], - [ "mvex", [ "trex" ] ], - [ "moof", [ "traf" ] ], - [ "traf", [ "trun", "sgpd", "sbgp" ] ], - [ "vttc" ], - [ "tref" ], - [ "iref" ], - [ "mfra", [ "tfra" ] ], - [ "meco" ], - [ "hnti" ], - [ "hinf" ], - [ "strk" ], - [ "strd" ], - [ "sinf" ], - [ "rinf" ], - [ "schi" ], - [ "trgr" ], - [ "udta", ["kind"] ], - [ "iprp", ["ipma"] ], - [ "ipco"] - ], - // Boxes effectively created - boxCodes : [], - fullBoxCodes : [], - containerBoxCodes : [], - sampleEntryCodes : {}, - sampleGroupEntryCodes: [], - trackGroupTypes: [], - UUIDBoxes: {}, - UUIDs: [], - initialize: function() { - BoxParser.FullBox.prototype = new BoxParser.Box(); - BoxParser.ContainerBox.prototype = new BoxParser.Box(); - BoxParser.SampleEntry.prototype = new BoxParser.Box(); - BoxParser.TrackGroupTypeBox.prototype = new BoxParser.FullBox(); - - /* creating constructors for simple boxes */ - BoxParser.BASIC_BOXES.forEach(function(type) { - BoxParser.createBoxCtor(type) - }); - BoxParser.FULL_BOXES.forEach(function(type) { - BoxParser.createFullBoxCtor(type); - }); - BoxParser.CONTAINER_BOXES.forEach(function(types) { - BoxParser.createContainerBoxCtor(types[0], null, types[1]); - }); - }, - Box: function(_type, _size, _uuid) { - this.type = _type; - this.size = _size; - this.uuid = _uuid; - }, - FullBox: function(type, size, uuid) { - BoxParser.Box.call(this, type, size, uuid); - this.flags = 0; - this.version = 0; - }, - ContainerBox: function(type, size, uuid) { - BoxParser.Box.call(this, type, size, uuid); - this.boxes = []; - }, - SampleEntry: function(type, size, hdr_size, start) { - BoxParser.ContainerBox.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; - }, - SampleGroupEntry: function(type) { - this.grouping_type = type; - }, - TrackGroupTypeBox: function(type, size) { - BoxParser.FullBox.call(this, type, size); - }, - createBoxCtor: function(type, parseMethod){ - BoxParser.boxCodes.push(type); - BoxParser[type+"Box"] = function(size) { - BoxParser.Box.call(this, type, size); - } - BoxParser[type+"Box"].prototype = new BoxParser.Box(); - if (parseMethod) BoxParser[type+"Box"].prototype.parse = parseMethod; - }, - createFullBoxCtor: function(type, parseMethod) { - //BoxParser.fullBoxCodes.push(type); - BoxParser[type+"Box"] = function(size) { - BoxParser.FullBox.call(this, type, size); - } - BoxParser[type+"Box"].prototype = new BoxParser.FullBox(); - BoxParser[type+"Box"].prototype.parse = function(stream) { - this.parseFullHeader(stream); - if (parseMethod) { - parseMethod.call(this, stream); - } - }; - }, - addSubBoxArrays: function(subBoxNames) { - if (subBoxNames) { - this.subBoxNames = subBoxNames; - var nbSubBoxes = subBoxNames.length; - for (var k = 0; k parentSize) { - Log.error("BoxParser", "Box of type '"+type+"' has a size "+size+" greater than its container size "+parentSize); - return { code: BoxParser.ERR_NOT_ENOUGH_DATA, type: type, size: size, hdr_size: hdr_size, start: start }; - } - if (size !== 0 && start + size > stream.getEndPosition()) { - stream.seek(start); - Log.info("BoxParser", "Not enough data in stream to parse the entire '"+type+"' box"); - return { code: BoxParser.ERR_NOT_ENOUGH_DATA, type: type, size: size, hdr_size: hdr_size, start: start }; - } - if (headerOnly) { - return { code: BoxParser.OK, type: type, size: size, hdr_size: hdr_size, start: start }; - } else { - if (BoxParser[type+"Box"]) { - box = new BoxParser[type+"Box"](size); - } else { - if (type !== "uuid") { - Log.warn("BoxParser", "Unknown box type: '"+type+"'"); - box = new BoxParser.Box(type, size); - box.has_unparsed_data = true; - } else { - if (BoxParser.UUIDBoxes[uuid]) { - box = new BoxParser.UUIDBoxes[uuid](size); - } else { - Log.warn("BoxParser", "Unknown uuid type: '"+uuid+"'"); - box = new BoxParser.Box(type, size); - box.uuid = uuid; - box.has_unparsed_data = true; - } - } - } - } - box.hdr_size = hdr_size; - /* recording the position of the box in the input stream */ - box.start = start; - if (box.write === BoxParser.Box.prototype.write && box.type !== "mdat") { - Log.info("BoxParser", "'"+box_type+"' box writing not yet implemented, keeping unparsed data in memory for later write"); - box.parseDataAndRewind(stream); - } - box.parse(stream); - diff = stream.getPosition() - (box.start+box.size); - if (diff < 0) { - Log.warn("BoxParser", "Parsing of box '"+box_type+"' did not read the entire indicated box data size (missing "+(-diff)+" bytes), seeking forward"); - stream.seek(box.start+box.size); - } else if (diff > 0) { - Log.error("BoxParser", "Parsing of box '"+box_type+"' read "+diff+" more bytes than the indicated box data size, seeking backwards"); - if (box.size !== 0) stream.seek(box.start+box.size); - } - return { code: BoxParser.OK, box: box, size: box.size }; -} - -BoxParser.Box.prototype.parse = function(stream) { - if (this.type != "mdat") { - this.data = stream.readUint8Array(this.size-this.hdr_size); - } else { - if (this.size === 0) { - stream.seek(stream.getEndPosition()); - } else { - stream.seek(this.start+this.size); - } - } -} - -/* Used to parse a box without consuming its data, to allow detailled parsing - Useful for boxes for which a write method is not yet implemented */ -BoxParser.Box.prototype.parseDataAndRewind = function(stream) { - this.data = stream.readUint8Array(this.size-this.hdr_size); - // rewinding - stream.position -= this.size-this.hdr_size; -} - -BoxParser.FullBox.prototype.parseDataAndRewind = function(stream) { - this.parseFullHeader(stream); - this.data = stream.readUint8Array(this.size-this.hdr_size); - // restore the header size as if the full header had not been parsed - this.hdr_size -= 4; - // rewinding - stream.position -= this.size-this.hdr_size; -} - -BoxParser.FullBox.prototype.parseFullHeader = function (stream) { - this.version = stream.readUint8(); - this.flags = stream.readUint24(); - this.hdr_size += 4; -} - -BoxParser.FullBox.prototype.parse = function (stream) { - this.parseFullHeader(stream); - this.data = stream.readUint8Array(this.size-this.hdr_size); -} - -BoxParser.ContainerBox.prototype.parse = function(stream) { - var ret; - var box; - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = ret.box; - /* store the box in the 'boxes' array to preserve box order (for offset) but also store box in a property for more direct access */ - this.boxes.push(box); - if (this.subBoxNames && this.subBoxNames.indexOf(box.type) != -1) { - this[this.subBoxNames[this.subBoxNames.indexOf(box.type)]+"s"].push(box); - } else { - var box_type = box.type !== "uuid" ? box.type : box.uuid; - if (this[box_type]) { - Log.warn("Box of type "+box_type+" already stored in field of this type"); - } else { - this[box_type] = box; - } - } - } else { - return; - } - } -} - -BoxParser.Box.prototype.parseLanguage = function(stream) { - this.language = stream.readUint16(); - var chars = []; - chars[0] = (this.language>>10)&0x1F; - chars[1] = (this.language>>5)&0x1F; - chars[2] = (this.language)&0x1F; - this.languageString = String.fromCharCode(chars[0]+0x60, chars[1]+0x60, chars[2]+0x60); -} - -// file:src/parsing/sampleentries/sampleentry.js -BoxParser.SAMPLE_ENTRY_TYPE_VISUAL = "Visual"; -BoxParser.SAMPLE_ENTRY_TYPE_AUDIO = "Audio"; -BoxParser.SAMPLE_ENTRY_TYPE_HINT = "Hint"; -BoxParser.SAMPLE_ENTRY_TYPE_METADATA = "Metadata"; -BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE = "Subtitle"; -BoxParser.SAMPLE_ENTRY_TYPE_SYSTEM = "System"; -BoxParser.SAMPLE_ENTRY_TYPE_TEXT = "Text"; - -BoxParser.SampleEntry.prototype.parseHeader = function(stream) { - stream.readUint8Array(6); - this.data_reference_index = stream.readUint16(); - this.hdr_size += 8; -} - -BoxParser.SampleEntry.prototype.parse = function(stream) { - this.parseHeader(stream); - this.data = stream.readUint8Array(this.size - this.hdr_size); -} - -BoxParser.SampleEntry.prototype.parseDataAndRewind = function(stream) { - this.parseHeader(stream); - this.data = stream.readUint8Array(this.size - this.hdr_size); - // restore the header size as if the sample entry header had not been parsed - this.hdr_size -= 8; - // rewinding - stream.position -= this.size-this.hdr_size; -} - -BoxParser.SampleEntry.prototype.parseFooter = function(stream) { - BoxParser.ContainerBox.prototype.parse.call(this, stream); -} - -// Base SampleEntry types with default parsing -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_HINT); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SYSTEM); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_TEXT); - -//Base SampleEntry types for Audio and Video with specific parsing -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, function(stream) { - var compressorname_length; - this.parseHeader(stream); - stream.readUint16(); - stream.readUint16(); - stream.readUint32Array(3); - this.width = stream.readUint16(); - this.height = stream.readUint16(); - this.horizresolution = stream.readUint32(); - this.vertresolution = stream.readUint32(); - stream.readUint32(); - this.frame_count = stream.readUint16(); - compressorname_length = Math.min(31, stream.readUint8()); - this.compressorname = stream.readString(compressorname_length); - if (compressorname_length < 31) { - stream.readString(31 - compressorname_length); - } - this.depth = stream.readUint16(); - stream.readUint16(); - this.parseFooter(stream); -}); - -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, function(stream) { - this.parseHeader(stream); - stream.readUint32Array(2); - this.channel_count = stream.readUint16(); - this.samplesize = stream.readUint16(); - stream.readUint16(); - stream.readUint16(); - this.samplerate = (stream.readUint32()/(1<<16)); - this.parseFooter(stream); -}); - -// Sample entries inheriting from Audio and Video -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc2"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc3"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc4"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "av01"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "hvc1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "hev1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvc1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvi1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvs1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvcN"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vp08"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vp09"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "mp4a"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "ac-3"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "ec-3"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "Opus"); - -// Encrypted sample entries -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "encv"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "enca"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "encu"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SYSTEM, "encs"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_TEXT, "enct"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "encm"); -// file:src/parsing/a1lx.js -BoxParser.createBoxCtor("a1lx", function(stream) { - var large_size = stream.readUint8() & 1; - var FieldLength = ((large_size & 1) + 1) * 16; - this.layer_size = []; - for (var i = 0; i < 3; i++) { - if (FieldLength == 16) { - this.layer_size[i] = stream.readUint16(); - } else { - this.layer_size[i] = stream.readUint32(); - } - } -});// file:src/parsing/a1op.js -BoxParser.createBoxCtor("a1op", function(stream) { - this.op_index = stream.readUint8(); -});// file:src/parsing/auxC.js -BoxParser.createFullBoxCtor("auxC", function(stream) { - this.aux_type = stream.readCString(); - var aux_subtype_length = this.size - this.hdr_size - (this.aux_type.length + 1); - this.aux_subtype = stream.readUint8Array(aux_subtype_length); -});// file:src/parsing/av1C.js -BoxParser.createBoxCtor("av1C", function(stream) { - var i; - var toparse; - var tmp = stream.readUint8(); - if ((tmp >> 7) & 0x1 !== 1) { - Log.error("av1C marker problem"); - return; - } - this.version = tmp & 0x7F; - if (this.version !== 1) { - Log.error("av1C version "+this.version+" not supported"); - return; - } - tmp = stream.readUint8(); - this.seq_profile = (tmp >> 5) & 0x7; - this.seq_level_idx_0 = tmp & 0x1F; - tmp = stream.readUint8(); - this.seq_tier_0 = (tmp >> 7) & 0x1; - this.high_bitdepth = (tmp >> 6) & 0x1; - this.twelve_bit = (tmp >> 5) & 0x1; - this.monochrome = (tmp >> 4) & 0x1; - this.chroma_subsampling_x = (tmp >> 3) & 0x1; - this.chroma_subsampling_y = (tmp >> 2) & 0x1; - this.chroma_sample_position = (tmp & 0x3); - tmp = stream.readUint8(); - this.reserved_1 = (tmp >> 5) & 0x7; - if (this.reserved_1 !== 0) { - Log.error("av1C reserved_1 parsing problem"); - return; - } - this.initial_presentation_delay_present = (tmp >> 4) & 0x1; - if (this.initial_presentation_delay_present === 1) { - this.initial_presentation_delay_minus_one = (tmp & 0xF); - } else { - this.reserved_2 = (tmp & 0xF); - if (this.reserved_2 !== 0) { - Log.error("av1C reserved_2 parsing problem"); - return; - } - } - - var configOBUs_length = this.size - this.hdr_size - 4; - this.configOBUs = stream.readUint8Array(configOBUs_length); -}); - -// file:src/parsing/avcC.js -BoxParser.createBoxCtor("avcC", function(stream) { - var i; - var toparse; - this.configurationVersion = stream.readUint8(); - this.AVCProfileIndication = stream.readUint8(); - this.profile_compatibility = stream.readUint8(); - this.AVCLevelIndication = stream.readUint8(); - this.lengthSizeMinusOne = (stream.readUint8() & 0x3); - this.nb_SPS_nalus = (stream.readUint8() & 0x1F); - toparse = this.size - this.hdr_size - 6; - this.SPS = []; - for (i = 0; i < this.nb_SPS_nalus; i++) { - this.SPS[i] = {}; - this.SPS[i].length = stream.readUint16(); - this.SPS[i].nalu = stream.readUint8Array(this.SPS[i].length); - toparse -= 2+this.SPS[i].length; - } - this.nb_PPS_nalus = stream.readUint8(); - toparse--; - this.PPS = []; - for (i = 0; i < this.nb_PPS_nalus; i++) { - this.PPS[i] = {}; - this.PPS[i].length = stream.readUint16(); - this.PPS[i].nalu = stream.readUint8Array(this.PPS[i].length); - toparse -= 2+this.PPS[i].length; - } - if (toparse>0) { - this.ext = stream.readUint8Array(toparse); - } -}); - -// file:src/parsing/btrt.js -BoxParser.createBoxCtor("btrt", function(stream) { - this.bufferSizeDB = stream.readUint32(); - this.maxBitrate = stream.readUint32(); - this.avgBitrate = stream.readUint32(); -}); - -// file:src/parsing/clap.js -BoxParser.createBoxCtor("clap", function(stream) { - this.cleanApertureWidthN = stream.readUint32(); - this.cleanApertureWidthD = stream.readUint32(); - this.cleanApertureHeightN = stream.readUint32(); - this.cleanApertureHeightD = stream.readUint32(); - this.horizOffN = stream.readUint32(); - this.horizOffD = stream.readUint32(); - this.vertOffN = stream.readUint32(); - this.vertOffD = stream.readUint32(); -});// file:src/parsing/clli.js -BoxParser.createBoxCtor("clli", function(stream) { - this.max_content_light_level = stream.readUint16(); - this.max_pic_average_light_level = stream.readUint16(); -}); - -// file:src/parsing/co64.js -BoxParser.createFullBoxCtor("co64", function(stream) { - var entry_count; - var i; - entry_count = stream.readUint32(); - this.chunk_offsets = []; - if (this.version === 0) { - for(i=0; i> 7; - } else if (this.colour_type === 'rICC') { - this.ICC_profile = stream.readUint8Array(this.size - 4); - } else if (this.colour_type === 'prof') { - this.ICC_profile = stream.readUint8Array(this.size - 4); - } -});// file:src/parsing/cprt.js -BoxParser.createFullBoxCtor("cprt", function (stream) { - this.parseLanguage(stream); - this.notice = stream.readCString(); -}); - -// file:src/parsing/cslg.js -BoxParser.createFullBoxCtor("cslg", function(stream) { - var entry_count; - if (this.version === 0) { - this.compositionToDTSShift = stream.readInt32(); /* signed */ - this.leastDecodeToDisplayDelta = stream.readInt32(); /* signed */ - this.greatestDecodeToDisplayDelta = stream.readInt32(); /* signed */ - this.compositionStartTime = stream.readInt32(); /* signed */ - this.compositionEndTime = stream.readInt32(); /* signed */ - } -}); - -// file:src/parsing/ctts.js -BoxParser.createFullBoxCtor("ctts", function(stream) { - var entry_count; - var i; - entry_count = stream.readUint32(); - this.sample_counts = []; - this.sample_offsets = []; - if (this.version === 0) { - for(i=0; i> 6; - this.bsid = ((tmp_byte1 >> 1) & 0x1F); - this.bsmod = ((tmp_byte1 & 0x1) << 2) | ((tmp_byte2 >> 6) & 0x3); - this.acmod = ((tmp_byte2 >> 3) & 0x7); - this.lfeon = ((tmp_byte2 >> 2) & 0x1); - this.bit_rate_code = (tmp_byte2 & 0x3) | ((tmp_byte3 >> 5) & 0x7); -}); - -// file:src/parsing/dec3.js -BoxParser.createBoxCtor("dec3", function(stream) { - var tmp_16 = stream.readUint16(); - this.data_rate = tmp_16 >> 3; - this.num_ind_sub = tmp_16 & 0x7; - this.ind_subs = []; - for (var i = 0; i < this.num_ind_sub+1; i++) { - var ind_sub = {}; - this.ind_subs.push(ind_sub); - var tmp_byte1 = stream.readUint8(); - var tmp_byte2 = stream.readUint8(); - var tmp_byte3 = stream.readUint8(); - ind_sub.fscod = tmp_byte1 >> 6; - ind_sub.bsid = ((tmp_byte1 >> 1) & 0x1F); - ind_sub.bsmod = ((tmp_byte1 & 0x1) << 4) | ((tmp_byte2 >> 4) & 0xF); - ind_sub.acmod = ((tmp_byte2 >> 1) & 0x7); - ind_sub.lfeon = (tmp_byte2 & 0x1); - ind_sub.num_dep_sub = ((tmp_byte3 >> 1) & 0xF); - if (ind_sub.num_dep_sub > 0) { - ind_sub.chan_loc = ((tmp_byte3 & 0x1) << 8) | stream.readUint8(); - } - } -}); - -// file:src/parsing/dfLa.js -BoxParser.createFullBoxCtor("dfLa", function(stream) { - var BLOCKTYPE_MASK = 0x7F; - var LASTMETADATABLOCKFLAG_MASK = 0x80; - - var boxesFound = []; - var knownBlockTypes = [ - "STREAMINFO", - "PADDING", - "APPLICATION", - "SEEKTABLE", - "VORBIS_COMMENT", - "CUESHEET", - "PICTURE", - "RESERVED" - ]; - - // dfLa is a FullBox - this.parseFullHeader(stream); - - // for (i=0; ; i++) { // to end of box - do { - var flagAndType = stream.readUint8(); - - var type = Math.min( - (flagAndType & BLOCKTYPE_MASK), - (knownBlockTypes.length - 1) - ); - - // if this is a STREAMINFO block, read the true samplerate since this - // can be different to the AudioSampleEntry samplerate. - if (!(type)) { - // read past all the other stuff - stream.readUint8Array(13); - - // extract samplerate - this.samplerate = (stream.readUint32() >> 12); - - // read to end of STREAMINFO - stream.readUint8Array(20); - } else { - // not interested in other block types so just discard length bytes - stream.readUint8Array(stream.readUint24()); - } - - boxesFound.push(knownBlockTypes[type]); - - if (!!(flagAndType & LASTMETADATABLOCKFLAG_MASK)) { - break; - } - } while (true); - - this.numMetadataBlocks = - boxesFound.length + " (" + boxesFound.join(", ") + ")"; -}); -// file:src/parsing/dimm.js -BoxParser.createBoxCtor("dimm", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/dmax.js -BoxParser.createBoxCtor("dmax", function(stream) { - this.time = stream.readUint32(); -}); - -// file:src/parsing/dmed.js -BoxParser.createBoxCtor("dmed", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/dOps.js -BoxParser.createBoxCtor("dOps", function(stream) { - this.Version = stream.readUint8(); - this.OutputChannelCount = stream.readUint8(); - this.PreSkip = stream.readUint16(); - this.InputSampleRate = stream.readUint32(); - this.OutputGain = stream.readInt16(); - this.ChannelMappingFamily = stream.readUint8(); - if (this.ChannelMappingFamily !== 0) { - this.StreamCount = stream.readUint8(); - this.CoupledCount = stream.readUint8(); - this.ChannelMapping = []; - for (var i = 0; i < this.OutputChannelCount; i++) { - this.ChannelMapping[i] = stream.readUint8(); - } - } -}); - -// file:src/parsing/dref.js -BoxParser.createFullBoxCtor("dref", function(stream) { - var ret; - var box; - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = ret.box; - this.entries.push(box); - } else { - return; - } - } -}); - -// file:src/parsing/drep.js -BoxParser.createBoxCtor("drep", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/elng.js -BoxParser.createFullBoxCtor("elng", function(stream) { - this.extended_language = stream.readString(this.size-this.hdr_size); -}); - -// file:src/parsing/elst.js -BoxParser.createFullBoxCtor("elst", function(stream) { - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - var entry = {}; - this.entries.push(entry); - if (this.version === 1) { - entry.segment_duration = stream.readUint64(); - entry.media_time = stream.readInt64(); - } else { - entry.segment_duration = stream.readUint32(); - entry.media_time = stream.readInt32(); - } - entry.media_rate_integer = stream.readInt16(); - entry.media_rate_fraction = stream.readInt16(); - } -}); - -// file:src/parsing/emsg.js -BoxParser.createFullBoxCtor("emsg", function(stream) { - if (this.version == 1) { - this.timescale = stream.readUint32(); - this.presentation_time = stream.readUint64(); - this.event_duration = stream.readUint32(); - this.id = stream.readUint32(); - this.scheme_id_uri = stream.readCString(); - this.value = stream.readCString(); - } else { - this.scheme_id_uri = stream.readCString(); - this.value = stream.readCString(); - this.timescale = stream.readUint32(); - this.presentation_time_delta = stream.readUint32(); - this.event_duration = stream.readUint32(); - this.id = stream.readUint32(); - } - var message_size = this.size - this.hdr_size - (4*4 + (this.scheme_id_uri.length+1) + (this.value.length+1)); - if (this.version == 1) { - message_size -= 4; - } - this.message_data = stream.readUint8Array(message_size); -}); - -// file:src/parsing/esds.js -BoxParser.createFullBoxCtor("esds", function(stream) { - var esd_data = stream.readUint8Array(this.size-this.hdr_size); - if (typeof MPEG4DescriptorParser !== "undefined") { - var esd_parser = new MPEG4DescriptorParser(); - this.esd = esd_parser.parseOneDescriptor(new DataStream(esd_data.buffer, 0, DataStream.BIG_ENDIAN)); - } -}); - -// file:src/parsing/fiel.js -BoxParser.createBoxCtor("fiel", function(stream) { - this.fieldCount = stream.readUint8(); - this.fieldOrdering = stream.readUint8(); -}); - -// file:src/parsing/frma.js -BoxParser.createBoxCtor("frma", function(stream) { - this.data_format = stream.readString(4); -}); - -// file:src/parsing/ftyp.js -BoxParser.createBoxCtor("ftyp", function(stream) { - var toparse = this.size - this.hdr_size; - this.major_brand = stream.readString(4); - this.minor_version = stream.readUint32(); - toparse -= 8; - this.compatible_brands = []; - var i = 0; - while (toparse>=4) { - this.compatible_brands[i] = stream.readString(4); - toparse -= 4; - i++; - } -}); - -// file:src/parsing/hdlr.js -BoxParser.createFullBoxCtor("hdlr", function(stream) { - if (this.version === 0) { - stream.readUint32(); - this.handler = stream.readString(4); - stream.readUint32Array(3); - this.name = stream.readString(this.size-this.hdr_size-20); - if (this.name[this.name.length-1]==='\0') { - this.name = this.name.slice(0,-1); - } - } -}); - -// file:src/parsing/hvcC.js -BoxParser.createBoxCtor("hvcC", function(stream) { - var i, j; - var nb_nalus; - var length; - var tmp_byte; - this.configurationVersion = stream.readUint8(); - tmp_byte = stream.readUint8(); - this.general_profile_space = tmp_byte >> 6; - this.general_tier_flag = (tmp_byte & 0x20) >> 5; - this.general_profile_idc = (tmp_byte & 0x1F); - this.general_profile_compatibility = stream.readUint32(); - this.general_constraint_indicator = stream.readUint8Array(6); - this.general_level_idc = stream.readUint8(); - this.min_spatial_segmentation_idc = stream.readUint16() & 0xFFF; - this.parallelismType = (stream.readUint8() & 0x3); - this.chroma_format_idc = (stream.readUint8() & 0x3); - this.bit_depth_luma_minus8 = (stream.readUint8() & 0x7); - this.bit_depth_chroma_minus8 = (stream.readUint8() & 0x7); - this.avgFrameRate = stream.readUint16(); - tmp_byte = stream.readUint8(); - this.constantFrameRate = (tmp_byte >> 6); - this.numTemporalLayers = (tmp_byte & 0XD) >> 3; - this.temporalIdNested = (tmp_byte & 0X4) >> 2; - this.lengthSizeMinusOne = (tmp_byte & 0X3); - - this.nalu_arrays = []; - var numOfArrays = stream.readUint8(); - for (i = 0; i < numOfArrays; i++) { - var nalu_array = []; - this.nalu_arrays.push(nalu_array); - tmp_byte = stream.readUint8() - nalu_array.completeness = (tmp_byte & 0x80) >> 7; - nalu_array.nalu_type = tmp_byte & 0x3F; - var numNalus = stream.readUint16(); - for (j = 0; j < numNalus; j++) { - var nalu = {} - nalu_array.push(nalu); - length = stream.readUint16(); - nalu.data = stream.readUint8Array(length); - } - } -}); - -// file:src/parsing/iinf.js -BoxParser.createFullBoxCtor("iinf", function(stream) { - var ret; - if (this.version === 0) { - this.entry_count = stream.readUint16(); - } else { - this.entry_count = stream.readUint32(); - } - this.item_infos = []; - for (var i = 0; i < this.entry_count; i++) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - if (ret.box.type !== "infe") { - Log.error("BoxParser", "Expected 'infe' box, got "+ret.box.type); - } - this.item_infos[i] = ret.box; - } else { - return; - } - } -}); - -// file:src/parsing/iloc.js -BoxParser.createFullBoxCtor("iloc", function(stream) { - var byte; - byte = stream.readUint8(); - this.offset_size = (byte >> 4) & 0xF; - this.length_size = byte & 0xF; - byte = stream.readUint8(); - this.base_offset_size = (byte >> 4) & 0xF; - if (this.version === 1 || this.version === 2) { - this.index_size = byte & 0xF; - } else { - this.index_size = 0; - // reserved = byte & 0xF; - } - this.items = []; - var item_count = 0; - if (this.version < 2) { - item_count = stream.readUint16(); - } else if (this.version === 2) { - item_count = stream.readUint32(); - } else { - throw "version of iloc box not supported"; - } - for (var i = 0; i < item_count; i++) { - var item = {}; - this.items.push(item); - if (this.version < 2) { - item.item_ID = stream.readUint16(); - } else if (this.version === 2) { - item.item_ID = stream.readUint16(); - } else { - throw "version of iloc box not supported"; - } - if (this.version === 1 || this.version === 2) { - item.construction_method = (stream.readUint16() & 0xF); - } else { - item.construction_method = 0; - } - item.data_reference_index = stream.readUint16(); - switch(this.base_offset_size) { - case 0: - item.base_offset = 0; - break; - case 4: - item.base_offset = stream.readUint32(); - break; - case 8: - item.base_offset = stream.readUint64(); - break; - default: - throw "Error reading base offset size"; - } - var extent_count = stream.readUint16(); - item.extents = []; - for (var j=0; j < extent_count; j++) { - var extent = {}; - item.extents.push(extent); - if (this.version === 1 || this.version === 2) { - switch(this.index_size) { - case 0: - extent.extent_index = 0; - break; - case 4: - extent.extent_index = stream.readUint32(); - break; - case 8: - extent.extent_index = stream.readUint64(); - break; - default: - throw "Error reading extent index"; - } - } - switch(this.offset_size) { - case 0: - extent.extent_offset = 0; - break; - case 4: - extent.extent_offset = stream.readUint32(); - break; - case 8: - extent.extent_offset = stream.readUint64(); - break; - default: - throw "Error reading extent index"; - } - switch(this.length_size) { - case 0: - extent.extent_length = 0; - break; - case 4: - extent.extent_length = stream.readUint32(); - break; - case 8: - extent.extent_length = stream.readUint64(); - break; - default: - throw "Error reading extent index"; - } - } - } -}); - -// file:src/parsing/imir.js -BoxParser.createBoxCtor("imir", function(stream) { - var tmp = stream.readUint8(); - this.reserved = tmp >> 7; - this.axis = tmp & 1; -});// file:src/parsing/infe.js -BoxParser.createFullBoxCtor("infe", function(stream) { - if (this.version === 0 || this.version === 1) { - this.item_ID = stream.readUint16(); - this.item_protection_index = stream.readUint16(); - this.item_name = stream.readCString(); - this.content_type = stream.readCString(); - this.content_encoding = stream.readCString(); - } - if (this.version === 1) { - this.extension_type = stream.readString(4); - Log.warn("BoxParser", "Cannot parse extension type"); - stream.seek(this.start+this.size); - return; - } - if (this.version >= 2) { - if (this.version === 2) { - this.item_ID = stream.readUint16(); - } else if (this.version === 3) { - this.item_ID = stream.readUint32(); - } - this.item_protection_index = stream.readUint16(); - this.item_type = stream.readString(4); - this.item_name = stream.readCString(); - if (this.item_type === "mime") { - this.content_type = stream.readCString(); - this.content_encoding = stream.readCString(); - } else if (this.item_type === "uri ") { - this.item_uri_type = stream.readCString(); - } - } -}); -// file:src/parsing/ipma.js -BoxParser.createFullBoxCtor("ipma", function(stream) { - var i, j; - entry_count = stream.readUint32(); - this.associations = []; - for(i=0; i> 7) === 1; - if (this.flags & 0x1) { - p.property_index = (tmp & 0x7F) << 8 | stream.readUint8(); - } else { - p.property_index = (tmp & 0x7F); - } - } - } -}); - -// file:src/parsing/iref.js -BoxParser.createFullBoxCtor("iref", function(stream) { - var ret; - var entryCount; - var box; - this.references = []; - - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, true, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - if (this.version === 0) { - box = new BoxParser.SingleItemTypeReferenceBox(ret.type, ret.size, ret.hdr_size, ret.start); - } else { - box = new BoxParser.SingleItemTypeReferenceBoxLarge(ret.type, ret.size, ret.hdr_size, ret.start); - } - if (box.write === BoxParser.Box.prototype.write && box.type !== "mdat") { - Log.warn("BoxParser", box.type+" box writing not yet implemented, keeping unparsed data in memory for later write"); - box.parseDataAndRewind(stream); - } - box.parse(stream); - this.references.push(box); - } else { - return; - } - } -}); -// file:src/parsing/irot.js -BoxParser.createBoxCtor("irot", function(stream) { - this.angle = stream.readUint8() & 0x3; -}); - -// file:src/parsing/ispe.js -BoxParser.createFullBoxCtor("ispe", function(stream) { - this.image_width = stream.readUint32(); - this.image_height = stream.readUint32(); -});// file:src/parsing/kind.js -BoxParser.createFullBoxCtor("kind", function(stream) { - this.schemeURI = stream.readCString(); - this.value = stream.readCString(); -}); -// file:src/parsing/leva.js -BoxParser.createFullBoxCtor("leva", function(stream) { - var count = stream.readUint8(); - this.levels = []; - for (var i = 0; i < count; i++) { - var level = {}; - this.levels[i] = level; - level.track_ID = stream.readUint32(); - var tmp_byte = stream.readUint8(); - level.padding_flag = tmp_byte >> 7; - level.assignment_type = tmp_byte & 0x7F; - switch (level.assignment_type) { - case 0: - level.grouping_type = stream.readString(4); - break; - case 1: - level.grouping_type = stream.readString(4); - level.grouping_type_parameter = stream.readUint32(); - break; - case 2: - break; - case 3: - break; - case 4: - level.sub_track_id = stream.readUint32(); - break; - default: - Log.warn("BoxParser", "Unknown leva assignement type"); - } - } -}); - -// file:src/parsing/lsel.js -BoxParser.createBoxCtor("lsel", function(stream) { - this.layer_id = stream.readUint16(); -});// file:src/parsing/maxr.js -BoxParser.createBoxCtor("maxr", function(stream) { - this.period = stream.readUint32(); - this.bytes = stream.readUint32(); -}); - -// file:src/parsing/mdcv.js -BoxParser.createBoxCtor("mdcv", function(stream) { - this.display_primaries = []; - this.display_primaries[0] = {}; - this.display_primaries[0].x = stream.readUint16(); - this.display_primaries[0].y = stream.readUint16(); - this.display_primaries[1] = {}; - this.display_primaries[1].x = stream.readUint16(); - this.display_primaries[1].y = stream.readUint16(); - this.display_primaries[2] = {}; - this.display_primaries[2].x = stream.readUint16(); - this.display_primaries[2].y = stream.readUint16(); - this.white_point = {}; - this.white_point.x = stream.readUint16(); - this.white_point.y = stream.readUint16(); - this.max_display_mastering_luminance = stream.readUint32(); - this.min_display_mastering_luminance = stream.readUint32(); -}); - -// file:src/parsing/mdhd.js -BoxParser.createFullBoxCtor("mdhd", function(stream) { - if (this.version == 1) { - this.creation_time = stream.readUint64(); - this.modification_time = stream.readUint64(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint64(); - } else { - this.creation_time = stream.readUint32(); - this.modification_time = stream.readUint32(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint32(); - } - this.parseLanguage(stream); - stream.readUint16(); -}); - -// file:src/parsing/mehd.js -BoxParser.createFullBoxCtor("mehd", function(stream) { - if (this.flags & 0x1) { - Log.warn("BoxParser", "mehd box incorrectly uses flags set to 1, converting version to 1"); - this.version = 1; - } - if (this.version == 1) { - this.fragment_duration = stream.readUint64(); - } else { - this.fragment_duration = stream.readUint32(); - } -}); - -// file:src/parsing/meta.js -BoxParser.createFullBoxCtor("meta", function(stream) { - this.boxes = []; - BoxParser.ContainerBox.prototype.parse.call(this, stream); -}); -// file:src/parsing/mfhd.js -BoxParser.createFullBoxCtor("mfhd", function(stream) { - this.sequence_number = stream.readUint32(); -}); - -// file:src/parsing/mfro.js -BoxParser.createFullBoxCtor("mfro", function(stream) { - this._size = stream.readUint32(); -}); - -// file:src/parsing/mvhd.js -BoxParser.createFullBoxCtor("mvhd", function(stream) { - if (this.version == 1) { - this.creation_time = stream.readUint64(); - this.modification_time = stream.readUint64(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint64(); - } else { - this.creation_time = stream.readUint32(); - this.modification_time = stream.readUint32(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint32(); - } - this.rate = stream.readUint32(); - this.volume = stream.readUint16()>>8; - stream.readUint16(); - stream.readUint32Array(2); - this.matrix = stream.readUint32Array(9); - stream.readUint32Array(6); - this.next_track_id = stream.readUint32(); -}); -// file:src/parsing/npck.js -BoxParser.createBoxCtor("npck", function(stream) { - this.packetssent = stream.readUint32(); -}); - -// file:src/parsing/nump.js -BoxParser.createBoxCtor("nump", function(stream) { - this.packetssent = stream.readUint64(); -}); - -// file:src/parsing/padb.js -BoxParser.createFullBoxCtor("padb", function(stream) { - var sample_count = stream.readUint32(); - this.padbits = []; - for (var i = 0; i < Math.floor((sample_count+1)/2); i++) { - this.padbits = stream.readUint8(); - } -}); - -// file:src/parsing/pasp.js -BoxParser.createBoxCtor("pasp", function(stream) { - this.hSpacing = stream.readUint32(); - this.vSpacing = stream.readUint32(); -});// file:src/parsing/payl.js -BoxParser.createBoxCtor("payl", function(stream) { - this.text = stream.readString(this.size - this.hdr_size); -}); - -// file:src/parsing/payt.js -BoxParser.createBoxCtor("payt", function(stream) { - this.payloadID = stream.readUint32(); - var count = stream.readUint8(); - this.rtpmap_string = stream.readString(count); -}); - -// file:src/parsing/pdin.js -BoxParser.createFullBoxCtor("pdin", function(stream) { - var count = (this.size - this.hdr_size)/8; - this.rate = []; - this.initial_delay = []; - for (var i = 0; i < count; i++) { - this.rate[i] = stream.readUint32(); - this.initial_delay[i] = stream.readUint32(); - } -}); - -// file:src/parsing/pitm.js -BoxParser.createFullBoxCtor("pitm", function(stream) { - if (this.version === 0) { - this.item_id = stream.readUint16(); - } else { - this.item_id = stream.readUint32(); - } -}); - -// file:src/parsing/pixi.js -BoxParser.createFullBoxCtor("pixi", function(stream) { - var i; - this.num_channels = stream.readUint8(); - this.bits_per_channels = []; - for (i = 0; i < this.num_channels; i++) { - this.bits_per_channels[i] = stream.readUint8(); - } -}); - -// file:src/parsing/pmax.js -BoxParser.createBoxCtor("pmax", function(stream) { - this.bytes = stream.readUint32(); -}); - -// file:src/parsing/prft.js -BoxParser.createFullBoxCtor("prft", function(stream) { - this.ref_track_id = stream.readUint32(); - this.ntp_timestamp = stream.readUint64(); - if (this.version === 0) { - this.media_time = stream.readUint32(); - } else { - this.media_time = stream.readUint64(); - } -}); - -// file:src/parsing/pssh.js -BoxParser.createFullBoxCtor("pssh", function(stream) { - this.system_id = BoxParser.parseHex16(stream); - if (this.version > 0) { - var count = stream.readUint32(); - this.kid = []; - for (var i = 0; i < count; i++) { - this.kid[i] = BoxParser.parseHex16(stream); - } - } - var datasize = stream.readUint32(); - if (datasize > 0) { - this.data = stream.readUint8Array(datasize); - } -}); - -// file:src/parsing/qt/clef.js -BoxParser.createFullBoxCtor("clef", function(stream) { - this.width = stream.readUint32(); - this.height = stream.readUint32(); -});// file:src/parsing/qt/enof.js -BoxParser.createFullBoxCtor("enof", function(stream) { - this.width = stream.readUint32(); - this.height = stream.readUint32(); -});// file:src/parsing/qt/prof.js -BoxParser.createFullBoxCtor("prof", function(stream) { - this.width = stream.readUint32(); - this.height = stream.readUint32(); -});// file:src/parsing/qt/tapt.js -BoxParser.createContainerBoxCtor("tapt", null, [ "clef", "prof", "enof"]);// file:src/parsing/rtp.js -BoxParser.createBoxCtor("rtp ", function(stream) { - this.descriptionformat = stream.readString(4); - this.sdptext = stream.readString(this.size - this.hdr_size - 4); -}); - -// file:src/parsing/saio.js -BoxParser.createFullBoxCtor("saio", function(stream) { - if (this.flags & 0x1) { - this.aux_info_type = stream.readUint32(); - this.aux_info_type_parameter = stream.readUint32(); - } - var count = stream.readUint32(); - this.offset = []; - for (var i = 0; i < count; i++) { - if (this.version === 0) { - this.offset[i] = stream.readUint32(); - } else { - this.offset[i] = stream.readUint64(); - } - } -}); -// file:src/parsing/saiz.js -BoxParser.createFullBoxCtor("saiz", function(stream) { - if (this.flags & 0x1) { - this.aux_info_type = stream.readUint32(); - this.aux_info_type_parameter = stream.readUint32(); - } - this.default_sample_info_size = stream.readUint8(); - var count = stream.readUint32(); - this.sample_info_size = []; - if (this.default_sample_info_size === 0) { - for (var i = 0; i < count; i++) { - this.sample_info_size[i] = stream.readUint8(); - } - } -}); - -// file:src/parsing/sampleentries/mett.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "mett", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.mime_format = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/metx.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "metx", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.namespace = stream.readCString(); - this.schema_location = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/sbtt.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "sbtt", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.mime_format = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/stpp.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "stpp", function(stream) { - this.parseHeader(stream); - this.namespace = stream.readCString(); - this.schema_location = stream.readCString(); - this.auxiliary_mime_types = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/stxt.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "stxt", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.mime_format = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/tx3g.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "tx3g", function(stream) { - this.parseHeader(stream); - this.displayFlags = stream.readUint32(); - this.horizontal_justification = stream.readInt8(); - this.vertical_justification = stream.readInt8(); - this.bg_color_rgba = stream.readUint8Array(4); - this.box_record = stream.readInt16Array(4); - this.style_record = stream.readUint8Array(12); - this.parseFooter(stream); -}); -// file:src/parsing/sampleentries/wvtt.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "wvtt", function(stream) { - this.parseHeader(stream); - this.parseFooter(stream); -}); - -// file:src/parsing/samplegroups/alst.js -BoxParser.createSampleGroupCtor("alst", function(stream) { - var i; - var roll_count = stream.readUint16(); - this.first_output_sample = stream.readUint16(); - this.sample_offset = []; - for (i = 0; i < roll_count; i++) { - this.sample_offset[i] = stream.readUint32(); - } - var remaining = this.description_length - 4 - 4*roll_count; - this.num_output_samples = []; - this.num_total_samples = []; - for (i = 0; i < remaining/4; i++) { - this.num_output_samples[i] = stream.readUint16(); - this.num_total_samples[i] = stream.readUint16(); - } -}); - -// file:src/parsing/samplegroups/avll.js -BoxParser.createSampleGroupCtor("avll", function(stream) { - this.layerNumber = stream.readUint8(); - this.accurateStatisticsFlag = stream.readUint8(); - this.avgBitRate = stream.readUint16(); - this.avgFrameRate = stream.readUint16(); -}); - -// file:src/parsing/samplegroups/avss.js -BoxParser.createSampleGroupCtor("avss", function(stream) { - this.subSequenceIdentifier = stream.readUint16(); - this.layerNumber = stream.readUint8(); - var tmp_byte = stream.readUint8(); - this.durationFlag = tmp_byte >> 7; - this.avgRateFlag = (tmp_byte >> 6) & 0x1; - if (this.durationFlag) { - this.duration = stream.readUint32(); - } - if (this.avgRateFlag) { - this.accurateStatisticsFlag = stream.readUint8(); - this.avgBitRate = stream.readUint16(); - this.avgFrameRate = stream.readUint16(); - } - this.dependency = []; - var numReferences = stream.readUint8(); - for (var i = 0; i < numReferences; i++) { - var dependencyInfo = {}; - this.dependency.push(dependencyInfo); - dependencyInfo.subSeqDirectionFlag = stream.readUint8(); - dependencyInfo.layerNumber = stream.readUint8(); - dependencyInfo.subSequenceIdentifier = stream.readUint16(); - } -}); - -// file:src/parsing/samplegroups/dtrt.js -BoxParser.createSampleGroupCtor("dtrt", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/mvif.js -BoxParser.createSampleGroupCtor("mvif", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/prol.js -BoxParser.createSampleGroupCtor("prol", function(stream) { - this.roll_distance = stream.readInt16(); -}); - -// file:src/parsing/samplegroups/rap.js -BoxParser.createSampleGroupCtor("rap ", function(stream) { - var tmp_byte = stream.readUint8(); - this.num_leading_samples_known = tmp_byte >> 7; - this.num_leading_samples = tmp_byte & 0x7F; -}); - -// file:src/parsing/samplegroups/rash.js -BoxParser.createSampleGroupCtor("rash", function(stream) { - this.operation_point_count = stream.readUint16(); - if (this.description_length !== 2+(this.operation_point_count === 1?2:this.operation_point_count*6)+9) { - Log.warn("BoxParser", "Mismatch in "+this.grouping_type+" sample group length"); - this.data = stream.readUint8Array(this.description_length-2); - } else { - if (this.operation_point_count === 1) { - this.target_rate_share = stream.readUint16(); - } else { - this.target_rate_share = []; - this.available_bitrate = []; - for (var i = 0; i < this.operation_point_count; i++) { - this.available_bitrate[i] = stream.readUint32(); - this.target_rate_share[i] = stream.readUint16(); - } - } - this.maximum_bitrate = stream.readUint32(); - this.minimum_bitrate = stream.readUint32(); - this.discard_priority = stream.readUint8(); - } -}); - -// file:src/parsing/samplegroups/roll.js -BoxParser.createSampleGroupCtor("roll", function(stream) { - this.roll_distance = stream.readInt16(); -}); - -// file:src/parsing/samplegroups/samplegroup.js -BoxParser.SampleGroupEntry.prototype.parse = function(stream) { - Log.warn("BoxParser", "Unknown Sample Group type: "+this.grouping_type); - this.data = stream.readUint8Array(this.description_length); -} - -// file:src/parsing/samplegroups/scif.js -BoxParser.createSampleGroupCtor("scif", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/scnm.js -BoxParser.createSampleGroupCtor("scnm", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/seig.js -BoxParser.createSampleGroupCtor("seig", function(stream) { - this.reserved = stream.readUint8(); - var tmp = stream.readUint8(); - this.crypt_byte_block = tmp >> 4; - this.skip_byte_block = tmp & 0xF; - this.isProtected = stream.readUint8(); - this.Per_Sample_IV_Size = stream.readUint8(); - this.KID = BoxParser.parseHex16(stream); - this.constant_IV_size = 0; - this.constant_IV = 0; - if (this.isProtected === 1 && this.Per_Sample_IV_Size === 0) { - this.constant_IV_size = stream.readUint8(); - this.constant_IV = stream.readUint8Array(this.constant_IV_size); - } -}); - -// file:src/parsing/samplegroups/stsa.js -BoxParser.createSampleGroupCtor("stsa", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/sync.js -BoxParser.createSampleGroupCtor("sync", function(stream) { - var tmp_byte = stream.readUint8(); - this.NAL_unit_type = tmp_byte & 0x3F; -}); - -// file:src/parsing/samplegroups/tele.js -BoxParser.createSampleGroupCtor("tele", function(stream) { - var tmp_byte = stream.readUint8(); - this.level_independently_decodable = tmp_byte >> 7; -}); - -// file:src/parsing/samplegroups/tsas.js -BoxParser.createSampleGroupCtor("tsas", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/tscl.js -BoxParser.createSampleGroupCtor("tscl", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/vipr.js -BoxParser.createSampleGroupCtor("vipr", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/sbgp.js -BoxParser.createFullBoxCtor("sbgp", function(stream) { - this.grouping_type = stream.readString(4); - if (this.version === 1) { - this.grouping_type_parameter = stream.readUint32(); - } else { - this.grouping_type_parameter = 0; - } - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - var entry = {}; - this.entries.push(entry); - entry.sample_count = stream.readInt32(); - entry.group_description_index = stream.readInt32(); - } -}); - -// file:src/parsing/schm.js -BoxParser.createFullBoxCtor("schm", function(stream) { - this.scheme_type = stream.readString(4); - this.scheme_version = stream.readUint32(); - if (this.flags & 0x000001) { - this.scheme_uri = stream.readString(this.size - this.hdr_size - 8); - } -}); - -// file:src/parsing/sdp.js -BoxParser.createBoxCtor("sdp ", function(stream) { - this.sdptext = stream.readString(this.size - this.hdr_size); -}); - -// file:src/parsing/sdtp.js -BoxParser.createFullBoxCtor("sdtp", function(stream) { - var tmp_byte; - var count = (this.size - this.hdr_size); - this.is_leading = []; - this.sample_depends_on = []; - this.sample_is_depended_on = []; - this.sample_has_redundancy = []; - for (var i = 0; i < count; i++) { - tmp_byte = stream.readUint8(); - this.is_leading[i] = tmp_byte >> 6; - this.sample_depends_on[i] = (tmp_byte >> 4) & 0x3; - this.sample_is_depended_on[i] = (tmp_byte >> 2) & 0x3; - this.sample_has_redundancy[i] = tmp_byte & 0x3; - } -}); - -// file:src/parsing/senc.js -// Cannot be fully parsed because Per_Sample_IV_Size needs to be known -BoxParser.createFullBoxCtor("senc" /*, function(stream) { - this.parseFullHeader(stream); - var sample_count = stream.readUint32(); - this.samples = []; - for (var i = 0; i < sample_count; i++) { - var sample = {}; - // tenc.default_Per_Sample_IV_Size or seig.Per_Sample_IV_Size - sample.InitializationVector = this.readUint8Array(Per_Sample_IV_Size*8); - if (this.flags & 0x2) { - sample.subsamples = []; - subsample_count = stream.readUint16(); - for (var j = 0; j < subsample_count; j++) { - var subsample = {}; - subsample.BytesOfClearData = stream.readUint16(); - subsample.BytesOfProtectedData = stream.readUint32(); - sample.subsamples.push(subsample); - } - } - // TODO - this.samples.push(sample); - } -}*/); -// file:src/parsing/sgpd.js -BoxParser.createFullBoxCtor("sgpd", function(stream) { - this.grouping_type = stream.readString(4); - Log.debug("BoxParser", "Found Sample Groups of type "+this.grouping_type); - if (this.version === 1) { - this.default_length = stream.readUint32(); - } else { - this.default_length = 0; - } - if (this.version >= 2) { - this.default_group_description_index = stream.readUint32(); - } - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - var entry; - if (BoxParser[this.grouping_type+"SampleGroupEntry"]) { - entry = new BoxParser[this.grouping_type+"SampleGroupEntry"](this.grouping_type); - } else { - entry = new BoxParser.SampleGroupEntry(this.grouping_type); - } - this.entries.push(entry); - if (this.version === 1) { - if (this.default_length === 0) { - entry.description_length = stream.readUint32(); - } else { - entry.description_length = this.default_length; - } - } else { - entry.description_length = this.default_length; - } - if (entry.write === BoxParser.SampleGroupEntry.prototype.write) { - Log.info("BoxParser", "SampleGroup for type "+this.grouping_type+" writing not yet implemented, keeping unparsed data in memory for later write"); - // storing data - entry.data = stream.readUint8Array(entry.description_length); - // rewinding - stream.position -= entry.description_length; - } - entry.parse(stream); - } -}); - -// file:src/parsing/sidx.js -BoxParser.createFullBoxCtor("sidx", function(stream) { - this.reference_ID = stream.readUint32(); - this.timescale = stream.readUint32(); - if (this.version === 0) { - this.earliest_presentation_time = stream.readUint32(); - this.first_offset = stream.readUint32(); - } else { - this.earliest_presentation_time = stream.readUint64(); - this.first_offset = stream.readUint64(); - } - stream.readUint16(); - this.references = []; - var count = stream.readUint16(); - for (var i = 0; i < count; i++) { - var ref = {}; - this.references.push(ref); - var tmp_32 = stream.readUint32(); - ref.reference_type = (tmp_32 >> 31) & 0x1; - ref.referenced_size = tmp_32 & 0x7FFFFFFF; - ref.subsegment_duration = stream.readUint32(); - tmp_32 = stream.readUint32(); - ref.starts_with_SAP = (tmp_32 >> 31) & 0x1; - ref.SAP_type = (tmp_32 >> 28) & 0x7; - ref.SAP_delta_time = tmp_32 & 0xFFFFFFF; - } -}); - -// file:src/parsing/singleitemtypereference.js -BoxParser.SingleItemTypeReferenceBox = function(type, size, hdr_size, start) { - BoxParser.Box.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; -} -BoxParser.SingleItemTypeReferenceBox.prototype = new BoxParser.Box(); -BoxParser.SingleItemTypeReferenceBox.prototype.parse = function(stream) { - this.from_item_ID = stream.readUint16(); - var count = stream.readUint16(); - this.references = []; - for(var i = 0; i < count; i++) { - this.references[i] = stream.readUint16(); - } -} - -// file:src/parsing/singleitemtypereferencelarge.js -BoxParser.SingleItemTypeReferenceBoxLarge = function(type, size, hdr_size, start) { - BoxParser.Box.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; -} -BoxParser.SingleItemTypeReferenceBoxLarge.prototype = new BoxParser.Box(); -BoxParser.SingleItemTypeReferenceBoxLarge.prototype.parse = function(stream) { - this.from_item_ID = stream.readUint32(); - var count = stream.readUint16(); - this.references = []; - for(var i = 0; i < count; i++) { - this.references[i] = stream.readUint32(); - } -} - -// file:src/parsing/SmDm.js -BoxParser.createFullBoxCtor("SmDm", function(stream) { - this.primaryRChromaticity_x = stream.readUint16(); - this.primaryRChromaticity_y = stream.readUint16(); - this.primaryGChromaticity_x = stream.readUint16(); - this.primaryGChromaticity_y = stream.readUint16(); - this.primaryBChromaticity_x = stream.readUint16(); - this.primaryBChromaticity_y = stream.readUint16(); - this.whitePointChromaticity_x = stream.readUint16(); - this.whitePointChromaticity_y = stream.readUint16(); - this.luminanceMax = stream.readUint32(); - this.luminanceMin = stream.readUint32(); -}); - -// file:src/parsing/smhd.js -BoxParser.createFullBoxCtor("smhd", function(stream) { - this.balance = stream.readUint16(); - stream.readUint16(); -}); - -// file:src/parsing/ssix.js -BoxParser.createFullBoxCtor("ssix", function(stream) { - this.subsegments = []; - var subsegment_count = stream.readUint32(); - for (var i = 0; i < subsegment_count; i++) { - var subsegment = {}; - this.subsegments.push(subsegment); - subsegment.ranges = []; - var range_count = stream.readUint32(); - for (var j = 0; j < range_count; j++) { - var range = {}; - subsegment.ranges.push(range); - range.level = stream.readUint8(); - range.range_size = stream.readUint24(); - } - } -}); - -// file:src/parsing/stco.js -BoxParser.createFullBoxCtor("stco", function(stream) { - var entry_count; - entry_count = stream.readUint32(); - this.chunk_offsets = []; - if (this.version === 0) { - for (var i = 0; i < entry_count; i++) { - this.chunk_offsets.push(stream.readUint32()); - } - } -}); - -// file:src/parsing/stdp.js -BoxParser.createFullBoxCtor("stdp", function(stream) { - var count = (this.size - this.hdr_size)/2; - this.priority = []; - for (var i = 0; i < count; i++) { - this.priority[i] = stream.readUint16(); - } -}); - -// file:src/parsing/sthd.js -BoxParser.createFullBoxCtor("sthd"); - -// file:src/parsing/stri.js -BoxParser.createFullBoxCtor("stri", function(stream) { - this.switch_group = stream.readUint16(); - this.alternate_group = stream.readUint16(); - this.sub_track_id = stream.readUint32(); - var count = (this.size - this.hdr_size - 8)/4; - this.attribute_list = []; - for (var i = 0; i < count; i++) { - this.attribute_list[i] = stream.readUint32(); - } -}); - -// file:src/parsing/stsc.js -BoxParser.createFullBoxCtor("stsc", function(stream) { - var entry_count; - var i; - entry_count = stream.readUint32(); - this.first_chunk = []; - this.samples_per_chunk = []; - this.sample_description_index = []; - if (this.version === 0) { - for(i=0; i> 4) & 0xF; - this.sample_sizes[i+1] = tmp & 0xF; - } - } else if (this.field_size === 8) { - for (i = 0; i < sample_count; i++) { - this.sample_sizes[i] = stream.readUint8(); - } - } else if (this.field_size === 16) { - for (i = 0; i < sample_count; i++) { - this.sample_sizes[i] = stream.readUint16(); - } - } else { - Log.error("BoxParser", "Error in length field in stz2 box"); - } - } -}); - -// file:src/parsing/subs.js -BoxParser.createFullBoxCtor("subs", function(stream) { - var i,j; - var entry_count; - var subsample_count; - entry_count = stream.readUint32(); - this.entries = []; - for (i = 0; i < entry_count; i++) { - var sampleInfo = {}; - this.entries[i] = sampleInfo; - sampleInfo.sample_delta = stream.readUint32(); - sampleInfo.subsamples = []; - subsample_count = stream.readUint16(); - if (subsample_count>0) { - for (j = 0; j < subsample_count; j++) { - var subsample = {}; - sampleInfo.subsamples.push(subsample); - if (this.version == 1) { - subsample.size = stream.readUint32(); - } else { - subsample.size = stream.readUint16(); - } - subsample.priority = stream.readUint8(); - subsample.discardable = stream.readUint8(); - subsample.codec_specific_parameters = stream.readUint32(); - } - } - } -}); - -// file:src/parsing/tenc.js -BoxParser.createFullBoxCtor("tenc", function(stream) { - stream.readUint8(); // reserved - if (this.version === 0) { - stream.readUint8(); - } else { - var tmp = stream.readUint8(); - this.default_crypt_byte_block = (tmp >> 4) & 0xF; - this.default_skip_byte_block = tmp & 0xF; - } - this.default_isProtected = stream.readUint8(); - this.default_Per_Sample_IV_Size = stream.readUint8(); - this.default_KID = BoxParser.parseHex16(stream); - if (this.default_isProtected === 1 && this.default_Per_Sample_IV_Size === 0) { - this.default_constant_IV_size = stream.readUint8(); - this.default_constant_IV = stream.readUint8Array(this.default_constant_IV_size); - } -});// file:src/parsing/tfdt.js -BoxParser.createFullBoxCtor("tfdt", function(stream) { - if (this.version == 1) { - this.baseMediaDecodeTime = stream.readUint64(); - } else { - this.baseMediaDecodeTime = stream.readUint32(); - } -}); - -// file:src/parsing/tfhd.js -BoxParser.createFullBoxCtor("tfhd", function(stream) { - var readBytes = 0; - this.track_id = stream.readUint32(); - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET)) { - this.base_data_offset = stream.readUint64(); - readBytes += 8; - } else { - this.base_data_offset = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC)) { - this.default_sample_description_index = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_description_index = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR)) { - this.default_sample_duration = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_duration = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE)) { - this.default_sample_size = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_size = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS)) { - this.default_sample_flags = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_flags = 0; - } -}); - -// file:src/parsing/tfra.js -BoxParser.createFullBoxCtor("tfra", function(stream) { - this.track_ID = stream.readUint32(); - stream.readUint24(); - var tmp_byte = stream.readUint8(); - this.length_size_of_traf_num = (tmp_byte >> 4) & 0x3; - this.length_size_of_trun_num = (tmp_byte >> 2) & 0x3; - this.length_size_of_sample_num = (tmp_byte) & 0x3; - this.entries = []; - var number_of_entries = stream.readUint32(); - for (var i = 0; i < number_of_entries; i++) { - if (this.version === 1) { - this.time = stream.readUint64(); - this.moof_offset = stream.readUint64(); - } else { - this.time = stream.readUint32(); - this.moof_offset = stream.readUint32(); - } - this.traf_number = stream["readUint"+(8*(this.length_size_of_traf_num+1))](); - this.trun_number = stream["readUint"+(8*(this.length_size_of_trun_num+1))](); - this.sample_number = stream["readUint"+(8*(this.length_size_of_sample_num+1))](); - } -}); - -// file:src/parsing/tkhd.js -BoxParser.createFullBoxCtor("tkhd", function(stream) { - if (this.version == 1) { - this.creation_time = stream.readUint64(); - this.modification_time = stream.readUint64(); - this.track_id = stream.readUint32(); - stream.readUint32(); - this.duration = stream.readUint64(); - } else { - this.creation_time = stream.readUint32(); - this.modification_time = stream.readUint32(); - this.track_id = stream.readUint32(); - stream.readUint32(); - this.duration = stream.readUint32(); - } - stream.readUint32Array(2); - this.layer = stream.readInt16(); - this.alternate_group = stream.readInt16(); - this.volume = stream.readInt16()>>8; - stream.readUint16(); - this.matrix = stream.readInt32Array(9); - this.width = stream.readUint32(); - this.height = stream.readUint32(); -}); - -// file:src/parsing/tmax.js -BoxParser.createBoxCtor("tmax", function(stream) { - this.time = stream.readUint32(); -}); - -// file:src/parsing/tmin.js -BoxParser.createBoxCtor("tmin", function(stream) { - this.time = stream.readUint32(); -}); - -// file:src/parsing/totl.js -BoxParser.createBoxCtor("totl",function(stream) { - this.bytessent = stream.readUint32(); -}); - -// file:src/parsing/tpay.js -BoxParser.createBoxCtor("tpay", function(stream) { - this.bytessent = stream.readUint32(); -}); - -// file:src/parsing/tpyl.js -BoxParser.createBoxCtor("tpyl", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/TrackGroup.js -BoxParser.TrackGroupTypeBox.prototype.parse = function(stream) { - this.parseFullHeader(stream); - this.track_group_id = stream.readUint32(); -} - -// file:src/parsing/trackgroups/msrc.js -BoxParser.createTrackGroupCtor("msrc");// file:src/parsing/TrakReference.js -BoxParser.TrackReferenceTypeBox = function(type, size, hdr_size, start) { - BoxParser.Box.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; -} -BoxParser.TrackReferenceTypeBox.prototype = new BoxParser.Box(); -BoxParser.TrackReferenceTypeBox.prototype.parse = function(stream) { - this.track_ids = stream.readUint32Array((this.size-this.hdr_size)/4); -} - -// file:src/parsing/tref.js -BoxParser.trefBox.prototype.parse = function(stream) { - var ret; - var box; - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, true, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = new BoxParser.TrackReferenceTypeBox(ret.type, ret.size, ret.hdr_size, ret.start); - if (box.write === BoxParser.Box.prototype.write && box.type !== "mdat") { - Log.info("BoxParser", "TrackReference "+box.type+" box writing not yet implemented, keeping unparsed data in memory for later write"); - box.parseDataAndRewind(stream); - } - box.parse(stream); - this.boxes.push(box); - } else { - return; - } - } -} - -// file:src/parsing/trep.js -BoxParser.createFullBoxCtor("trep", function(stream) { - this.track_ID = stream.readUint32(); - this.boxes = []; - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = ret.box; - this.boxes.push(box); - } else { - return; - } - } -}); - -// file:src/parsing/trex.js -BoxParser.createFullBoxCtor("trex", function(stream) { - this.track_id = stream.readUint32(); - this.default_sample_description_index = stream.readUint32(); - this.default_sample_duration = stream.readUint32(); - this.default_sample_size = stream.readUint32(); - this.default_sample_flags = stream.readUint32(); -}); - -// file:src/parsing/trpy.js -BoxParser.createBoxCtor("trpy", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/trun.js -BoxParser.createFullBoxCtor("trun", function(stream) { - var readBytes = 0; - this.sample_count = stream.readUint32(); - readBytes+= 4; - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) ) { - this.data_offset = stream.readInt32(); //signed - readBytes += 4; - } else { - this.data_offset = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) ) { - this.first_sample_flags = stream.readUint32(); - readBytes += 4; - } else { - this.first_sample_flags = 0; - } - this.sample_duration = []; - this.sample_size = []; - this.sample_flags = []; - this.sample_composition_time_offset = []; - if (this.size - this.hdr_size > readBytes) { - for (var i = 0; i < this.sample_count; i++) { - if (this.flags & BoxParser.TRUN_FLAGS_DURATION) { - this.sample_duration[i] = stream.readUint32(); - } - if (this.flags & BoxParser.TRUN_FLAGS_SIZE) { - this.sample_size[i] = stream.readUint32(); - } - if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) { - this.sample_flags[i] = stream.readUint32(); - } - if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - if (this.version === 0) { - this.sample_composition_time_offset[i] = stream.readUint32(); - } else { - this.sample_composition_time_offset[i] = stream.readInt32(); //signed - } - } - } - } -}); - -// file:src/parsing/tsel.js -BoxParser.createFullBoxCtor("tsel", function(stream) { - this.switch_group = stream.readUint32(); - var count = (this.size - this.hdr_size - 4)/4; - this.attribute_list = []; - for (var i = 0; i < count; i++) { - this.attribute_list[i] = stream.readUint32(); - } -}); - -// file:src/parsing/txtC.js -BoxParser.createFullBoxCtor("txtC", function(stream) { - this.config = stream.readCString(); -}); - -// file:src/parsing/url.js -BoxParser.createFullBoxCtor("url ", function(stream) { - if (this.flags !== 0x000001) { - this.location = stream.readCString(); - } -}); - -// file:src/parsing/urn.js -BoxParser.createFullBoxCtor("urn ", function(stream) { - this.name = stream.readCString(); - if (this.size - this.hdr_size - this.name.length - 1 > 0) { - this.location = stream.readCString(); - } -}); - -// file:src/parsing/uuid/piff/piffLsm.js -BoxParser.createUUIDBox("a5d40b30e81411ddba2f0800200c9a66", true, false, function(stream) { - this.LiveServerManifest = stream.readString(this.size - this.hdr_size) - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); -});// file:src/parsing/uuid/piff/piffPssh.js -BoxParser.createUUIDBox("d08a4f1810f34a82b6c832d8aba183d3", true, false, function(stream) { - this.system_id = BoxParser.parseHex16(stream); - var datasize = stream.readUint32(); - if (datasize > 0) { - this.data = stream.readUint8Array(datasize); - } -}); - -// file:src/parsing/uuid/piff/piffSenc.js -BoxParser.createUUIDBox("a2394f525a9b4f14a2446c427c648df4", true, false /*, function(stream) { - if (this.flags & 0x1) { - this.AlgorithmID = stream.readUint24(); - this.IV_size = stream.readUint8(); - this.KID = BoxParser.parseHex16(stream); - } - var sample_count = stream.readUint32(); - this.samples = []; - for (var i = 0; i < sample_count; i++) { - var sample = {}; - sample.InitializationVector = this.readUint8Array(this.IV_size*8); - if (this.flags & 0x2) { - sample.subsamples = []; - sample.NumberOfEntries = stream.readUint16(); - for (var j = 0; j < sample.NumberOfEntries; j++) { - var subsample = {}; - subsample.BytesOfClearData = stream.readUint16(); - subsample.BytesOfProtectedData = stream.readUint32(); - sample.subsamples.push(subsample); - } - } - this.samples.push(sample); - } -}*/); -// file:src/parsing/uuid/piff/piffTenc.js -BoxParser.createUUIDBox("8974dbce7be74c5184f97148f9882554", true, false, function(stream) { - this.default_AlgorithmID = stream.readUint24(); - this.default_IV_size = stream.readUint8(); - this.default_KID = BoxParser.parseHex16(stream); -});// file:src/parsing/uuid/piff/piffTfrf.js -BoxParser.createUUIDBox("d4807ef2ca3946958e5426cb9e46a79f", true, false, function(stream) { - this.fragment_count = stream.readUint8(); - this.entries = []; - - for (var i = 0; i < this.fragment_count; i++) { - var entry = {}; - var absolute_time = 0; - var absolute_duration = 0; - - if (this.version === 1) { - absolute_time = stream.readUint64(); - absolute_duration = stream.readUint64(); - } else { - absolute_time = stream.readUint32(); - absolute_duration = stream.readUint32(); - } - - entry.absolute_time = absolute_time; - entry.absolute_duration = absolute_duration; - - this.entries.push(entry); - } -});// file:src/parsing/uuid/piff/piffTfxd.js -BoxParser.createUUIDBox("6d1d9b0542d544e680e2141daff757b2", true, false, function(stream) { - if (this.version === 1) { - this.absolute_time = stream.readUint64(); - this.duration = stream.readUint64(); - } else { - this.absolute_time = stream.readUint32(); - this.duration = stream.readUint32(); - } -});// file:src/parsing/vmhd.js -BoxParser.createFullBoxCtor("vmhd", function(stream) { - this.graphicsmode = stream.readUint16(); - this.opcolor = stream.readUint16Array(3); -}); - -// file:src/parsing/vpcC.js -BoxParser.createFullBoxCtor("vpcC", function (stream) { - var tmp; - if (this.version === 1) { - this.profile = stream.readUint8(); - this.level = stream.readUint8(); - tmp = stream.readUint8(); - this.bitDepth = tmp >> 4; - this.chromaSubsampling = (tmp >> 1) & 0x7; - this.videoFullRangeFlag = tmp & 0x1; - this.colourPrimaries = stream.readUint8(); - this.transferCharacteristics = stream.readUint8(); - this.matrixCoefficients = stream.readUint8(); - this.codecIntializationDataSize = stream.readUint16(); - this.codecIntializationData = stream.readUint8Array(this.codecIntializationDataSize); - } else { - this.profile = stream.readUint8(); - this.level = stream.readUint8(); - tmp = stream.readUint8(); - this.bitDepth = (tmp >> 4) & 0xF; - this.colorSpace = tmp & 0xF; - tmp = stream.readUint8(); - this.chromaSubsampling = (tmp >> 4) & 0xF; - this.transferFunction = (tmp >> 1) & 0x7; - this.videoFullRangeFlag = tmp & 0x1; - this.codecIntializationDataSize = stream.readUint16(); - this.codecIntializationData = stream.readUint8Array(this.codecIntializationDataSize); - } -});// file:src/parsing/vttC.js -BoxParser.createBoxCtor("vttC", function(stream) { - this.text = stream.readString(this.size - this.hdr_size); -}); - -// file:src/parsing/vvcC.js -BoxParser.createFullBoxCtor("vvcC", function (stream) { - var i, j; - - // helper object to simplify extracting individual bits - var bitReader = { - held_bits: undefined, - num_held_bits: 0, - - stream_read_1_bytes: function (strm) { - this.held_bits = strm.readUint8(); - this.num_held_bits = 1 * 8; - }, - stream_read_2_bytes: function (strm) { - this.held_bits = strm.readUint16(); - this.num_held_bits = 2 * 8; - }, - - extract_bits: function (num_bits) { - var ret = (this.held_bits >> (this.num_held_bits - num_bits)) & ((1 << num_bits) - 1); - this.num_held_bits -= num_bits; - return ret; - } - }; - - // VvcDecoderConfigurationRecord - bitReader.stream_read_1_bytes(stream); - bitReader.extract_bits(5); // reserved - this.lengthSizeMinusOne = bitReader.extract_bits(2); - this.ptl_present_flag = bitReader.extract_bits(1); - - if (this.ptl_present_flag) { - bitReader.stream_read_2_bytes(stream); - this.ols_idx = bitReader.extract_bits(9); - this.num_sublayers = bitReader.extract_bits(3); - this.constant_frame_rate = bitReader.extract_bits(2); - this.chroma_format_idc = bitReader.extract_bits(2); - - bitReader.stream_read_1_bytes(stream); - this.bit_depth_minus8 = bitReader.extract_bits(3); - bitReader.extract_bits(5); // reserved - - // VvcPTLRecord - { - bitReader.stream_read_2_bytes(stream); - bitReader.extract_bits(2); // reserved - this.num_bytes_constraint_info = bitReader.extract_bits(6); - this.general_profile_idc = bitReader.extract_bits(7); - this.general_tier_flag = bitReader.extract_bits(1); - - this.general_level_idc = stream.readUint8(); - - bitReader.stream_read_1_bytes(stream); - this.ptl_frame_only_constraint_flag = bitReader.extract_bits(1); - this.ptl_multilayer_enabled_flag = bitReader.extract_bits(1); - - this.general_constraint_info = new Uint8Array(this.num_bytes_constraint_info); - if (this.num_bytes_constraint_info) { - for (i = 0; i < this.num_bytes_constraint_info - 1; i++) { - var cnstr1 = bitReader.extract_bits(6); - bitReader.stream_read_1_bytes(stream); - var cnstr2 = bitReader.extract_bits(2); - - this.general_constraint_info[i] = ((cnstr1 << 2) | cnstr2); - } - this.general_constraint_info[this.num_bytes_constraint_info - 1] = bitReader.extract_bits(6); - } else { - //forbidden in spec! - bitReader.extract_bits(6); - } - - bitReader.stream_read_1_bytes(stream); - this.ptl_sublayer_present_mask = 0; - for (j = this.num_sublayers - 2; j >= 0; --j) { - var val = bitReader.extract_bits(1); - this.ptl_sublayer_present_mask |= val << j; - } - for (j = this.num_sublayers; j <= 8 && this.num_sublayers > 1; ++j) { - bitReader.extract_bits(1); // ptl_reserved_zero_bit - } - - for (j = this.num_sublayers - 2; j >= 0; --j) { - if (this.ptl_sublayer_present_mask & (1 << j)) { - this.sublayer_level_idc[j] = stream.readUint8(); - } - } - - this.ptl_num_sub_profiles = stream.readUint8(); - this.general_sub_profile_idc = []; - if (this.ptl_num_sub_profiles) { - for (i = 0; i < this.ptl_num_sub_profiles; i++) { - this.general_sub_profile_idc.push(stream.readUint32()); - } - } - } // end VvcPTLRecord - - this.max_picture_width = stream.readUint16(); - this.max_picture_height = stream.readUint16(); - this.avg_frame_rate = stream.readUint16(); - } - - var VVC_NALU_OPI = 12; - var VVC_NALU_DEC_PARAM = 13; - - this.nalu_arrays = []; - var num_of_arrays = stream.readUint8(); - for (i = 0; i < num_of_arrays; i++) { - var nalu_array = []; - this.nalu_arrays.push(nalu_array); - - bitReader.stream_read_1_bytes(stream); - nalu_array.completeness = bitReader.extract_bits(1); - bitReader.extract_bits(2); // reserved - nalu_array.nalu_type = bitReader.extract_bits(5); - - var numNalus = 1; - if (nalu_array.nalu_type != VVC_NALU_DEC_PARAM && nalu_array.nalu_type != VVC_NALU_OPI) { - numNalus = stream.readUint16(); - } - - for (j = 0; j < numNalus; j++) { - var len = stream.readUint16(); - nalu_array.push({ - data: stream.readUint8Array(len), - length: len - }); - } - } -}); -// file:src/parsing/vvnC.js -BoxParser.createFullBoxCtor("vvnC", function (stream) { - // VvcNALUConfigBox - var tmp = strm.readUint8(); - this.lengthSizeMinusOne = (tmp & 0x3); -}); -// file:src/box-codecs.js -BoxParser.SampleEntry.prototype.isVideo = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isAudio = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isSubtitle = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isMetadata = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isHint = function() { - return false; -} - -BoxParser.SampleEntry.prototype.getCodec = function() { - return this.type.replace('.',''); -} - -BoxParser.SampleEntry.prototype.getWidth = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getHeight = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getChannelCount = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getSampleRate = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getSampleSize = function() { - return ""; -} - -BoxParser.VisualSampleEntry.prototype.isVideo = function() { - return true; -} - -BoxParser.VisualSampleEntry.prototype.getWidth = function() { - return this.width; -} - -BoxParser.VisualSampleEntry.prototype.getHeight = function() { - return this.height; -} - -BoxParser.AudioSampleEntry.prototype.isAudio = function() { - return true; -} - -BoxParser.AudioSampleEntry.prototype.getChannelCount = function() { - return this.channel_count; -} - -BoxParser.AudioSampleEntry.prototype.getSampleRate = function() { - return this.samplerate; -} - -BoxParser.AudioSampleEntry.prototype.getSampleSize = function() { - return this.samplesize; -} - -BoxParser.SubtitleSampleEntry.prototype.isSubtitle = function() { - return true; -} - -BoxParser.MetadataSampleEntry.prototype.isMetadata = function() { - return true; -} - - -BoxParser.decimalToHex = function(d, padding) { - var hex = Number(d).toString(16); - padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; - while (hex.length < padding) { - hex = "0" + hex; - } - return hex; -} - -BoxParser.avc1SampleEntry.prototype.getCodec = -BoxParser.avc2SampleEntry.prototype.getCodec = -BoxParser.avc3SampleEntry.prototype.getCodec = -BoxParser.avc4SampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.avcC) { - return baseCodec+"."+BoxParser.decimalToHex(this.avcC.AVCProfileIndication)+ - ""+BoxParser.decimalToHex(this.avcC.profile_compatibility)+ - ""+BoxParser.decimalToHex(this.avcC.AVCLevelIndication); - } else { - return baseCodec; - } -} - -BoxParser.hev1SampleEntry.prototype.getCodec = -BoxParser.hvc1SampleEntry.prototype.getCodec = function() { - var i; - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.hvcC) { - baseCodec += '.'; - switch (this.hvcC.general_profile_space) { - case 0: - baseCodec += ''; - break; - case 1: - baseCodec += 'A'; - break; - case 2: - baseCodec += 'B'; - break; - case 3: - baseCodec += 'C'; - break; - } - baseCodec += this.hvcC.general_profile_idc; - baseCodec += '.'; - var val = this.hvcC.general_profile_compatibility; - var reversed = 0; - for (i=0; i<32; i++) { - reversed |= val & 1; - if (i==31) break; - reversed <<= 1; - val >>=1; - } - baseCodec += BoxParser.decimalToHex(reversed, 0); - baseCodec += '.'; - if (this.hvcC.general_tier_flag === 0) { - baseCodec += 'L'; - } else { - baseCodec += 'H'; - } - baseCodec += this.hvcC.general_level_idc; - var hasByte = false; - var constraint_string = ""; - for (i = 5; i >= 0; i--) { - if (this.hvcC.general_constraint_indicator[i] || hasByte) { - constraint_string = "."+BoxParser.decimalToHex(this.hvcC.general_constraint_indicator[i], 0)+constraint_string; - hasByte = true; - } - } - baseCodec += constraint_string; - } - return baseCodec; -} - -BoxParser.vvc1SampleEntry.prototype.getCodec = -BoxParser.vvi1SampleEntry.prototype.getCodec = function () { - var i; - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.vvcC) { - baseCodec += '.' + this.vvcC.general_profile_idc; - if (this.vvcC.general_tier_flag) { - baseCodec += '.H'; - } else { - baseCodec += '.L'; - } - baseCodec += this.vvcC.general_level_idc; - - var constraint_string = ""; - if (this.vvcC.general_constraint_info) { - var bytes = []; - var byte = 0; - byte |= this.vvcC.ptl_frame_only_constraint << 7; - byte |= this.vvcC.ptl_multilayer_enabled << 6; - var last_nonzero; - for (i = 0; i < this.vvcC.general_constraint_info.length; ++i) { - byte |= (this.vvcC.general_constraint_info[i] >> 2) & 0x3f; - bytes.push(byte); - if (byte) { - last_nonzero = i; - } - - byte = (this.vvcC.general_constraint_info[i] >> 2) & 0x03; - } - - if (last_nonzero === undefined) { - constraint_string = ".CA"; - } - else { - constraint_string = ".C" - var base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - var held_bits = 0; - var num_held_bits = 0; - for (i = 0; i <= last_nonzero; ++i) { - held_bits = (held_bits << 8) | bytes[i]; - num_held_bits += 8; - - while (num_held_bits >= 5) { - var val = (held_bits >> (num_held_bits - 5)) & 0x1f; - constraint_string += base32_chars[val]; - - num_held_bits -= 5; - held_bits &= (1 << num_held_bits) - 1; - } - } - if (num_held_bits) { - held_bits <<= (5 - num_held_bits); // right-pad with zeros to 5 bits (is this correct?) - constraint_string += base32_chars[held_bits & 0x1f]; - } - } - } - baseCodec += constraint_string; - } - return baseCodec; -} - -BoxParser.mp4aSampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.esds && this.esds.esd) { - var oti = this.esds.esd.getOTI(); - var dsi = this.esds.esd.getAudioConfig(); - return baseCodec+"."+BoxParser.decimalToHex(oti)+(dsi ? "."+dsi: ""); - } else { - return baseCodec; - } -} - -BoxParser.stxtSampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if(this.mime_format) { - return baseCodec + "." + this.mime_format; - } else { - return baseCodec - } -} - -BoxParser.vp08SampleEntry.prototype.getCodec = -BoxParser.vp09SampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - var level = this.vpcC.level; - if (level == 0) { - level = "00"; - } - var bitDepth = this.vpcC.bitDepth; - if (bitDepth == 8) { - bitDepth = "08"; - } - return baseCodec + ".0" + this.vpcC.profile + "." + level + "." + bitDepth; -} - -BoxParser.av01SampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - var level = this.av1C.seq_level_idx_0; - if (level < 10) { - level = "0" + level; - } - var bitdepth; - if (this.av1C.seq_profile === 2 && this.av1C.high_bitdepth === 1) { - bitdepth = (this.av1C.twelve_bit === 1) ? "12" : "10"; - } else if ( this.av1C.seq_profile <= 2 ) { - bitdepth = (this.av1C.high_bitdepth === 1) ? "10" : "08"; - } - // TODO need to parse the SH to find color config - return baseCodec+"."+this.av1C.seq_profile+"."+level+(this.av1C.seq_tier_0?"H":"M")+"."+bitdepth;//+"."+this.av1C.monochrome+"."+this.av1C.chroma_subsampling_x+""+this.av1C.chroma_subsampling_y+""+this.av1C.chroma_sample_position; -} -// file:src/box-write.js -/* - * Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -BoxParser.Box.prototype.writeHeader = function(stream, msg) { - this.size += 8; - if (this.size > MAX_SIZE) { - this.size += 8; - } - if (this.type === "uuid") { - this.size += 16; - } - Log.debug("BoxWriter", "Writing box "+this.type+" of size: "+this.size+" at position "+stream.getPosition()+(msg || "")); - if (this.size > MAX_SIZE) { - stream.writeUint32(1); - } else { - this.sizePosition = stream.getPosition(); - stream.writeUint32(this.size); - } - stream.writeString(this.type, null, 4); - if (this.type === "uuid") { - stream.writeUint8Array(this.uuid); - } - if (this.size > MAX_SIZE) { - stream.writeUint64(this.size); - } -} - -BoxParser.FullBox.prototype.writeHeader = function(stream) { - this.size += 4; - BoxParser.Box.prototype.writeHeader.call(this, stream, " v="+this.version+" f="+this.flags); - stream.writeUint8(this.version); - stream.writeUint24(this.flags); -} - -BoxParser.Box.prototype.write = function(stream) { - if (this.type === "mdat") { - /* TODO: fix this */ - if (this.data) { - this.size = this.data.length; - this.writeHeader(stream); - stream.writeUint8Array(this.data); - } - } else { - this.size = (this.data ? this.data.length : 0); - this.writeHeader(stream); - if (this.data) { - stream.writeUint8Array(this.data); - } - } -} - -BoxParser.ContainerBox.prototype.write = function(stream) { - this.size = 0; - this.writeHeader(stream); - for (var i=0; i= 2) { - stream.writeUint32(this.default_sample_description_index); - } - stream.writeUint32(this.entries.length); - for (i = 0; i < this.entries.length; i++) { - entry = this.entries[i]; - if (this.version === 1) { - if (this.default_length === 0) { - stream.writeUint32(entry.description_length); - } - } - entry.write(stream); - } -} - - -// file:src/writing/sidx.js -BoxParser.sidxBox.prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = 4*4+2+2+12*this.references.length; - this.writeHeader(stream); - stream.writeUint32(this.reference_ID); - stream.writeUint32(this.timescale); - stream.writeUint32(this.earliest_presentation_time); - stream.writeUint32(this.first_offset); - stream.writeUint16(0); - stream.writeUint16(this.references.length); - for (var i = 0; i < this.references.length; i++) { - var ref = this.references[i]; - stream.writeUint32(ref.reference_type << 31 | ref.referenced_size); - stream.writeUint32(ref.subsegment_duration); - stream.writeUint32(ref.starts_with_SAP << 31 | ref.SAP_type << 28 | ref.SAP_delta_time); - } -} - -// file:src/writing/smhd.js -BoxParser.smhdBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 1; - this.size = 4; - this.writeHeader(stream); - stream.writeUint16(this.balance); - stream.writeUint16(0); -} -// file:src/writing/stco.js -BoxParser.stcoBox.prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = 4+4*this.chunk_offsets.length; - this.writeHeader(stream); - stream.writeUint32(this.chunk_offsets.length); - stream.writeUint32Array(this.chunk_offsets); -} - -// file:src/writing/stsc.js -BoxParser.stscBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 0; - this.size = 4+12*this.first_chunk.length; - this.writeHeader(stream); - stream.writeUint32(this.first_chunk.length); - for(i=0; i 0) { - i = 0; - while (i+1 < this.sample_sizes.length) { - if (this.sample_sizes[i+1] !== this.sample_sizes[0]) { - constant = false; - break; - } else { - i++; - } - } - } else { - constant = false; - } - this.size = 8; - if (!constant) { - this.size += 4*this.sample_sizes.length; - } - this.writeHeader(stream); - if (!constant) { - stream.writeUint32(0); - } else { - stream.writeUint32(this.sample_sizes[0]); - } - stream.writeUint32(this.sample_sizes.length); - if (!constant) { - stream.writeUint32Array(this.sample_sizes); - } -} - -// file:src/writing/stts.js -BoxParser.sttsBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 0; - this.size = 4+8*this.sample_counts.length; - this.writeHeader(stream); - stream.writeUint32(this.sample_counts.length); - for(i=0; i UINT32_MAX ? 1 : 0; - this.flags = 0; - this.size = 4; - if (this.version === 1) { - this.size += 4; - } - this.writeHeader(stream); - if (this.version === 1) { - stream.writeUint64(this.baseMediaDecodeTime); - } else { - stream.writeUint32(this.baseMediaDecodeTime); - } -} - -// file:src/writing/tfhd.js -BoxParser.tfhdBox.prototype.write = function(stream) { - this.version = 0; - this.size = 4; - if (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET) { - this.size += 8; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) { - this.size += 4; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) { - this.size += 4; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) { - this.size += 4; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) { - this.size += 4; - } - this.writeHeader(stream); - stream.writeUint32(this.track_id); - if (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET) { - stream.writeUint64(this.base_data_offset); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) { - stream.writeUint32(this.default_sample_description_index); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) { - stream.writeUint32(this.default_sample_duration); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) { - stream.writeUint32(this.default_sample_size); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) { - stream.writeUint32(this.default_sample_flags); - } -} - -// file:src/writing/tkhd.js -BoxParser.tkhdBox.prototype.write = function(stream) { - this.version = 0; - //this.flags = 0; - this.size = 4*18+2*4; - this.writeHeader(stream); - stream.writeUint32(this.creation_time); - stream.writeUint32(this.modification_time); - stream.writeUint32(this.track_id); - stream.writeUint32(0); - stream.writeUint32(this.duration); - stream.writeUint32(0); - stream.writeUint32(0); - stream.writeInt16(this.layer); - stream.writeInt16(this.alternate_group); - stream.writeInt16(this.volume<<8); - stream.writeUint16(0); - stream.writeInt32Array(this.matrix); - stream.writeUint32(this.width); - stream.writeUint32(this.height); -} - -// file:src/writing/trex.js -BoxParser.trexBox.prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = 4*5; - this.writeHeader(stream); - stream.writeUint32(this.track_id); - stream.writeUint32(this.default_sample_description_index); - stream.writeUint32(this.default_sample_duration); - stream.writeUint32(this.default_sample_size); - stream.writeUint32(this.default_sample_flags); -} - -// file:src/writing/trun.js -BoxParser.trunBox.prototype.write = function(stream) { - this.version = 0; - this.size = 4; - if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) { - this.size += 4; - } - if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) { - this.size += 4; - } - if (this.flags & BoxParser.TRUN_FLAGS_DURATION) { - this.size += 4*this.sample_duration.length; - } - if (this.flags & BoxParser.TRUN_FLAGS_SIZE) { - this.size += 4*this.sample_size.length; - } - if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) { - this.size += 4*this.sample_flags.length; - } - if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - this.size += 4*this.sample_composition_time_offset.length; - } - this.writeHeader(stream); - stream.writeUint32(this.sample_count); - if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) { - this.data_offset_position = stream.getPosition(); - stream.writeInt32(this.data_offset); //signed - } - if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) { - stream.writeUint32(this.first_sample_flags); - } - for (var i = 0; i < this.sample_count; i++) { - if (this.flags & BoxParser.TRUN_FLAGS_DURATION) { - stream.writeUint32(this.sample_duration[i]); - } - if (this.flags & BoxParser.TRUN_FLAGS_SIZE) { - stream.writeUint32(this.sample_size[i]); - } - if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) { - stream.writeUint32(this.sample_flags[i]); - } - if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - if (this.version === 0) { - stream.writeUint32(this.sample_composition_time_offset[i]); - } else { - stream.writeInt32(this.sample_composition_time_offset[i]); //signed - } - } - } -} - -// file:src/writing/url.js -BoxParser["url Box"].prototype.write = function(stream) { - this.version = 0; - if (this.location) { - this.flags = 0; - this.size = this.location.length+1; - } else { - this.flags = 0x000001; - this.size = 0; - } - this.writeHeader(stream); - if (this.location) { - stream.writeCString(this.location); - } -} - -// file:src/writing/urn.js -BoxParser["urn Box"].prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = this.name.length+1+(this.location ? this.location.length+1 : 0); - this.writeHeader(stream); - stream.writeCString(this.name); - if (this.location) { - stream.writeCString(this.location); - } -} - -// file:src/writing/vmhd.js -BoxParser.vmhdBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 1; - this.size = 8; - this.writeHeader(stream); - stream.writeUint16(this.graphicsmode); - stream.writeUint16Array(this.opcolor); -} - -// file:src/box-unpack.js -/* - * Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -BoxParser.cttsBox.prototype.unpack = function(samples) { - var i, j, k; - k = 0; - for (i = 0; i < this.sample_counts.length; i++) { - for (j = 0; j < this.sample_counts[i]; j++) { - samples[k].pts = samples[k].dts + this.sample_offsets[i]; - k++; - } - } -} - -BoxParser.sttsBox.prototype.unpack = function(samples) { - var i, j, k; - k = 0; - for (i = 0; i < this.sample_counts.length; i++) { - for (j = 0; j < this.sample_counts[i]; j++) { - if (k === 0) { - samples[k].dts = 0; - } else { - samples[k].dts = samples[k-1].dts + this.sample_deltas[i]; - } - k++; - } - } -} - -BoxParser.stcoBox.prototype.unpack = function(samples) { - var i; - for (i = 0; i < this.chunk_offsets.length; i++) { - samples[i].offset = this.chunk_offsets[i]; - } -} - -BoxParser.stscBox.prototype.unpack = function(samples) { - var i, j, k, l, m; - l = 0; - m = 0; - for (i = 0; i < this.first_chunk.length; i++) { - for (j = 0; j < (i+1 < this.first_chunk.length ? this.first_chunk[i+1] : Infinity); j++) { - m++; - for (k = 0; k < this.samples_per_chunk[i]; k++) { - if (samples[l]) { - samples[l].description_index = this.sample_description_index[i]; - samples[l].chunk_index = m; - } else { - return; - } - l++; - } - } - } -} - -BoxParser.stszBox.prototype.unpack = function(samples) { - var i; - for (i = 0; i < this.sample_sizes.length; i++) { - samples[i].size = this.sample_sizes[i]; - } -} -// file:src/box-diff.js - -BoxParser.DIFF_BOXES_PROP_NAMES = [ "boxes", "entries", "references", "subsamples", - "items", "item_infos", "extents", "associations", - "subsegments", "ranges", "seekLists", "seekPoints", - "esd", "levels"]; - -BoxParser.DIFF_PRIMITIVE_ARRAY_PROP_NAMES = [ "compatible_brands", "matrix", "opcolor", "sample_counts", "sample_counts", "sample_deltas", -"first_chunk", "samples_per_chunk", "sample_sizes", "chunk_offsets", "sample_offsets", "sample_description_index", "sample_duration" ]; - -BoxParser.boxEqualFields = function(box_a, box_b) { - if (box_a && !box_b) return false; - var prop; - for (prop in box_a) { - if (BoxParser.DIFF_BOXES_PROP_NAMES.indexOf(prop) > -1) { - continue; - // } else if (excluded_fields && excluded_fields.indexOf(prop) > -1) { - // continue; - } else if (box_a[prop] instanceof BoxParser.Box || box_b[prop] instanceof BoxParser.Box) { - continue; - } else if (typeof box_a[prop] === "undefined" || typeof box_b[prop] === "undefined") { - continue; - } else if (typeof box_a[prop] === "function" || typeof box_b[prop] === "function") { - continue; - } else if ( - (box_a.subBoxNames && box_a.subBoxNames.indexOf(prop.slice(0,4)) > -1) || - (box_b.subBoxNames && box_b.subBoxNames.indexOf(prop.slice(0,4)) > -1)) { - continue; - } else { - if (prop === "data" || prop === "start" || prop === "size" || prop === "creation_time" || prop === "modification_time") { - continue; - } else if (BoxParser.DIFF_PRIMITIVE_ARRAY_PROP_NAMES.indexOf(prop) > -1) { - continue; - } else { - if (box_a[prop] !== box_b[prop]) { - return false; - } - } - } - } - return true; -} - -BoxParser.boxEqual = function(box_a, box_b) { - if (!BoxParser.boxEqualFields(box_a, box_b)) { - return false; - } - for (var j = 0; j < BoxParser.DIFF_BOXES_PROP_NAMES.length; j++) { - var name = BoxParser.DIFF_BOXES_PROP_NAMES[j]; - if (box_a[name] && box_b[name]) { - if (!BoxParser.boxEqual(box_a[name], box_b[name])) { - return false; - } - } - } - return true; -}// file:src/text-mp4.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var VTTin4Parser = function() { -} - -VTTin4Parser.prototype.parseSample = function(data) { - var cues, cue; - var stream = new MP4BoxStream(data.buffer); - cues = []; - while (!stream.isEos()) { - cue = BoxParser.parseOneBox(stream, false); - if (cue.code === BoxParser.OK && cue.box.type === "vttc") { - cues.push(cue.box); - } - } - return cues; -} - -VTTin4Parser.prototype.getText = function (startTime, endTime, data) { - function pad(n, width, z) { - z = z || '0'; - n = n + ''; - return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; - } - function secToTimestamp(insec) { - var h = Math.floor(insec/3600); - var m = Math.floor((insec - h*3600)/60); - var s = Math.floor(insec - h*3600 - m*60); - var ms = Math.floor((insec - h*3600 - m*60 - s)*1000); - return ""+pad(h, 2)+":"+pad(m,2)+":"+pad(s, 2)+"."+pad(ms, 3); - } - var cues = this.parseSample(data); - var string = ""; - for (var i = 0; i < cues.length; i++) { - var cueIn4 = cues[i]; - string += secToTimestamp(startTime)+" --> "+secToTimestamp(endTime)+"\r\n"; - string += cueIn4.payl.text; - } - return string; -} - -var XMLSubtitlein4Parser = function() { -} - -XMLSubtitlein4Parser.prototype.parseSample = function(sample) { - var res = {}; - var i; - res.resources = []; - var stream = new MP4BoxStream(sample.data.buffer); - if (!sample.subsamples || sample.subsamples.length === 0) { - res.documentString = stream.readString(sample.data.length); - } else { - res.documentString = stream.readString(sample.subsamples[0].size); - if (sample.subsamples.length > 1) { - for (i = 1; i < sample.subsamples.length; i++) { - res.resources[i] = stream.readUint8Array(sample.subsamples[i].size); - } - } - } - if (typeof (DOMParser) !== "undefined") { - res.document = (new DOMParser()).parseFromString(res.documentString, "application/xml"); - } - return res; -} - -var Textin4Parser = function() { -} - -Textin4Parser.prototype.parseSample = function(sample) { - var textString; - var stream = new MP4BoxStream(sample.data.buffer); - textString = stream.readString(sample.data.length); - return textString; -} - -Textin4Parser.prototype.parseConfig = function(data) { - var textString; - var stream = new MP4BoxStream(data.buffer); - stream.readUint32(); // version & flags - textString = stream.readCString(); - return textString; -} - -if (typeof exports !== 'undefined') { - exports.XMLSubtitlein4Parser = XMLSubtitlein4Parser; - exports.Textin4Parser = Textin4Parser; -} -// file:src/isofile.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var ISOFile = function (stream) { - /* MutiBufferStream object used to parse boxes */ - this.stream = stream || new MultiBufferStream(); - /* Array of all boxes (in order) found in the file */ - this.boxes = []; - /* Array of all mdats */ - this.mdats = []; - /* Array of all moofs */ - this.moofs = []; - /* Boolean indicating if the file is compatible with progressive parsing (moov first) */ - this.isProgressive = false; - /* Boolean used to fire moov start event only once */ - this.moovStartFound = false; - /* Callback called when the moov parsing starts */ - this.onMoovStart = null; - /* Boolean keeping track of the call to onMoovStart, to avoid double calls */ - this.moovStartSent = false; - /* Callback called when the moov is entirely parsed */ - this.onReady = null; - /* Boolean keeping track of the call to onReady, to avoid double calls */ - this.readySent = false; - /* Callback to call when segments are ready */ - this.onSegment = null; - /* Callback to call when samples are ready */ - this.onSamples = null; - /* Callback to call when there is an error in the parsing or processing of samples */ - this.onError = null; - /* Boolean indicating if the moov box run-length encoded tables of sample information have been processed */ - this.sampleListBuilt = false; - /* Array of Track objects for which fragmentation of samples is requested */ - this.fragmentedTracks = []; - /* Array of Track objects for which extraction of samples is requested */ - this.extractedTracks = []; - /* Boolean indicating that fragmention is ready */ - this.isFragmentationInitialized = false; - /* Boolean indicating that fragmented has started */ - this.sampleProcessingStarted = false; - /* Number of the next 'moof' to generate when fragmenting */ - this.nextMoofNumber = 0; - /* Boolean indicating if the initial list of items has been produced */ - this.itemListBuilt = false; - /* Callback called when the sidx box is entirely parsed */ - this.onSidx = null; - /* Boolean keeping track of the call to onSidx, to avoid double calls */ - this.sidxSent = false; -} - -ISOFile.prototype.setSegmentOptions = function(id, user, options) { - var trak = this.getTrackById(id); - if (trak) { - var fragTrack = {}; - this.fragmentedTracks.push(fragTrack); - fragTrack.id = id; - fragTrack.user = user; - fragTrack.trak = trak; - trak.nextSample = 0; - fragTrack.segmentStream = null; - fragTrack.nb_samples = 1000; - fragTrack.rapAlignement = true; - if (options) { - if (options.nbSamples) fragTrack.nb_samples = options.nbSamples; - if (options.rapAlignement) fragTrack.rapAlignement = options.rapAlignement; - } - } -} - -ISOFile.prototype.unsetSegmentOptions = function(id) { - var index = -1; - for (var i = 0; i < this.fragmentedTracks.length; i++) { - var fragTrack = this.fragmentedTracks[i]; - if (fragTrack.id == id) { - index = i; - } - } - if (index > -1) { - this.fragmentedTracks.splice(index, 1); - } -} - -ISOFile.prototype.setExtractionOptions = function(id, user, options) { - var trak = this.getTrackById(id); - if (trak) { - var extractTrack = {}; - this.extractedTracks.push(extractTrack); - extractTrack.id = id; - extractTrack.user = user; - extractTrack.trak = trak; - trak.nextSample = 0; - extractTrack.nb_samples = 1000; - extractTrack.samples = []; - if (options) { - if (options.nbSamples) extractTrack.nb_samples = options.nbSamples; - } - } -} - -ISOFile.prototype.unsetExtractionOptions = function(id) { - var index = -1; - for (var i = 0; i < this.extractedTracks.length; i++) { - var extractTrack = this.extractedTracks[i]; - if (extractTrack.id == id) { - index = i; - } - } - if (index > -1) { - this.extractedTracks.splice(index, 1); - } -} - -ISOFile.prototype.parse = function() { - var found; - var ret; - var box; - var parseBoxHeadersOnly = false; - - if (this.restoreParsePosition) { - if (!this.restoreParsePosition()) { - return; - } - } - - while (true) { - - if (this.hasIncompleteMdat && this.hasIncompleteMdat()) { - if (this.processIncompleteMdat()) { - continue; - } else { - return; - } - } else { - if (this.saveParsePosition) { - this.saveParsePosition(); - } - ret = BoxParser.parseOneBox(this.stream, parseBoxHeadersOnly); - if (ret.code === BoxParser.ERR_NOT_ENOUGH_DATA) { - if (this.processIncompleteBox) { - if (this.processIncompleteBox(ret)) { - continue; - } else { - return; - } - } else { - return; - } - } else { - var box_type; - /* the box is entirely parsed */ - box = ret.box; - box_type = (box.type !== "uuid" ? box.type : box.uuid); - /* store the box in the 'boxes' array to preserve box order (for file rewrite if needed) */ - this.boxes.push(box); - /* but also store box in a property for more direct access */ - switch (box_type) { - case "mdat": - this.mdats.push(box); - break; - case "moof": - this.moofs.push(box); - break; - case "moov": - this.moovStartFound = true; - if (this.mdats.length === 0) { - this.isProgressive = true; - } - /* no break */ - /* falls through */ - default: - if (this[box_type] !== undefined) { - Log.warn("ISOFile", "Duplicate Box of type: "+box_type+", overriding previous occurrence"); - } - this[box_type] = box; - break; - } - if (this.updateUsedBytes) { - this.updateUsedBytes(box, ret); - } - } - } - } -} - -ISOFile.prototype.checkBuffer = function (ab) { - if (ab === null || ab === undefined) { - throw("Buffer must be defined and non empty"); - } - if (ab.fileStart === undefined) { - throw("Buffer must have a fileStart property"); - } - if (ab.byteLength === 0) { - Log.warn("ISOFile", "Ignoring empty buffer (fileStart: "+ab.fileStart+")"); - this.stream.logBufferLevel(); - return false; - } - Log.info("ISOFile", "Processing buffer (fileStart: "+ab.fileStart+")"); - - /* mark the bytes in the buffer as not being used yet */ - ab.usedBytes = 0; - this.stream.insertBuffer(ab); - this.stream.logBufferLevel(); - - if (!this.stream.initialized()) { - Log.warn("ISOFile", "Not ready to start parsing"); - return false; - } - return true; -} - -/* Processes a new ArrayBuffer (with a fileStart property) - Returns the next expected file position, or undefined if not ready to parse */ -ISOFile.prototype.appendBuffer = function(ab, last) { - var nextFileStart; - if (!this.checkBuffer(ab)) { - return; - } - - /* Parse whatever is in the existing buffers */ - this.parse(); - - /* Check if the moovStart callback needs to be called */ - if (this.moovStartFound && !this.moovStartSent) { - this.moovStartSent = true; - if (this.onMoovStart) this.onMoovStart(); - } - - if (this.moov) { - /* A moov box has been entirely parsed */ - - /* if this is the first call after the moov is found we initialize the list of samples (may be empty in fragmented files) */ - if (!this.sampleListBuilt) { - this.buildSampleLists(); - this.sampleListBuilt = true; - } - - /* We update the sample information if there are any new moof boxes */ - this.updateSampleLists(); - - /* If the application needs to be informed that the 'moov' has been found, - we create the information object and callback the application */ - if (this.onReady && !this.readySent) { - this.readySent = true; - this.onReady(this.getInfo()); - } - - /* See if any sample extraction or segment creation needs to be done with the available samples */ - this.processSamples(last); - - /* Inform about the best range to fetch next */ - if (this.nextSeekPosition) { - nextFileStart = this.nextSeekPosition; - this.nextSeekPosition = undefined; - } else { - nextFileStart = this.nextParsePosition; - } - if (this.stream.getEndFilePositionAfter) { - nextFileStart = this.stream.getEndFilePositionAfter(nextFileStart); - } - } else { - if (this.nextParsePosition) { - /* moov has not been parsed but the first buffer was received, - the next fetch should probably be the next box start */ - nextFileStart = this.nextParsePosition; - } else { - /* No valid buffer has been parsed yet, we cannot know what to parse next */ - nextFileStart = 0; - } - } - if (this.sidx) { - if (this.onSidx && !this.sidxSent) { - this.onSidx(this.sidx); - this.sidxSent = true; - } - } - if (this.meta) { - if (this.flattenItemInfo && !this.itemListBuilt) { - this.flattenItemInfo(); - this.itemListBuilt = true; - } - if (this.processItems) { - this.processItems(this.onItem); - } - } - - if (this.stream.cleanBuffers) { - Log.info("ISOFile", "Done processing buffer (fileStart: "+ab.fileStart+") - next buffer to fetch should have a fileStart position of "+nextFileStart); - this.stream.logBufferLevel(); - this.stream.cleanBuffers(); - this.stream.logBufferLevel(true); - Log.info("ISOFile", "Sample data size in memory: "+this.getAllocatedSampleDataSize()); - } - return nextFileStart; -} - -ISOFile.prototype.getInfo = function() { - var i, j; - var movie = {}; - var trak; - var track; - var ref; - var sample_desc; - var _1904 = (new Date('1904-01-01T00:00:00Z').getTime()); - - if (this.moov) { - movie.hasMoov = true; - movie.duration = this.moov.mvhd.duration; - movie.timescale = this.moov.mvhd.timescale; - movie.isFragmented = (this.moov.mvex != null); - if (movie.isFragmented && this.moov.mvex.mehd) { - movie.fragment_duration = this.moov.mvex.mehd.fragment_duration; - } - movie.isProgressive = this.isProgressive; - movie.hasIOD = (this.moov.iods != null); - movie.brands = []; - movie.brands.push(this.ftyp.major_brand); - movie.brands = movie.brands.concat(this.ftyp.compatible_brands); - movie.created = new Date(_1904+this.moov.mvhd.creation_time*1000); - movie.modified = new Date(_1904+this.moov.mvhd.modification_time*1000); - movie.tracks = []; - movie.audioTracks = []; - movie.videoTracks = []; - movie.subtitleTracks = []; - movie.metadataTracks = []; - movie.hintTracks = []; - movie.otherTracks = []; - for (i = 0; i < this.moov.traks.length; i++) { - trak = this.moov.traks[i]; - sample_desc = trak.mdia.minf.stbl.stsd.entries[0]; - track = {}; - movie.tracks.push(track); - track.id = trak.tkhd.track_id; - track.name = trak.mdia.hdlr.name; - track.references = []; - if (trak.tref) { - for (j = 0; j < trak.tref.boxes.length; j++) { - ref = {}; - track.references.push(ref); - ref.type = trak.tref.boxes[j].type; - ref.track_ids = trak.tref.boxes[j].track_ids; - } - } - if (trak.edts) { - track.edits = trak.edts.elst.entries; - } - track.created = new Date(_1904+trak.tkhd.creation_time*1000); - track.modified = new Date(_1904+trak.tkhd.modification_time*1000); - track.movie_duration = trak.tkhd.duration; - track.movie_timescale = movie.timescale; - track.layer = trak.tkhd.layer; - track.alternate_group = trak.tkhd.alternate_group; - track.volume = trak.tkhd.volume; - track.matrix = trak.tkhd.matrix; - track.track_width = trak.tkhd.width/(1<<16); - track.track_height = trak.tkhd.height/(1<<16); - track.timescale = trak.mdia.mdhd.timescale; - track.cts_shift = trak.mdia.minf.stbl.cslg; - track.duration = trak.mdia.mdhd.duration; - track.samples_duration = trak.samples_duration; - track.codec = sample_desc.getCodec(); - track.kind = (trak.udta && trak.udta.kinds.length ? trak.udta.kinds[0] : { schemeURI: "", value: ""}); - track.language = (trak.mdia.elng ? trak.mdia.elng.extended_language : trak.mdia.mdhd.languageString); - track.nb_samples = trak.samples.length; - track.size = trak.samples_size; - track.bitrate = (track.size*8*track.timescale)/track.samples_duration; - if (sample_desc.isAudio()) { - track.type = "audio"; - movie.audioTracks.push(track); - track.audio = {}; - track.audio.sample_rate = sample_desc.getSampleRate(); - track.audio.channel_count = sample_desc.getChannelCount(); - track.audio.sample_size = sample_desc.getSampleSize(); - } else if (sample_desc.isVideo()) { - track.type = "video"; - movie.videoTracks.push(track); - track.video = {}; - track.video.width = sample_desc.getWidth(); - track.video.height = sample_desc.getHeight(); - } else if (sample_desc.isSubtitle()) { - track.type = "subtitles"; - movie.subtitleTracks.push(track); - } else if (sample_desc.isHint()) { - track.type = "metadata"; - movie.hintTracks.push(track); - } else if (sample_desc.isMetadata()) { - track.type = "metadata"; - movie.metadataTracks.push(track); - } else { - track.type = "metadata"; - movie.otherTracks.push(track); - } - } - } else { - movie.hasMoov = false; - } - movie.mime = ""; - if (movie.hasMoov && movie.tracks) { - if (movie.videoTracks && movie.videoTracks.length > 0) { - movie.mime += 'video/mp4; codecs=\"'; - } else if (movie.audioTracks && movie.audioTracks.length > 0) { - movie.mime += 'audio/mp4; codecs=\"'; - } else { - movie.mime += 'application/mp4; codecs=\"'; - } - for (i = 0; i < movie.tracks.length; i++) { - if (i !== 0) movie.mime += ','; - movie.mime+= movie.tracks[i].codec; - } - movie.mime += '\"; profiles=\"'; - movie.mime += this.ftyp.compatible_brands.join(); - movie.mime += '\"'; - } - return movie; -} - -ISOFile.prototype.processSamples = function(last) { - var i; - var trak; - if (!this.sampleProcessingStarted) return; - - /* For each track marked for fragmentation, - check if the next sample is there (i.e. if the sample information is known (i.e. moof has arrived) and if it has been downloaded) - and create a fragment with it */ - if (this.isFragmentationInitialized && this.onSegment !== null) { - for (i = 0; i < this.fragmentedTracks.length; i++) { - var fragTrak = this.fragmentedTracks[i]; - trak = fragTrak.trak; - while (trak.nextSample < trak.samples.length && this.sampleProcessingStarted) { - /* The sample information is there (either because the file is not fragmented and this is not the last sample, - or because the file is fragmented and the moof for that sample has been received */ - Log.debug("ISOFile", "Creating media fragment on track #"+fragTrak.id +" for sample "+trak.nextSample); - var result = this.createFragment(fragTrak.id, trak.nextSample, fragTrak.segmentStream); - if (result) { - fragTrak.segmentStream = result; - trak.nextSample++; - } else { - /* The fragment could not be created because the media data is not there (not downloaded), wait for it */ - break; - } - /* A fragment is created by sample, but the segment is the accumulation in the buffer of these fragments. - It is flushed only as requested by the application (nb_samples) to avoid too many callbacks */ - if (trak.nextSample % fragTrak.nb_samples === 0 || (last || trak.nextSample >= trak.samples.length)) { - Log.info("ISOFile", "Sending fragmented data on track #"+fragTrak.id+" for samples ["+Math.max(0,trak.nextSample-fragTrak.nb_samples)+","+(trak.nextSample-1)+"]"); - Log.info("ISOFile", "Sample data size in memory: "+this.getAllocatedSampleDataSize()); - if (this.onSegment) { - this.onSegment(fragTrak.id, fragTrak.user, fragTrak.segmentStream.buffer, trak.nextSample, (last || trak.nextSample >= trak.samples.length)); - } - /* force the creation of a new buffer */ - fragTrak.segmentStream = null; - if (fragTrak !== this.fragmentedTracks[i]) { - /* make sure we can stop fragmentation if needed */ - break; - } - } - } - } - } - - if (this.onSamples !== null) { - /* For each track marked for data export, - check if the next sample is there (i.e. has been downloaded) and send it */ - for (i = 0; i < this.extractedTracks.length; i++) { - var extractTrak = this.extractedTracks[i]; - trak = extractTrak.trak; - while (trak.nextSample < trak.samples.length && this.sampleProcessingStarted) { - Log.debug("ISOFile", "Exporting on track #"+extractTrak.id +" sample #"+trak.nextSample); - var sample = this.getSample(trak, trak.nextSample); - if (sample) { - trak.nextSample++; - extractTrak.samples.push(sample); - } else { - break; - } - if (trak.nextSample % extractTrak.nb_samples === 0 || trak.nextSample >= trak.samples.length) { - Log.debug("ISOFile", "Sending samples on track #"+extractTrak.id+" for sample "+trak.nextSample); - if (this.onSamples) { - this.onSamples(extractTrak.id, extractTrak.user, extractTrak.samples); - } - extractTrak.samples = []; - if (extractTrak !== this.extractedTracks[i]) { - /* check if the extraction needs to be stopped */ - break; - } - } - } - } - } -} - -/* Find and return specific boxes using recursion and early return */ -ISOFile.prototype.getBox = function(type) { - var result = this.getBoxes(type, true); - return (result.length ? result[0] : null); -} - -ISOFile.prototype.getBoxes = function(type, returnEarly) { - var result = []; - ISOFile._sweep.call(this, type, result, returnEarly); - return result; -} - -ISOFile._sweep = function(type, result, returnEarly) { - if (this.type && this.type == type) result.push(this); - for (var box in this.boxes) { - if (result.length && returnEarly) return; - ISOFile._sweep.call(this.boxes[box], type, result, returnEarly); - } -} - -ISOFile.prototype.getTrackSamplesInfo = function(track_id) { - var track = this.getTrackById(track_id); - if (track) { - return track.samples; - } else { - return; - } -} - -ISOFile.prototype.getTrackSample = function(track_id, number) { - var track = this.getTrackById(track_id); - var sample = this.getSample(track, number); - return sample; -} - -/* Called by the application to release the resources associated to samples already forwarded to the application */ -ISOFile.prototype.releaseUsedSamples = function (id, sampleNum) { - var size = 0; - var trak = this.getTrackById(id); - if (!trak.lastValidSample) trak.lastValidSample = 0; - for (var i = trak.lastValidSample; i < sampleNum; i++) { - size+=this.releaseSample(trak, i); - } - Log.info("ISOFile", "Track #"+id+" released samples up to "+sampleNum+" (released size: "+size+", remaining: "+this.samplesDataSize+")"); - trak.lastValidSample = sampleNum; -} - -ISOFile.prototype.start = function() { - this.sampleProcessingStarted = true; - this.processSamples(false); -} - -ISOFile.prototype.stop = function() { - this.sampleProcessingStarted = false; -} - -/* Called by the application to flush the remaining samples (e.g. once the download is finished or when no more samples will be added) */ -ISOFile.prototype.flush = function() { - Log.info("ISOFile", "Flushing remaining samples"); - this.updateSampleLists(); - this.processSamples(true); - this.stream.cleanBuffers(); - this.stream.logBufferLevel(true); -} - -/* Finds the byte offset for a given time on a given track - also returns the time of the previous rap */ -ISOFile.prototype.seekTrack = function(time, useRap, trak) { - var j; - var sample; - var seek_offset = Infinity; - var rap_seek_sample_num = 0; - var seek_sample_num = 0; - var timescale; - - if (trak.samples.length === 0) { - Log.info("ISOFile", "No sample in track, cannot seek! Using time "+Log.getDurationString(0, 1) +" and offset: "+0); - return { offset: 0, time: 0 }; - } - - for (j = 0; j < trak.samples.length; j++) { - sample = trak.samples[j]; - if (j === 0) { - seek_sample_num = 0; - timescale = sample.timescale; - } else if (sample.cts > time * sample.timescale) { - seek_sample_num = j-1; - break; - } - if (useRap && sample.is_sync) { - rap_seek_sample_num = j; - } - } - if (useRap) { - seek_sample_num = rap_seek_sample_num; - } - time = trak.samples[seek_sample_num].cts; - trak.nextSample = seek_sample_num; - while (trak.samples[seek_sample_num].alreadyRead === trak.samples[seek_sample_num].size) { - // No remaining samples to look for, all are downloaded. - if (!trak.samples[seek_sample_num + 1]) { - break; - } - seek_sample_num++; - } - seek_offset = trak.samples[seek_sample_num].offset+trak.samples[seek_sample_num].alreadyRead; - Log.info("ISOFile", "Seeking to "+(useRap ? "RAP": "")+" sample #"+trak.nextSample+" on track "+trak.tkhd.track_id+", time "+Log.getDurationString(time, timescale) +" and offset: "+seek_offset); - return { offset: seek_offset, time: time/timescale }; -} - -/* Finds the byte offset in the file corresponding to the given time or to the time of the previous RAP */ -ISOFile.prototype.seek = function(time, useRap) { - var moov = this.moov; - var trak; - var trak_seek_info; - var i; - var seek_info = { offset: Infinity, time: Infinity }; - if (!this.moov) { - throw "Cannot seek: moov not received!"; - } else { - for (i = 0; i -1) { - media_type = mediaType; - break; - } - } - } - switch(media_type) { - case "Visual": - minf.add("vmhd").set("graphicsmode",0).set("opcolor", [ 0, 0, 0 ]); - sample_description_entry.set("width", options.width) - .set("height", options.height) - .set("horizresolution", 0x48<<16) - .set("vertresolution", 0x48<<16) - .set("frame_count", 1) - .set("compressorname", options.type+" Compressor") - .set("depth", 0x18); - if (options.avcDecoderConfigRecord) { - var avcC = new BoxParser.avcCBox(); - var stream = new MP4BoxStream(options.avcDecoderConfigRecord); - avcC.parse(stream); - sample_description_entry.addBox(avcC); - } - break; - case "Audio": - minf.add("smhd").set("balance", options.balance || 0); - sample_description_entry.set("channel_count", options.channel_count || 2) - .set("samplesize", options.samplesize || 16) - .set("samplerate", options.samplerate || 1<<16); - break; - case "Hint": - minf.add("hmhd"); // TODO: add properties - break; - case "Subtitle": - minf.add("sthd"); - switch (options.type) { - case "stpp": - sample_description_entry.set("namespace", options.namespace || "nonamespace") - .set("schema_location", options.schema_location || "") - .set("auxiliary_mime_types", options.auxiliary_mime_types || ""); - break; - } - break; - case "Metadata": - minf.add("nmhd"); - break; - case "System": - minf.add("nmhd"); - break; - default: - minf.add("nmhd"); - break; - } - if (options.description) { - sample_description_entry.addBox(options.description); - } - if (options.description_boxes) { - options.description_boxes.forEach(function (b) { - sample_description_entry.addBox(b); - }); - } - minf.add("dinf").add("dref").addEntry((new BoxParser["url Box"]()).set("flags", 0x1)); - var stbl = minf.add("stbl"); - stbl.add("stsd").addEntry(sample_description_entry); - stbl.add("stts").set("sample_counts", []) - .set("sample_deltas", []); - stbl.add("stsc").set("first_chunk", []) - .set("samples_per_chunk", []) - .set("sample_description_index", []); - stbl.add("stco").set("chunk_offsets", []); - stbl.add("stsz").set("sample_sizes", []); - - this.moov.mvex.add("trex").set("track_id", options.id) - .set("default_sample_description_index", options.default_sample_description_index || 1) - .set("default_sample_duration", options.default_sample_duration || 0) - .set("default_sample_size", options.default_sample_size || 0) - .set("default_sample_flags", options.default_sample_flags || 0); - this.buildTrakSampleLists(trak); - return options.id; -} - -BoxParser.Box.prototype.computeSize = function(stream_) { - var stream = stream_ || new DataStream(); - stream.endianness = DataStream.BIG_ENDIAN; - this.write(stream); -} - -ISOFile.prototype.addSample = function (track_id, data, _options) { - var options = _options || {}; - var sample = {}; - var trak = this.getTrackById(track_id); - if (trak === null) return; - sample.number = trak.samples.length; - sample.track_id = trak.tkhd.track_id; - sample.timescale = trak.mdia.mdhd.timescale; - sample.description_index = (options.sample_description_index ? options.sample_description_index - 1: 0); - sample.description = trak.mdia.minf.stbl.stsd.entries[sample.description_index]; - sample.data = data; - sample.size = data.byteLength; - sample.alreadyRead = sample.size; - sample.duration = options.duration || 1; - sample.cts = options.cts || 0; - sample.dts = options.dts || 0; - sample.is_sync = options.is_sync || false; - sample.is_leading = options.is_leading || 0; - sample.depends_on = options.depends_on || 0; - sample.is_depended_on = options.is_depended_on || 0; - sample.has_redundancy = options.has_redundancy || 0; - sample.degradation_priority = options.degradation_priority || 0; - sample.offset = 0; - sample.subsamples = options.subsamples; - trak.samples.push(sample); - trak.samples_size += sample.size; - trak.samples_duration += sample.duration; - if (!trak.first_dts) { - trak.first_dts = options.dts; - } - - this.processSamples(); - - var moof = this.createSingleSampleMoof(sample); - this.addBox(moof); - moof.computeSize(); - /* adjusting the data_offset now that the moof size is known*/ - moof.trafs[0].truns[0].data_offset = moof.size+8; //8 is mdat header - this.add("mdat").data = new Uint8Array(data); - return sample; -} - -ISOFile.prototype.createSingleSampleMoof = function(sample) { - var sample_flags = 0; - if (sample.is_sync) - sample_flags = (1 << 25); // sample_depends_on_none (I picture) - else - sample_flags = (1 << 16); // non-sync - - var moof = new BoxParser.moofBox(); - moof.add("mfhd").set("sequence_number", this.nextMoofNumber); - this.nextMoofNumber++; - var traf = moof.add("traf"); - var trak = this.getTrackById(sample.track_id); - traf.add("tfhd").set("track_id", sample.track_id) - .set("flags", BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF); - traf.add("tfdt").set("baseMediaDecodeTime", (sample.dts - (trak.first_dts || 0))); - traf.add("trun").set("flags", BoxParser.TRUN_FLAGS_DATA_OFFSET | BoxParser.TRUN_FLAGS_DURATION | - BoxParser.TRUN_FLAGS_SIZE | BoxParser.TRUN_FLAGS_FLAGS | - BoxParser.TRUN_FLAGS_CTS_OFFSET) - .set("data_offset",0) - .set("first_sample_flags",0) - .set("sample_count",1) - .set("sample_duration",[sample.duration]) - .set("sample_size",[sample.size]) - .set("sample_flags",[sample_flags]) - .set("sample_composition_time_offset", [sample.cts - sample.dts]); - return moof; -} - -// file:src/isofile-sample-processing.js -/* Index of the last moof box received */ -ISOFile.prototype.lastMoofIndex = 0; - -/* size of the buffers allocated for samples */ -ISOFile.prototype.samplesDataSize = 0; - -/* Resets all sample tables */ -ISOFile.prototype.resetTables = function () { - var i; - var trak, stco, stsc, stsz, stts, ctts, stss; - this.initial_duration = this.moov.mvhd.duration; - this.moov.mvhd.duration = 0; - for (i = 0; i < this.moov.traks.length; i++) { - trak = this.moov.traks[i]; - trak.tkhd.duration = 0; - trak.mdia.mdhd.duration = 0; - stco = trak.mdia.minf.stbl.stco || trak.mdia.minf.stbl.co64; - stco.chunk_offsets = []; - stsc = trak.mdia.minf.stbl.stsc; - stsc.first_chunk = []; - stsc.samples_per_chunk = []; - stsc.sample_description_index = []; - stsz = trak.mdia.minf.stbl.stsz || trak.mdia.minf.stbl.stz2; - stsz.sample_sizes = []; - stts = trak.mdia.minf.stbl.stts; - stts.sample_counts = []; - stts.sample_deltas = []; - ctts = trak.mdia.minf.stbl.ctts; - if (ctts) { - ctts.sample_counts = []; - ctts.sample_offsets = []; - } - stss = trak.mdia.minf.stbl.stss; - var k = trak.mdia.minf.stbl.boxes.indexOf(stss); - if (k != -1) trak.mdia.minf.stbl.boxes[k] = null; - } -} - -ISOFile.initSampleGroups = function(trak, traf, sbgps, trak_sgpds, traf_sgpds) { - var l; - var k; - var sample_groups_info; - var sample_group_info; - var sample_group_key; - function SampleGroupInfo(_type, _parameter, _sbgp) { - this.grouping_type = _type; - this.grouping_type_parameter = _parameter; - this.sbgp = _sbgp; - this.last_sample_in_run = -1; - this.entry_index = -1; - } - if (traf) { - traf.sample_groups_info = []; - } - if (!trak.sample_groups_info) { - trak.sample_groups_info = []; - } - for (k = 0; k < sbgps.length; k++) { - sample_group_key = sbgps[k].grouping_type +"/"+ sbgps[k].grouping_type_parameter; - sample_group_info = new SampleGroupInfo(sbgps[k].grouping_type, sbgps[k].grouping_type_parameter, sbgps[k]); - if (traf) { - traf.sample_groups_info[sample_group_key] = sample_group_info; - } - if (!trak.sample_groups_info[sample_group_key]) { - trak.sample_groups_info[sample_group_key] = sample_group_info; - } - for (l=0; l = 2) { - sample_group_key = trak_sgpds[k].grouping_type +"/0"; - sample_group_info = new SampleGroupInfo(trak_sgpds[k].grouping_type, 0); - if (!trak.sample_groups_info[sample_group_key]) { - trak.sample_groups_info[sample_group_key] = sample_group_info; - } - } - } - } else { - if (traf_sgpds) { - for (k = 0; k < traf_sgpds.length; k++) { - if (!traf_sgpds[k].used && traf_sgpds[k].version >= 2) { - sample_group_key = traf_sgpds[k].grouping_type +"/0"; - sample_group_info = new SampleGroupInfo(traf_sgpds[k].grouping_type, 0); - sample_group_info.is_fragment = true; - if (!traf.sample_groups_info[sample_group_key]) { - traf.sample_groups_info[sample_group_key] = sample_group_info; - } - } - } - } - } -} - -ISOFile.setSampleGroupProperties = function(trak, sample, sample_number, sample_groups_info) { - var k; - var index; - sample.sample_groups = []; - for (k in sample_groups_info) { - sample.sample_groups[k] = {}; - sample.sample_groups[k].grouping_type = sample_groups_info[k].grouping_type; - sample.sample_groups[k].grouping_type_parameter = sample_groups_info[k].grouping_type_parameter; - if (sample_number >= sample_groups_info[k].last_sample_in_run) { - if (sample_groups_info[k].last_sample_in_run < 0) { - sample_groups_info[k].last_sample_in_run = 0; - } - sample_groups_info[k].entry_index++; - if (sample_groups_info[k].entry_index <= sample_groups_info[k].sbgp.entries.length - 1) { - sample_groups_info[k].last_sample_in_run += sample_groups_info[k].sbgp.entries[sample_groups_info[k].entry_index].sample_count; - } - } - if (sample_groups_info[k].entry_index <= sample_groups_info[k].sbgp.entries.length - 1) { - sample.sample_groups[k].group_description_index = sample_groups_info[k].sbgp.entries[sample_groups_info[k].entry_index].group_description_index; - } else { - sample.sample_groups[k].group_description_index = -1; // special value for not defined - } - if (sample.sample_groups[k].group_description_index !== 0) { - var description; - if (sample_groups_info[k].fragment_description) { - description = sample_groups_info[k].fragment_description; - } else { - description = sample_groups_info[k].description; - } - if (sample.sample_groups[k].group_description_index > 0) { - if (sample.sample_groups[k].group_description_index > 65535) { - index = (sample.sample_groups[k].group_description_index >> 16)-1; - } else { - index = sample.sample_groups[k].group_description_index-1; - } - if (description && index >= 0) { - sample.sample_groups[k].description = description.entries[index]; - } - } else { - if (description && description.version >= 2) { - if (description.default_group_description_index > 0) { - sample.sample_groups[k].description = description.entries[description.default_group_description_index-1]; - } - } - } - } - } -} - -ISOFile.process_sdtp = function (sdtp, sample, number) { - if (!sample) { - return; - } - if (sdtp) { - sample.is_leading = sdtp.is_leading[number]; - sample.depends_on = sdtp.sample_depends_on[number]; - sample.is_depended_on = sdtp.sample_is_depended_on[number]; - sample.has_redundancy = sdtp.sample_has_redundancy[number]; - } else { - sample.is_leading = 0; - sample.depends_on = 0; - sample.is_depended_on = 0 - sample.has_redundancy = 0; - } -} - -/* Build initial sample list from sample tables */ -ISOFile.prototype.buildSampleLists = function() { - var i; - var trak; - for (i = 0; i < this.moov.traks.length; i++) { - trak = this.moov.traks[i]; - this.buildTrakSampleLists(trak); - } -} - -ISOFile.prototype.buildTrakSampleLists = function(trak) { - var j, k; - var stco, stsc, stsz, stts, ctts, stss, stsd, subs, sbgps, sgpds, stdp; - var chunk_run_index, chunk_index, last_chunk_in_run, offset_in_chunk, last_sample_in_chunk; - var last_sample_in_stts_run, stts_run_index, last_sample_in_ctts_run, ctts_run_index, last_stss_index, last_subs_index, subs_entry_index, last_subs_sample_index; - - trak.samples = []; - trak.samples_duration = 0; - trak.samples_size = 0; - stco = trak.mdia.minf.stbl.stco || trak.mdia.minf.stbl.co64; - stsc = trak.mdia.minf.stbl.stsc; - stsz = trak.mdia.minf.stbl.stsz || trak.mdia.minf.stbl.stz2; - stts = trak.mdia.minf.stbl.stts; - ctts = trak.mdia.minf.stbl.ctts; - stss = trak.mdia.minf.stbl.stss; - stsd = trak.mdia.minf.stbl.stsd; - subs = trak.mdia.minf.stbl.subs; - stdp = trak.mdia.minf.stbl.stdp; - sbgps = trak.mdia.minf.stbl.sbgps; - sgpds = trak.mdia.minf.stbl.sgpds; - - last_sample_in_stts_run = -1; - stts_run_index = -1; - last_sample_in_ctts_run = -1; - ctts_run_index = -1; - last_stss_index = 0; - subs_entry_index = 0; - last_subs_sample_index = 0; - - ISOFile.initSampleGroups(trak, null, sbgps, sgpds); - - if (typeof stsz === "undefined") { - return; - } - - /* we build the samples one by one and compute their properties */ - for (j = 0; j < stsz.sample_sizes.length; j++) { - var sample = {}; - sample.number = j; - sample.track_id = trak.tkhd.track_id; - sample.timescale = trak.mdia.mdhd.timescale; - sample.alreadyRead = 0; - trak.samples[j] = sample; - /* size can be known directly */ - sample.size = stsz.sample_sizes[j]; - trak.samples_size += sample.size; - /* computing chunk-based properties (offset, sample description index)*/ - if (j === 0) { - chunk_index = 1; /* the first sample is in the first chunk (chunk indexes are 1-based) */ - chunk_run_index = 0; /* the first chunk is the first entry in the first_chunk table */ - sample.chunk_index = chunk_index; - sample.chunk_run_index = chunk_run_index; - last_sample_in_chunk = stsc.samples_per_chunk[chunk_run_index]; - offset_in_chunk = 0; - - /* Is there another entry in the first_chunk table ? */ - if (chunk_run_index + 1 < stsc.first_chunk.length) { - /* The last chunk in the run is the chunk before the next first chunk */ - last_chunk_in_run = stsc.first_chunk[chunk_run_index+1]-1; - } else { - /* There is only one entry in the table, it is valid for all future chunks*/ - last_chunk_in_run = Infinity; - } - } else { - if (j < last_sample_in_chunk) { - /* the sample is still in the current chunk */ - sample.chunk_index = chunk_index; - sample.chunk_run_index = chunk_run_index; - } else { - /* the sample is in the next chunk */ - chunk_index++; - sample.chunk_index = chunk_index; - /* reset the accumulated offset in the chunk */ - offset_in_chunk = 0; - if (chunk_index <= last_chunk_in_run) { - /* stay in the same entry of the first_chunk table */ - /* chunk_run_index unmodified */ - } else { - chunk_run_index++; - /* Is there another entry in the first_chunk table ? */ - if (chunk_run_index + 1 < stsc.first_chunk.length) { - /* The last chunk in the run is the chunk before the next first chunk */ - last_chunk_in_run = stsc.first_chunk[chunk_run_index+1]-1; - } else { - /* There is only one entry in the table, it is valid for all future chunks*/ - last_chunk_in_run = Infinity; - } - - } - sample.chunk_run_index = chunk_run_index; - last_sample_in_chunk += stsc.samples_per_chunk[chunk_run_index]; - } - } - - sample.description_index = stsc.sample_description_index[sample.chunk_run_index]-1; - sample.description = stsd.entries[sample.description_index]; - sample.offset = stco.chunk_offsets[sample.chunk_index-1] + offset_in_chunk; /* chunk indexes are 1-based */ - offset_in_chunk += sample.size; - - /* setting dts, cts, duration and rap flags */ - if (j > last_sample_in_stts_run) { - stts_run_index++; - if (last_sample_in_stts_run < 0) { - last_sample_in_stts_run = 0; - } - last_sample_in_stts_run += stts.sample_counts[stts_run_index]; - } - if (j > 0) { - trak.samples[j-1].duration = stts.sample_deltas[stts_run_index]; - trak.samples_duration += trak.samples[j-1].duration; - sample.dts = trak.samples[j-1].dts + trak.samples[j-1].duration; - } else { - sample.dts = 0; - } - if (ctts) { - if (j >= last_sample_in_ctts_run) { - ctts_run_index++; - if (last_sample_in_ctts_run < 0) { - last_sample_in_ctts_run = 0; - } - last_sample_in_ctts_run += ctts.sample_counts[ctts_run_index]; - } - sample.cts = trak.samples[j].dts + ctts.sample_offsets[ctts_run_index]; - } else { - sample.cts = sample.dts; - } - if (stss) { - if (j == stss.sample_numbers[last_stss_index] - 1) { // sample numbers are 1-based - sample.is_sync = true; - last_stss_index++; - } else { - sample.is_sync = false; - sample.degradation_priority = 0; - } - if (subs) { - if (subs.entries[subs_entry_index].sample_delta + last_subs_sample_index == j+1) { - sample.subsamples = subs.entries[subs_entry_index].subsamples; - last_subs_sample_index += subs.entries[subs_entry_index].sample_delta; - subs_entry_index++; - } - } - } else { - sample.is_sync = true; - } - ISOFile.process_sdtp(trak.mdia.minf.stbl.sdtp, sample, sample.number); - if (stdp) { - sample.degradation_priority = stdp.priority[j]; - } else { - sample.degradation_priority = 0; - } - if (subs) { - if (subs.entries[subs_entry_index].sample_delta + last_subs_sample_index == j) { - sample.subsamples = subs.entries[subs_entry_index].subsamples; - last_subs_sample_index += subs.entries[subs_entry_index].sample_delta; - } - } - if (sbgps.length > 0 || sgpds.length > 0) { - ISOFile.setSampleGroupProperties(trak, sample, j, trak.sample_groups_info); - } - } - if (j>0) { - trak.samples[j-1].duration = Math.max(trak.mdia.mdhd.duration - trak.samples[j-1].dts, 0); - trak.samples_duration += trak.samples[j-1].duration; - } -} - -/* Update sample list when new 'moof' boxes are received */ -ISOFile.prototype.updateSampleLists = function() { - var i, j, k; - var default_sample_description_index, default_sample_duration, default_sample_size, default_sample_flags; - var last_run_position; - var box, moof, traf, trak, trex; - var sample; - var sample_flags; - - if (this.moov === undefined) { - return; - } - /* if the input file is fragmented and fetched in multiple downloads, we need to update the list of samples */ - while (this.lastMoofIndex < this.moofs.length) { - box = this.moofs[this.lastMoofIndex]; - this.lastMoofIndex++; - if (box.type == "moof") { - moof = box; - for (i = 0; i < moof.trafs.length; i++) { - traf = moof.trafs[i]; - trak = this.getTrackById(traf.tfhd.track_id); - trex = this.getTrexById(traf.tfhd.track_id); - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) { - default_sample_description_index = traf.tfhd.default_sample_description_index; - } else { - default_sample_description_index = (trex ? trex.default_sample_description_index: 1); - } - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) { - default_sample_duration = traf.tfhd.default_sample_duration; - } else { - default_sample_duration = (trex ? trex.default_sample_duration : 0); - } - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) { - default_sample_size = traf.tfhd.default_sample_size; - } else { - default_sample_size = (trex ? trex.default_sample_size : 0); - } - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) { - default_sample_flags = traf.tfhd.default_sample_flags; - } else { - default_sample_flags = (trex ? trex.default_sample_flags : 0); - } - traf.sample_number = 0; - /* process sample groups */ - if (traf.sbgps.length > 0) { - ISOFile.initSampleGroups(trak, traf, traf.sbgps, trak.mdia.minf.stbl.sgpds, traf.sgpds); - } - for (j = 0; j < traf.truns.length; j++) { - var trun = traf.truns[j]; - for (k = 0; k < trun.sample_count; k++) { - sample = {}; - sample.moof_number = this.lastMoofIndex; - sample.number_in_traf = traf.sample_number; - traf.sample_number++; - sample.number = trak.samples.length; - traf.first_sample_index = trak.samples.length; - trak.samples.push(sample); - sample.track_id = trak.tkhd.track_id; - sample.timescale = trak.mdia.mdhd.timescale; - sample.description_index = default_sample_description_index-1; - sample.description = trak.mdia.minf.stbl.stsd.entries[sample.description_index]; - sample.size = default_sample_size; - if (trun.flags & BoxParser.TRUN_FLAGS_SIZE) { - sample.size = trun.sample_size[k]; - } - trak.samples_size += sample.size; - sample.duration = default_sample_duration; - if (trun.flags & BoxParser.TRUN_FLAGS_DURATION) { - sample.duration = trun.sample_duration[k]; - } - trak.samples_duration += sample.duration; - if (trak.first_traf_merged || k > 0) { - sample.dts = trak.samples[trak.samples.length-2].dts+trak.samples[trak.samples.length-2].duration; - } else { - if (traf.tfdt) { - sample.dts = traf.tfdt.baseMediaDecodeTime; - } else { - sample.dts = 0; - } - trak.first_traf_merged = true; - } - sample.cts = sample.dts; - if (trun.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - sample.cts = sample.dts + trun.sample_composition_time_offset[k]; - } - sample_flags = default_sample_flags; - if (trun.flags & BoxParser.TRUN_FLAGS_FLAGS) { - sample_flags = trun.sample_flags[k]; - } else if (k === 0 && (trun.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG)) { - sample_flags = trun.first_sample_flags; - } - sample.is_sync = ((sample_flags >> 16 & 0x1) ? false : true); - sample.is_leading = (sample_flags >> 26 & 0x3); - sample.depends_on = (sample_flags >> 24 & 0x3); - sample.is_depended_on = (sample_flags >> 22 & 0x3); - sample.has_redundancy = (sample_flags >> 20 & 0x3); - sample.degradation_priority = (sample_flags & 0xFFFF); - //ISOFile.process_sdtp(traf.sdtp, sample, sample.number_in_traf); - var bdop = (traf.tfhd.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET) ? true : false; - var dbim = (traf.tfhd.flags & BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF) ? true : false; - var dop = (trun.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) ? true : false; - var bdo = 0; - if (!bdop) { - if (!dbim) { - if (j === 0) { // the first track in the movie fragment - bdo = moof.start; // the position of the first byte of the enclosing Movie Fragment Box - } else { - bdo = last_run_position; // end of the data defined by the preceding *track* (irrespective of the track id) fragment in the moof - } - } else { - bdo = moof.start; - } - } else { - bdo = traf.tfhd.base_data_offset; - } - if (j === 0 && k === 0) { - if (dop) { - sample.offset = bdo + trun.data_offset; // If the data-offset is present, it is relative to the base-data-offset established in the track fragment header - } else { - sample.offset = bdo; // the data for this run starts the base-data-offset defined by the track fragment header - } - } else { - sample.offset = last_run_position; // this run starts immediately after the data of the previous run - } - last_run_position = sample.offset + sample.size; - if (traf.sbgps.length > 0 || traf.sgpds.length > 0 || - trak.mdia.minf.stbl.sbgps.length > 0 || trak.mdia.minf.stbl.sgpds.length > 0) { - ISOFile.setSampleGroupProperties(trak, sample, sample.number_in_traf, traf.sample_groups_info); - } - } - } - if (traf.subs) { - trak.has_fragment_subsamples = true; - var sample_index = traf.first_sample_index; - for (j = 0; j < traf.subs.entries.length; j++) { - sample_index += traf.subs.entries[j].sample_delta; - sample = trak.samples[sample_index-1]; - sample.subsamples = traf.subs.entries[j].subsamples; - } - } - } - } - } -} - -/* Try to get sample data for a given sample: - returns null if not found - returns the same sample if already requested - */ -ISOFile.prototype.getSample = function(trak, sampleNum) { - var buffer; - var sample = trak.samples[sampleNum]; - - if (!this.moov) { - return null; - } - - if (!sample.data) { - /* Not yet fetched */ - sample.data = new Uint8Array(sample.size); - sample.alreadyRead = 0; - this.samplesDataSize += sample.size; - Log.debug("ISOFile", "Allocating sample #"+sampleNum+" on track #"+trak.tkhd.track_id+" of size "+sample.size+" (total: "+this.samplesDataSize+")"); - } else if (sample.alreadyRead == sample.size) { - /* Already fetched entirely */ - return sample; - } - - /* The sample has only been partially fetched, we need to check in all buffers */ - while(true) { - var index = this.stream.findPosition(true, sample.offset + sample.alreadyRead, false); - if (index > -1) { - buffer = this.stream.buffers[index]; - var lengthAfterStart = buffer.byteLength - (sample.offset + sample.alreadyRead - buffer.fileStart); - if (sample.size - sample.alreadyRead <= lengthAfterStart) { - /* the (rest of the) sample is entirely contained in this buffer */ - - Log.debug("ISOFile","Getting sample #"+sampleNum+" data (alreadyRead: "+sample.alreadyRead+" offset: "+ - (sample.offset+sample.alreadyRead - buffer.fileStart)+" read size: "+(sample.size - sample.alreadyRead)+" full size: "+sample.size+")"); - - DataStream.memcpy(sample.data.buffer, sample.alreadyRead, - buffer, sample.offset+sample.alreadyRead - buffer.fileStart, sample.size - sample.alreadyRead); - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += sample.size - sample.alreadyRead; - this.stream.logBufferLevel(); - - sample.alreadyRead = sample.size; - - return sample; - } else { - /* the sample does not end in this buffer */ - - if (lengthAfterStart === 0) return null; - - Log.debug("ISOFile","Getting sample #"+sampleNum+" partial data (alreadyRead: "+sample.alreadyRead+" offset: "+ - (sample.offset+sample.alreadyRead - buffer.fileStart)+" read size: "+lengthAfterStart+" full size: "+sample.size+")"); - - DataStream.memcpy(sample.data.buffer, sample.alreadyRead, - buffer, sample.offset+sample.alreadyRead - buffer.fileStart, lengthAfterStart); - sample.alreadyRead += lengthAfterStart; - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += lengthAfterStart; - this.stream.logBufferLevel(); - - /* keep looking in the next buffer */ - } - } else { - return null; - } - } -} - -/* Release the memory used to store the data of the sample */ -ISOFile.prototype.releaseSample = function(trak, sampleNum) { - var sample = trak.samples[sampleNum]; - if (sample.data) { - this.samplesDataSize -= sample.size; - sample.data = null; - sample.alreadyRead = 0; - return sample.size; - } else { - return 0; - } -} - -ISOFile.prototype.getAllocatedSampleDataSize = function() { - return this.samplesDataSize; -} - -/* Builds the MIME Type 'codecs' sub-parameters for the whole file */ -ISOFile.prototype.getCodecs = function() { - var i; - var codecs = ""; - for (i = 0; i < this.moov.traks.length; i++) { - var trak = this.moov.traks[i]; - if (i>0) { - codecs+=","; - } - codecs += trak.mdia.minf.stbl.stsd.entries[0].getCodec(); - } - return codecs; -} - -/* Helper function */ -ISOFile.prototype.getTrexById = function(id) { - var i; - if (!this.moov || !this.moov.mvex) return null; - for (i = 0; i < this.moov.mvex.trexs.length; i++) { - var trex = this.moov.mvex.trexs[i]; - if (trex.track_id == id) return trex; - } - return null; -} - -/* Helper function */ -ISOFile.prototype.getTrackById = function(id) { - if (this.moov === undefined) { - return null; - } - for (var j = 0; j < this.moov.traks.length; j++) { - var trak = this.moov.traks[j]; - if (trak.tkhd.track_id == id) return trak; - } - return null; -} -// file:src/isofile-item-processing.js -ISOFile.prototype.items = []; -/* size of the buffers allocated for samples */ -ISOFile.prototype.itemsDataSize = 0; - -ISOFile.prototype.flattenItemInfo = function() { - var items = this.items; - var i, j; - var item; - var meta = this.meta; - if (meta === null || meta === undefined) return; - if (meta.hdlr === undefined) return; - if (meta.iinf === undefined) return; - for (i = 0; i < meta.iinf.item_infos.length; i++) { - item = {}; - item.id = meta.iinf.item_infos[i].item_ID; - items[item.id] = item; - item.ref_to = []; - item.name = meta.iinf.item_infos[i].item_name; - if (meta.iinf.item_infos[i].protection_index > 0) { - item.protection = meta.ipro.protections[meta.iinf.item_infos[i].protection_index-1]; - } - if (meta.iinf.item_infos[i].item_type) { - item.type = meta.iinf.item_infos[i].item_type; - } else { - item.type = "mime"; - } - item.content_type = meta.iinf.item_infos[i].content_type; - item.content_encoding = meta.iinf.item_infos[i].content_encoding; - } - if (meta.iloc) { - for(i = 0; i < meta.iloc.items.length; i++) { - var offset; - var itemloc = meta.iloc.items[i]; - item = items[itemloc.item_ID]; - if (itemloc.data_reference_index !== 0) { - Log.warn("Item storage with reference to other files: not supported"); - item.source = meta.dinf.boxes[itemloc.data_reference_index-1]; - } - switch(itemloc.construction_method) { - case 0: // offset into the file referenced by the data reference index - break; - case 1: // offset into the idat box of this meta box - Log.warn("Item storage with construction_method : not supported"); - break; - case 2: // offset into another item - Log.warn("Item storage with construction_method : not supported"); - break; - } - item.extents = []; - item.size = 0; - for (j = 0; j < itemloc.extents.length; j++) { - item.extents[j] = {}; - item.extents[j].offset = itemloc.extents[j].extent_offset + itemloc.base_offset; - item.extents[j].length = itemloc.extents[j].extent_length; - item.extents[j].alreadyRead = 0; - item.size += item.extents[j].length; - } - } - } - if (meta.pitm) { - items[meta.pitm.item_id].primary = true; - } - if (meta.iref) { - for (i=0; i 0 && propEntry.property_index-1 < meta.iprp.ipco.boxes.length) { - var propbox = meta.iprp.ipco.boxes[propEntry.property_index-1]; - item.properties[propbox.type] = propbox; - item.properties.boxes.push(propbox); - } - } - } - } - } -} - -ISOFile.prototype.getItem = function(item_id) { - var buffer; - var item; - - if (!this.meta) { - return null; - } - - item = this.items[item_id]; - if (!item.data && item.size) { - /* Not yet fetched */ - item.data = new Uint8Array(item.size); - item.alreadyRead = 0; - this.itemsDataSize += item.size; - Log.debug("ISOFile", "Allocating item #"+item_id+" of size "+item.size+" (total: "+this.itemsDataSize+")"); - } else if (item.alreadyRead === item.size) { - /* Already fetched entirely */ - return item; - } - - /* The item has only been partially fetched, we need to check in all buffers to find the remaining extents*/ - - for (var i = 0; i < item.extents.length; i++) { - var extent = item.extents[i]; - if (extent.alreadyRead === extent.length) { - continue; - } else { - var index = this.stream.findPosition(true, extent.offset + extent.alreadyRead, false); - if (index > -1) { - buffer = this.stream.buffers[index]; - var lengthAfterStart = buffer.byteLength - (extent.offset + extent.alreadyRead - buffer.fileStart); - if (extent.length - extent.alreadyRead <= lengthAfterStart) { - /* the (rest of the) extent is entirely contained in this buffer */ - - Log.debug("ISOFile","Getting item #"+item_id+" extent #"+i+" data (alreadyRead: "+extent.alreadyRead+ - " offset: "+(extent.offset+extent.alreadyRead - buffer.fileStart)+" read size: "+(extent.length - extent.alreadyRead)+ - " full extent size: "+extent.length+" full item size: "+item.size+")"); - - DataStream.memcpy(item.data.buffer, item.alreadyRead, - buffer, extent.offset+extent.alreadyRead - buffer.fileStart, extent.length - extent.alreadyRead); - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += extent.length - extent.alreadyRead; - this.stream.logBufferLevel(); - - item.alreadyRead += (extent.length - extent.alreadyRead); - extent.alreadyRead = extent.length; - } else { - /* the sample does not end in this buffer */ - - Log.debug("ISOFile","Getting item #"+item_id+" extent #"+i+" partial data (alreadyRead: "+extent.alreadyRead+" offset: "+ - (extent.offset+extent.alreadyRead - buffer.fileStart)+" read size: "+lengthAfterStart+ - " full extent size: "+extent.length+" full item size: "+item.size+")"); - - DataStream.memcpy(item.data.buffer, item.alreadyRead, - buffer, extent.offset+extent.alreadyRead - buffer.fileStart, lengthAfterStart); - extent.alreadyRead += lengthAfterStart; - item.alreadyRead += lengthAfterStart; - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += lengthAfterStart; - this.stream.logBufferLevel(); - return null; - } - } else { - return null; - } - } - } - if (item.alreadyRead === item.size) { - /* fetched entirely */ - return item; - } else { - return null; - } -} - -/* Release the memory used to store the data of the item */ -ISOFile.prototype.releaseItem = function(item_id) { - var item = this.items[item_id]; - if (item.data) { - this.itemsDataSize -= item.size; - item.data = null; - item.alreadyRead = 0; - for (var i = 0; i < item.extents.length; i++) { - var extent = item.extents[i]; - extent.alreadyRead = 0; - } - return item.size; - } else { - return 0; - } -} - - -ISOFile.prototype.processItems = function(callback) { - for(var i in this.items) { - var item = this.items[i]; - this.getItem(item.id); - if (callback && !item.sent) { - callback(item); - item.sent = true; - item.data = null; - } - } -} - -ISOFile.prototype.hasItem = function(name) { - for(var i in this.items) { - var item = this.items[i]; - if (item.name === name) { - return item.id; - } - } - return -1; -} - -ISOFile.prototype.getMetaHandler = function() { - if (!this.meta) { - return null; - } else { - return this.meta.hdlr.handler; - } -} - -ISOFile.prototype.getPrimaryItem = function() { - if (!this.meta || !this.meta.pitm) { - return null; - } else { - return this.getItem(this.meta.pitm.item_id); - } -} - -ISOFile.prototype.itemToFragmentedTrackFile = function(_options) { - var options = _options || {}; - var item = null; - if (options.itemId) { - item = this.getItem(options.itemId); - } else { - item = this.getPrimaryItem(); - } - if (item == null) return null; - - var file = new ISOFile(); - file.discardMdatData = false; - // assuming the track type is the same as the item type - var trackOptions = { type: item.type, description_boxes: item.properties.boxes}; - if (item.properties.ispe) { - trackOptions.width = item.properties.ispe.image_width; - trackOptions.height = item.properties.ispe.image_height; - } - var trackId = file.addTrack(trackOptions); - if (trackId) { - file.addSample(trackId, item.data); - return file; - } else { - return null; - } -} - -// file:src/isofile-write.js -/* Rewrite the entire file */ -ISOFile.prototype.write = function(outstream) { - for (var i=0; i0 ? this.moov.traks[i].samples[0].duration: 0)); - initSegs.push(seg); - } - return initSegs; -} - -// file:src/box-print.js -/* - * Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -BoxParser.Box.prototype.printHeader = function(output) { - this.size += 8; - if (this.size > MAX_SIZE) { - this.size += 8; - } - if (this.type === "uuid") { - this.size += 16; - } - output.log(output.indent+"size:"+this.size); - output.log(output.indent+"type:"+this.type); -} - -BoxParser.FullBox.prototype.printHeader = function(output) { - this.size += 4; - BoxParser.Box.prototype.printHeader.call(this, output); - output.log(output.indent+"version:"+this.version); - output.log(output.indent+"flags:"+this.flags); -} - -BoxParser.Box.prototype.print = function(output) { - this.printHeader(output); -} - -BoxParser.ContainerBox.prototype.print = function(output) { - this.printHeader(output); - for (var i=0; i>8)); - output.log(output.indent+"matrix: "+this.matrix.join(", ")); - output.log(output.indent+"next_track_id: "+this.next_track_id); -} - -BoxParser.tkhdBox.prototype.print = function(output) { - BoxParser.FullBox.prototype.printHeader.call(this, output); - output.log(output.indent+"creation_time: "+this.creation_time); - output.log(output.indent+"modification_time: "+this.modification_time); - output.log(output.indent+"track_id: "+this.track_id); - output.log(output.indent+"duration: "+this.duration); - output.log(output.indent+"volume: "+(this.volume>>8)); - output.log(output.indent+"matrix: "+this.matrix.join(", ")); - output.log(output.indent+"layer: "+this.layer); - output.log(output.indent+"alternate_group: "+this.alternate_group); - output.log(output.indent+"width: "+this.width); - output.log(output.indent+"height: "+this.height); -}// file:src/mp4box.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var MP4Box = {}; - -MP4Box.createFile = function (_keepMdatData, _stream) { - /* Boolean indicating if bytes containing media data should be kept in memory */ - var keepMdatData = (_keepMdatData !== undefined ? _keepMdatData : true); - var file = new ISOFile(_stream); - file.discardMdatData = (keepMdatData ? false : true); - return file; -} - -if (typeof exports !== 'undefined') { - exports.createFile = MP4Box.createFile; -} diff --git a/player/src/mp4/mp4box.d.ts b/player/src/mp4/mp4box.d.ts new file mode 100644 index 0000000..7ce91f7 --- /dev/null +++ b/player/src/mp4/mp4box.d.ts @@ -0,0 +1,148 @@ +// https://github.com/gpac/mp4box.js/issues/233 + +declare module "mp4box" { + interface MP4MediaTrack { + id: number; + created: Date; + modified: Date; + movie_duration: number; + layer: number; + alternate_group: number; + volume: number; + track_width: number; + track_height: number; + timescale: number; + duration: number; + bitrate: number; + codec: string; + language: string; + nb_samples: number; + } + + interface MP4VideoData { + width: number; + height: number; + } + + interface MP4VideoTrack extends MP4MediaTrack { + video: MP4VideoData; + } + + interface MP4AudioData { + sample_rate: number; + channel_count: number; + sample_size: number; + } + + interface MP4AudioTrack extends MP4MediaTrack { + audio: MP4AudioData; + } + + type MP4Track = MP4VideoTrack | MP4AudioTrack; + + export interface MP4Info { + duration: number; + timescale: number; + fragment_duration: number; + isFragmented: boolean; + isProgressive: boolean; + hasIOD: boolean; + brands: string[]; + created: Date; + modified: Date; + tracks: MP4Track[]; + mime: string; + audioTracks: MP4AudioTrack[]; + videoTracks: MP4VideoTrack[]; + } + + export type MP4ArrayBuffer = ArrayBuffer & {fileStart: number}; + + export interface MP4File { + onMoovStart?: () => void; + onReady?: (info: MP4Info) => void; + onError?: (e: string) => void; + onSamples?: (id: number, user: any, samples: Sample[]) => void; + + appendBuffer(data: MP4ArrayBuffer): number; + start(): void; + stop(): void; + flush(): void; + + setExtractionOptions(id: number, user: any, options: ExtractionOptions): void; + } + + export function createFile(): MP4File; + + export interface Sample { + number: number; + track_id: number; + timescale: number; + description_index: number; + description: any; + data: ArrayBuffer; + size: number; + alreadyRead: number; + duration: number; + cts: number; + dts: number; + is_sync: boolean; + is_leading: number; + depends_on: number; + is_depended_on: number; + has_redundancy: number; + degration_priority: number; + offset: number; + subsamples: any; + } + + export interface ExtractionOptions { + nbSamples: number; + } + + const BIG_ENDIAN: boolean; + const LITTLE_ENDIAN: boolean; + + export class DataStream { + constructor(buffer: ArrayBuffer, byteOffset?: number, littleEndian?: boolean); + getPosition(): number; + + get byteLength(): number; + get buffer(): ArrayBuffer; + set buffer(v: ArrayBuffer); + get byteOffset(): number; + set byteOffset(v: number); + get dataView(): DataView; + set dataView(v: DataView); + + seek(pos: number): void; + isEof(): boolean; + + mapUint8Array(length: number): Uint8Array; + readInt32Array(length: number, littleEndian: boolean): Int32Array; + readInt16Array(length: number, littleEndian: boolean): Int16Array; + readInt8(length: number): Int8Array; + readUint32Array(length: number, littleEndian: boolean): Uint32Array; + readUint16Array(length: number, littleEndian: boolean): Uint16Array; + readUint8(length: number): Uint8Array; + readFloat64Array(length: number, littleEndian: boolean): Float64Array; + readFloat32Array(length: number, littleEndian: boolean): Float32Array; + + readInt32(littleEndian: boolean): number; + readInt16(littleEndian: boolean): number; + readInt8(): number; + readUint32(littleEndian: boolean): number; + readUint16(littleEndian: boolean): number; + readUint8(): number; + readFloat32(littleEndian: boolean): number; + readFloat64(littleEndian: boolean): number; + + endianness: boolean; + + memcpy(dst: ArrayBufferLike, dstOffset: number, src: ArrayBufferLike, srcOffset: number, byteLength: number): void; + + // TODO I got bored porting the remaining functions + } + + export { }; +} \ No newline at end of file diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index 39243c0..32330ee 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -8,8 +8,6 @@ import Video from "../video" // @ts-ignore bundler embeds data import fingerprint from 'bundle-text:./fingerprint.hex'; -/// - export interface PlayerInit { url: string; canvas: HTMLCanvasElement; diff --git a/player/src/types/webtransport.d.ts b/player/src/transport/webtransport.d.ts similarity index 100% rename from player/src/types/webtransport.d.ts rename to player/src/transport/webtransport.d.ts diff --git a/player/tsconfig.json b/player/tsconfig.json index 5fce1da..9fcdf11 100644 --- a/player/tsconfig.json +++ b/player/tsconfig.json @@ -7,9 +7,5 @@ "module": "es2022", "moduleResolution": "node", "strict": true, - "typeRoots": [ - "src/types" - ], - "allowJs": true } } \ No newline at end of file diff --git a/player/yarn.lock b/player/yarn.lock index c4a13b7..34f2706 100644 --- a/player/yarn.lock +++ b/player/yarn.lock @@ -1196,6 +1196,11 @@ micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mp4box@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/mp4box/-/mp4box-0.5.2.tgz#6a2d36fdd0e2d3f2f2bee446d2067edf0b3871bc" + integrity sha512-zRmGlvxy+YdW3Dmt+TR4xPHynbxwXtAQDTN/Fo9N3LMxaUlB2C5KmZpzYyGKy4c7k4Jf3RCR0A2pm9SZELOLXw== + msgpackr-extract@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" From 9e29d552a01f1ab441d2c1542c0b404ae2968132 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 3 Apr 2023 12:48:02 -0700 Subject: [PATCH 09/10] Audio worksish. --- player/src/audio/{ => decoder}/decoder.ts | 78 ++++++++---- player/src/audio/decoder/index.ts | 29 +++++ player/src/audio/decoder/worker.ts | 17 +++ player/src/audio/index.ts | 34 ++++-- player/src/audio/message.ts | 8 +- player/src/audio/renderer.ts | 24 ---- player/src/audio/renderer/index.ts | 47 +++++++ player/src/audio/renderer/worklet.ts | 69 +++++++++++ player/src/audio/ring.ts | 142 ++++++++++++++++++++++ player/src/audio/source/index.ts | 64 ---------- player/src/audio/source/message.ts | 11 -- player/src/audio/source/ring.ts | 131 -------------------- player/src/audio/source/worklet.ts | 68 ----------- player/src/audio/worker.ts | 25 ---- player/src/index.css | 2 +- player/src/index.html | 1 + player/src/index.ts | 18 ++- player/src/player/index.ts | 40 ++++++ player/src/transport/index.ts | 18 ++- player/src/video/decoder.ts | 6 +- player/src/video/index.ts | 10 +- player/src/video/message.ts | 2 +- player/src/video/renderer.ts | 4 +- player/src/video/worker.ts | 4 +- 24 files changed, 468 insertions(+), 384 deletions(-) rename player/src/audio/{ => decoder}/decoder.ts (57%) create mode 100644 player/src/audio/decoder/index.ts create mode 100644 player/src/audio/decoder/worker.ts delete mode 100644 player/src/audio/renderer.ts create mode 100644 player/src/audio/renderer/index.ts create mode 100644 player/src/audio/renderer/worklet.ts create mode 100644 player/src/audio/ring.ts delete mode 100644 player/src/audio/source/index.ts delete mode 100644 player/src/audio/source/message.ts delete mode 100644 player/src/audio/source/ring.ts delete mode 100644 player/src/audio/source/worklet.ts delete mode 100644 player/src/audio/worker.ts create mode 100644 player/src/player/index.ts diff --git a/player/src/audio/decoder.ts b/player/src/audio/decoder/decoder.ts similarity index 57% rename from player/src/audio/decoder.ts rename to player/src/audio/decoder/decoder.ts index 388d326..47d7fb3 100644 --- a/player/src/audio/decoder.ts +++ b/player/src/audio/decoder/decoder.ts @@ -1,33 +1,53 @@ -import * as Message from "./message"; -import * as MP4 from "../mp4" -import * as Stream from "../stream" -import * as Util from "../util" +import * as Message from "../message"; +import * as MP4 from "../../mp4" +import * as Stream from "../../stream" +import * as Util from "../../util" +import { Ring, RingState } from "../ring" -import { Renderer } from "./renderer" + // Ignore the timestamp output by WebCodecs since it's in microseconds + // We will manually set the timestamp based on the sample rate. + let frameCount = 0 -export class Decoder { +export default class Decoder { // Store the init message for each track - tracks: Map> - renderer: Renderer; + tracks: Map>; + sampleRate: number; + channels: Ring[]; + sync?: number; // the first timestamp - constructor(config: Message.Config, renderer: Renderer) { + constructor(config: Message.Config) { this.tracks = new Map(); - this.renderer = renderer; + this.sampleRate = config.sampleRate + + this.channels = [] + for (let state of config.channels) { + this.channels.push(new Ring(state)) + } } - async init(msg: Message.Init) { - let track = this.tracks.get(msg.track); - if (!track) { - track = new Util.Deferred() - this.tracks.set(msg.track, track) + init(msg: Message.Init) { + let defer = this.tracks.get(msg.track); + if (!defer) { + defer = new Util.Deferred() + this.tracks.set(msg.track, defer) } - console.log(msg.info) if (msg.info.audioTracks.length != 1 || msg.info.videoTracks.length != 0) { throw new Error("Expected a single audio track") } - track.resolve(msg) + const track = msg.info.audioTracks[0] + const audio = track.audio + + if (audio.sample_rate != this.sampleRate) { + throw new Error("sample rate not supported") + } + + if (audio.channel_count > this.channels.length) { + throw new Error("channel count not supported") + } + + defer.resolve(msg) } async decode(msg: Message.Segment) { @@ -39,18 +59,20 @@ export class Decoder { // Wait for the init segment to be fully received and parsed const init = await track.promise; - const info = init.info; - const audio = info.audioTracks[0] + const audio = init.info.audioTracks[0] const decoder = new AudioDecoder({ output: (frame: AudioData) => { - this.renderer.emit(frame) + for (let i = 0; i < frame.numberOfChannels; i += 1) { + this.channels[i].emit(frameCount, frame, i) + } + + frameCount += frame.numberOfFrames; }, error: (err: Error) => { console.warn(err) } }); - console.log(audio) decoder.configure({ codec: audio.codec, @@ -63,12 +85,20 @@ export class Decoder { input.onSamples = (id: number, user: any, samples: MP4.Sample[]) => { for (let sample of samples) { - // TODO this assumes that timescale == sample rate + if (!this.sync) { + this.sync = sample.dts; + } + + // Convert to milliseconds + const timestamp = 1000 * (sample.dts - this.sync) / sample.timescale + const duration = 1000 * sample.duration / sample.timescale + + // This assumes that timescale == sample rate decoder.decode(new EncodedAudioChunk({ type: sample.is_sync ? "key" : "delta", data: sample.data, - duration: sample.duration, - timestamp: sample.dts, + duration: duration, + timestamp: timestamp, })) } } diff --git a/player/src/audio/decoder/index.ts b/player/src/audio/decoder/index.ts new file mode 100644 index 0000000..d181e43 --- /dev/null +++ b/player/src/audio/decoder/index.ts @@ -0,0 +1,29 @@ +import * as Message from "../message" + +// Wrapper to run the decoder in a Worker +export default class Decoder { + worker: Worker; + + constructor(config: Message.Config) { + const url = new URL('worker.ts', import.meta.url) + this.worker = new Worker(url, { + name: "audio", + type: "module", + }) + + this.worker.onmessage = this.onMessage.bind(this) + this.worker.postMessage({ config }) + } + + init(init: Message.Init) { + this.worker.postMessage({ init }) + } + + segment(segment: Message.Segment) { + this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ]) + } + + private onMessage(e: MessageEvent) { + // TODO + } +} diff --git a/player/src/audio/decoder/worker.ts b/player/src/audio/decoder/worker.ts new file mode 100644 index 0000000..3914e14 --- /dev/null +++ b/player/src/audio/decoder/worker.ts @@ -0,0 +1,17 @@ +import Decoder from "./decoder" + +let decoder: Decoder + +self.addEventListener('message', (e: MessageEvent) => { + if (e.data.config) { + decoder = new Decoder(e.data.config) + } + + if (e.data.init) { + decoder.init(e.data.init) + } + + if (e.data.segment) { + decoder.decode(e.data.segment) + } +}) \ No newline at end of file diff --git a/player/src/audio/index.ts b/player/src/audio/index.ts index 670e874..3847d3f 100644 --- a/player/src/audio/index.ts +++ b/player/src/audio/index.ts @@ -1,21 +1,35 @@ import * as Message from "./message" -import { Renderer } from "./renderer" -import { Decoder } from "./decoder" +import Renderer from "./renderer" +import Decoder from "./decoder" +import { RingState } from "./ring" +// Abstracts the Worker and Worklet into a simpler API +// This class must be created on the main thread due to AudioContext. export default class Audio { - renderer: Renderer; - decoder: Decoder; + decoder: Decoder; // WebWorker + renderer: Renderer; // AudioWorklet - constructor(config: Message.Config) { + constructor() { + // Assume 44.1kHz and two audio channels + const config = { + sampleRate: 44100, + channels: [ new RingState(44100), new RingState(44100) ], + } + + // Start loading the worker script + this.decoder = new Decoder(config) this.renderer = new Renderer(config) - this.decoder = new Decoder(config, this.renderer) } - async init(init: Message.Init) { - await this.decoder.init(init) + init(init: Message.Init) { + this.decoder.init(init) } - async segment(segment: Message.Segment) { - await this.decoder.decode(segment) + segment(segment: Message.Segment) { + this.decoder.segment(segment) + } + + play() { + this.renderer.play() } } \ No newline at end of file diff --git a/player/src/audio/message.ts b/player/src/audio/message.ts index 39bab14..752e72c 100644 --- a/player/src/audio/message.ts +++ b/player/src/audio/message.ts @@ -1,17 +1,19 @@ import * as MP4 from "../mp4" +import { RingState } from "./ring" export interface Config { - ctx: AudioContext; + sampleRate: number; + channels: RingState[]; } export interface Init { track: string; info: MP4.Info; - raw: MP4.ArrayBufferOffset[]; + raw: MP4.ArrayBuffer[]; } export interface Segment { track: string; buffer: Uint8Array; // unread buffered data reader: ReadableStream; // unread unbuffered data -} +} \ No newline at end of file diff --git a/player/src/audio/renderer.ts b/player/src/audio/renderer.ts deleted file mode 100644 index a379685..0000000 --- a/player/src/audio/renderer.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as Message from "./message"; - -import Source from "./source"; - -export class Renderer { - source: Source; - - render: number; // non-zero if requestAnimationFrame has been called - last?: number; // the timestamp of the last rendered frame - - maxDuration: number; // the maximum duration allowed in the buffer - - constructor(config: Message.Config) { - this.render = 0; - this.maxDuration = 10 * 1000 - - // TODO evaluate { latencyHint: "interactive" } - this.source = new Source(config.ctx) - } - - emit(frame: AudioData) { - this.source.emit(frame) - } -} \ No newline at end of file diff --git a/player/src/audio/renderer/index.ts b/player/src/audio/renderer/index.ts new file mode 100644 index 0000000..38bb07e --- /dev/null +++ b/player/src/audio/renderer/index.ts @@ -0,0 +1,47 @@ +import * as Message from "../message" + +export default class Renderer { + context: AudioContext; + worklet: Promise; + + constructor(config: Message.Config) { + this.context = new AudioContext({ + latencyHint: "interactive", + sampleRate: config.sampleRate, + }) + + this.worklet = this.setup(config) + } + + private async setup(config: Message.Config): Promise { + // Load the worklet source code. + const url = new URL('worklet.ts', import.meta.url) + await this.context.audioWorklet.addModule(url) + + const volume = this.context.createGain() + volume.gain.value = 2.0; + + // Create a worklet + const worklet = new AudioWorkletNode(this.context, 'renderer'); + worklet.onprocessorerror = (e: Event) => { + console.error("Audio worklet error:", e) + }; + + worklet.port.onmessage = this.onMessage.bind(this) + worklet.port.postMessage({ config }) + + // Connect the worklet to the volume node and then to the speakers + worklet.connect(volume) + volume.connect(this.context.destination) + + return worklet + } + + private onMessage(e: MessageEvent) { + // TODO + } + + play() { + this.context.resume() + } +} \ No newline at end of file diff --git a/player/src/audio/renderer/worklet.ts b/player/src/audio/renderer/worklet.ts new file mode 100644 index 0000000..068645c --- /dev/null +++ b/player/src/audio/renderer/worklet.ts @@ -0,0 +1,69 @@ +// This is an AudioWorklet that acts as a media source. +// The renderer copies audio samples to a ring buffer read by this worklet. +// The worklet then outputs those samples to emit audio. + +import * as Message from "../message" + +import { Ring, RingState } from "../ring" + +class Renderer extends AudioWorkletProcessor { + channels?: Ring[]; + + constructor(params: AudioWorkletNodeOptions) { + // The super constructor call is required. + super(); + + this.port.onmessage = this.onMessage.bind(this) + } + + onMessage(e: MessageEvent) { + if (e.data.config) { + this.config(e.data.config) + } + } + + config(config: Message.Config) { + this.channels = [] + for (let state of config.channels) { + this.channels.push(new Ring(state)) + } + } + + // Inputs and outputs in groups of 128 samples. + process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: Record): boolean { + if (!this.channels) { + // Not initialized yet + return true + } + + if (inputs.length != 1 && outputs.length != 1) { + throw new Error("only a single track is supported") + } + + const output = outputs[0] + + for (let i = 0; i < output.length; i += 1) { + const source = this.channels[i] + const channel = output[i]; + + const parts = source.peek(channel.length) + + let offset = 0 + for (let i = 0; i < parts.length; i += 1) { + channel.set(parts[i], offset) + offset += parts[i].length + } + + if (offset < channel.length) { + // TODO render silence + } + + // Always advance the full amount. + source.advance(channel.length) + } + + return true; + } +} + +registerProcessor("renderer", Renderer); \ No newline at end of file diff --git a/player/src/audio/ring.ts b/player/src/audio/ring.ts new file mode 100644 index 0000000..c6a4a51 --- /dev/null +++ b/player/src/audio/ring.ts @@ -0,0 +1,142 @@ +// Ring buffer with audio samples. + +enum STATE { + START = 0, // First timestamp in the ring buffer. + END, // Last timestamp in the ring buffer. + LENGTH // Clever way of saving the total number of enums values. +} + +export class Ring { + state: RingState; + + constructor(state: RingState) { + this.state = state + } + + // Add the samples for single audio frame with the given channel + emit(timestamp: number, frame: AudioData, channel: number) { + let start = timestamp; + + // The number of samples to skip at the start. + let offset = this.start - timestamp; + if (offset > 0) { + console.warn("dropping old samples", offset) + start += offset; + } else { + offset = 0 + } + + let count = frame.numberOfFrames - offset; + if (count <= 0) { + frame.close() + + // Skip the entire frame + return + } + + let end = timestamp + count; + + if (end >= start + this.state.capacity) { + // The renderer has to buffer frames; we have a fixed capacity. + // TODO maybe it's better to buffer here instead. + throw new Error("exceeded capacity") + } + + const startIndex = start % this.state.capacity; + const endIndex = end % this.state.capacity; + + if (startIndex < endIndex) { + // One continuous range to copy. + const full = new Float32Array(this.state.buffer, 4*startIndex, endIndex-startIndex) + + frame.copyTo(full, { + planeIndex: channel, + frameOffset: offset, + frameCount: count, + }) + } else { + // Wrapped around the ring buffer, so we have to copy twice. + const wrap = this.state.capacity - startIndex; + + const first = new Float32Array(this.state.buffer, 4*startIndex, this.state.capacity - startIndex) + const second = new Float32Array(this.state.buffer, 0, endIndex) + + frame.copyTo(first, { + planeIndex: channel, + frameOffset: offset, + frameCount: wrap, + }) + + frame.copyTo(second, { + planeIndex: channel, + frameOffset: offset + wrap, + frameCount: endIndex, + }) + } + + if (this.end < end) { + this.end = end + } + } + + peek(count: number): Float32Array[] { + const start = this.start + + let end = this.end + if (end > start + count) { + end = start + count + } + + const startIndex = start % this.state.capacity; + const endIndex = end % this.state.capacity; + + if (startIndex < endIndex) { + const full = new Float32Array(this.state.buffer, 4*startIndex, endIndex - startIndex) + return [ full ] + } else { + const first = new Float32Array(this.state.buffer, 4*startIndex, this.state.capacity - startIndex) + const second = new Float32Array(this.state.buffer, 0, endIndex) + return [ first, second ] + } + } + + advance(count: number) { + this.start += count + } + + set start(start: number) { + Atomics.store(this.state.stateView, STATE.START, start); + } + + get start(): number { + return Atomics.load(this.state.stateView, STATE.START); + } + + set end(end: number) { + Atomics.store(this.state.stateView, STATE.END, end); + } + + get end(): number { + return Atomics.load(this.state.stateView, STATE.END); + } +} + +// No prototype to make this easier to send via postMessage +export class RingState { + state: SharedArrayBuffer; + stateView: Int32Array; + + buffer: SharedArrayBuffer; + + capacity: number; + + constructor(capacity: number) { + // Store this many samples in a ring buffer. + this.buffer = new SharedArrayBuffer(capacity * Float32Array.BYTES_PER_ELEMENT) + this.capacity = capacity + + // Store the current state in a separate ring buffer. + this.state = new SharedArrayBuffer(STATE.LENGTH * Int32Array.BYTES_PER_ELEMENT) + this.stateView = new Int32Array(this.state) + } +} \ No newline at end of file diff --git a/player/src/audio/source/index.ts b/player/src/audio/source/index.ts deleted file mode 100644 index 4f2ebd0..0000000 --- a/player/src/audio/source/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as Message from "./message" -import Ring from "./ring" - -// Wrapper around the AudioWorklet API to make it easier to use. -export default class Source { - ctx: AudioContext; - worklet?: AudioWorkletNode; // async initialization - channels: Ring[]; - - ready: Promise; - - constructor(ctx: AudioContext) { - this.ctx = ctx - - // two channels, holding a maximum of 1s at 44khz - this.channels = [ - new Ring(44000), - new Ring(44000), - ] - - // Start loading the worklet - this.ready = this.setup() - } - - private async setup(): Promise { - // Load the worklet source code. - await this.ctx.audioWorklet.addModule('worklet.ts') - - // Create a worklet - this.worklet = new AudioWorkletNode(this.ctx, 'source'); - - this.worklet.port.onmessage = this.onMessage.bind(this) - - this.worklet.onprocessorerror = (e: Event) => { - console.error("Audio worklet error:", e); - }; - - const config: Message.Config = { - channels: this.channels, - } - - this.worklet.port.postMessage({ config }) - } - - private async onMessage(e: MessageEvent) { - if (e.data.configReply) { - const reply = e.data.configReply as Message.ConfigReply - - if (reply.error) { - throw reply.error - } - - // Start playback - this.worklet?.connect(this.ctx.destination); - } - } - - emit(frame: AudioData) { - for (let i = 0; i < frame.numberOfChannels; i += 1) { - const ring = this.channels[i] - ring.set(frame, i) - } - } -} \ No newline at end of file diff --git a/player/src/audio/source/message.ts b/player/src/audio/source/message.ts deleted file mode 100644 index 9f21a71..0000000 --- a/player/src/audio/source/message.ts +++ /dev/null @@ -1,11 +0,0 @@ -import Ring from "./ring" - -// Sent to the worklet to share ring buffers. -export interface Config { - channels: Ring[]; -} - -// Reply from the worklet indicating when the configuration was suscessful. -export interface ConfigReply { - error?: Error; -} \ No newline at end of file diff --git a/player/src/audio/source/ring.ts b/player/src/audio/source/ring.ts deleted file mode 100644 index dedb1d0..0000000 --- a/player/src/audio/source/ring.ts +++ /dev/null @@ -1,131 +0,0 @@ -// Ring buffer with audio samples. - -// TODO typescript enums when I have internet access -const STATE = { - START: 0, - END: 1, -} - -export default class Ring { - state: SharedArrayBuffer; - stateView: Int32Array; - - buffer: SharedArrayBuffer; - capacity: number; - - constructor(samples: number) { - this.state = new SharedArrayBuffer(Object.keys(STATE).length * Int32Array.BYTES_PER_ELEMENT) - this.stateView = new Int32Array(this.state) - - this.setStart(0) - this.setEnd(0) - - this.capacity = samples; - - // TODO better way to loop in modern Javascript? - this.buffer = new SharedArrayBuffer(samples * Float32Array.BYTES_PER_ELEMENT) - } - - setStart(start: number) { - return Atomics.store(this.stateView, STATE.START, start); - } - - getStart(): number { - return Atomics.load(this.stateView, STATE.START); - } - - setEnd(end: number) { - return Atomics.store(this.stateView, STATE.START, end); - } - - getEnd(): number { - return Atomics.load(this.stateView, STATE.END); - } - - set(frame: AudioFrame, channel: number) { - let start = this.getStart() - - // The number of samples to skip at the start. - let offset = start - frame.timestamp; - if (offset > 0) { - console.warn("dropping old samples", offset) - } else { - offset = 0 - } - - let count = frame.numberOfFrames - offset; - if (count <= 0) { - frame.close() - - // Skip the entire frame - return - } - - if (start + this.capacity < frame.timestamp + count) { - // The renderer has to buffer frames; we have a fixed capacity. - // TODO maybe it's better to buffer here instead. - throw new Error("exceeded capacity") - } - - let end = this.getEnd() - - const startIndex = start % this.capacity; - const endIndex = end % this.capacity; - - if (startIndex < endIndex) { - // One continuous range to copy. - const full = new Float32Array(this.buffer, startIndex, endIndex-startIndex) - - frame.copyTo(full, { - planeIndex: channel, - frameOffset: offset, - frameCount: count, - }) - } else { - // Wrapped around the ring buffer, so we have to copy twice. - const wrap = this.capacity - startIndex; - - const first = new Float32Array(this.buffer, startIndex) - const second = new Float32Array(this.buffer, 0, endIndex) - - frame.copyTo(first, { - planeIndex: channel, - frameOffset: offset, - frameCount: wrap, - }) - - frame.copyTo(second, { - planeIndex: channel, - frameOffset: offset + wrap, - frameCount: endIndex, - }) - } - - // TODO insert silence when index > end - if (frame.timestamp + count > end) { - end = frame.timestamp + count - this.setEnd(end) - } - } - - peek(count: number): Float32Array[] { - const start = this.getStart() - const end = this.getEnd() - - const startIndex = start % this.capacity; - const endIndex = end % this.capacity; - - if (startIndex < endIndex) { - const full = new Float32Array(this.buffer, startIndex, endIndex - startIndex) - return [ full ] - } else { - const first = new Float32Array(this.buffer, startIndex) - const second = new Float32Array(this.buffer, 0, endIndex) - return [ first, second ] - } - } - - advance(count: number) { - this.setStart(this.getStart() + count) - } -} \ No newline at end of file diff --git a/player/src/audio/source/worklet.ts b/player/src/audio/source/worklet.ts deleted file mode 100644 index 8ade137..0000000 --- a/player/src/audio/source/worklet.ts +++ /dev/null @@ -1,68 +0,0 @@ -// This is an AudioWorklet that acts as a media source. -// The renderer copies audio samples to a ring buffer read by this worklet. -// The worklet then outputs those samples to emit audio. - -import * as Message from "../message" -import * as Util from "../../util" - -import Ring from "./ring" - -class Source extends AudioWorkletProcessor { - channels?: Ring[]; - - constructor() { - // The super constructor call is required. - super(); - - this.port.onmessage = (e: MessageEvent) => { - if (e.data.config) { - this.config(e.data.config as Message.Config) - } - } - } - - static get parameterDescriptors() { - return []; - } - - config(config: Message.Config) { - this.channels = config.channels; - } - - // TODO correct types - process(inputs: any, outputs: any, parameters: any) { - if (!this.channels) { - return - } - - if (outputs.length != 1) { - throw new Error("only a single track is supported") - } - - const track = outputs[0]; - - for (let i = 0; i < track.length; i += 1) { - const input = this.channels[i] - const output = track[i]; - - const parts = input.peek(output.length) - - let offset = 0 - for (let i = 0; i < parts.length; i += 1) { - output.set(parts[i], offset) - offset += parts[i].length - } - - if (offset < output.length) { - // TODO render silence - } - - // Always advance the full amount. - input.advance(output.length) - } - - return true; - } -} - -self.registerProcessor("source", Source); \ No newline at end of file diff --git a/player/src/audio/worker.ts b/player/src/audio/worker.ts deleted file mode 100644 index bd5ec76..0000000 --- a/player/src/audio/worker.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Renderer } from "./renderer" -import { Decoder } from "./decoder" -import * as Message from "./message" - -let renderer: Renderer; -let decoder: Decoder; - -self.addEventListener('message', async (e: MessageEvent) => { - if (e.data.config) { - const config = e.data.config as Message.Config; - - renderer = new Renderer(config) - decoder = new Decoder(config, renderer) - } - - if (e.data.init) { - const init = e.data.init as Message.Init - await decoder.init(init) - } - - if (e.data.segment) { - const segment = e.data.segment as Message.Segment - await decoder.decode(segment) - } -}) diff --git a/player/src/index.css b/player/src/index.css index c406cf4..cc3d895 100644 --- a/player/src/index.css +++ b/player/src/index.css @@ -16,7 +16,7 @@ body { position: relative; } -#play { +#screen #play { position: absolute; width: 100%; height: 100%; diff --git a/player/src/index.html b/player/src/index.html index 7ec7729..aeda194 100644 --- a/player/src/index.html +++ b/player/src/index.html @@ -11,6 +11,7 @@
+
click for audio
diff --git a/player/src/index.ts b/player/src/index.ts index 39e074a..dfa93a9 100644 --- a/player/src/index.ts +++ b/player/src/index.ts @@ -1,11 +1,23 @@ -import { Player } from "./transport/index" +import Player from "./player" const params = new URLSearchParams(window.location.search) const url = params.get("url") || "https://localhost:4443/watch" -const canvas = document.querySelector("canvas#video")!; +const canvas = document.querySelector("canvas#video")! const player = new Player({ url: url, canvas: canvas, -}) \ No newline at end of file +}) + +const play = document.querySelector("#screen #play")! + +let playFunc = (e: Event) => { + player.play() + e.preventDefault() + + play.removeEventListener('click', playFunc) + play.style.display = "none" +} + +play.addEventListener('click', playFunc) \ No newline at end of file diff --git a/player/src/player/index.ts b/player/src/player/index.ts new file mode 100644 index 0000000..2d44048 --- /dev/null +++ b/player/src/player/index.ts @@ -0,0 +1,40 @@ +import Audio from "../audio" +import Transport from "../transport" +import Video from "../video" + +export interface PlayerInit { + url: string; + canvas: HTMLCanvasElement; +} + +export default class Player { + audio: Audio; + video: Video; + transport: Transport; + + constructor(props: PlayerInit) { + this.audio = new Audio() + this.video = new Video({ + canvas: props.canvas.transferControlToOffscreen(), + }) + + this.transport = new Transport({ + url: props.url, + audio: this.audio, + video: this.video, + }) + } + + async close() { + this.transport.close() + } + + async connect(url: string) { + await this.transport.connect(url) + } + + play() { + this.audio.play() + this.video.play() + } +} \ No newline at end of file diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index 32330ee..138688d 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -8,12 +8,13 @@ import Video from "../video" // @ts-ignore bundler embeds data import fingerprint from 'bundle-text:./fingerprint.hex'; -export interface PlayerInit { +export interface TransportInit { url: string; - canvas: HTMLCanvasElement; + audio: Audio; + video: Video; } -export class Player { +export default class Transport { quic: Promise; api: Promise; tracks: Map @@ -21,16 +22,11 @@ export class Player { audio: Audio; video: Video; - constructor(props: PlayerInit) { + constructor(props: TransportInit) { this.tracks = new Map(); - // TODO move these to another class so this only deals with the transport. - this.audio = new Audio({ - ctx: new AudioContext(), - }) - this.video = new Video({ - canvas: props.canvas.transferControlToOffscreen(), - }) + this.audio = props.audio; + this.video = props.video; this.quic = this.connect(props.url) diff --git a/player/src/video/decoder.ts b/player/src/video/decoder.ts index 276e2ff..376f99a 100644 --- a/player/src/video/decoder.ts +++ b/player/src/video/decoder.ts @@ -3,9 +3,9 @@ import * as MP4 from "../mp4" import * as Stream from "../stream" import * as Util from "../util" -import { Renderer } from "./renderer" +import Renderer from "./renderer" -export class Decoder { +export default class Decoder { // Store the init message for each track tracks: Map> renderer: Renderer; @@ -116,7 +116,7 @@ export class Decoder { box.set(atom) // and for some reason we need to modify the underlying ArrayBuffer with offset - let buffer = box.buffer as MP4.ArrayBufferOffset + let buffer = box.buffer as MP4.ArrayBuffer buffer.fileStart = offset // Parse the data diff --git a/player/src/video/index.ts b/player/src/video/index.ts index d314ccd..f447072 100644 --- a/player/src/video/index.ts +++ b/player/src/video/index.ts @@ -5,7 +5,11 @@ export default class Video { worker: Worker; constructor(config: Message.Config) { - this.worker = new Worker(new URL('worker.ts', import.meta.url), { type: "module" }) + const url = new URL('worker.ts', import.meta.url) + this.worker = new Worker(url, { + type: "module", + name: "video", + }) this.worker.postMessage({ config }, [ config.canvas ]) } @@ -16,4 +20,8 @@ export default class Video { segment(segment: Message.Segment) { this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ]) } + + play() { + // TODO + } } \ No newline at end of file diff --git a/player/src/video/message.ts b/player/src/video/message.ts index c9f7a01..61e4b6d 100644 --- a/player/src/video/message.ts +++ b/player/src/video/message.ts @@ -7,7 +7,7 @@ export interface Config { export interface Init { track: string; info: MP4.Info; - raw: MP4.ArrayBufferOffset[]; + raw: MP4.ArrayBuffer[]; } export interface Segment { diff --git a/player/src/video/renderer.ts b/player/src/video/renderer.ts index e863746..71e6a59 100644 --- a/player/src/video/renderer.ts +++ b/player/src/video/renderer.ts @@ -1,6 +1,6 @@ import * as Message from "./message"; -export class Renderer { +export default class Renderer { canvas: OffscreenCanvas; queue: Array; render: number; // non-zero if requestAnimationFrame has been called @@ -34,7 +34,7 @@ export class Renderer { let high = this.queue.length; // Fast path because we normally append to the end. - if (this.queue.length > 0 && this.queue[this.queue.length].timestamp <= frame.timestamp) { + if (this.queue.length > 0 && this.queue[this.queue.length-1].timestamp <= frame.timestamp) { this.queue.push(frame) } else { // Do a full binary search diff --git a/player/src/video/worker.ts b/player/src/video/worker.ts index 2ee48e6..8eeede8 100644 --- a/player/src/video/worker.ts +++ b/player/src/video/worker.ts @@ -1,5 +1,5 @@ -import { Renderer } from "./renderer" -import { Decoder } from "./decoder" +import Renderer from "./renderer" +import Decoder from "./decoder" import * as Message from "./message" let decoder: Decoder; From 948d2ea1f1bfde6be4baf5a88402e3fc3bfc0243 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 6 Apr 2023 13:55:35 -0700 Subject: [PATCH 10/10] Good enough 5 me. --- player/src/audio/{decoder => }/decoder.ts | 75 +++----- player/src/audio/decoder/index.ts | 29 --- player/src/audio/index.ts | 64 +++++-- player/src/audio/message.ts | 15 +- player/src/audio/renderer.ts | 85 ++++++++ player/src/audio/renderer/index.ts | 47 ----- player/src/audio/ring.ts | 213 +++++++++++---------- player/src/audio/{decoder => }/worker.ts | 11 +- player/src/audio/{renderer => }/worklet.ts | 36 +--- player/src/player/index.ts | 10 +- player/src/video/renderer.ts | 32 +--- 11 files changed, 315 insertions(+), 302 deletions(-) rename player/src/audio/{decoder => }/decoder.ts (59%) delete mode 100644 player/src/audio/decoder/index.ts create mode 100644 player/src/audio/renderer.ts delete mode 100644 player/src/audio/renderer/index.ts rename player/src/audio/{decoder => }/worker.ts (51%) rename player/src/audio/{renderer => }/worklet.ts (53%) diff --git a/player/src/audio/decoder/decoder.ts b/player/src/audio/decoder.ts similarity index 59% rename from player/src/audio/decoder/decoder.ts rename to player/src/audio/decoder.ts index 47d7fb3..d23bdc3 100644 --- a/player/src/audio/decoder/decoder.ts +++ b/player/src/audio/decoder.ts @@ -1,28 +1,23 @@ -import * as Message from "../message"; -import * as MP4 from "../../mp4" -import * as Stream from "../../stream" -import * as Util from "../../util" -import { Ring, RingState } from "../ring" +import * as Message from "./message"; +import * as MP4 from "../mp4" +import * as Stream from "../stream" +import * as Util from "../util" - // Ignore the timestamp output by WebCodecs since it's in microseconds - // We will manually set the timestamp based on the sample rate. - let frameCount = 0 +import Renderer from "./renderer" export default class Decoder { // Store the init message for each track tracks: Map>; - sampleRate: number; - channels: Ring[]; - sync?: number; // the first timestamp + decoder: AudioDecoder; // TODO one per track + sync: Message.Sync; - constructor(config: Message.Config) { + constructor(config: Message.Config, renderer: Renderer) { this.tracks = new Map(); - this.sampleRate = config.sampleRate - this.channels = [] - for (let state of config.channels) { - this.channels.push(new Ring(state)) - } + this.decoder = new AudioDecoder({ + output: renderer.emit.bind(renderer), + error: console.warn, + }); } init(msg: Message.Init) { @@ -39,14 +34,6 @@ export default class Decoder { const track = msg.info.audioTracks[0] const audio = track.audio - if (audio.sample_rate != this.sampleRate) { - throw new Error("sample rate not supported") - } - - if (audio.channel_count > this.channels.length) { - throw new Error("channel count not supported") - } - defer.resolve(msg) } @@ -61,40 +48,24 @@ export default class Decoder { const init = await track.promise; const audio = init.info.audioTracks[0] - const decoder = new AudioDecoder({ - output: (frame: AudioData) => { - for (let i = 0; i < frame.numberOfChannels; i += 1) { - this.channels[i].emit(frameCount, frame, i) - } - - frameCount += frame.numberOfFrames; - }, - error: (err: Error) => { - console.warn(err) - } - }); - - decoder.configure({ - codec: audio.codec, - numberOfChannels: audio.audio.channel_count, - sampleRate: audio.audio.sample_rate, - // optimizeForLatency: true - }) + if (this.decoder.state == "unconfigured") { + this.decoder.configure({ + codec: audio.codec, + numberOfChannels: audio.audio.channel_count, + sampleRate: audio.audio.sample_rate, + }) + } const input = MP4.New(); input.onSamples = (id: number, user: any, samples: MP4.Sample[]) => { for (let sample of samples) { - if (!this.sync) { - this.sync = sample.dts; - } - - // Convert to milliseconds - const timestamp = 1000 * (sample.dts - this.sync) / sample.timescale - const duration = 1000 * sample.duration / sample.timescale + // Convert to microseconds + const timestamp = 1000 * 1000 * sample.dts / sample.timescale + const duration = 1000 * 1000 * sample.duration / sample.timescale // This assumes that timescale == sample rate - decoder.decode(new EncodedAudioChunk({ + this.decoder.decode(new EncodedAudioChunk({ type: sample.is_sync ? "key" : "delta", data: sample.data, duration: duration, diff --git a/player/src/audio/decoder/index.ts b/player/src/audio/decoder/index.ts deleted file mode 100644 index d181e43..0000000 --- a/player/src/audio/decoder/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as Message from "../message" - -// Wrapper to run the decoder in a Worker -export default class Decoder { - worker: Worker; - - constructor(config: Message.Config) { - const url = new URL('worker.ts', import.meta.url) - this.worker = new Worker(url, { - name: "audio", - type: "module", - }) - - this.worker.onmessage = this.onMessage.bind(this) - this.worker.postMessage({ config }) - } - - init(init: Message.Init) { - this.worker.postMessage({ init }) - } - - segment(segment: Message.Segment) { - this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ]) - } - - private onMessage(e: MessageEvent) { - // TODO - } -} diff --git a/player/src/audio/index.ts b/player/src/audio/index.ts index 3847d3f..725bc11 100644 --- a/player/src/audio/index.ts +++ b/player/src/audio/index.ts @@ -1,35 +1,77 @@ import * as Message from "./message" import Renderer from "./renderer" import Decoder from "./decoder" -import { RingState } from "./ring" + +import { RingInit } from "./ring" // Abstracts the Worker and Worklet into a simpler API // This class must be created on the main thread due to AudioContext. export default class Audio { - decoder: Decoder; // WebWorker - renderer: Renderer; // AudioWorklet + context: AudioContext; + worker: Worker; + worklet: Promise; constructor() { // Assume 44.1kHz and two audio channels const config = { sampleRate: 44100, - channels: [ new RingState(44100), new RingState(44100) ], + ring: new RingInit(2, 4410), // 100ms at 44.1khz } - // Start loading the worker script - this.decoder = new Decoder(config) - this.renderer = new Renderer(config) + this.context = new AudioContext({ + latencyHint: "interactive", + sampleRate: config.sampleRate, + }) + + this.worker = this.setupWorker(config) + this.worklet = this.setupWorklet(config) + } + + private setupWorker(config: Message.Config): Worker { + const url = new URL('worker.ts', import.meta.url) + const worker = new Worker(url, { + name: "audio", + type: "module", + }) + + worker.postMessage({ config }) + + return worker + } + + private async setupWorklet(config: Message.Config): Promise { + // Load the worklet source code. + const url = new URL('worklet.ts', import.meta.url) + await this.context.audioWorklet.addModule(url) + + const volume = this.context.createGain() + volume.gain.value = 2.0; + + // Create a worklet + const worklet = new AudioWorkletNode(this.context, 'renderer'); + worklet.onprocessorerror = (e: Event) => { + console.error("Audio worklet error:", e) + }; + + worklet.port.postMessage({ config }) + + // Connect the worklet to the volume node and then to the speakers + worklet.connect(volume) + volume.connect(this.context.destination) + + return worklet } init(init: Message.Init) { - this.decoder.init(init) + this.worker.postMessage({ init }) } segment(segment: Message.Segment) { - this.decoder.segment(segment) + this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ]) } - play() { - this.renderer.play() + play(play: Message.Play) { + this.context.resume() + //this.worker.postMessage({ play }) } } \ No newline at end of file diff --git a/player/src/audio/message.ts b/player/src/audio/message.ts index 752e72c..73d4f1c 100644 --- a/player/src/audio/message.ts +++ b/player/src/audio/message.ts @@ -1,9 +1,9 @@ import * as MP4 from "../mp4" -import { RingState } from "./ring" +import { RingInit } from "./ring" export interface Config { sampleRate: number; - channels: RingState[]; + ring: RingInit; } export interface Init { @@ -16,4 +16,15 @@ export interface Segment { track: string; buffer: Uint8Array; // unread buffered data reader: ReadableStream; // unread unbuffered data +} + +// Audio tells video when the given timestamp should be rendered. +export interface Sync { + origin: number; + clock: DOMHighResTimeStamp; + timestamp: number; +} + +export interface Play { + timestamp?: number; } \ No newline at end of file diff --git a/player/src/audio/renderer.ts b/player/src/audio/renderer.ts new file mode 100644 index 0000000..7c5ec9e --- /dev/null +++ b/player/src/audio/renderer.ts @@ -0,0 +1,85 @@ +import * as Message from "./message" +import { Ring } from "./ring" + +export default class Renderer { + ring: Ring; + queue: Array; + sync?: DOMHighResTimeStamp + running: number; + + constructor(config: Message.Config) { + this.ring = new Ring(config.ring) + this.queue = []; + this.running = 0 + } + + emit(frame: AudioData) { + if (!this.sync) { + // Save the frame as the sync point + this.sync = 1000 * performance.now() - frame.timestamp + } + + // Insert the frame into the queue sorted by timestamp. + if (this.queue.length > 0 && this.queue[this.queue.length-1].timestamp <= frame.timestamp) { + // Fast path because we normally append to the end. + this.queue.push(frame) + } else { + // Do a full binary search + let low = 0 + let high = this.queue.length; + + while (low < high) { + var mid = (low + high) >>> 1; + if (this.queue[mid].timestamp < frame.timestamp) low = mid + 1; + else high = mid; + } + + this.queue.splice(low, 0, frame) + } + + if (!this.running) { + // Wait for the next animation frame + this.running = self.requestAnimationFrame(this.render.bind(this)) + } + } + + render() { + // Determine the target timestamp. + const target = 1000 * performance.now() - this.sync! + + // Check if we should skip some frames + while (this.queue.length) { + const next = this.queue[0] + if (next.timestamp >= target) { + break + } + + console.warn("dropping audio") + + this.queue.shift() + next.close() + } + + // Push as many as we can to the ring buffer. + while (this.queue.length) { + let frame = this.queue[0] + let ok = this.ring.write(frame) + if (!ok) { + break + } + + frame.close() + this.queue.shift() + } + + if (this.queue.length) { + this.running = self.requestAnimationFrame(this.render.bind(this)) + } else { + this.running = 0 + } + } + + play(play: Message.Play) { + this.ring.reset() + } +} \ No newline at end of file diff --git a/player/src/audio/renderer/index.ts b/player/src/audio/renderer/index.ts deleted file mode 100644 index 38bb07e..0000000 --- a/player/src/audio/renderer/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as Message from "../message" - -export default class Renderer { - context: AudioContext; - worklet: Promise; - - constructor(config: Message.Config) { - this.context = new AudioContext({ - latencyHint: "interactive", - sampleRate: config.sampleRate, - }) - - this.worklet = this.setup(config) - } - - private async setup(config: Message.Config): Promise { - // Load the worklet source code. - const url = new URL('worklet.ts', import.meta.url) - await this.context.audioWorklet.addModule(url) - - const volume = this.context.createGain() - volume.gain.value = 2.0; - - // Create a worklet - const worklet = new AudioWorkletNode(this.context, 'renderer'); - worklet.onprocessorerror = (e: Event) => { - console.error("Audio worklet error:", e) - }; - - worklet.port.onmessage = this.onMessage.bind(this) - worklet.port.postMessage({ config }) - - // Connect the worklet to the volume node and then to the speakers - worklet.connect(volume) - volume.connect(this.context.destination) - - return worklet - } - - private onMessage(e: MessageEvent) { - // TODO - } - - play() { - this.context.resume() - } -} \ No newline at end of file diff --git a/player/src/audio/ring.ts b/player/src/audio/ring.ts index c6a4a51..cd796ee 100644 --- a/player/src/audio/ring.ts +++ b/player/src/audio/ring.ts @@ -1,142 +1,143 @@ // Ring buffer with audio samples. enum STATE { - START = 0, // First timestamp in the ring buffer. - END, // Last timestamp in the ring buffer. - LENGTH // Clever way of saving the total number of enums values. + READ_INDEX = 0, // Index of the current read position (mod capacity) + WRITE_INDEX, // Index of the current write position (mod capacity) + LENGTH // Clever way of saving the total number of enums values. } export class Ring { - state: RingState; + state: Int32Array; + channels: Float32Array[]; + capacity: number; - constructor(state: RingState) { - this.state = state - } + constructor(init: RingInit) { + this.state = new Int32Array(init.state) - // Add the samples for single audio frame with the given channel - emit(timestamp: number, frame: AudioData, channel: number) { - let start = timestamp; - - // The number of samples to skip at the start. - let offset = this.start - timestamp; - if (offset > 0) { - console.warn("dropping old samples", offset) - start += offset; - } else { - offset = 0 + this.channels = [] + for (let channel of init.channels) { + this.channels.push(new Float32Array(channel)) } - let count = frame.numberOfFrames - offset; - if (count <= 0) { - frame.close() + this.capacity = init.capacity + } - // Skip the entire frame + // Add the samples for single audio frame + write(frame: AudioData): boolean { + let count = frame.numberOfFrames; + + let readIndex = Atomics.load(this.state, STATE.READ_INDEX) + let writeIndex = Atomics.load(this.state, STATE.WRITE_INDEX) + let writeIndexNew = writeIndex + count; + + // There's not enough space in the ring buffer + if (writeIndexNew - readIndex > this.capacity) { + return false + } + + let startIndex = writeIndex % this.capacity; + let endIndex = writeIndexNew % this.capacity; + + // Loop over each channel + for (let i = 0; i < this.channels.length; i += 1) { + const channel = this.channels[i] + + if (startIndex < endIndex) { + // One continuous range to copy. + const full = channel.subarray(startIndex, endIndex) + + frame.copyTo(full, { + planeIndex: i, + frameCount: count, + }) + } else { + const first = channel.subarray(startIndex) + const second = channel.subarray(0, endIndex) + + frame.copyTo(first, { + planeIndex: i, + frameCount: first.length, + }) + + frame.copyTo(second, { + planeIndex: i, + frameOffset: first.length, + frameCount: second.length, + }) + } + } + + Atomics.store(this.state, STATE.WRITE_INDEX, writeIndexNew) + + return true + } + + read(dst: Float32Array[]) { + let readIndex = Atomics.load(this.state, STATE.READ_INDEX) + let writeIndex = Atomics.load(this.state, STATE.WRITE_INDEX) + if (readIndex >= writeIndex) { + // nothing to read return } - let end = timestamp + count; - - if (end >= start + this.state.capacity) { - // The renderer has to buffer frames; we have a fixed capacity. - // TODO maybe it's better to buffer here instead. - throw new Error("exceeded capacity") + let readIndexNew = readIndex + dst[0].length + if (readIndexNew > writeIndex) { + // Partial read + readIndexNew = writeIndex } - const startIndex = start % this.state.capacity; - const endIndex = end % this.state.capacity; + let startIndex = readIndex % this.capacity; + let endIndex = readIndexNew % this.capacity; - if (startIndex < endIndex) { - // One continuous range to copy. - const full = new Float32Array(this.state.buffer, 4*startIndex, endIndex-startIndex) + // Loop over each channel + for (let i = 0; i < dst.length; i += 1) { + if (i >= this.channels.length) { + // ignore excess channels + } - frame.copyTo(full, { - planeIndex: channel, - frameOffset: offset, - frameCount: count, - }) - } else { - // Wrapped around the ring buffer, so we have to copy twice. - const wrap = this.state.capacity - startIndex; + const input = this.channels[i] + const output = dst[i] - const first = new Float32Array(this.state.buffer, 4*startIndex, this.state.capacity - startIndex) - const second = new Float32Array(this.state.buffer, 0, endIndex) + if (startIndex < endIndex) { + const full = input.subarray(startIndex, endIndex) + output.set(full) + } else { + const first = input.subarray(startIndex) + const second = input.subarray(0, endIndex) - frame.copyTo(first, { - planeIndex: channel, - frameOffset: offset, - frameCount: wrap, - }) - - frame.copyTo(second, { - planeIndex: channel, - frameOffset: offset + wrap, - frameCount: endIndex, - }) + output.set(first) + output.set(second, first.length) + } } - if (this.end < end) { - this.end = end - } + Atomics.store(this.state, STATE.READ_INDEX, readIndexNew) } - peek(count: number): Float32Array[] { - const start = this.start - - let end = this.end - if (end > start + count) { - end = start + count - } - - const startIndex = start % this.state.capacity; - const endIndex = end % this.state.capacity; - - if (startIndex < endIndex) { - const full = new Float32Array(this.state.buffer, 4*startIndex, endIndex - startIndex) - return [ full ] - } else { - const first = new Float32Array(this.state.buffer, 4*startIndex, this.state.capacity - startIndex) - const second = new Float32Array(this.state.buffer, 0, endIndex) - return [ first, second ] - } - } - - advance(count: number) { - this.start += count - } - - set start(start: number) { - Atomics.store(this.state.stateView, STATE.START, start); - } - - get start(): number { - return Atomics.load(this.state.stateView, STATE.START); - } - - set end(end: number) { - Atomics.store(this.state.stateView, STATE.END, end); - } - - get end(): number { - return Atomics.load(this.state.stateView, STATE.END); + // TODO not thread safe + clear() { + const writeIndex = Atomics.load(this.state, STATE.WRITE_INDEX) + Atomics.store(this.state, STATE.READ_INDEX, writeIndex) } } // No prototype to make this easier to send via postMessage -export class RingState { +export class RingInit { state: SharedArrayBuffer; - stateView: Int32Array; - - buffer: SharedArrayBuffer; + channels: SharedArrayBuffer[]; capacity: number; - constructor(capacity: number) { - // Store this many samples in a ring buffer. - this.buffer = new SharedArrayBuffer(capacity * Float32Array.BYTES_PER_ELEMENT) - this.capacity = capacity - + constructor(channels: number, capacity: number) { // Store the current state in a separate ring buffer. this.state = new SharedArrayBuffer(STATE.LENGTH * Int32Array.BYTES_PER_ELEMENT) - this.stateView = new Int32Array(this.state) + + // Create a buffer for each audio channel + this.channels = [] + for (let i = 0; i < channels; i += 1) { + const buffer = new SharedArrayBuffer(capacity * Float32Array.BYTES_PER_ELEMENT) + this.channels.push(buffer) + } + + this.capacity = capacity } } \ No newline at end of file diff --git a/player/src/audio/decoder/worker.ts b/player/src/audio/worker.ts similarity index 51% rename from player/src/audio/decoder/worker.ts rename to player/src/audio/worker.ts index 3914e14..7ed9003 100644 --- a/player/src/audio/decoder/worker.ts +++ b/player/src/audio/worker.ts @@ -1,10 +1,15 @@ import Decoder from "./decoder" +import Renderer from "./renderer" + +import * as Message from "./message" let decoder: Decoder +let renderer: Renderer; self.addEventListener('message', (e: MessageEvent) => { if (e.data.config) { - decoder = new Decoder(e.data.config) + renderer = new Renderer(e.data.config) + decoder = new Decoder(e.data.config, renderer) } if (e.data.init) { @@ -14,4 +19,8 @@ self.addEventListener('message', (e: MessageEvent) => { if (e.data.segment) { decoder.decode(e.data.segment) } + + if (e.data.play) { + renderer.play(e.data.play) + } }) \ No newline at end of file diff --git a/player/src/audio/renderer/worklet.ts b/player/src/audio/worklet.ts similarity index 53% rename from player/src/audio/renderer/worklet.ts rename to player/src/audio/worklet.ts index 068645c..401eec9 100644 --- a/player/src/audio/renderer/worklet.ts +++ b/player/src/audio/worklet.ts @@ -2,17 +2,19 @@ // The renderer copies audio samples to a ring buffer read by this worklet. // The worklet then outputs those samples to emit audio. -import * as Message from "../message" +import * as Message from "./message" -import { Ring, RingState } from "../ring" +import { Ring } from "./ring" class Renderer extends AudioWorkletProcessor { - channels?: Ring[]; + ring?: Ring; + base: number; constructor(params: AudioWorkletNodeOptions) { // The super constructor call is required. super(); + this.base = 0 this.port.onmessage = this.onMessage.bind(this) } @@ -23,15 +25,12 @@ class Renderer extends AudioWorkletProcessor { } config(config: Message.Config) { - this.channels = [] - for (let state of config.channels) { - this.channels.push(new Ring(state)) - } + this.ring = new Ring(config.ring) } // Inputs and outputs in groups of 128 samples. process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: Record): boolean { - if (!this.channels) { + if (!this.ring) { // Not initialized yet return true } @@ -41,26 +40,7 @@ class Renderer extends AudioWorkletProcessor { } const output = outputs[0] - - for (let i = 0; i < output.length; i += 1) { - const source = this.channels[i] - const channel = output[i]; - - const parts = source.peek(channel.length) - - let offset = 0 - for (let i = 0; i < parts.length; i += 1) { - channel.set(parts[i], offset) - offset += parts[i].length - } - - if (offset < channel.length) { - // TODO render silence - } - - // Always advance the full amount. - source.advance(channel.length) - } + this.ring.read(output) return true; } diff --git a/player/src/player/index.ts b/player/src/player/index.ts index 2d44048..00438b2 100644 --- a/player/src/player/index.ts +++ b/player/src/player/index.ts @@ -34,7 +34,13 @@ export default class Player { } play() { - this.audio.play() - this.video.play() + this.audio.play({}) + //this.video.play() } + + onMessage(msg: any) { + if (msg.sync) { + msg.sync + } + } } \ No newline at end of file diff --git a/player/src/video/renderer.ts b/player/src/video/renderer.ts index 71e6a59..98fd3b6 100644 --- a/player/src/video/renderer.ts +++ b/player/src/video/renderer.ts @@ -4,17 +4,13 @@ export default class Renderer { canvas: OffscreenCanvas; queue: Array; render: number; // non-zero if requestAnimationFrame has been called - sync: DOMHighResTimeStamp; // the wall clock value for timestamp 0 + sync?: DOMHighResTimeStamp; // the wall clock value for timestamp 0 last?: number; // the timestamp of the last rendered frame - maxDuration: number; // the maximum duration allowed in the buffer - constructor(config: Message.Config) { this.canvas = config.canvas; this.queue = []; this.render = 0; - this.sync = 0; - this.maxDuration = 10 * 1000 } emit(frame: VideoFrame) { @@ -30,14 +26,14 @@ export default class Renderer { } // Insert the frame into the queue sorted by timestamp. - let low = 0 - let high = this.queue.length; - - // Fast path because we normally append to the end. if (this.queue.length > 0 && this.queue[this.queue.length-1].timestamp <= frame.timestamp) { + // Fast path because we normally append to the end. this.queue.push(frame) } else { // Do a full binary search + let low = 0 + let high = this.queue.length; + while (low < high) { var mid = (low + high) >>> 1; if (this.queue[mid].timestamp < frame.timestamp) low = mid + 1; @@ -47,18 +43,6 @@ export default class Renderer { this.queue.splice(low, 0, frame) } - // Trim the max size of the buffer - const last = this.queue[this.queue.length-1].timestamp - while (1) { - const first = this.queue[0] - if (first.timestamp + this.maxDuration >= last) { - break - } - - first.close() - this.queue.shift() - } - // Queue up to render the next frame. if (!this.render) { this.render = self.requestAnimationFrame(this.draw.bind(this)) @@ -67,10 +51,10 @@ export default class Renderer { draw(now: DOMHighResTimeStamp) { // Determine the target timestamp. - const target = now - this.sync + const target = now - this.sync! let frame = this.queue[0] - if (frame.timestamp > target) { + if (frame.timestamp >= target) { // nothing to render yet, wait for the next animation frame this.render = self.requestAnimationFrame(this.draw.bind(this)) return @@ -92,7 +76,7 @@ export default class Renderer { } const ctx = this.canvas.getContext("2d"); - ctx?.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height) // TODO aspect ratio + ctx!.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height) // TODO aspect ratio this.last = frame.timestamp; frame.close()