| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/ja/docs/Web/API/WebCodecs_API/Using_the_WebCodecs_API | [Back] [Original] |
Get to know MDN better
WebCodecs API VideoFrame AudioData
VideoEncoder output error output EncodedVideoChunk metadata metadata EncodedVideoChunkMetadata decoderConfig
const encoder = new VideoEncoder({
output(chunk, meta) {
// Do something with chunk, typically send to muxing library
},
error(e) {
// Handle the error
},
});
encoder.configure({
codec: "vp09.00.40.08.00", // See codec selection guide
width: 1280,
height: 720,
bitrate: 1_000_000, // 1 Mbps
framerate: 30,
});
VideoFrame VideoFrame keyFrame
for (let i = 0; i < 60; i++) {
const timestamp = (i * 1e6) / 30; // 30 fps, in microseconds
const frame = new VideoFrame(canvas, { timestamp });
encoder.encode(frame, { keyFrame: i % 60 === 0 });
frame.close();
}
VideoEncoder 30 60
VideoFrame VideoFrame 100
VideoEncoder encodeQueue 30 fps encoder.encode(frame) 10 fps
dequeue encodeQueueSize
encoder.addEventListener("dequeue", (event) => {
// Queue up more encoding work
});
flush()
await encoder.flush();
flush() EncodedVideoChunk VideoEncoder close()
encoder.close();
VideoEncoder "closed" VideoEncoder
if (encoder.state === "closed") {
// Close the old encoder, instantiate and configure a new encoder
}
encoder.encode(frame, { keyFrame: true });
output error VideoDecoder output VideoFrame
const decoder = new VideoDecoder({
output(frame) {
// Do something with the VideoFrame
},
error(e) {
/** Handle the error */
},
});
decoder.configure(/* config */);
1
let chunkIndex = 0;
// Process chunks in batches, not one at a time nor all at once
for (let i = 0; i < BATCH_LENGTH; i++) {
decoder.decode(chunks[chunkIndex]);
chunkIndex++;
}
VideoEncoder VideoDecoder VideoDecoder VideoDecoder.decodeQueueSize dequeue
decoder.addEventListener("dequeue", (event) => {
// Queue up more decoding work
});
flush
await decoder.flush();
flush() VideoFrame VideoDecoder close()
decoder.close();
VideoDecoder EncodedVideoChunk "closed" VideoDecoder
let chunkIndex = 0;
for (let i = 0; i < BATCH_LENGTH; i++) {
// Check if decoder failed
if (decoder.state === "closed") {
// Seek forward to the next key frame from the current position
for (let j = chunkIndex; j < chunks.length; j++) {
if (chunks[j].type === "key") {
chunkIndex = j;
break;
}
}
// Close the old decoder, instantiate and configure a new decoder
}
decoder.decode(chunks[chunkIndex]);
chunkIndex++;
}
VideoFrame VideoDecoder
VideoFrame
const bitmapFrame = new VideoFrame(imgBitmap, { timestamp: 0 });
const imageFrame = new VideoFrame(htmlImageEl, { timestamp: 0 });
const videoFrame = new VideoFrame(htmlVideoEl, { timestamp: 0 });
const canvasFrame = new VideoFrame(canvasEl, { timestamp: 0 });
Canvas VideoFrame Canvas VideoFrame
const rgbaFrame = new VideoFrame(rgbaData, {
timestamp: 0,
format: "RGBA",
codedWidth: 1920,
codedHeight: 1080,
});
VideoFrame CanvasBitmapVideoImage VideoFrame
ArrayBuffer Uint8ClampedArray VideoFrame CPU
VideoDecoderEncodedVideoChunkVideoFrame
VideoFrame Canvas
drawImage CanvasRenderingContext2D
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext("2d");
ctx.drawImage(frame, 0, 0);
2D API
ImageBitmap transferFromImageBitmap ImageBitmapRenderingContext
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext("bitmaprenderer");
const bitmap = await createImageBitmap(frame);
ctx.transferFromImageBitmap(bitmap);
frame.close();
1 Canvas2D API
VideoFrame WebGPU importExternalTexture
const externalTexture = device.importExternalTexture({ source: frame });
importExternalTexture WebGPU VideoFrame VideoFrame
VideoFrame GPU 1
frame.close();
encoder.encode(frame, { keyFrame: true });
frame.close();
ctx.drawImage(frame, 0, 0);
frame.close();
worker.postMessage(frame, [frame]);
WebCodecs AudioEncoder AudioDecoder Opus AAC
EncodedAudioChunk WebCodecs API AudioData AudioBuffer API
EncodedAudioChunk AudioContext.decodeAudioData()
// mux encoded chunks to an ArrayBuffer using a muxing library
const buffer = await muxAudioToBuffer(encodedChunks);
const audioBuffer = await audioContext.decodeAudioData(buffer);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start();
copyTo() AudioData AudioBuffer CPU
AudioData 1 EncodedAudioChunk
const encoder = new AudioEncoder({
output(chunk) {
// send to muxer
},
error(e) {
console.error(e);
},
});
encoder.configure({
codec: "opus",
sampleRate: 48000,
numberOfChannels: 2,
});
for (const audioData of rawAudio) {
encoder.encode(audioData);
audioData.close();
}
await encoder.flush();
const decoder = new AudioDecoder({
output(audioData) {
// process AudioData
audioData.close();
},
error(e) {
console.error(e);
},
});
// config comes from demuxer library
decoder.configure(decoderConfig);
for (const chunk of encodedChunks) {
decoder.decode(chunk);
}
await decoder.flush();
AudioData 0.2 0.5 AudioData.copyTo() Float32Array AudioData format
f32-planar planeIndex
// f32-planar: each channel stored separately
const leftChannel = new Float32Array(audioData.numberOfFrames);
audioData.copyTo(leftChannel, { planeIndex: 0 });
const rightChannel = new Float32Array(audioData.numberOfFrames);
audioData.copyTo(rightChannel, { planeIndex: 1 });
f32 [L, R, L, R, ...]
// f32: channels interleaved in a single array
const interleaved = new Float32Array(
audioData.numberOfFrames * audioData.numberOfChannels,
);
audioData.copyTo(interleaved, { planeIndex: 0 });
const leftChannel = new Float32Array(audioData.numberOfFrames);
const rightChannel = new Float32Array(audioData.numberOfFrames);
for (let i = 0; i < audioData.numberOfFrames; i++) {
leftChannel[i] = interleaved[i * 2];
rightChannel[i] = interleaved[i * 2 + 1];
}
if (audioData.format.includes("planar")) {
// f32-planar: copy each channel by planeIndex
} else {
// f32: copy interleaved, then de-interleave
}
AudioData 1 Float32Array f32-planar numberOfFrames
const framesPerChunk = 1024;
const data = new Float32Array(framesPerChunk * 2); // 2 channels
data.set(leftChannel, 0);
data.set(rightChannel, framesPerChunk);
const audioData = new AudioData({
format: "f32-planar",
sampleRate: 48000,
numberOfFrames: framesPerChunk,
numberOfChannels: 2,
timestamp: sourceAudioData.timestamp,
data,
});
AAC mp4a.40.5mp4a.40.05mp4a.40.29 (SBR) 2 audioData.sampleRate
VideoFrame AudioData
audioData.close();
AudioData VideoFrame 48kHz 1 1.4GB
| Web Proxy Viewer | New URL | Original Page |