The play workflow follows a fixed sequence of RTSP method calls. Every step must succeed before you proceed to the next.
Connect to the server
Parse the URL, populate Scheme and Host, then call Start().u, err := base.ParseURL("rtsp://myuser:mypass@localhost:8554/mystream")
if err != nil {
panic(err)
}
c := gortsplib.Client{
Scheme: u.Scheme,
Host: u.Host,
}
err = c.Start()
if err != nil {
panic(err)
}
defer c.Close()
Call defer c.Close() immediately after Start() succeeds — not after later calls — so resources are always released even when Describe or SetupAll fails.
Describe available medias
Describe sends a DESCRIBE request and returns a *description.Session that lists every media track on the path.desc, _, err := c.Describe(u)
if err != nil {
panic(err)
}
Set up all medias
SetupAll sends a SETUP request for each media in the description.err = c.SetupAll(desc.BaseURL, desc.Medias)
if err != nil {
panic(err)
}
Register packet callbacks
Register your callbacks before calling Play. The library delivers packets on these callbacks from a background goroutine.// called for every RTP packet on any media
c.OnPacketRTPAny(func(medi *description.Media, _ format.Format, _ *rtp.Packet) {
log.Printf("RTP packet from media %v\n", medi)
})
// called for every RTCP packet on any media
c.OnPacketRTCPAny(func(medi *description.Media, pkt rtcp.Packet) {
log.Printf("RTCP packet from media %v, type %T\n", medi, pkt)
})
Start playing
Play sends the PLAY request. The server begins streaming immediately._, err = c.Play(nil)
if err != nil {
panic(err)
}
// block until a fatal error occurs
panic(c.Wait())
c.Wait() blocks the calling goroutine until the connection is terminated — either by a network error, a server close, or your own call to c.Close(). Always call it (or select on a channel) to keep the process alive.
Complete example
The following program connects to an RTSP server and logs every incoming RTP and RTCP packet.
// 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/rtcp"
"github.com/pion/rtp"
)
// This example shows how to:
// 1. connect to a RTSP server.
// 2. read all media streams on a path.
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, _ *rtp.Packet) {
log.Printf("RTP packet from media %v\n", medi)
})
// called when a RTCP packet arrives
c.OnPacketRTCPAny(func(medi *description.Media, pkt rtcp.Packet) {
log.Printf("RTCP packet from media %v, type %T\n", medi, pkt)
})
// start playing
_, err = c.Play(nil)
if err != nil {
panic(err)
}
// wait until a fatal error
panic(c.Wait())
}
Querying available streams
If you only need to inspect what medias a server offers — without actually receiving packets — call Describe and inspect the result without proceeding to SetupAll or Play.
// Package main contains an example.
package main
import (
"log"
"github.com/bluenviron/gortsplib/v5"
"github.com/bluenviron/gortsplib/v5/pkg/base"
)
// This example shows how to:
// 1. connect to a RTSP server.
// 2. get and print informations about medias published on a path.
func main() {
u, err := base.ParseURL("rtsp://myuser:mypass@localhost:8554/mypath")
if err != nil {
panic(err)
}
c := gortsplib.Client{
Scheme: u.Scheme,
Host: u.Host,
}
err = c.Start()
if err != nil {
panic(err)
}
defer c.Close()
desc, _, err := c.Describe(u)
if err != nil {
panic(err)
}
log.Printf("available medias: %v\n", desc.Medias)
}
Reading timestamps
Use c.PacketPTS and c.PacketNTP inside any RTP callback to retrieve timing information for each packet.
client-play-timestamp/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())
}
PacketNTP returns a value only when the server sends RTCP sender reports containing NTP timestamps. For streams that don’t send sender reports, ntpAvailable is false.
Pause and resume
Call c.Pause() to send a PAUSE request that suspends packet delivery without disconnecting. Call c.Play(nil) again to resume from where you left off.
client-play-pause/main.go
// Package main contains an example.
package main
import (
"log"
"time"
"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/rtcp"
"github.com/pion/rtp"
)
// This example shows how to:
// 1. connect to a RTSP server and read all medias on a path.
// 2. wait for 5 seconds.
// 3. pause for 5 seconds.
// 4. repeat.
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, _ *rtp.Packet) {
log.Printf("RTP packet from media %v\n", medi)
})
// called when a RTCP packet arrives
c.OnPacketRTCPAny(func(medi *description.Media, pkt rtcp.Packet) {
log.Printf("RTCP packet from media %v, type %T\n", medi, pkt)
})
// start playing
_, err = c.Play(nil)
if err != nil {
panic(err)
}
for {
// wait
time.Sleep(5 * time.Second)
// pause
_, err = c.Pause()
if err != nil {
panic(err)
}
// wait
time.Sleep(5 * time.Second)
// play again
_, err = c.Play(nil)
if err != nil {
panic(err)
}
}
}
Not all servers honor PAUSE requests. If the server does not support it, c.Pause() returns an error.