mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-09 16:53:47 +02:00
Replace ArrayBufferTarget with BufferTarget, now spitting out Uint8Array and using .resize
This commit is contained in:
+3
-3
@@ -44,7 +44,7 @@
|
|||||||
//console.log(await videoTrack.computeSampleStats());
|
//console.log(await videoTrack.computeSampleStats());
|
||||||
//return;
|
//return;
|
||||||
|
|
||||||
const target = new Metamuxer.ArrayBufferTarget();
|
const target = new Metamuxer.BufferTarget();
|
||||||
const output = new Metamuxer.Output({
|
const output = new Metamuxer.Output({
|
||||||
format: new Metamuxer.MovOutputFormat({ fastStart: undefined }),
|
format: new Metamuxer.MovOutputFormat({ fastStart: undefined }),
|
||||||
target
|
target
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
const decoderConfig = await audioTrack.getDecoderConfig();
|
const decoderConfig = await audioTrack.getDecoderConfig();
|
||||||
const sampleSource = new Metamuxer.EncodedAudioSampleSource(await audioTrack.getCodec());
|
const sampleSource = new Metamuxer.EncodedAudioSampleSource(await audioTrack.getCodec());
|
||||||
const audioDataSource = new Metamuxer.AudioDataSource({ codec: 'ulaw' });
|
const audioDataSource = new Metamuxer.AudioDataSource({ codec: 'aac', bitrate: 128e3 });
|
||||||
const videoSampleSource = new Metamuxer.EncodedVideoSampleSource(await videoTrack.getCodec());
|
const videoSampleSource = new Metamuxer.EncodedVideoSampleSource(await videoTrack.getCodec());
|
||||||
output.addAudioTrack(audioDataSource ?? sampleSource);
|
output.addAudioTrack(audioDataSource ?? sampleSource);
|
||||||
output.addVideoTrack(videoSampleSource);
|
output.addVideoTrack(videoSampleSource);
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
a.click();
|
a.click();
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
download(new Blob([target.buffer]), 'converted.mov');
|
//download(new Blob([target.buffer]), 'converted.mov');
|
||||||
|
|
||||||
document.body.textContent = performance.now() - start;
|
document.body.textContent = performance.now() - start;
|
||||||
|
|
||||||
|
|||||||
+3
-9
@@ -390,10 +390,8 @@ export const buildAudioCodecString = (codec: AudioCodec, numberOfChannels: numbe
|
|||||||
return 'vorbis';
|
return 'vorbis';
|
||||||
} else if (codec === 'flac') {
|
} else if (codec === 'flac') {
|
||||||
return 'flac';
|
return 'flac';
|
||||||
} else if (codec === 'ulaw') {
|
} else if ((PCM_CODECS as readonly string[]).includes(codec)) {
|
||||||
return 'ulaw';
|
return codec;
|
||||||
} else if (codec === 'alaw') {
|
|
||||||
return 'alaw';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeError(`Unhandled codec '${codec}'.`);
|
throw new TypeError(`Unhandled codec '${codec}'.`);
|
||||||
@@ -427,12 +425,8 @@ export const extractAudioCodecString = (trackInfo: {
|
|||||||
return 'vorbis';
|
return 'vorbis';
|
||||||
} else if (codec === 'flac') {
|
} else if (codec === 'flac') {
|
||||||
return 'flac';
|
return 'flac';
|
||||||
} else if (codec?.startsWith('pcm-')) {
|
} else if (codec && (PCM_CODECS as readonly string[]).includes(codec)) {
|
||||||
return codec;
|
return codec;
|
||||||
} else if (codec === 'ulaw') {
|
|
||||||
return 'ulaw';
|
|
||||||
} else if (codec === 'alaw') {
|
|
||||||
return 'alaw';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeError(`Unhandled codec '${codec}'.`);
|
throw new TypeError(`Unhandled codec '${codec}'.`);
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ export {
|
|||||||
getEncodableAudioCodecs,
|
getEncodableAudioCodecs,
|
||||||
getEncodableSubtitleCodecs,
|
getEncodableSubtitleCodecs,
|
||||||
} from './codec';
|
} from './codec';
|
||||||
export { Target, ArrayBufferTarget, StreamTarget, StreamTargetChunk, StreamTargetOptions } from './target';
|
export { Target, BufferTarget, StreamTarget, StreamTargetChunk, StreamTargetOptions } from './target';
|
||||||
export { Rotation, TransformationMatrix, AnyIterable } from './misc';
|
export { Rotation, TransformationMatrix, AnyIterable } from './misc';
|
||||||
export { Source, BufferSource, BlobSource, UrlSource } from './source';
|
export { Source, BufferSource, BlobSource, UrlSource } from './source';
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { Box, free, ftyp, IsobmffBoxWriter, mdat, mfra, moof, moov, vtta, vttc, vtte } from './isobmff-boxes';
|
import { Box, free, ftyp, IsobmffBoxWriter, mdat, mfra, moof, moov, vtta, vttc, vtte } from './isobmff-boxes';
|
||||||
import { Muxer } from '../muxer';
|
import { Muxer } from '../muxer';
|
||||||
import { Output, OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from '../output';
|
import { Output, OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from '../output';
|
||||||
import { ArrayBufferTargetWriter, Writer } from '../writer';
|
import { BufferTargetWriter, Writer } from '../writer';
|
||||||
import { assert, last } from '../misc';
|
import { assert, last } from '../misc';
|
||||||
import { IsobmffOutputFormatOptions, IsobmffOutputFormat, MovOutputFormat } from '../output-format';
|
import { IsobmffOutputFormatOptions, IsobmffOutputFormat, MovOutputFormat } from '../output-format';
|
||||||
import { inlineTimestampRegex, SubtitleConfig, SubtitleCue, SubtitleMetadata } from '../subtitles';
|
import { inlineTimestampRegex, SubtitleConfig, SubtitleCue, SubtitleMetadata } from '../subtitles';
|
||||||
import { ArrayBufferTarget } from '../target';
|
|
||||||
import {
|
import {
|
||||||
parsePcmCodec,
|
parsePcmCodec,
|
||||||
PCM_CODECS,
|
PCM_CODECS,
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
validateVideoChunkMetadata,
|
validateVideoChunkMetadata,
|
||||||
} from '../codec';
|
} from '../codec';
|
||||||
import { EncodedAudioSample, EncodedVideoSample } from '../sample';
|
import { EncodedAudioSample, EncodedVideoSample } from '../sample';
|
||||||
|
import { BufferTarget } from '../target';
|
||||||
|
|
||||||
export const GLOBAL_TIMESCALE = 1000;
|
export const GLOBAL_TIMESCALE = 1000;
|
||||||
const TIMESTAMP_OFFSET = 2_082_844_800; // Seconds between Jan 1 1904 and Jan 1 1970
|
const TIMESTAMP_OFFSET = 2_082_844_800; // Seconds between Jan 1 1904 and Jan 1 1970
|
||||||
@@ -102,7 +102,7 @@ export class IsobmffMuxer extends Muxer {
|
|||||||
private isMov: boolean;
|
private isMov: boolean;
|
||||||
private fastStart: NonNullable<IsobmffOutputFormatOptions['fastStart']>;
|
private fastStart: NonNullable<IsobmffOutputFormatOptions['fastStart']>;
|
||||||
|
|
||||||
private auxTarget = new ArrayBufferTarget();
|
private auxTarget = new BufferTarget();
|
||||||
private auxWriter = this.auxTarget._createWriter();
|
private auxWriter = this.auxTarget._createWriter();
|
||||||
private auxBoxWriter = new IsobmffBoxWriter(this.auxWriter);
|
private auxBoxWriter = new IsobmffBoxWriter(this.auxWriter);
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ export class IsobmffMuxer extends Muxer {
|
|||||||
|
|
||||||
// If the fastStart option isn't defined, enable in-memory fast start if the target is an ArrayBuffer, as the
|
// If the fastStart option isn't defined, enable in-memory fast start if the target is an ArrayBuffer, as the
|
||||||
// memory usage remains identical
|
// memory usage remains identical
|
||||||
const fastStartDefault = this.writer instanceof ArrayBufferTargetWriter ? 'in-memory' : false;
|
const fastStartDefault = this.writer instanceof BufferTargetWriter ? 'in-memory' : false;
|
||||||
this.fastStart = format._options.fastStart ?? fastStartDefault;
|
this.fastStart = format._options.fastStart ?? fastStartDefault;
|
||||||
|
|
||||||
if (this.fastStart === 'in-memory' || this.fastStart === 'fragmented') {
|
if (this.fastStart === 'in-memory' || this.fastStart === 'fragmented') {
|
||||||
|
|||||||
+4
-5
@@ -1,4 +1,4 @@
|
|||||||
import { ArrayBufferTargetWriter, ChunkedStreamTargetWriter, StreamTargetWriter, Writer } from './writer';
|
import { BufferTargetWriter, ChunkedStreamTargetWriter, StreamTargetWriter, Writer } from './writer';
|
||||||
import { Output } from './output';
|
import { Output } from './output';
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
@@ -11,13 +11,12 @@ export abstract class Target {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
// TODO: Switch to Uint8ArrayTarget for efficiency?
|
export class BufferTarget extends Target {
|
||||||
export class ArrayBufferTarget extends Target {
|
buffer: Uint8Array | null = null;
|
||||||
buffer: ArrayBuffer | null = null;
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_createWriter() {
|
_createWriter() {
|
||||||
return new ArrayBufferTargetWriter(this);
|
return new BufferTargetWriter(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+48
-18
@@ -1,4 +1,4 @@
|
|||||||
import { ArrayBufferTarget, StreamTarget, StreamTargetChunk } from './target';
|
import { BufferTarget, StreamTarget, StreamTargetChunk } from './target';
|
||||||
import { assert } from './misc';
|
import { assert } from './misc';
|
||||||
|
|
||||||
export abstract class Writer {
|
export abstract class Writer {
|
||||||
@@ -21,21 +21,36 @@ export abstract class Writer {
|
|||||||
abstract close(): Promise<void>;
|
abstract close(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const ARRAY_BUFFER_INITIAL_SIZE = 2 ** 16;
|
||||||
* Writes to an ArrayBufferTarget. Maintains a growable internal buffer during the muxing process, which will then be
|
const ARRAY_BUFFER_MAX_SIZE = 2 ** 32;
|
||||||
* written to the ArrayBufferTarget once the muxing finishes.
|
|
||||||
*/
|
|
||||||
export class ArrayBufferTargetWriter extends Writer {
|
|
||||||
private pos = 0;
|
|
||||||
private target: ArrayBufferTarget;
|
|
||||||
private buffer = new ArrayBuffer(2 ** 16);
|
|
||||||
private bytes = new Uint8Array(this.buffer);
|
|
||||||
private maxPos = 0;
|
|
||||||
|
|
||||||
constructor(target: ArrayBufferTarget) {
|
export class BufferTargetWriter extends Writer {
|
||||||
|
private pos = 0;
|
||||||
|
private target: BufferTarget;
|
||||||
|
private buffer: ArrayBuffer;
|
||||||
|
private bytes: Uint8Array;
|
||||||
|
private maxPos = 0;
|
||||||
|
private supportsResize: boolean;
|
||||||
|
|
||||||
|
constructor(target: BufferTarget) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.target = target;
|
this.target = target;
|
||||||
|
|
||||||
|
this.supportsResize = 'resize' in new ArrayBuffer(0);
|
||||||
|
if (this.supportsResize) {
|
||||||
|
try {
|
||||||
|
// @ts-expect-error Don't want to bump "lib" in tsconfig
|
||||||
|
this.buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE, { maxByteLength: ARRAY_BUFFER_MAX_SIZE });
|
||||||
|
} catch {
|
||||||
|
this.buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE);
|
||||||
|
this.supportsResize = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bytes = new Uint8Array(this.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ensureSize(size: number) {
|
private ensureSize(size: number) {
|
||||||
@@ -44,12 +59,27 @@ export class ArrayBufferTargetWriter extends Writer {
|
|||||||
|
|
||||||
if (newLength === this.buffer.byteLength) return;
|
if (newLength === this.buffer.byteLength) return;
|
||||||
|
|
||||||
const newBuffer = new ArrayBuffer(newLength);
|
if (newLength > ARRAY_BUFFER_MAX_SIZE) {
|
||||||
const newBytes = new Uint8Array(newBuffer);
|
throw new Error(
|
||||||
newBytes.set(this.bytes, 0);
|
`ArrayBuffer exceeded maximum size of ${ARRAY_BUFFER_MAX_SIZE} bytes. Please consider using another`
|
||||||
|
+ ` target.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.buffer = newBuffer;
|
if (this.supportsResize) {
|
||||||
this.bytes = newBytes;
|
// Use resize if it exists
|
||||||
|
// @ts-expect-error Don't want to bump "lib" in tsconfig
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
this.buffer.resize(newLength);
|
||||||
|
// The Uint8Array scales automatically
|
||||||
|
} else {
|
||||||
|
const newBuffer = new ArrayBuffer(newLength);
|
||||||
|
const newBytes = new Uint8Array(newBuffer);
|
||||||
|
newBytes.set(this.bytes, 0);
|
||||||
|
|
||||||
|
this.buffer = newBuffer;
|
||||||
|
this.bytes = newBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write(data: Uint8Array) {
|
write(data: Uint8Array) {
|
||||||
@@ -73,7 +103,7 @@ export class ArrayBufferTargetWriter extends Writer {
|
|||||||
|
|
||||||
async finalize() {
|
async finalize() {
|
||||||
this.ensureSize(this.pos);
|
this.ensureSize(this.pos);
|
||||||
this.target.buffer = this.buffer.slice(0, Math.max(this.maxPos, this.pos));
|
this.target.buffer = this.bytes.subarray(0, Math.max(this.maxPos, this.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
async close() {}
|
async close() {}
|
||||||
|
|||||||
Reference in New Issue
Block a user