WebCodecs 4K Transcode

by dustinkerstein

HTML

<script src="https://w3c.github.io/webcodecs/samples/mp4-decode/mp4box.all.min.js"></script>
<script src="https://w3c.github.io/webcodecs/samples/mp4-decode/mp4_demuxer.js"></script>
<html>
  <head>
  </head>
  <body>
    <canvas id="decodedFrames"></canvas>
    <canvas id="transcodedFrames"></canvas>
  </body>
</html>

CSS

body {
  margin: 0;
  background-color: #1f2227;
}
canvas {
  width: 100vw;
  height: 50vh;
}

JavaScript

// Note all stats are in the console. Transcode will take ~15 seconds.
var t0 = performance.now();
var t1;
var demuxer = new MP4Demuxer("https://files.panomoments.com/uhd_dar.mp4"); // 3840x2160 243 frames
var canvasDecodedFrames = document.getElementById('decodedFrames');
var canvasTranscodedFrames = document.getElementById('transcodedFrames');
var ctxDecodedFrames = canvasDecodedFrames.getContext('2d');
var ctxTranscodedFrames = canvasTranscodedFrames.getContext('2d');
var encoderStarted;
var timeout;
var fpsArray = [];
var startTime = 0;
var decoder1FrameCount = 0;
var decoder2FrameCount = 0;
var encoderFrameCount = 0;
const bitrate = 10000000;
const framerate = 30;
const drawCanvas = true; // Enabling will hurt performance
const optimizeForLatency = true;
const decoderHardwareAcceleration = "prefer-hardware";

console.log("drawCanvas: " + drawCanvas);
console.log("optimizeForLatency: " + optimizeForLatency);
console.log("decoderHardwareAcceleration: " + decoderHardwareAcceleration);
console.log(" ");

async function onFrame1(frame) {
  decoder1FrameCount++;
  encoder.encode(frame, {
    keyFrame: false
  });
  if (drawCanvas) ctxDecodedFrames.drawImage(frame, 0, 0, canvasDecodedFrames.width, canvasDecodedFrames.height);
  frame.close();
  if (decoder1FrameCount == 243) { // HACK
    console.log("Flushing encoder");
    encoder.flush();
  }
}

async function onFrame2(frame) {
  decoder2FrameCount++;
  if (drawCanvas) ctxTranscodedFrames.drawImage(frame, 0, 0, canvasTranscodedFrames.width, canvasTranscodedFrames.height);
  frame.close();
  if (timeout) clearTimeout(timeout);
  t1 = performance.now();
  timeout = setTimeout(summary, 1000);
}

function onChunk(chunk, encodedVideoChunkMetadata) {
  const fps = getEncoderFPS();
  if (fps) fpsArray.push(parseInt(fps));
  if (!encoderStarted) {
    encodedVideoChunkMetadata.decoderConfig.optimizeForLatency = optimizeForLatency;
    encodedVideoChunkMetadata.decoderConfig.hardwareAcceleration =...