[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/ja/docs/Web/API/WebCodecs_API/Using_the_WebCodecs_API [Back]  [Original]

WebCodecs API - Web API | MDN

MDN Web Docs

View in English Always switch to English

WebCodecs API

WebCodecs API VideoFrame AudioData

VideoEncoder output error output EncodedVideoChunk metadata metadata EncodedVideoChunkMetadata decoderConfig

js
const encoder = new VideoEncoder({
  output(chunk, meta) {
    // Do something with chunk, typically send to muxing library
  },
  error(e) {
    // Handle the error
  },
});

js
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

js
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

VideoEncoder.encodeQueueSize

dequeue encodeQueueSize

js
encoder.addEventListener("dequeue", (event) => {
  // Queue up more encoding work
});

flush()

js
await encoder.flush();

flush() EncodedVideoChunk VideoEncoder close()

js
encoder.close();

VideoEncoder "closed" VideoEncoder

js
if (encoder.state === "closed") {
  // Close the old encoder, instantiate and configure a new encoder
}

encoder.encode(frame, { keyFrame: true });

output error VideoDecoder output VideoFrame

js
const decoder = new VideoDecoder({
  output(frame) {
    // Do something with the VideoFrame
  },
  error(e) {
    /** Handle the error */
  },
});

WebCodecs VideoEncoder

js
decoder.configure(/* config */);

1

js
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

js
decoder.addEventListener("dequeue", (event) => {
  // Queue up more decoding work
});

flush

js
await decoder.flush();

flush() VideoFrame VideoDecoder close()

js
decoder.close();

VideoDecoder EncodedVideoChunk "closed" VideoDecoder

js
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

VideoFrame VideoDecoder

VideoFrame

js
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

ArrayBuffer VideoFrame format

js
const rgbaFrame = new VideoFrame(rgbaData, {
  timestamp: 0,
  format: "RGBA",
  codedWidth: 1920,
  codedHeight: 1080,
});

VideoFrame CanvasBitmapVideoImage VideoFrame

ArrayBuffer Uint8ClampedArray VideoFrame CPU

VideoDecoderEncodedVideoChunkVideoFrame

VideoFrame Canvas

Canvas2D

drawImage CanvasRenderingContext2D

js
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext("2d");
ctx.drawImage(frame, 0, 0);

2D API

BitmapRenderer

ImageBitmap transferFromImageBitmap ImageBitmapRenderingContext

js
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext("bitmaprenderer");

const bitmap = await createImageBitmap(frame);
ctx.transferFromImageBitmap(bitmap);
frame.close();

1 Canvas2D API

WebGPU

VideoFrame WebGPU importExternalTexture

js
const externalTexture = device.importExternalTexture({ source: frame });

importExternalTexture WebGPU VideoFrame VideoFrame

VideoFrame GPU 1

js
frame.close();

js
encoder.encode(frame, { keyFrame: true });
frame.close();

js
ctx.drawImage(frame, 0, 0);
frame.close();

VideoFrame

js
worker.postMessage(frame, [frame]);

WebCodecs AudioEncoder AudioDecoder Opus AAC

  • : EncodedAudioChunk
  • : WebCodecs API API
  • : WebCodecs Opus AAC MP3

WebCodecs API AudioData AudioBuffer API

EncodedAudioChunk AudioContext.decodeAudioData()

js
// 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

js
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();

Opus AAC

js
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

AudioData 0.2 0.5 AudioData.copyTo() Float32Array AudioData format

f32-planar planeIndex

js
// 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, ...]

js
// 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];
}

js
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

js
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

js
audioData.close();

AudioData VideoFrame 48kHz 1 1.4GB


Web Proxy Viewer  |  New URL  |  Original Page