Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
324 changes: 145 additions & 179 deletions webrtc-encoded-transform/script-metadata-transform.https.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,39 @@
}
}

async function gatherMetadata(test, kind)
async function gatherMetadata(test, kind, neededExtensions = [])
{
worker = new Worker('script-metadata-transform-worker.js');
const data = await new Promise(resolve => worker.onmessage = (event) => resolve(event.data));
assert_equals(data, 'registered');

// Both audio and vido are needed at one time or another
// so asking for both permissions
// so asking for both permissions.
await setMediaPermission();
const localStream = await navigator.mediaDevices.getUserMedia({[kind]: true});

let sender, receiver;
const senderTransform = new RTCRtpScriptTransform(worker, {name:'sender'});
const receiverTransform = new RTCRtpScriptTransform(worker, {name:'receiver'});

await new Promise((resolve, reject) => {
createConnections(test, (firstConnection) => {
pc1 = firstConnection;
sender = firstConnection.addTrack(localStream.getTracks()[0], localStream);
const sender = firstConnection.addTrack(localStream.getTracks()[0], localStream);
sender.transform = senderTransform;
if ('getHeaderExtensionsToNegotiate' in RTCRtpTransceiver.prototype) {
const transceiver = pc1.getTransceivers()[0];
const extensions = transceiver.getHeaderExtensionsToNegotiate();
extensions.forEach(ext => {
if (neededExtensions.includes(ext.uri)) {
ext.direction = 'sendrecv';
}
});
transceiver.setHeaderExtensionsToNegotiate(extensions);
}
}, (secondConnection) => {
pc2 = secondConnection;
secondConnection.ontrack = (trackEvent) => {
receiver = trackEvent.receiver;
const receiver = trackEvent.receiver;
receiver.transform = receiverTransform;
resolve(trackEvent.streams[0]);
};
Expand Down Expand Up @@ -79,176 +88,119 @@
}
}

promise_test(async (test) => {
const data = await gatherMetadata(test, 'audio');

assert_equals(typeof data.senderBeforeWrite.timestamp, 'number');
assert_not_equals(data.senderBeforeWrite.timestamp, 0);
assert_equals(data.senderBeforeWrite.timestamp,
data.senderAfterWrite.timestamp,
'timestamp matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.timestamp,
data.receiverBeforeWrite.timestamp,
'timestamp matches (for sender and receiver)');
assert_equals(data.receiverBeforeWrite.timestamp,
data.receiverAfterWrite.timestamp,
'timestamp matches (for receiver before and after write)');
}, 'audio metadata: timestamp');

promise_test(async (test) => {
const data = await gatherMetadata(test, 'audio');

assert_equals(typeof data.senderBeforeWrite.metadata.synchronizationSource, 'number');
assert_not_equals(data.senderBeforeWrite.metadata.synchronizationSource, 0);
assert_equals(data.senderBeforeWrite.metadata.synchronizationSource,
data.senderAfterWrite.metadata.synchronizationSource,
'ssrc matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata.synchronizationSource,
data.receiverBeforeWrite.metadata.synchronizationSource,
'ssrc matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata.synchronizationSource,
data.receiverAfterWrite.metadata.synchronizationSource,
'ssrc matches (for receiver before and after write)');
}, 'audio metadata: synchronizationSource');

promise_test(async (test) => {
const data = await gatherMetadata(test, 'audio');

assert_equals(typeof data.senderBeforeWrite.metadata.payloadType, 'number');
assert_equals(data.senderBeforeWrite.metadata.payloadType,
data.senderAfterWrite.metadata.payloadType,
'payload type matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata.payloadType,
data.receiverBeforeWrite.metadata.payloadType,
'payload type matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata.payloadType,
data.receiverAfterWrite.metadata.payloadType,
'payload type matches (for receiver before and after write)');
}, 'audio metadata: payloadType');
function checkSendReceive(data, propertyName, propertyType) {
assert_equals(typeof data.senderBeforeWrite.metadata[propertyName], propertyType);
assert_equals(data.senderBeforeWrite.metadata[propertyName],
data.senderAfterWrite.metadata[propertyName],
propertyName + ' matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata[propertyName],
data.receiverBeforeWrite.metadata[propertyName],
propertyName + ' matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata[propertyName],
data.receiverAfterWrite.metadata[propertyName],
propertyName + ' matches (for receiver before and after write)');
}

promise_test(async (test) => {
const data = await gatherMetadata(test, 'audio');
function checkReceiveOnly(data, propertyName, propertyType) {
assert_equals(data.senderBeforeWrite.metadata[propertyName], undefined);
assert_equals(data.senderBeforeWrite.metadata[propertyName],
data.senderAfterWrite[propertyName],
propertyName + ' matches (for sender before and after write)');
assert_equals(typeof data.receiverBeforeWrite.metadata[propertyName], propertyType);
assert_equals(data.receiverBeforeWrite.metadata[propertyName],
data.receiverAfterWrite.metadata[propertyName],
propertyName + ' matches (for receiver before and after write)');
}

assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.senderAfterWrite.metadata.contributingSources,
'csrcs are arrays, and match (for sender before and after write)');
assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.receiverBeforeWrite.metadata.contributingSources,
'csrcs are arrays, and match');
assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.receiverAfterWrite.metadata.contributingSources,
'csrcs are arrays, and match (for receiver before and after write)');
}, 'audio metadata: contributingSources');
function checkSendOnly(data, propertyName, propertyType) {
assert_equals(typeof data.senderBeforeWrite.metadata[propertyName], propertyType);
assert_equals(data.senderBeforeWrite.metadata[propertyName],
data.senderAfterWrite.metadata[propertyName],
propertyName + ' matches (for sender before and after write)');
assert_equals(data.receiverBeforeWrite.metadata[propertyName], undefined);
assert_equals(data.receiverBeforeWrite.metadata[propertyName],
data.receiverAfterWrite.metadata[propertyName],
propertyName + ' matches (for receiver before and after write)');
}

// https://w3c.github.io/webrtc-encoded-transform/#RTCEncodedFrameMetadata
['audio', 'video'].forEach(kind => {
promise_test(async (test) => {
const data = await gatherMetadata(test, kind);
checkSendReceive(data, 'synchronizationSource', 'number');
}, kind + ' metadata: synchronizationSource');

promise_test(async (test) => {
const data = await gatherMetadata(test, kind);
checkSendReceive(data, 'payloadType', 'number');
}, kind + ' metadata: payloadType');

promise_test(async (test) => {
const data = await gatherMetadata(test, kind);

assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.senderAfterWrite.metadata.contributingSources,
'csrcs are arrays, and match (for sender before and after write)');
assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.receiverBeforeWrite.metadata.contributingSources,
'csrcs are arrays, and match');
assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.receiverAfterWrite.metadata.contributingSources,
'csrcs are arrays, and match (for receiver before and after write)');
}, kind + ' metadata: contributingSources');

promise_test(async (test) => {
const data = await gatherMetadata(test, kind);
checkSendReceive(data, 'rtpTimestamp', 'number');
}, kind + ' metadata: rtpTimestamp');

promise_test(async (test) => {
const data = await gatherMetadata(test, kind);
checkReceiveOnly(data, 'receiveTime', 'number');
}, kind + ' metadata: receiveTime');

// Requires abs-capture-time header extension.
promise_test(async (test) => {
// TODO: crbug.com/391114797 - should negotiate the extension
// and checkSendReceive.
const data = await gatherMetadata(test, kind, [
// 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time',
]);
checkSendOnly(data, 'captureTime', 'number');
}, kind + ' metadata: captureTime');

// Requires abs-capture-time header extension.
promise_test(async (test) => {
const data = await gatherMetadata(test, kind, [
'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time',
]);
checkReceiveOnly(data, 'senderCaptureTimeOffset', 'number');
}, kind + ' metadata: senderCaptureTimeOffset');

promise_test(async (test) => {
const data = await gatherMetadata(test, kind);
checkSendReceive(data, 'mimeType', 'string');
}, kind + ' metadata: mimeType');
});

// https://w3c.github.io/webrtc-encoded-transform/#RTCEncodedAudioFrameMetadata
promise_test(async (test) => {
const data = await gatherMetadata(test, 'audio');

assert_equals(typeof data.receiverBeforeWrite.metadata.sequenceNumber,
'number');
assert_equals(data.receiverBeforeWrite.metadata.sequenceNumber,
data.receiverAfterWrite.metadata.sequenceNumber,
'sequenceNumber matches (for receiver before and after write)');
// spec says sequenceNumber exists only for incoming audio frames
assert_equals(data.senderBeforeWrite.metadata.sequenceNumber, undefined);
assert_equals(data.senderAfterWrite.metadata.sequenceNumber, undefined);
checkReceiveOnly(data, 'sequenceNumber', 'number');
}, 'audio metadata: sequenceNumber');

promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');

assert_equals(typeof data.senderBeforeWrite.timestamp, 'number');
assert_equals(data.senderBeforeWrite.timestamp,
data.senderAfterWrite.timestamp,
'timestamp matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.timestamp,
data.receiverBeforeWrite.timestamp,
'timestamp matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.timestamp,
data.receiverAfterWrite.timestamp,
'timestamp matches (for receiver before and after write)');
}, 'video metadata: timestamp');

promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');

assert_equals(typeof data.senderBeforeWrite.metadata.synchronizationSource,
'number');
assert_equals(data.senderBeforeWrite.metadata.synchronizationSource,
data.senderAfterWrite.metadata.synchronizationSource,
'ssrc matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata.synchronizationSource,
data.receiverBeforeWrite.metadata.synchronizationSource,
'ssrc matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata.synchronizationSource,
data.receiverAfterWrite.metadata.synchronizationSource,
'ssrc matches (for receiver before and after write)');
}, 'video metadata: ssrc');

promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');

assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.senderAfterWrite.metadata.contributingSources,
'csrcs are arrays, and match (for sender before and after write)');
assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.receiverBeforeWrite.metadata.contributingSources,
'csrcs are arrays, and match');
assert_array_equals(data.senderBeforeWrite.metadata.contributingSources,
data.receiverAfterWrite.metadata.contributingSources,
'csrcs are arrays, and match (for receiver before and after write)');
}, 'video metadata: csrcs');

promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');

assert_equals(typeof data.senderBeforeWrite.metadata.height, 'number');
assert_equals(data.senderBeforeWrite.metadata.height,
data.senderAfterWrite.metadata.height,
'height matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata.height,
data.receiverBeforeWrite.metadata.height,
'height matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata.height,
data.receiverAfterWrite.metadata.height,
'height matches (for receiver before and after write)');
assert_equals(typeof data.senderBeforeWrite.metadata.width, 'number');
assert_equals(data.senderBeforeWrite.metadata.width,
data.senderAfterWrite.metadata.width,
'width matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata.width,
data.receiverBeforeWrite.metadata.width,
'width matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata.width,
data.receiverAfterWrite.metadata.width,
'width matches (for receiver before and after write)');
}, 'video metadata: width and height');
const data = await gatherMetadata(test, 'audio');
checkSendReceive(data, 'audioLevel', 'number');
}, 'audio metadata: audioLevel');

// https://w3c.github.io/webrtc-encoded-transform/#RTCEncodedVideoFrame-interface
promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');

assert_equals(typeof data.senderBeforeWrite.metadata.spatialIndex,
'number');
assert_equals(data.senderBeforeWrite.metadata.spatialIndex,
data.senderAfterWrite.metadata.spatialIndex,
'spatialIndex matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata.spatialIndex,
data.receiverBeforeWrite.metadata.spatialIndex,
'spatialIndex matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata.spatialIndex,
data.receiverAfterWrite.metadata.spatialIndex,
'spatialIndex matches (for receiver before and after write)');
assert_equals(typeof data.senderBeforeWrite.metadata.temporalIndex,
'number');
assert_equals(data.senderBeforeWrite.metadata.temporalIndex,
data.senderAfterWrite.metadata.temporalIndex,
'temporalIndex matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.metadata.temporalIndex,
data.receiverBeforeWrite.metadata.temporalIndex,
'temporalIndex matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.metadata.temporalIndex,
data.receiverAfterWrite.metadata.temporalIndex,
'temporalIndex matches (for receiver before and after write)');
}, 'video metadata: spatial and temporal index');
const data = await gatherMetadata(test, 'video', [
'https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension',
]);
checkSendReceive(data, 'frameId', 'number');
}, 'video metadata: frameId');

promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');
Expand All @@ -266,20 +218,17 @@

promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');
checkSendReceive(data, 'width', 'number');
checkSendReceive(data, 'height', 'number');
}, 'video metadata: width and height');

assert_equals(typeof data.senderBeforeWrite.metadata.frameId, 'number');
assert_equals(data.senderBeforeWrite.metadata.frameId,
data.senderAfterWrite.metadata.frameId,
'frameId matches (for sender before and after write)');

assert_true(data.senderBeforeWrite.metadata.frameId == data.receiverBeforeWrite.metadata.frameId ||
data.receiverBeforeWrite.metadata.frameId == undefined,
'frameId matches (for sender and receiver)');
assert_equals(data.receiverBeforeWrite.metadata.frameId,
data.receiverAfterWrite.metadata.frameId,
'frameId matches (for receiver before and after write)');
}, 'video metadata: frameId');
promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');
checkSendReceive(data, 'spatialIndex', 'number');
checkSendReceive(data, 'temporalIndex', 'number');
}, 'video metadata: spatial and temporal index');

// https://w3c.github.io/webrtc-encoded-transform/#RTCEncodedVideoFrame-members
promise_test(async (test) => {
const data = await gatherMetadata(test, 'video');

Expand All @@ -294,8 +243,25 @@
assert_equals(data.senderBeforeWrite.type,
data.receiverAfterWrite.type,
'type matches (for receiver before and after write)');
}, 'video metadata: type');

}, 'video frame: type');

// Legacy timestamp, moved to metadata.rtpTimestamp.
['audio', 'video'].forEach(kind => {
promise_test(async (test) => {
const data = await gatherMetadata(test, kind);

assert_equals(typeof data.senderBeforeWrite.timestamp, 'number');
assert_equals(data.senderBeforeWrite.timestamp,
data.senderAfterWrite.timestamp,
'timestamp matches (for sender before and after write)');
assert_equals(data.senderBeforeWrite.timestamp,
data.receiverBeforeWrite.timestamp,
'timestamp matches (for sender and receiver)');
assert_equals(data.senderBeforeWrite.timestamp,
data.receiverAfterWrite.timestamp,
'type matches (for receiver before and after write)');
}, kind + ' frame: type (renamed to metadata.rtpTimestamp');
});
</script>
</body>
</html>
Loading