v145 · Web APIs · WebRTC

Expose rtpTimestamp from WebRTC video frames via VideoFrame.metadata()

Chrome 145 adds rtpTimestamp to the metadata returned by VideoFrame.metadata() for WebRTC-sourced video frames, enabling web apps to correlate decoded frames with the original RTP packet timestamps.

background

When processing WebRTC video through the Insertable Streams API, frames arrive as VideoFrame objects. Previously, there was no way to determine which RTP packet a frame came from. The RTP timestamp is essential for A/V sync algorithms, jitter buffer analysis, and custom video processing pipelines that need to match frames to network-level events.

Chrome 145 exposes the rtpTimestamp field in the object returned by VideoFrame.metadata() for frames received via RTCRtpReceiver.

concepts

  1. rtpTimestamp Demo

    Uses a MediaStreamTrack processor to read VideoFrame.metadata() from a local video stream and display the rtpTimestamp and other timing fields available.

  2. VideoFrame Metadata

    Reference for all fields in the VideoFrameMetadata dictionary — rtpTimestamp, captureTime, receiveTime, presentationTime, and how they relate to each other.

  3. Jitter Tracker

    Computes RFC 3550 inter-arrival jitter across synthetic (or real-webcam) rtpTimestamp samples and plots the result. Inject a network spike and watch the curve react.

  4. RTP Clock Converter

    Convert an rtpTimestamp value to wall-clock seconds, compute inter-frame deltas, and use the frame-drop detector — which flags any gap larger than 1.5× the expected frame interval. Simulate a frame sequence and inject a drop to see the detector fire.

the change

// Chrome 145: rtpTimestamp available on WebRTC-received VideoFrames
const receiver = peerConnection.getReceivers()
  .find(r => r.track.kind === 'video');

const streams = receiver.createEncodedStreams();
const transformer = new TransformStream({
  transform(chunk, controller) {
    // chunk is an EncodedVideoChunk — for decoded frames use MediaStreamTrackProcessor
    controller.enqueue(chunk);
  }
});

// Via MediaStreamTrackProcessor (decoded frames)
const processor = new MediaStreamTrackProcessor({ track: videoTrack });
const reader = processor.readable.getReader();

while (true) {
  const { value: frame, done } = await reader.read();
  if (done) break;

  const meta = frame.metadata();
  console.log('RTP timestamp:', meta.rtpTimestamp);  // new in Chrome 145
  console.log('Capture time:', meta.captureTime);
  console.log('Receive time:', meta.receiveTime);

  frame.close();
}

references