Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

webm-muxer

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webm-muxer - npm Package Compare versions

Comparing version 2.2.3 to 3.0.0

189

build/webm-muxer.d.ts
/**
* Describes the properties used to configure an instance of `WebMMuxer`.
* Describes the properties used to configure an instance of `Muxer`.
*/
declare interface WebMMuxerOptions {
declare interface MuxerOptions<T extends Target> {
/**
* Specifies where the muxed WebM file is written to.
*
* When using `'buffer'`, the muxed file is simply written to a buffer in memory, which is then returned by the
* muxer's `finalize` method.
*
* If the target is a function, it will be called each time data is output by the muxer - this is useful if you want
* to stream the data. The function will be called with three arguments: the data to write, the offset in bytes at
* which to write the data and a boolean indicating whether the muxer is done writing data. Note that the same
* segment of bytes might be written to multiple times and therefore you need to write the data in the same order
* the function gave it to you.
*
* If the target is of type `FileSystemWritableFileStream`, the file will be written directly to disk as it is being
* muxed. The benefit of this target is the ability to write out very large files, easily exceeding the RAM of the
* machine being used.
* Specifies what happens with the data created by the muxer.
*/
target: 'buffer' | ((data: Uint8Array, offset: number, done: boolean) => void) | FileSystemWritableFileStream,
target: T,

@@ -105,71 +92,113 @@ /**

declare global {
declare type Target = ArrayBufferTarget | StreamTarget | FileSystemWritableFileStreamTarget;
/** The file data will be written into a single large buffer, which is then stored in `buffer`. */
declare class ArrayBufferTarget {
buffer: ArrayBuffer;
}
/**
* This target defines callbacks that will get called whenever there is new data available - this is useful if
* you want to stream the data, e.g. pipe it somewhere else.
*
* When using `chunked: true` in the options, data created by the muxer will first be accumulated and only written out
* once it has reached sufficient size (~16 MB). This is useful for reducing the total amount of writes, at the cost of
* latency.
*/
declare class StreamTarget {
constructor(
onData: (data: Uint8Array, position: number) => void,
onDone?: () => void,
options?: { chunked?: true }
);
}
/**
* This is essentially a wrapper around `StreamTarget` with the intention of simplifying the use of this library with
* the File System Access API. Writing the file directly to disk as it's being created comes with many benefits, such as
* creating files way larger than the available RAM.
*/
declare class FileSystemWritableFileStreamTarget {
constructor(stream: FileSystemWritableFileStream);
}
/**
* Used to multiplex video and audio chunks into a single WebM file. For each WebM file you want to create, create
* one instance of `Muxer`.
*/
declare class Muxer<T extends Target> {
target: T;
/**
* Used to multiplex video and audio chunks into a single WebM file. For each WebM file you want to create, create
* one instance of `WebMMuxer`.
* Creates a new instance of `Muxer`.
* @param options Specifies configuration and metadata for the WebM file.
*/
class WebMMuxer {
/**
* Creates a new instance of `WebMMuxer`.
* @param options Specifies configuration and metadata for the WebM file.
*/
constructor(options: WebMMuxerOptions);
constructor(options: MuxerOptions<T>);
/**
* Adds a new, encoded video chunk to the WebM file.
* @param chunk The encoded video chunk. Can be obtained through a `VideoEncoder`.
* @param meta The metadata about the encoded video, also provided by `VideoEncoder`.
* @param timestamp Optionally, the timestamp to use for the video chunk. When not provided, it will use the one
* specified in `chunk`.
*/
addVideoChunk(chunk: EncodedVideoChunk, meta: EncodedVideoChunkMetadata, timestamp?: number): void;
/**
* Adds a new, encoded audio chunk to the WebM file.
* @param chunk The encoded audio chunk. Can be obtained through an `AudioEncoder`.
* @param meta The metadata about the encoded audio, also provided by `AudioEncoder`.
* @param timestamp Optionally, the timestamp to use for the audio chunk. When not provided, it will use the one
* specified in `chunk`.
*/
addAudioChunk(chunk: EncodedAudioChunk, meta: EncodedAudioChunkMetadata, timestamp?: number): void;
/**
* Adds a new, encoded video chunk to the WebM file.
* @param chunk The encoded video chunk. Can be obtained through a `VideoEncoder`.
* @param meta The metadata about the encoded video, also provided by `VideoEncoder`.
* @param timestamp Optionally, the timestamp to use for the video chunk. When not provided, it will use the one
* specified in `chunk`.
*/
addVideoChunk(chunk: EncodedVideoChunk, meta: EncodedVideoChunkMetadata, timestamp?: number): void;
/**
* Adds a new, encoded audio chunk to the WebM file.
* @param chunk The encoded audio chunk. Can be obtained through an `AudioEncoder`.
* @param meta The metadata about the encoded audio, also provided by `AudioEncoder`.
* @param timestamp Optionally, the timestamp to use for the audio chunk. When not provided, it will use the one
* specified in `chunk`.
*/
addAudioChunk(chunk: EncodedAudioChunk, meta: EncodedAudioChunkMetadata, timestamp?: number): void;
/**
* Adds a raw video chunk to the WebM file. This method should be used when the encoded video is not obtained
* through a `VideoEncoder` but through some other means, where no instance of `EncodedVideoChunk`is available.
* @param data The raw data of the video chunk.
* @param type Whether the video chunk is a keyframe or delta frame.
* @param timestamp The timestamp of the video chunk.
* @param meta Optionally, any encoder metadata.
*/
addVideoChunkRaw(
data: Uint8Array,
type: 'key' | 'delta',
timestamp: number,
meta?: EncodedVideoChunkMetadata
): void;
/**
* Adds a raw audio chunk to the WebM file. This method should be used when the encoded audio is not obtained
* through an `AudioEncoder` but through some other means, where no instance of `EncodedAudioChunk`is available.
* @param data The raw data of the audio chunk.
* @param type Whether the audio chunk is a keyframe or delta frame.
* @param timestamp The timestamp of the audio chunk.
* @param meta Optionally, any encoder metadata.
*/
addAudioChunkRaw(
data: Uint8Array,
type: 'key' | 'delta',
timestamp: number,
meta?: EncodedAudioChunkMetadata
): void;
/**
* Adds a raw video chunk to the WebM file. This method should be used when the encoded video is not obtained
* through a `VideoEncoder` but through some other means, where no instance of `EncodedVideoChunk`is available.
* @param data The raw data of the video chunk.
* @param type Whether the video chunk is a keyframe or delta frame.
* @param timestamp The timestamp of the video chunk.
* @param meta Optionally, any encoder metadata.
*/
addVideoChunkRaw(
data: Uint8Array,
type: 'key' | 'delta',
timestamp: number,
meta?: EncodedVideoChunkMetadata
): void;
/**
* Adds a raw audio chunk to the WebM file. This method should be used when the encoded audio is not obtained
* through an `AudioEncoder` but through some other means, where no instance of `EncodedAudioChunk`is available.
* @param data The raw data of the audio chunk.
* @param type Whether the audio chunk is a keyframe or delta frame.
* @param timestamp The timestamp of the audio chunk.
* @param meta Optionally, any encoder metadata.
*/
addAudioChunkRaw(
data: Uint8Array,
type: 'key' | 'delta',
timestamp: number,
meta?: EncodedAudioChunkMetadata
): void;
/**
* Is to be called after all media chunks have been added to the muxer. Make sure to call and await the `flush`
* method on your `VideoEncoder` and/or `AudioEncoder` before calling this method to ensure all encoding has
* finished. This method will then finish up the writing process of the WebM file.
* @returns Should you have used `target: 'buffer'` in the configuration options, this method will return the
* buffer containing the final WebM file.
*/
finalize(): ArrayBuffer | null;
}
/**
* Is to be called after all media chunks have been added to the muxer. Make sure to call and await the `flush`
* method on your `VideoEncoder` and/or `AudioEncoder` before calling this method to ensure all encoding has
* finished. This method will then finish up the writing process of the WebM file.
* @returns Should you have used `target: 'buffer'` in the configuration options, this method will return the
* buffer containing the final WebM file.
*/
finalize(): ArrayBuffer | null;
}
export = WebMMuxer;
declare namespace WebMMuxer {
export { Muxer, ArrayBufferTarget, StreamTarget, FileSystemWritableFileStreamTarget };
}
declare global {
let WebMMuxer: typeof WebMMuxer;
}
export { Muxer, ArrayBufferTarget, StreamTarget, FileSystemWritableFileStreamTarget };
export as namespace WebMMuxer;
export default WebMMuxer;

@@ -58,6 +58,9 @@ "use strict";

// src/main.ts
var main_exports = {};
__export(main_exports, {
default: () => main_default
// src/index.ts
var src_exports = {};
__export(src_exports, {
ArrayBufferTarget: () => ArrayBufferTarget,
FileSystemWritableFileStreamTarget: () => FileSystemWritableFileStreamTarget,
Muxer: () => Muxer,
StreamTarget: () => StreamTarget
});

@@ -109,5 +112,48 @@

// src/write_target.ts
// src/misc.ts
var readBits = (bytes, start, end) => {
let result = 0;
for (let i = start; i < end; i++) {
let byteIndex = Math.floor(i / 8);
let byte = bytes[byteIndex];
let bitIndex = 7 - (i & 7);
let bit = (byte & 1 << bitIndex) >> bitIndex;
result <<= 1;
result |= bit;
}
return result;
};
var writeBits = (bytes, start, end, value) => {
for (let i = start; i < end; i++) {
let byteIndex = Math.floor(i / 8);
let byte = bytes[byteIndex];
let bitIndex = 7 - (i & 7);
byte &= ~(1 << bitIndex);
byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex;
bytes[byteIndex] = byte;
}
};
// src/target.ts
var ArrayBufferTarget = class {
constructor() {
this.buffer = null;
}
};
var StreamTarget = class {
constructor(onData, onDone, options) {
this.onData = onData;
this.onDone = onDone;
this.options = options;
}
};
var FileSystemWritableFileStreamTarget = class {
constructor(stream) {
this.stream = stream;
}
};
// src/writer.ts
var _helper, _helperView, _writeByte, writeByte_fn, _writeFloat32, writeFloat32_fn, _writeFloat64, writeFloat64_fn, _writeUnsignedInt, writeUnsignedInt_fn, _writeString, writeString_fn;
var WriteTarget = class {
var Writer = class {
constructor() {

@@ -120,6 +166,6 @@ __privateAdd(this, _writeByte);

this.pos = 0;
__privateAdd(this, _helper, new Uint8Array(8));
__privateAdd(this, _helperView, new DataView(__privateGet(this, _helper).buffer));
this.offsets = /* @__PURE__ */ new WeakMap();
this.dataOffsets = /* @__PURE__ */ new WeakMap();
__privateAdd(this, _helper, new Uint8Array(8));
__privateAdd(this, _helperView, new DataView(__privateGet(this, _helper).buffer));
}

@@ -264,23 +310,14 @@ seek(newPos) {

};
var _buffer, _bytes;
var ArrayBufferWriteTarget = class extends WriteTarget {
constructor() {
var _target, _buffer, _bytes, _ensureSize, ensureSize_fn;
var ArrayBufferTargetWriter = class extends Writer {
constructor(target) {
super();
__privateAdd(this, _ensureSize);
__privateAdd(this, _target, void 0);
__privateAdd(this, _buffer, new ArrayBuffer(__pow(2, 16)));
__privateAdd(this, _bytes, new Uint8Array(__privateGet(this, _buffer)));
__privateSet(this, _target, target);
}
ensureSize(size) {
let newLength = __privateGet(this, _buffer).byteLength;
while (newLength < size)
newLength *= 2;
if (newLength === __privateGet(this, _buffer).byteLength)
return;
let newBuffer = new ArrayBuffer(newLength);
let newBytes = new Uint8Array(newBuffer);
newBytes.set(__privateGet(this, _bytes), 0);
__privateSet(this, _buffer, newBuffer);
__privateSet(this, _bytes, newBytes);
}
write(data) {
this.ensureSize(this.pos + data.byteLength);
__privateMethod(this, _ensureSize, ensureSize_fn).call(this, this.pos + data.byteLength);
__privateGet(this, _bytes).set(data, this.pos);

@@ -290,112 +327,31 @@ this.pos += data.byteLength;

finalize() {
this.ensureSize(this.pos);
return __privateGet(this, _buffer).slice(0, this.pos);
__privateMethod(this, _ensureSize, ensureSize_fn).call(this, this.pos);
__privateGet(this, _target).buffer = __privateGet(this, _buffer).slice(0, this.pos);
}
};
_target = new WeakMap();
_buffer = new WeakMap();
_bytes = new WeakMap();
var FILE_CHUNK_SIZE = __pow(2, 24);
var MAX_CHUNKS_AT_ONCE = 2;
var _stream, _chunks;
var FileSystemWritableFileStreamWriteTarget = class extends WriteTarget {
constructor(stream) {
super();
__privateAdd(this, _stream, void 0);
__privateAdd(this, _chunks, []);
__privateSet(this, _stream, stream);
}
write(data) {
this.writeDataIntoChunks(data, this.pos);
this.flushChunks();
this.pos += data.byteLength;
}
writeDataIntoChunks(data, position) {
let chunkIndex = __privateGet(this, _chunks).findIndex((x) => x.start <= position && position < x.start + FILE_CHUNK_SIZE);
if (chunkIndex === -1)
chunkIndex = this.createChunk(position);
let chunk = __privateGet(this, _chunks)[chunkIndex];
let relativePosition = position - chunk.start;
let toWrite = data.subarray(0, Math.min(FILE_CHUNK_SIZE - relativePosition, data.byteLength));
chunk.data.set(toWrite, relativePosition);
let section = {
start: relativePosition,
end: relativePosition + toWrite.byteLength
};
insertSectionIntoFileChunk(chunk, section);
if (chunk.written[0].start === 0 && chunk.written[0].end === FILE_CHUNK_SIZE) {
chunk.shouldFlush = true;
}
if (__privateGet(this, _chunks).length > MAX_CHUNKS_AT_ONCE) {
for (let i = 0; i < __privateGet(this, _chunks).length - 1; i++) {
__privateGet(this, _chunks)[i].shouldFlush = true;
}
this.flushChunks();
}
if (toWrite.byteLength < data.byteLength) {
this.writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength);
}
}
createChunk(includesPosition) {
let start = Math.floor(includesPosition / FILE_CHUNK_SIZE) * FILE_CHUNK_SIZE;
let chunk = {
start,
data: new Uint8Array(FILE_CHUNK_SIZE),
written: [],
shouldFlush: false
};
__privateGet(this, _chunks).push(chunk);
__privateGet(this, _chunks).sort((a, b) => a.start - b.start);
return __privateGet(this, _chunks).indexOf(chunk);
}
flushChunks(force = false) {
for (let i = 0; i < __privateGet(this, _chunks).length; i++) {
let chunk = __privateGet(this, _chunks)[i];
if (!chunk.shouldFlush && !force)
continue;
for (let section of chunk.written) {
__privateGet(this, _stream).write({
type: "write",
data: chunk.data.subarray(section.start, section.end),
position: chunk.start + section.start
});
}
__privateGet(this, _chunks).splice(i--, 1);
}
}
finalize() {
this.flushChunks(true);
}
_ensureSize = new WeakSet();
ensureSize_fn = function(size) {
let newLength = __privateGet(this, _buffer).byteLength;
while (newLength < size)
newLength *= 2;
if (newLength === __privateGet(this, _buffer).byteLength)
return;
let newBuffer = new ArrayBuffer(newLength);
let newBytes = new Uint8Array(newBuffer);
newBytes.set(__privateGet(this, _bytes), 0);
__privateSet(this, _buffer, newBuffer);
__privateSet(this, _bytes, newBytes);
};
_stream = new WeakMap();
_chunks = new WeakMap();
var insertSectionIntoFileChunk = (chunk, section) => {
let low = 0;
let high = chunk.written.length - 1;
let index = -1;
while (low <= high) {
let mid = Math.floor(low + (high - low + 1) / 2);
if (chunk.written[mid].start <= section.start) {
low = mid + 1;
index = mid;
} else {
high = mid - 1;
}
}
chunk.written.splice(index + 1, 0, section);
if (index === -1 || chunk.written[index].end < section.start)
index++;
while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {
chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);
chunk.written.splice(index + 1, 1);
}
};
var _sections, _onFlush, _lastFlushEnd, _ensureMonotonicity;
var StreamingWriteTarget = class extends WriteTarget {
constructor(onFlush, ensureMonotonicity) {
var _target2, _sections, _lastFlushEnd, _ensureMonotonicity;
var StreamTargetWriter = class extends Writer {
constructor(target, ensureMonotonicity) {
super();
__privateAdd(this, _target2, void 0);
__privateAdd(this, _sections, []);
__privateAdd(this, _onFlush, void 0);
__privateAdd(this, _lastFlushEnd, 0);
__privateAdd(this, _ensureMonotonicity, void 0);
__privateSet(this, _onFlush, onFlush);
__privateSet(this, _target2, target);
__privateSet(this, _ensureMonotonicity, ensureMonotonicity);

@@ -410,3 +366,3 @@ }

}
flush(done) {
flush() {
if (__privateGet(this, _sections).length === 0)

@@ -442,4 +398,3 @@ return;

}
let isLastFlush = done && chunk === chunks[chunks.length - 1];
__privateGet(this, _onFlush).call(this, chunk.data, chunk.start, isLastFlush);
__privateGet(this, _target2).onData(chunk.data, chunk.start);
__privateSet(this, _lastFlushEnd, chunk.start + chunk.data.byteLength);

@@ -449,9 +404,137 @@ }

}
finalize() {
var _a, _b;
(_b = (_a = __privateGet(this, _target2)).onDone) == null ? void 0 : _b.call(_a);
}
};
_target2 = new WeakMap();
_sections = new WeakMap();
_onFlush = new WeakMap();
_lastFlushEnd = new WeakMap();
_ensureMonotonicity = new WeakMap();
var CHUNK_SIZE = __pow(2, 24);
var MAX_CHUNKS_AT_ONCE = 2;
var _target3, _chunks, _lastFlushEnd2, _ensureMonotonicity2, _writeDataIntoChunks, writeDataIntoChunks_fn, _insertSectionIntoChunk, insertSectionIntoChunk_fn, _createChunk, createChunk_fn, _flushChunks, flushChunks_fn;
var ChunkedStreamTargetWriter = class extends Writer {
constructor(target, ensureMonotonicity) {
super();
__privateAdd(this, _writeDataIntoChunks);
__privateAdd(this, _insertSectionIntoChunk);
__privateAdd(this, _createChunk);
__privateAdd(this, _flushChunks);
__privateAdd(this, _target3, void 0);
__privateAdd(this, _chunks, []);
__privateAdd(this, _lastFlushEnd2, 0);
__privateAdd(this, _ensureMonotonicity2, void 0);
__privateSet(this, _target3, target);
__privateSet(this, _ensureMonotonicity2, ensureMonotonicity);
}
write(data) {
__privateMethod(this, _writeDataIntoChunks, writeDataIntoChunks_fn).call(this, data, this.pos);
__privateMethod(this, _flushChunks, flushChunks_fn).call(this);
this.pos += data.byteLength;
}
finalize() {
var _a, _b;
__privateMethod(this, _flushChunks, flushChunks_fn).call(this, true);
(_b = (_a = __privateGet(this, _target3)).onDone) == null ? void 0 : _b.call(_a);
}
};
_target3 = new WeakMap();
_chunks = new WeakMap();
_lastFlushEnd2 = new WeakMap();
_ensureMonotonicity2 = new WeakMap();
_writeDataIntoChunks = new WeakSet();
writeDataIntoChunks_fn = function(data, position) {
let chunkIndex = __privateGet(this, _chunks).findIndex((x) => x.start <= position && position < x.start + CHUNK_SIZE);
if (chunkIndex === -1)
chunkIndex = __privateMethod(this, _createChunk, createChunk_fn).call(this, position);
let chunk = __privateGet(this, _chunks)[chunkIndex];
let relativePosition = position - chunk.start;
let toWrite = data.subarray(0, Math.min(CHUNK_SIZE - relativePosition, data.byteLength));
chunk.data.set(toWrite, relativePosition);
let section = {
start: relativePosition,
end: relativePosition + toWrite.byteLength
};
__privateMethod(this, _insertSectionIntoChunk, insertSectionIntoChunk_fn).call(this, chunk, section);
if (chunk.written[0].start === 0 && chunk.written[0].end === CHUNK_SIZE) {
chunk.shouldFlush = true;
}
if (__privateGet(this, _chunks).length > MAX_CHUNKS_AT_ONCE) {
for (let i = 0; i < __privateGet(this, _chunks).length - 1; i++) {
__privateGet(this, _chunks)[i].shouldFlush = true;
}
__privateMethod(this, _flushChunks, flushChunks_fn).call(this);
}
if (toWrite.byteLength < data.byteLength) {
__privateMethod(this, _writeDataIntoChunks, writeDataIntoChunks_fn).call(this, data.subarray(toWrite.byteLength), position + toWrite.byteLength);
}
};
_insertSectionIntoChunk = new WeakSet();
insertSectionIntoChunk_fn = function(chunk, section) {
let low = 0;
let high = chunk.written.length - 1;
let index = -1;
while (low <= high) {
let mid = Math.floor(low + (high - low + 1) / 2);
if (chunk.written[mid].start <= section.start) {
low = mid + 1;
index = mid;
} else {
high = mid - 1;
}
}
chunk.written.splice(index + 1, 0, section);
if (index === -1 || chunk.written[index].end < section.start)
index++;
while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {
chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);
chunk.written.splice(index + 1, 1);
}
};
_createChunk = new WeakSet();
createChunk_fn = function(includesPosition) {
let start = Math.floor(includesPosition / CHUNK_SIZE) * CHUNK_SIZE;
let chunk = {
start,
data: new Uint8Array(CHUNK_SIZE),
written: [],
shouldFlush: false
};
__privateGet(this, _chunks).push(chunk);
__privateGet(this, _chunks).sort((a, b) => a.start - b.start);
return __privateGet(this, _chunks).indexOf(chunk);
};
_flushChunks = new WeakSet();
flushChunks_fn = function(force = false) {
for (let i = 0; i < __privateGet(this, _chunks).length; i++) {
let chunk = __privateGet(this, _chunks)[i];
if (!chunk.shouldFlush && !force)
continue;
for (let section of chunk.written) {
if (__privateGet(this, _ensureMonotonicity2) && chunk.start + section.start < __privateGet(this, _lastFlushEnd2)) {
throw new Error("Internal error: Monotonicity violation.");
}
__privateGet(this, _target3).onData(
chunk.data.subarray(section.start, section.end),
chunk.start + section.start
);
__privateSet(this, _lastFlushEnd2, chunk.start + section.end);
}
__privateGet(this, _chunks).splice(i--, 1);
}
};
var FileSystemWritableFileStreamTargetWriter = class extends ChunkedStreamTargetWriter {
constructor(target, ensureMonotonicity) {
super(new StreamTarget(
(data, position) => target.stream.write({
type: "write",
data,
position
})
), ensureMonotonicity);
}
};
// src/main.ts
// src/muxer.ts
var VIDEO_TRACK_NUMBER = 1;

@@ -467,4 +550,4 @@ var AUDIO_TRACK_NUMBER = 2;

var FIRST_TIMESTAMP_BEHAVIORS = ["strict", "offset", "permissive"];
var _target, _options, _segment, _segmentInfo, _seekHead, _tracksElement, _segmentDuration, _colourElement, _videoCodecPrivate, _audioCodecPrivate, _cues, _currentCluster, _currentClusterTimestamp, _duration, _videoChunkQueue, _audioChunkQueue, _firstVideoTimestamp, _firstAudioTimestamp, _lastVideoTimestamp, _lastAudioTimestamp, _colorSpace, _finalized, _validateOptions, validateOptions_fn, _createFileHeader, createFileHeader_fn, _writeEBMLHeader, writeEBMLHeader_fn, _createCodecPrivatePlaceholders, createCodecPrivatePlaceholders_fn, _createColourElement, createColourElement_fn, _createSeekHead, createSeekHead_fn, _createSegmentInfo, createSegmentInfo_fn, _createTracks, createTracks_fn, _createSegment, createSegment_fn, _createCues, createCues_fn, _maybeFlushStreamingTarget, maybeFlushStreamingTarget_fn, _segmentDataOffset, segmentDataOffset_get, _writeVideoDecoderConfig, writeVideoDecoderConfig_fn, _fixVP9ColorSpace, fixVP9ColorSpace_fn, _createInternalChunk, createInternalChunk_fn, _validateTimestamp, validateTimestamp_fn, _writeSimpleBlock, writeSimpleBlock_fn, _createCodecPrivateElement, createCodecPrivateElement_fn, _writeCodecPrivate, writeCodecPrivate_fn, _createNewCluster, createNewCluster_fn, _finalizeCurrentCluster, finalizeCurrentCluster_fn, _ensureNotFinalized, ensureNotFinalized_fn;
var WebMMuxer = class {
var _options, _writer, _segment, _segmentInfo, _seekHead, _tracksElement, _segmentDuration, _colourElement, _videoCodecPrivate, _audioCodecPrivate, _cues, _currentCluster, _currentClusterTimestamp, _duration, _videoChunkQueue, _audioChunkQueue, _firstVideoTimestamp, _firstAudioTimestamp, _lastVideoTimestamp, _lastAudioTimestamp, _colorSpace, _finalized, _validateOptions, validateOptions_fn, _createFileHeader, createFileHeader_fn, _writeEBMLHeader, writeEBMLHeader_fn, _createCodecPrivatePlaceholders, createCodecPrivatePlaceholders_fn, _createColourElement, createColourElement_fn, _createSeekHead, createSeekHead_fn, _createSegmentInfo, createSegmentInfo_fn, _createTracks, createTracks_fn, _createSegment, createSegment_fn, _createCues, createCues_fn, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn, _segmentDataOffset, segmentDataOffset_get, _writeVideoDecoderConfig, writeVideoDecoderConfig_fn, _fixVP9ColorSpace, fixVP9ColorSpace_fn, _createInternalChunk, createInternalChunk_fn, _validateTimestamp, validateTimestamp_fn, _writeSimpleBlock, writeSimpleBlock_fn, _createCodecPrivateElement, createCodecPrivateElement_fn, _writeCodecPrivate, writeCodecPrivate_fn, _createNewCluster, createNewCluster_fn, _finalizeCurrentCluster, finalizeCurrentCluster_fn, _ensureNotFinalized, ensureNotFinalized_fn;
var Muxer = class {
constructor(options) {

@@ -481,3 +564,3 @@ __privateAdd(this, _validateOptions);

__privateAdd(this, _createCues);
__privateAdd(this, _maybeFlushStreamingTarget);
__privateAdd(this, _maybeFlushStreamingTargetWriter);
__privateAdd(this, _segmentDataOffset);

@@ -494,4 +577,4 @@ __privateAdd(this, _writeVideoDecoderConfig);

__privateAdd(this, _ensureNotFinalized);
__privateAdd(this, _target, void 0);
__privateAdd(this, _options, void 0);
__privateAdd(this, _writer, void 0);
__privateAdd(this, _segment, void 0);

@@ -517,2 +600,3 @@ __privateAdd(this, _segmentInfo, void 0);

__privateAdd(this, _finalized, false);
var _a;
__privateMethod(this, _validateOptions, validateOptions_fn).call(this, options);

@@ -523,8 +607,10 @@ __privateSet(this, _options, __spreadValues({

}, options));
if (options.target === "buffer") {
__privateSet(this, _target, new ArrayBufferWriteTarget());
} else if (options.target instanceof FileSystemWritableFileStream) {
__privateSet(this, _target, new FileSystemWritableFileStreamWriteTarget(options.target));
} else if (typeof options.target === "function") {
__privateSet(this, _target, new StreamingWriteTarget(options.target, !!options.streaming));
this.target = options.target;
let ensureMonotonicity = !!__privateGet(this, _options).streaming;
if (options.target instanceof ArrayBufferTarget) {
__privateSet(this, _writer, new ArrayBufferTargetWriter(options.target));
} else if (options.target instanceof StreamTarget) {
__privateSet(this, _writer, ((_a = options.target.options) == null ? void 0 : _a.chunked) ? new ChunkedStreamTargetWriter(options.target, ensureMonotonicity) : new StreamTargetWriter(options.target, ensureMonotonicity));
} else if (options.target instanceof FileSystemWritableFileStreamTarget) {
__privateSet(this, _writer, new FileSystemWritableFileStreamTargetWriter(options.target, ensureMonotonicity));
} else {

@@ -561,3 +647,3 @@ throw new Error(`Invalid target: ${options.target}`);

}
__privateMethod(this, _maybeFlushStreamingTarget, maybeFlushStreamingTarget_fn).call(this);
__privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
}

@@ -593,3 +679,3 @@ addAudioChunk(chunk, meta, timestamp) {

}
__privateMethod(this, _maybeFlushStreamingTarget, maybeFlushStreamingTarget_fn).call(this);
__privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
}

@@ -604,31 +690,25 @@ finalize() {

}
__privateGet(this, _target).writeEBML(__privateGet(this, _cues));
__privateGet(this, _writer).writeEBML(__privateGet(this, _cues));
if (!__privateGet(this, _options).streaming) {
let endPos = __privateGet(this, _target).pos;
let segmentSize = __privateGet(this, _target).pos - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _target).seek(__privateGet(this, _target).offsets.get(__privateGet(this, _segment)) + 4);
__privateGet(this, _target).writeEBMLVarInt(segmentSize, SEGMENT_SIZE_BYTES);
let endPos = __privateGet(this, _writer).pos;
let segmentSize = __privateGet(this, _writer).pos - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _writer).seek(__privateGet(this, _writer).offsets.get(__privateGet(this, _segment)) + 4);
__privateGet(this, _writer).writeEBMLVarInt(segmentSize, SEGMENT_SIZE_BYTES);
__privateGet(this, _segmentDuration).data = new EBMLFloat64(__privateGet(this, _duration));
__privateGet(this, _target).seek(__privateGet(this, _target).offsets.get(__privateGet(this, _segmentDuration)));
__privateGet(this, _target).writeEBML(__privateGet(this, _segmentDuration));
__privateGet(this, _seekHead).data[0].data[1].data = __privateGet(this, _target).offsets.get(__privateGet(this, _cues)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _seekHead).data[1].data[1].data = __privateGet(this, _target).offsets.get(__privateGet(this, _segmentInfo)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _seekHead).data[2].data[1].data = __privateGet(this, _target).offsets.get(__privateGet(this, _tracksElement)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _target).seek(__privateGet(this, _target).offsets.get(__privateGet(this, _seekHead)));
__privateGet(this, _target).writeEBML(__privateGet(this, _seekHead));
__privateGet(this, _target).seek(endPos);
__privateGet(this, _writer).seek(__privateGet(this, _writer).offsets.get(__privateGet(this, _segmentDuration)));
__privateGet(this, _writer).writeEBML(__privateGet(this, _segmentDuration));
__privateGet(this, _seekHead).data[0].data[1].data = __privateGet(this, _writer).offsets.get(__privateGet(this, _cues)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _seekHead).data[1].data[1].data = __privateGet(this, _writer).offsets.get(__privateGet(this, _segmentInfo)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _seekHead).data[2].data[1].data = __privateGet(this, _writer).offsets.get(__privateGet(this, _tracksElement)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _writer).seek(__privateGet(this, _writer).offsets.get(__privateGet(this, _seekHead)));
__privateGet(this, _writer).writeEBML(__privateGet(this, _seekHead));
__privateGet(this, _writer).seek(endPos);
}
__privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
__privateGet(this, _writer).finalize();
__privateSet(this, _finalized, true);
if (__privateGet(this, _target) instanceof ArrayBufferWriteTarget) {
return __privateGet(this, _target).finalize();
} else if (__privateGet(this, _target) instanceof FileSystemWritableFileStreamWriteTarget) {
__privateGet(this, _target).finalize();
} else if (__privateGet(this, _target) instanceof StreamingWriteTarget) {
__privateGet(this, _target).flush(true);
}
return null;
}
};
_target = new WeakMap();
_options = new WeakMap();
_writer = new WeakMap();
_segment = new WeakMap();

@@ -678,3 +758,3 @@ _segmentInfo = new WeakMap();

__privateMethod(this, _createCues, createCues_fn).call(this);
__privateMethod(this, _maybeFlushStreamingTarget, maybeFlushStreamingTarget_fn).call(this);
__privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
};

@@ -693,3 +773,3 @@ _writeEBMLHeader = new WeakSet();

] };
__privateGet(this, _target).writeEBML(ebmlHeader);
__privateGet(this, _writer).writeEBML(ebmlHeader);
};

@@ -791,3 +871,3 @@ _createCodecPrivatePlaceholders = new WeakSet();

__privateSet(this, _segment, segment);
__privateGet(this, _target).writeEBML(segment);
__privateGet(this, _writer).writeEBML(segment);
};

