Add NullTarget

This commit is contained in:
Vanilagy
2025-09-02 17:20:09 +02:00
parent a769d099e2
commit 55dd8cdb4c
5 changed files with 78 additions and 4 deletions
+4 -2
View File
@@ -18,13 +18,15 @@
const file = fileInput.files[0];
const source = new Mediabunny.BlobSource(file);
const target = new Mediabunny.BufferTarget() ?? new Mediabunny.StreamTarget(new WritableStream({
const target = new Mediabunny.NullTarget() ?? new Mediabunny.BufferTarget() ?? new Mediabunny.StreamTarget(new WritableStream({
write: console.log
}), {
chunked: true,
chunkSize: 2**20
});
const outputFormat = new Mediabunny.WavOutputFormat({});
const outputFormat = new Mediabunny.Mp4OutputFormat({
onMoov: console.log
});
const button = document.createElement('button');
button.textContent = 'Cancel';
+39
View File
@@ -297,6 +297,45 @@ const output = new Output({
await output.finalize(); // Will automatically close the writable stream
```
### `NullTarget`
This target simply discards all data that is passed into it. It is useful for when you need an `Output` but extract data from it differently, for example through output format-specific callbacks or encoder events.
As an example, here we create a fragmented MP4 file and directly handle the individual fragments:
```ts
import { Output, NullTarget, Mp4OutputFormat } from 'mediabunny';
let ftyp: Uint8Array;
let lastMoof: Uint8Array;
const output = new Output({
target: new NullTarget(),
format: new Mp4OutputFormat({
fastStart: 'fragmented',
onFtyp: (data) => {
ftyp = data;
},
onMoov: (data) => {
const header = new Uint8Array(ftyp.length + data.length);
header.set(ftyp, 0);
header.set(data, ftyp.length);
// Do something with the header...
},
onMoof: (data) => {
lastMoof = data;
},
onMdat: (data) => {
const segment = new Uint8Array(lastMoof.length + data.length);
segment.set(lastMoof, 0);
segment.set(data, lastMoof.length);
// Do something with the segment...
},
}),
});
```
## Packet buffering
Some [output formats](./output-formats) require *packet buffering* for multi-track outputs. Packet buffering occurs because the `Output` must wait for data from all tracks for a given timestamp to continue writing data. For example, should you first encode all your video frames and then encode the audio afterward, the `Output` will have to hold all of the video frames in memory until the audio packets start coming in. This might lead to memory exhaustion should your video be very long. When there is only one media track, this issue does not arise.
+1 -1
View File
@@ -89,7 +89,7 @@ export {
getFirstEncodableAudioCodec,
getFirstEncodableSubtitleCodec,
} from './encode';
export { Target, BufferTarget, StreamTarget, StreamTargetChunk, StreamTargetOptions } from './target';
export { Target, BufferTarget, StreamTarget, StreamTargetChunk, StreamTargetOptions, NullTarget } from './target';
export { Rotation, AnyIterable, SetRequired, MaybePromise } from './misc';
export {
Source,
+13 -1
View File
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { BufferTargetWriter, StreamTargetWriter, Writer } from './writer';
import { BufferTargetWriter, NullTargetWriter, StreamTargetWriter, Writer } from './writer';
import { Output } from './output';
/**
@@ -104,3 +104,15 @@ export class StreamTarget extends Target {
return new StreamTargetWriter(this);
}
}
/**
* This target just discards all incoming data. It is useful for when you need an `Output` but extract data from it
* differently, for example through format-specific callbacks (`onMoof`, `onMdat`, ...) or encoder events.
* @public
*/
export class NullTarget extends Target {
/** @internal */
_createWriter() {
return new NullTargetWriter();
}
}
+21
View File
@@ -459,3 +459,24 @@ export class StreamTargetWriter extends Writer {
return this.writer?.close();
}
}
export class NullTargetWriter extends Writer {
private pos = 0;
write(data: Uint8Array) {
this.maybeTrackWrites(data);
this.pos += data.byteLength;
}
getPos() {
return this.pos;
}
seek(newPos: number) {
this.pos = newPos;
}
async flush() {}
async finalize() {}
async close() {}
}