Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/bluenviron/gortsplib/llms.txt

Use this file to discover all available pages before exploring further.

RTP packets carry two distinct kinds of time information:
  • PTS (presentation timestamp) — a relative timestamp derived from the RTP Timestamp field divided by the codec clock rate. It tells you when to present a sample relative to the start of the stream, but carries no wall-clock information.
  • NTP timestamp — an absolute wall-clock time derived from RTCP Sender Report (SR) packets that the server sends alongside the RTP stream. It maps RTP timestamps onto real UTC time.

Extracting timestamps

PTS

Call c.PacketPTS(medi, pkt) inside any packet callback. It returns an int64 clock-tick value and a boolean indicating whether the value is currently available. The returned value is in the codec’s native clock units (e.g. 90000 ticks = 1 second for H.264 at 90 kHz). PTS is computed by gortsplib’s global decoder which accounts for RTP timestamp rollover and cross-track synchronisation.
PTS is always available once at least one packet has been received. You do not need the server to send RTCP SR packets to compute it.

NTP

Call c.PacketNTP(medi, pkt) to retrieve the wall-clock time associated with that RTP timestamp. It returns a time.Time and a boolean. The boolean is false until the server sends at least one RTCP Sender Report for that media — some servers never send SR packets, in which case NTP is never available.
Not all RTSP servers send RTCP Sender Reports. If ntpAvailable is false after several seconds of playback, the upstream server does not provide NTP synchronisation. Fall back to PTS for relative timing, or use an external mechanism (such as the stream start time) to anchor the timeline.

Complete example

main.go
// Package main contains an example.
package main

import (
	"log"

	"github.com/bluenviron/gortsplib/v5"
	"github.com/bluenviron/gortsplib/v5/pkg/base"
	"github.com/bluenviron/gortsplib/v5/pkg/description"
	"github.com/bluenviron/gortsplib/v5/pkg/format"
	"github.com/pion/rtp"
)

// This example shows how to:
// 1. connect to a RTSP server.
// 2. read all media streams on a path.
// 3. Get PTS and NTP of inbound RTP packets.

func main() {
	// parse URL
	u, err := base.ParseURL("rtsp://myuser:mypass@localhost:8554/mystream")
	if err != nil {
		panic(err)
	}

	c := gortsplib.Client{
		Scheme: u.Scheme,
		Host:   u.Host,
	}

	// connect to the server
	err = c.Start()
	if err != nil {
		panic(err)
	}
	defer c.Close()

	// find available medias
	desc, _, err := c.Describe(u)
	if err != nil {
		panic(err)
	}

	// setup all medias
	err = c.SetupAll(desc.BaseURL, desc.Medias)
	if err != nil {
		panic(err)
	}

	// called when a RTP packet arrives
	c.OnPacketRTPAny(func(medi *description.Media, _ format.Format, pkt *rtp.Packet) {
		// get the PTS (presentation timestamp) of the packet
		pts, ptsAvailable := c.PacketPTS(medi, pkt)
		log.Printf("PTS: available=%v, value=%v\n", ptsAvailable, pts)

		// get the NTP (absolute timestamp) of the packet
		ntp, ntpAvailable := c.PacketNTP(medi, pkt)
		log.Printf("NTP: available=%v, value=%v\n", ntpAvailable, ntp)
	})

	// start playing
	_, err = c.Play(nil)
	if err != nil {
		panic(err)
	}

	// wait until a fatal error
	panic(c.Wait())
}

Converting PTS to time.Duration

PacketPTS returns an int64 in clock-tick units. To convert it to a time.Duration for display or container muxing:
// inside OnPacketRTPAny callback
pts, ok := c.PacketPTS(medi, pkt)
if ok {
    clockRate := int64(forma.ClockRate()) // e.g. 90000 for H.264
    ptsDuration := time.Duration(pts) * time.Second / time.Duration(clockRate)
    log.Printf("PTS: %v", ptsDuration)
}
The int64 type accommodates negative timestamps and values larger than ~2.5 hours at 90 kHz without overflow. gortsplib also handles 32-bit RTP timestamp rollover (the counter wraps after ~13 hours at 90 kHz) internally.

Audio/video synchronisation

NTP timestamps are the recommended mechanism for synchronised multi-track recording:
  1. Wait until ntpAvailable is true on both the audio and video medias.
  2. Use the time.Time values as the presentation wall-clock anchor for each sample.
  3. Write samples to disk (e.g., a container muxer) using these absolute times to keep tracks aligned regardless of clock drift between audio and video encoders.
When recording to a container format such as MP4 or MKV, pass the NTP-derived time.Time of the first packet as the recording start time, then use PTS offsets for all subsequent packets. This gives you both accurate absolute timestamps in metadata and correct relative timing within the file.

NTP encoding

The github.com/bluenviron/gortsplib/v5/pkg/ntp package exposes the low-level NTP encode/decode functions used internally:
import "github.com/bluenviron/gortsplib/v5/pkg/ntp"

// encode a time.Time to the 64-bit NTP fixed-point format (RFC 3550 §4)
encoded := ntp.Encode(time.Now())

// decode back to time.Time
t := ntp.Decode(encoded)
You typically do not need these directly — c.PacketNTP() returns a time.Time already decoded.

Build docs developers (and LLMs) love