@@ -798,6 +878,6 @@ _createCues = new WeakSet();

};
_maybeFlushStreamingTarget = new WeakSet();
maybeFlushStreamingTarget_fn = function() {
if (__privateGet(this, _target) instanceof StreamingWriteTarget) {
__privateGet(this, _target).flush(false);
_maybeFlushStreamingTargetWriter = new WeakSet();
maybeFlushStreamingTargetWriter_fn = function() {
if (__privateGet(this, _writer) instanceof StreamTargetWriter) {
__privateGet(this, _writer).flush();
}

@@ -807,3 +887,3 @@ };

segmentDataOffset_get = function() {
return __privateGet(this, _target).dataOffsets.get(__privateGet(this, _segment));
return __privateGet(this, _writer).dataOffsets.get(__privateGet(this, _segment));
};

@@ -837,6 +917,6 @@ _writeVideoDecoderConfig = new WeakSet();

if (!__privateGet(this, _options).streaming) {
let endPos = __privateGet(this, _target).pos;
__privateGet(this, _target).seek(__privateGet(this, _target).offsets.get(__privateGet(this, _colourElement)));
__privateGet(this, _target).writeEBML(__privateGet(this, _colourElement));
__privateGet(this, _target).seek(endPos);
let endPos = __privateGet(this, _writer).pos;
__privateGet(this, _writer).seek(__privateGet(this, _writer).offsets.get(__privateGet(this, _colourElement)));
__privateGet(this, _writer).writeEBML(__privateGet(this, _colourElement));
__privateGet(this, _writer).seek(endPos);
}

@@ -948,3 +1028,3 @@ }

] };
__privateGet(this, _target).writeEBML(simpleBlock);
__privateGet(this, _writer).writeEBML(simpleBlock);
__privateSet(this, _duration, Math.max(__privateGet(this, _duration), msTime));

