chromium/third_party/blink/web_tests/fast/peerconnection/RTCEncodedVideoFrame-set-metadata.html

<!DOCTYPE html>
<title>Test RTCEncodedVideoFrame.setMetadata in a loopback RTCPeerConnection</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="resources/RTCPeerConnection-helper.js"></script>
<script>
"use strict";

async function setupLoopbackWithCodedAndGetStreams(t, codec) {
  const caller = new RTCPeerConnection({encodedInsertableStreams:true});
  t.add_cleanup(() => caller.close());
  const callee = new RTCPeerConnection({encodedInsertableStreams:true});
  t.add_cleanup(() => callee.close());

  const stream = await navigator.mediaDevices.getUserMedia({video:true});
  const videoTrack = stream.getVideoTracks()[0];
  t.add_cleanup(() => videoTrack.stop());

  const transceiver = caller.addTransceiver(videoTrack);
  const codecCapability =
      RTCRtpSender.getCapabilities('video').codecs.find(capability => {
        return capability.mimeType.includes(codec);
      });
  assert_not_equals(codecCapability, undefined);
  transceiver.setCodecPreferences([codecCapability]);

  const senderStreams = transceiver.sender.createEncodedStreams();
  exchangeIceCandidates(caller, callee);

  const ontrackPromise = addEventListenerPromise(t, callee, 'track');

  await exchangeOfferAnswer(caller, callee);

  const trackEvent = await ontrackPromise;
  const receiverTransceiver = trackEvent.transceiver;
  const receiverStreams = receiverTransceiver.receiver.createEncodedStreams();

  return {senderStreams, receiverStreams};
}

async function setupLoopbackWithCodecAndGetReader(t, codec) {
  const {senderStreams, receiverStreams} = await setupLoopbackWithCodedAndGetStreams(t, codec);
  receiverStreams.readable.pipeTo(receiverStreams.writable);
  return senderStreams.readable.getReader();
}

promise_test(async t => {
  const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
  const result = await senderReader.read();
  const metadata = result.value.getMetadata();
  const clone = structuredClone(result.value);
  metadata.frameId = 2;
  metadata.dependencies = [1];
  clone.setMetadata(metadata);
 }, "setMetadata() does allow changes to frameId and dependencies without a flag");

promise_test(async t => {
  const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
  const result = await senderReader.read();
  const field_values = {
    'height':600,
    'payloadType':17,
    'spatialIndex':4,
    'synchronizationSource':17,
    'temporalIndex':5,
    'contributingSources' : [13],
    'width':800
  }
  for (const field in field_values) {
    const metadata = result.value.getMetadata();
    const clone = structuredClone(result.value);
    metadata[field] = field_values[field];
    assert_throws_dom('InvalidModificationError', () => clone.setMetadata(metadata), "For field \"" + field + "\",");
  }
}, "setMetadata() does not allow changes to fields without a flag");

promise_test(async t => {
  const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
  const result = await senderReader.read();
  const field_values = {
    'contributingSources':[13],
    'dependencies':[1],
    'frameId':2,
    'height':600,
    'spatialIndex':4,
    'synchronizationSource':1234567,
    'temporalIndex':5,
    'width':800
  }
  const metadata = result.value.getMetadata();
  const clone = structuredClone(result.value);
  for (const field in field_values) {
    metadata[field] = field_values[field];
  }
  clone.setMetadata(metadata);
  const cloneMetadata = clone.getMetadata();
  for (const field in field_values) {
    if(Array.isArray(field_values[field])) {
      assert_array_equals(cloneMetadata[field], field_values[field], field);
    } else {
      assert_equals(cloneMetadata[field], field_values[field], field);
    }
  }
}, "setMetadata() allows changes when AllowRTCEncodedVideoFrameSetMetadataAllFields is enabled");

promise_test(async t => {
  const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
  const result = await senderReader.read();
  const originalFrame = result.value;

  const metadata = originalFrame.getMetadata();
  const cloneFrame = structuredClone(originalFrame);

  metadata.frameId = undefined;
  metadata.dependencies = [1];
  assert_throws_dom('InvalidModificationError', () => cloneFrame.setMetadata(metadata), "frameID missing, but has dependencies in RTCEncodedVideoFrameMetadata.");
}, "setMetadata() does not allow metadata with dependencies but no frameId");

promise_test(async t => {
  const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
  const result = await senderReader.read();
  const originalFrame = result.value;

  const metadata = originalFrame.getMetadata();
  const cloneFrame = structuredClone(originalFrame);

  metadata.frameId = 2;
  metadata.dependencies = undefined;

  cloneFrame.setMetadata(metadata);
}, "setMetadata() does allow metadata with no dependencies but a frameId");
</script>