Use this file to discover all available pages before exploring further.
1
Install gortsplib
Add the library to your Go module:
go get github.com/bluenviron/gortsplib/v5
Go 1.25 or later is required. Run go version to confirm before proceeding.
2
Play a stream
The following program connects to an RTSP server, describes all available media streams, sets them up, and logs every incoming RTP and RTCP packet.
client-play/main.go
// Package main contains an example.package mainimport ( "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())}
Run it:
go run ./client-play/main.go
You should see log lines like:
2009/11/10 23:00:00 RTP packet from media &{video ...}2009/11/10 23:00:00 RTCP packet from media &{video ...}, type *rtcp.SenderReport
defer c.Close() is called immediately after c.Start() succeeds. Place it here — not after later calls — so the client is always cleaned up even when Describe or SetupAll returns an error.
3
Record a stream
To publish a stream, describe the formats you want to send, call StartRecording, and then write RTP packets in a loop.
client-record/main.go
package mainimport ( "github.com/bluenviron/gortsplib/v5" "github.com/bluenviron/gortsplib/v5/pkg/description" "github.com/bluenviron/gortsplib/v5/pkg/format" "github.com/pion/rtp")func main() { // describe the H264 format you want to publish forma := &format.H264{ PayloadTyp: 96, PacketizationMode: 1, } desc := &description.Session{ Medias: []*description.Media{{ Type: description.MediaTypeVideo, Formats: []format.Format{forma}, }}, } // connect to the server, announce the format, and start recording c := gortsplib.Client{} err := c.StartRecording("rtsp://myuser:mypass@localhost:8554/mystream", desc) if err != nil { panic(err) } defer c.Close() // write a single RTP packet to the first media pkt := &rtp.Packet{ Header: rtp.Header{ PayloadType: 96, }, Payload: []byte{0x00}, } err = c.WritePacketRTP(desc.Medias[0], pkt) if err != nil { panic(err) }}
The StartRecording call combines connecting, announcing the session description, and entering record state in a single step. For more advanced scenarios — such as encoding RGBA frames to H264 before sending — see the full example at examples/client-record-format-h264/main.go.
The H264 encoder example (examples/client-record-format-h264/main.go) carries a //go:build cgo tag and requires the FFmpeg development libraries. Pure RTP forwarding, as shown above, has no CGO dependency.
4
Build a server
A gortsplib server is built around a handler struct that implements the callbacks you care about. The example below accepts a single publisher and forwards its packets to all readers.
server/main.go
// Package main contains an example.package mainimport ( "log" "sync" "github.com/pion/rtp" "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")// This example shows how to:// 1. create a RTSP server which accepts plain connections.// 2. allow a single client to publish a stream.// 3. allow several clients to read the stream.type serverHandler struct { server *gortsplib.Server mutex sync.RWMutex stream *gortsplib.ServerStream publisher *gortsplib.ServerSession}// called when a connection is opened.func (sh *serverHandler) OnConnOpen(_ *gortsplib.ServerHandlerOnConnOpenCtx) { log.Printf("conn opened")}// called when a connection is closed.func (sh *serverHandler) OnConnClose(ctx *gortsplib.ServerHandlerOnConnCloseCtx) { log.Printf("conn closed (%v)", ctx.Error)}// called when a session is opened.func (sh *serverHandler) OnSessionOpen(_ *gortsplib.ServerHandlerOnSessionOpenCtx) { log.Printf("session opened")}// called when a session is closed.func (sh *serverHandler) OnSessionClose(ctx *gortsplib.ServerHandlerOnSessionCloseCtx) { log.Printf("session closed") sh.mutex.Lock() defer sh.mutex.Unlock() // if the session is the publisher, // close the stream and disconnect any reader. if sh.stream != nil && ctx.Session == sh.publisher { sh.stream.Close() sh.stream = nil }}// called when receiving a DESCRIBE request.func (sh *serverHandler) OnDescribe( _ *gortsplib.ServerHandlerOnDescribeCtx,) (*base.Response, *gortsplib.ServerStream, error) { log.Printf("DESCRIBE request") sh.mutex.RLock() defer sh.mutex.RUnlock() // no one is publishing yet if sh.stream == nil { return &base.Response{ StatusCode: base.StatusNotFound, }, nil, nil } // send medias that are being published to the client return &base.Response{ StatusCode: base.StatusOK, }, sh.stream, nil}// called when receiving an ANNOUNCE request.func (sh *serverHandler) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) (*base.Response, error) { log.Printf("ANNOUNCE request") sh.mutex.Lock() defer sh.mutex.Unlock() // disconnect existing publisher if sh.stream != nil { sh.stream.Close() sh.publisher.Close() } // create the stream and save the publisher sh.stream = &gortsplib.ServerStream{ Server: sh.server, Desc: ctx.Description, } err := sh.stream.Initialize() if err != nil { panic(err) } sh.publisher = ctx.Session return &base.Response{ StatusCode: base.StatusOK, }, nil}// called when receiving a SETUP request.func (sh *serverHandler) OnSetup(ctx *gortsplib.ServerHandlerOnSetupCtx) ( *base.Response, *gortsplib.ServerStream, error,) { log.Printf("SETUP request") // SETUP is used by both readers and publishers. In case of publishers, just return StatusOK. if ctx.Session.State() == gortsplib.ServerSessionStatePreRecord { return &base.Response{ StatusCode: base.StatusOK, }, nil, nil } sh.mutex.RLock() defer sh.mutex.RUnlock() // no one is publishing yet if sh.stream == nil { return &base.Response{ StatusCode: base.StatusNotFound, }, nil, nil } return &base.Response{ StatusCode: base.StatusOK, }, sh.stream, nil}// called when receiving a PLAY request.func (sh *serverHandler) OnPlay(_ *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) { log.Printf("PLAY request") return &base.Response{ StatusCode: base.StatusOK, }, nil}// called when receiving a RECORD request.func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.Response, error) { log.Printf("RECORD request") // called when receiving a RTP packet ctx.Session.OnPacketRTPAny(func(medi *description.Media, _ format.Format, pkt *rtp.Packet) { // route the RTP packet to all readers err := sh.stream.WritePacketRTP(medi, pkt) if err != nil { log.Printf("ERR: %v", err) } }) return &base.Response{ StatusCode: base.StatusOK, }, nil}func main() { // configure the server h := &serverHandler{} h.server = &gortsplib.Server{ Handler: h, RTSPAddress: ":8554", UDPRTPAddress: ":8000", UDPRTCPAddress: ":8001", MulticastIPRange: "224.1.0.0/16", MulticastRTPPort: 8002, MulticastRTCPPort: 8003, } // start server and wait until a fatal error log.Printf("server is ready on %s", h.server.RTSPAddress) panic(h.server.StartAndWait())}
Run it:
go run ./server/main.go
Expected output:
2009/11/10 23:00:00 server is ready on :8554
The server now accepts publisher connections on :8554 and streams RTP packets to any number of readers. UDP transport is available on ports 8000/8001, and multicast on 8002/8003.
Implement only the handler methods you need. If you omit OnAnnounce, the server will reject all ANNOUNCE requests automatically.