// META: global=window,dedicatedworker
// META: script=/webcodecs/video-encoder-utils.js
// META: variant=?av1
// META: variant=?vp8
// META: variant=?vp9
// META: variant=?h264

var ENCODER_CONFIG = null;
promise_setup(async () => {
  const config = {
    '?av1': {codec: 'av01.0.04M.08'},
    '?vp8': {codec: 'vp8'},
    '?vp9': {codec: 'vp09.00.10.08'},
    '?h264': {codec: 'avc1.42001E', avc: {format: 'annexb'}}
  }[location.search];
  config.hardwareAcceleration = 'prefer-software';
  config.width = 320;
  config.height = 200;
  config.bitrate = 1000000;
  config.bitrateMode = "constant";
  config.framerate = 30;
  ENCODER_CONFIG = config;
});

async function svc_test(t, layers, base_layer_decimator) {
  let encoder_config = { ...ENCODER_CONFIG };
  encoder_config.scalabilityMode = "L1T" + layers;
  const w = encoder_config.width;
  const h = encoder_config.height;
  await checkEncoderSupport(t, encoder_config);

  let frames_to_encode = 24;
  let frames_decoded = 0;
  let frames_encoded = 0;
  let chunks = [];
  let corrupted_frames = [];

  const encoder_init = {
    output(chunk, metadata) {
      frames_encoded++;

      // Filter out all frames, but base layer.
      assert_own_property(metadata, "svc");
      assert_own_property(metadata.svc, "temporalLayerId");
      assert_less_than(metadata.svc.temporalLayerId, layers);
      if (metadata.svc.temporalLayerId == 0)
        chunks.push(chunk);
    },
    error(e) {
      assert_unreached(e.message);
    }
  };

  let encoder = new VideoEncoder(encoder_init);
  encoder.configure(encoder_config);

  for (let i = 0; i < frames_to_encode; i++) {
    let frame = createDottedFrame(w, h, i);
    encoder.encode(frame, { keyFrame: false });
    frame.close();
  }
  await encoder.flush();

  let decoder = new VideoDecoder({
    output(frame) {
      frames_decoded++;
      // Check that we have intended number of dots and no more.
      // Completely black frame shouldn't pass the test.
      if(!validateBlackDots(frame, frame.timestamp) ||
         validateBlackDots(frame, frame.timestamp + 1)) {
        corrupted_frames.push(frame.timestamp)
      }
      frame.close();
    },
    error(e) {
      assert_unreached(e.message);
    }
  });

  let decoder_config = {
    codec: encoder_config.codec,
    hardwareAcceleration: encoder_config.hardwareAcceleration,
    codedWidth: w,
    codedHeight: h,
  };
  decoder.configure(decoder_config);

  for (let chunk of chunks) {
    decoder.decode(chunk);
  }
  await decoder.flush();

  encoder.close();
  decoder.close();
  assert_equals(frames_encoded, frames_to_encode);

  let base_layer_frames = frames_to_encode / base_layer_decimator;
  assert_equals(chunks.length, base_layer_frames);
  assert_equals(frames_decoded, base_layer_frames);
  assert_equals(corrupted_frames.length, 0,
    `corrupted_frames: ${corrupted_frames}`);
}

promise_test(async t => { return svc_test(t, 2, 2) }, "SVC L1T2");
promise_test(async t => { return svc_test(t, 3, 4) }, "SVC L1T3");