@@ -958,4 +1038,4 @@ };

writeCodecPrivate_fn = function(element, data) {
let endPos = __privateGet(this, _target).pos;
__privateGet(this, _target).seek(__privateGet(this, _target).offsets.get(element));
let endPos = __privateGet(this, _writer).pos;
__privateGet(this, _writer).seek(__privateGet(this, _writer).offsets.get(element));
element = [

@@ -965,4 +1045,4 @@ __privateMethod(this, _createCodecPrivateElement, createCodecPrivateElement_fn).call(this, data),

];
__privateGet(this, _target).writeEBML(element);
__privateGet(this, _target).seek(endPos);
__privateGet(this, _writer).writeEBML(element);
__privateGet(this, _writer).seek(endPos);
};

@@ -981,5 +1061,5 @@ _createNewCluster = new WeakSet();

});
__privateGet(this, _target).writeEBML(__privateGet(this, _currentCluster));
__privateGet(this, _writer).writeEBML(__privateGet(this, _currentCluster));
__privateSet(this, _currentClusterTimestamp, timestamp);
let clusterOffsetFromSegment = __privateGet(this, _target).offsets.get(__privateGet(this, _currentCluster)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
let clusterOffsetFromSegment = __privateGet(this, _writer).offsets.get(__privateGet(this, _currentCluster)) - __privateGet(this, _segmentDataOffset, segmentDataOffset_get);
__privateGet(this, _cues).data.push({ id: 187 /* CuePoint */, data: [

@@ -999,7 +1079,7 @@ { id: 179 /* CueTime */, data: timestamp },

finalizeCurrentCluster_fn = function() {
let clusterSize = __privateGet(this, _target).pos - __privateGet(this, _target).dataOffsets.get(__privateGet(this, _currentCluster));
let endPos = __privateGet(this, _target).pos;
__privateGet(this, _target).seek(__privateGet(this, _target).offsets.get(__privateGet(this, _currentCluster)) + 4);
__privateGet(this, _target).writeEBMLVarInt(clusterSize, CLUSTER_SIZE_BYTES);
__privateGet(this, _target).seek(endPos);
let clusterSize = __privateGet(this, _writer).pos - __privateGet(this, _writer).dataOffsets.get(__privateGet(this, _currentCluster));
let endPos = __privateGet(this, _writer).pos;
__privateGet(this, _writer).seek(__privateGet(this, _writer).offsets.get(__privateGet(this, _currentCluster)) + 4);
__privateGet(this, _writer).writeEBMLVarInt(clusterSize, CLUSTER_SIZE_BYTES);
__privateGet(this, _writer).seek(endPos);
};

@@ -1012,28 +1092,9 @@ _ensureNotFinalized = new WeakSet();

};
var main_default = WebMMuxer;
var readBits = (bytes, start, end) => {
let result = 0;
for (let i = start; i < end; i++) {
let byteIndex = Math.floor(i / 8);
let byte = bytes[byteIndex];
let bitIndex = 7 - (i & 7);
let bit = (byte & 1 << bitIndex) >> bitIndex;
result <<= 1;
result |= bit;
}
return result;
};
var writeBits = (bytes, start, end, value) => {
for (let i = start; i < end; i++) {
let byteIndex = Math.floor(i / 8);
let byte = bytes[byteIndex];
let bitIndex = 7 - (i & 7);
byte &= ~(1 << bitIndex);
byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex;
bytes[byteIndex] = byte;
}
};
return __toCommonJS(main_exports);
return __toCommonJS(src_exports);
})();
WebMMuxer = WebMMuxer.default;
if (typeof module === "object" && typeof module.exports === "object") module.exports = WebMMuxer;
if (typeof module === "object" && typeof module.exports === "object") {
module.exports.Muxer = WebMMuxer.Muxer;
module.exports.ArrayBufferTarget = WebMMuxer.ArrayBufferTarget;
module.exports.StreamTarget = WebMMuxer.StreamTarget;
module.exports.FileSystemWritableFileStreamTarget = WebMMuxer.FileSystemWritableFileStreamTarget;
}
{
"name": "webm-muxer",
"version": "2.2.3",
"version": "3.0.0",
"description": "WebM multiplexer in pure TypeScript with support for WebCodecs API, video & audio.",

@@ -5,0 +5,0 @@ "main": "./build/webm-muxer.js",

@@ -19,6 +19,6 @@ # webm-muxer - JavaScript WebM multiplexer

```js
import WebMMuxer from 'webm-muxer';
import { Muxer, ArrayBufferTarget } from 'webm-muxer';
let muxer = new WebMMuxer({
target: 'buffer',
let muxer = new Muxer({
target: new ArrayBufferTarget(),
video: {

@@ -45,3 +45,5 @@ codec: 'V_VP9',

await videoEncoder.flush();
let buffer = muxer.finalize(); // Buffer contains final WebM file
muxer.finalize();
let { buffer } = muxer.target; // Buffer contains final WebM file
```

@@ -62,10 +64,10 @@

```
The package has a single, default export, `WebMMuxer`:
You can import all exported classes like so:
```js
import WebMMuxer from 'webm-muxer';
import * as WebMMuxer from 'webm-muxer';
// Or, using CommonJS:
const WebMMuxer = require('webm-muxer');
```
Alternatively, you can simply include the library as a script in your HTML, which will add `WebMMuxer` to the global
object, like so:
Alternatively, you can simply include the library as a script in your HTML, which will add a `WebMMuxer` object,
containing all the exported classes, to the global object, like so:
```html

@@ -77,12 +79,15 @@ <script src="build/webm-muxer.js"></script>

### Initialization
For each WebM file you wish to create, create an instance of `WebMMuxer` like so:
For each WebM file you wish to create, create an instance of `Muxer` like so:
```js
let muxer = new WebMMuxer(options);
import { Muxer } from 'webm-muxer';
let muxer = new Muxer(options);
```
The available options are defined by the following interface:
```ts
interface WebMMuxerOptions {
target: 'buffer'
| ((data: Uint8Array, offset: number, done: boolean) => void)
| FileSystemWritableFileStream
interface MuxerOptions {
target:
| ArrayBufferTarget
| StreamTarget
| FileSystemWritableFileStreamTarget,

@@ -113,13 +118,11 @@ video?: {

#### `target`
This option specifies what will happens with the data created by the muxer. The options are:
- `'buffer'`: The file data will be written into a single, large buffer which is then returned by `finalize`.
This option specifies where the data created by the muxer will be written. The options are:
- `ArrayBufferTarget`: The file data will be written into a single large buffer, which is then stored in the target.
```js
let muxer = new WebMMuxer({
target: 'buffer',
video: {
codec: 'V_VP9',
width: 1280,
height: 720
}
import { Muxer, ArrayBufferTarget } from 'webm-muxer';
let muxer = new Muxer({
target: new ArrayBufferTarget(),
// ...
});

@@ -129,28 +132,40 @@

let buffer = muxer.finalize();
muxer.finalize();
let { buffer } = muxer.target;
```
- `function`: If the target is a function, it will be called each time data is output by the muxer - this is useful if
you want to stream the data. The function will be called with three arguments: the data to write, the offset in
bytes at which to write the data and a boolean indicating whether the muxer is done writing data. Note that the same
segment of bytes might be written to multiple times, and therefore you need to write the data in the same order the
function gave it to you. If you don't want this, set `streaming` to `true`.
- `StreamTarget`: This target defines callbacks that will get called whenever there is new data available - this is useful if
you want to stream the data, e.g. pipe it somewhere else. The constructor has the following signature:
```ts
constructor(
onData: (data: Uint8Array, position: number) => void,
onDone?: () => void,
options?: { chunked?: true }
);
```
The `position` parameter specifies the offset in bytes at which the data should be written. When using
`chunked: true` in the options, data created by the muxer will first be accumulated and only written out once it has
reached sufficient size (~16 MB). This is useful for reducing the total amount of writes, at the cost of latency.
Note that this target is **not** intended for *live-streaming*, i.e. playback before muxing has finished.
```js
let muxer = new WebMMuxer({
target: (data, offset, done) => {
// Do something with the data
},
audio: {
codec: 'A_OPUS',
numberOfChannels: 1,
sampleRate: 44100
}
import { Muxer, StreamTarget } from 'webm-muxer';
let muxer = new Muxer({
target: new StreamTarget(
(data, position) => { /* Do something with the data */ },
() => { /* Muxing has finished */ }
),
// ...
});
```
- `FileSystemWritableFileStream`: When acquired through the File System Access API, the
muxed file is written directly to disk, allowing for files way larger than what would fit in RAM. This functionality
could also be manually emulated by passing a `function` instead, however, this library has some built-in write
batching optimization which will be used when passing a FileSystemWritableFileStream.
- `FileSystemWritableFileStreamTarget`: This is essentially a wrapper around `StreamTarget` with the intention of
simplifying the use of this library with the File System Access API. Writing the file directly to disk as it's being
created comes with many benefits, such as creating files way larger than the available RAM.
```js
import { Muxer, FileSystemWritableFileStreamTarget } from 'webm-muxer';
let fileHandle = await window.showSaveFilePicker({

@@ -163,17 +178,12 @@ suggestedName: `video.webm`,

});
let fileWritableStream = await fileHandle.createWritable();
let muxer = new WebMMuxer({
target: fileWritableStream,
video: {
codec: 'V_VP9',
width: 1920,
height: 1080,
frameRate: 60
},
audio: {
codec: 'A_OPUS',
numberOfChannels: 2,
sampleRate: 48000
}
let fileStream = await fileHandle.createWritable();
let muxer = new Muxer({
target: new FileSystemWritableFileStreamTarget(fileStream),
// ...
});
// ...
muxer.finalize();
await fileStream.close(); // Make sure to close the stream
```

@@ -202,11 +212,20 @@ #### `streaming` (optional)

### Muxing media chunks
Then, with VideoEncoder and AudioEncoder set up, send encoded chunks to the muxer like so:
```js
muxer.addVideoChunk(encodedVideoChunk, encodedVideoChunkMetadata);
muxer.addAudioChunk(encodedAudioChunk, encodedAudioChunkMetadata);
Then, with VideoEncoder and AudioEncoder set up, send encoded chunks to the muxer using the following methods:
```ts
addVideoChunk(
chunk: EncodedVideoChunk,
meta: EncodedVideoChunkMetadata,
timestamp?: number
): void;
addAudioChunk(
chunk: EncodedAudioChunk,
meta: EncodedAudioChunkMetadata,
timestamp?: number
): void;
```
In addition, both methods accept an optional, third argument `timestamp` (microseconds) which, if specified, overrides
the `timestamp` property of the passed-in chunk. This is useful when getting chunks from a MediaStreamTrackProcessor
from live media, which usually come with huge timestamp values and don't start at 0, which we want.
Both methods accept an optional, third argument `timestamp` (microseconds) which, if specified, overrides
the `timestamp` property of the passed-in chunk.
The metadata comes from the second parameter of the `output` callback given to the

@@ -241,11 +260,15 @@ VideoEncoder or AudioEncoder's constructor and needs to be passed into the muxer, like so:

### Finishing up
When encoding is finished, call `finalize` on the `WebMMuxer` instance to finalize the WebM file. When using
`target: 'buffer'`, the resulting file's buffer is returned by this method:
When encoding is finished and all the encoders have been flushed, call `finalize` on the `Muxer` instance to finalize
the WebM file:
```js
let buffer = muxer.finalize();
muxer.finalize();
```
When using a FileSystemWritableFileStream, make sure to close the stream after calling `finalize`:
When using an ArrayBufferTarget, the final buffer will be accessible through it:
```js
await fileWritableStream.close();
let { buffer } = muxer.target;
```
When using a FileSystemWritableFileStreamTarget, make sure to close the stream after calling `finalize`:
```js
await fileStream.close();
```

@@ -252,0 +275,0 @@ ## Details

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc