demo · v130
timestamp-shifted forwarding
One of the chromestatus motivation's use cases by name: "Altering the timestamp of a frame to introduce a delay." Picture an SFU that needs to keep video and external IP-camera audio in lip-sync, or a real-time translator that emits the dubbed audio a few hundred ms after the original. Build an encoded transform that adds a constant offset to every frame's RTP timestamp before forwarding.
RTCEncodedVideoFrame/RTCEncodedAudioFrame with custom metadata is what Chrome 130 ships. The probe below shows whether the constructor accepts the new metadata dictionary. The simulation runs regardless.
camera/mic
read frame.metadata()
build new metadata
new RTCEncodedVideoFrame(...)
peer connection / SFU
| # | original RTP ts | shifted RTP ts | delta |
|---|
other modify-metadata use cases
SSRC re-keying
Forward a track from one PeerConnection to another with a new SSRC. The downstream side never sees that the frame originated from a different stream.
mime-type rewriting
Wrap an H.264 baseline frame as VP8 (or vice versa) after transcoding — the transform owns the metadata, not just the payload.
frame dropping with continuity
Skip every Nth B-frame to reduce bandwidth, then rewrite the remaining frames' presentation timestamps so the receiver doesn't see gaps.
lipsync to external audio
Pin video frames to the audio clock by shifting timestamps to match the original sample boundaries the transcribed audio was generated from.
the code
// In the worker that owns the RTCRtpScriptTransform.
self.onmessage = (e) => {
const transformer = new TransformStream({
transform(frame, controller) {
// Read metadata (this part worked pre-Chrome 130).
const meta = frame.getMetadata();
// Chrome 130: construct a new frame with mutated metadata.
const shifted = new RTCEncodedVideoFrame(frame, {
metadata: {
...meta,
rtpTimestamp: meta.rtpTimestamp + delaySamples,
},
});
controller.enqueue(shifted);
},
});
e.data.readable.pipeThrough(transformer).pipeTo(e.data.writable);
};
// Pre-Chrome 130: you could mutate the payload but not the metadata —
// rtpTimestamp / mimeType / contributingSources were read-only on the
// frame and the constructor did not take a metadata dictionary. The
// only way to "shift" a frame's timestamp was to drop it and forge a
// new one — which broke the contributingSources chain.