packages/docs/docs/webcodecs/create-video-decoder.mdx
:::warning We are phasing out Remotion WebCodecs and are moving to Mediabunny! :::
import {UnstableDisclaimer} from './UnstableDisclaimer';
:::warning
<UnstableDisclaimer /> :::This function is a wrapper around the VideoDecoder Web API.
import type {MediaParserVideoTrack} from '@remotion/media-parser';
const track = {} as unknown as MediaParserVideoTrack;
// ---cut---
import {createVideoDecoder} from '@remotion/webcodecs';
const decoder = await createVideoDecoder({
track,
onFrame: console.log,
onError: console.error,
});
VideoDecoder.waitForQueueToBeLessThan() and .waitForFinish().dequeue event is not supported as it is not reliable across browsers.EncodedVideoChunk, EncodedVideoChunkInit objects are also accepted for .decode().webcodecsController() instance can be passed in to the function, allowing for decoding to be paused, resumed and aborted..decode() is async, and returns a promise, allowing for a halt if the decoder is paused.logLevel can be passed in to the function, allowing the queue to be debugged.onFrame callback is being awaited. When rejected, the error lands in the onError callback. When resolved, only then the queue size counter will be decreased.Takes an object with the following properties:
trackAn VideoDecoderConfig object.
You may pass a MediaParserVideoTrack object from parseMedia(), which also is an VideoDecoderConfig object.
onFrameA callback that is called when a frame is decoded.
Takes a single argument, which is a VideoFrame object.
If the passed callback is asynchronous, the queue size counter will be decreased only after the callback has been resolved.
However, the callback for the next frame may already be called while your callback is still running.
We do not ensure that callbacks are running sequentially.
onErrorA callback that is called when an error occurs or the decode is aborted through the controller.
Takes a single argument, which is an Error object.
controller?A webcodecsController() instance.
If provided, you can call .pause(), .resume() and .abort() on the controller to pause, resume and abort the decoding.
logLevel?string <TsType type="LogLevel" source="@remotion/media-parser"/>
One of "error", "warn", "info", "debug", "trace".
Default value: "info", which logs only important information.
Returns an object with the following properties:
waitForQueueToBeLessThan()Pass a number to wait for the queue to be less than the given number.
A promise that resolves when the queue size is less than the given number.
The queue is only decremented when the onFrame callback resolves.
flush()Flushes the decoder, forcing the queue to be cleared. Returns a promise that resolves when all frames have been cleared and the onFrame() callback has beeen resolved for all frames.
reset()Clears the queue and resets the decoder. Same as VideoDecoder.reset() + VideoDecoder.configure().
close()Closes the decoder. Same as AudioDecoder.close().
checkReset()<AvailableFrom v="4.0.312" />Returns a handle with a wasReset() function. If the decoder was reset inbetween the call to .checkReset() and the call to wasReset(), wasReset() will return true. See below for an example.
getMostRecentSampleInput()<AvailableFrom v="4.0.312" />Return the .timestamp of the most recently input sample.
@remotion/media-parserimport {parseMedia} from '@remotion/media-parser';
import {createVideoDecoder} from '@remotion/webcodecs';
await parseMedia({
src: 'https://remotion.media/video.mp4',
onVideoTrack: async ({track, container}) => {
const decoder = await createVideoDecoder({
track,
onFrame: console.log,
onError: console.error,
});
return async (sample) => {
// Called on every sample
await decoder.waitForQueueToBeLessThan(10);
await decoder.decode(sample);
return async () => {
// Called when the track is done
await decoder.flush();
decoder.close();
};
};
},
});
A potential race condition you may face is that decoder.reset() is called while a sample is waiting for the queue to be less than a certain number. Use .checkReset() to check if the decoder was reset after any asynchronous operation, and abort the processing of the sample if needed.
import {parseMedia} from '@remotion/media-parser';
import {createVideoDecoder} from '@remotion/webcodecs';
await parseMedia({
src: 'https://remotion.media/video.mp4',
onVideoTrack: async ({track, container}) => {
const decoder = await createVideoDecoder({
track,
onFrame: console.log,
onError: console.error,
});
return async (sample) => {
const {wasReset} = decoder.checkReset();
await decoder.waitForQueueToBeLessThan(10);
if (wasReset()) {
return;
}
await decoder.decode(sample);
if (wasReset()) {
return;
}
return async () => {
if (wasReset()) {
return;
}
// Called when the track is done
await decoder.flush();
decoder.close();
};
};
},
});
If the video cannot be decoded by the browser, an VideoUndecodableError will be thrown.
import {VideoUndecodableError, createVideoDecoder} from '@remotion/webcodecs';
try {
await createVideoDecoder({
track: {codec: 'invalid'},
onFrame: console.log,
onError: console.error,
});
} catch (error) {
if (error instanceof VideoUndecodableError) {
console.log('The video cannot be decoded by this browser');
} else {
throw error;
}
}