Use this file to discover all available pages before exploring further.
The server dispatches incoming RTSP messages to your handler struct by checking which ServerHandler* interfaces it implements. Each interface defines a single method. Implement only the interfaces relevant to your use case.
Called when a new RTSP session is created. Sessions are created lazily on the first SETUP or ANNOUNCE request.
func (h *myHandler) OnSessionOpen(ctx *gortsplib.ServerHandlerOnSessionOpenCtx) { // ctx.Session — the new *ServerSession // ctx.Conn — the connection that created the session log.Printf("session opened")}
ServerHandlerOnSessionClose
Called when a session is closed. Use this to tear down any resources associated with the session — for example, closing a ServerStream when the publisher disconnects.
func (h *myHandler) OnSessionClose(ctx *gortsplib.ServerHandlerOnSessionCloseCtx) { // ctx.Session — the closed *ServerSession // ctx.Error — the reason for closure if h.stream != nil && ctx.Session == h.publisher { h.stream.Close() h.stream = nil }}
Called when a client sends a DESCRIBE request. Readers call this to fetch the stream description (SDP) before calling SETUP.Return the *ServerStream the client should be offered, or nil with StatusNotFound if no stream is available.
Called when a ServerStream fails to deliver a packet to a particular reader session. The reader is disconnected automatically; this callback is informational.
ServerStream holds the stream description and distributes packets to all readers. You create one in OnAnnounce (for live relay) or before starting the server (for file serving).
// Create and initialize a stream.stream := &gortsplib.ServerStream{ Server: s, // pointer to the running *Server Desc: sdpDesc, // *description.Session — the SDP}if err := stream.Initialize(); err != nil { return err}defer stream.Close()// Write a packet to all active readers.stream.WritePacketRTP(media, rtpPacket)// Write a packet with an explicit NTP timestamp.stream.WritePacketRTPWithNTP(media, rtpPacket, time.Now())// Write a raw RTCP packet.stream.WritePacketRTCP(media, rtcpPacket)
Call stream.Close() when the publisher disconnects. This immediately disconnects all active readers.
This example creates a ServerStream before accepting connections and streams a MPEG-TS file to all readers.
server-play-format-h264-from-disk/main.go
// Package main contains an example.package mainimport ( "log" "sync" "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")type serverHandler struct { server *gortsplib.Server stream *gortsplib.ServerStream mutex sync.RWMutex}func (sh *serverHandler) OnConnOpen(_ *gortsplib.ServerHandlerOnConnOpenCtx) { log.Printf("conn opened")}func (sh *serverHandler) OnConnClose(ctx *gortsplib.ServerHandlerOnConnCloseCtx) { log.Printf("conn closed (%v)", ctx.Error)}func (sh *serverHandler) OnSessionOpen(_ *gortsplib.ServerHandlerOnSessionOpenCtx) { log.Printf("session opened")}func (sh *serverHandler) OnSessionClose(_ *gortsplib.ServerHandlerOnSessionCloseCtx) { log.Printf("session closed")}func (sh *serverHandler) OnDescribe( _ *gortsplib.ServerHandlerOnDescribeCtx,) (*base.Response, *gortsplib.ServerStream, error) { log.Printf("DESCRIBE request") sh.mutex.RLock() defer sh.mutex.RUnlock() return &base.Response{ StatusCode: base.StatusOK, }, sh.stream, nil}func (sh *serverHandler) OnSetup( _ *gortsplib.ServerHandlerOnSetupCtx,) (*base.Response, *gortsplib.ServerStream, error) { log.Printf("SETUP request") sh.mutex.RLock() defer sh.mutex.RUnlock() return &base.Response{ StatusCode: base.StatusOK, }, sh.stream, nil}func (sh *serverHandler) OnPlay(_ *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) { log.Printf("PLAY request") return &base.Response{ StatusCode: base.StatusOK, }, nil}func main() { h := &serverHandler{} // prevent clients from connecting to the server until the stream is properly set up h.mutex.Lock() // create the server h.server = &gortsplib.Server{ Handler: h, RTSPAddress: ":8554", UDPRTPAddress: ":8000", UDPRTCPAddress: ":8001", MulticastIPRange: "224.1.0.0/16", MulticastRTPPort: 8002, MulticastRTCPPort: 8003, } // start the server err := h.server.Start() if err != nil { panic(err) } defer h.server.Close() // create a RTSP description that contains a H264 format desc := &description.Session{ Medias: []*description.Media{{ Type: description.MediaTypeVideo, Formats: []format.Format{&format.H264{ PayloadTyp: 96, PacketizationMode: 1, }}, }}, } // create a server stream h.stream = &gortsplib.ServerStream{ Server: h.server, Desc: desc, } err = h.stream.Initialize() if err != nil { panic(err) } defer h.stream.Close() // create file streamer r := &fileStreamer{stream: h.stream} err = r.initialize() if err != nil { panic(err) } defer r.close() // allow clients to connect h.mutex.Unlock() // wait until a fatal error log.Printf("server is ready on %s", h.server.RTSPAddress) panic(h.server.Wait())}
When serving a pre-built stream (not relay), hold the mutex locked during setup so no client can send a DESCRIBE before the stream is ready. Release it once Initialize() succeeds.