improve examples (#778)

This commit is contained in:
Alessandro Ros
2025-05-04 17:00:18 +02:00
committed by GitHub
parent 4e3c5e6c0d
commit cc0c96626c
55 changed files with 494 additions and 435 deletions

View File

@@ -95,8 +95,8 @@ Features:
* [server](examples/server/main.go)
* [server-tls](examples/server-tls/main.go)
* [server-auth](examples/server-auth/main.go)
* [server-h264-to-disk](examples/server-h264-to-disk/main.go)
* [server-h264-from-disk](examples/server-h264-from-disk/main.go)
* [server-record-format-h264-to-disk](examples/server-record-format-h264-to-disk/main.go)
* [server-play-format-h264-from-disk](examples/server-play-format-h264-from-disk/main.go)
* [proxy](examples/proxy/main.go)
## API Documentation

View File

@@ -13,9 +13,9 @@ import (
)
// This example shows how to
// 1. generate a dummy G711 audio stream
// 2. connect to a RTSP server, find a back channel that supports G711
// 3. route the G711 stream to the channel
// 1. generate a dummy G711 audio stream.
// 2. connect to a RTSP server, find a back channel that supports G711.
// 3. route the G711 stream to the channel.
func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d
@@ -72,7 +72,7 @@ func main() {
// find the back channel
medi, forma := findG711BackChannel(desc)
if medi == nil {
panic("media not found")
panic("back channel not found")
}
// setup a single media
@@ -137,7 +137,7 @@ func main() {
for _, pkt := range pkts {
pkt.Timestamp += uint32(int64(randomStart) + prevPTS)
err = c.WritePacketRTP(desc.Medias[0], pkt)
err = c.WritePacketRTP(medi, pkt)
if err != nil {
panic(err)
}

View File

@@ -19,10 +19,10 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a AV1 stream
// 3. decode the AV1 stream into RGBA frames
// 4. convert RGBA frames to JPEG images and save them on disk
// 1. connect to a RTSP server.
// 2. check if there's a AV1 stream.
// 3. decode the AV1 stream into RGBA frames.
// 4. convert RGBA frames to JPEG images and save them on disk.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -14,9 +14,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a AV1 stream
// 3. decode the AV1 stream into RGBA frames
// 1. connect to a RTSP server.
// 2. check if there's a AV1 stream.
// 3. decode the AV1 stream into RGBA frames.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -11,9 +11,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a G711 stream
// 3. decode the G711 stream into audio samples
// 1. connect to a RTSP server.
// 2. check if there's a G711 stream.
// 3. decode the G711 stream into audio samples.
func main() {
c := gortsplib.Client{}

View File

@@ -11,9 +11,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a H264 stream and a MPEG-4 audio stream
// 3. save the content of those formats in a file in MPEG-TS format
// 1. connect to a RTSP server.
// 2. check if there's a H264 stream and a MPEG-4 audio stream.
// 3. save the content of those formats in a file in MPEG-TS format.
func main() {
c := gortsplib.Client{}

View File

@@ -50,7 +50,11 @@ func (e *mpegtsMuxer) initialize() error {
},
}
e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.h264Track, e.mpeg4AudioTrack})
e.w = &mpegts.Writer{W: e.b, Tracks: []*mpegts.Track{e.h264Track, e.mpeg4AudioTrack}}
err = e.w.Initialize()
if err != nil {
return err
}
return nil
}

View File

@@ -11,9 +11,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a H264 stream
// 3. save the content of the format in a file in MPEG-TS format
// 1. connect to a RTSP server.
// 2. check if there's a H264 stream.
// 3. save the content of the format in a file in MPEG-TS format.
func main() {
c := gortsplib.Client{}

View File

@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
Codec: &mpegts.CodecH264{},
}
e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.track})
e.w = &mpegts.Writer{W: e.b, Tracks: []*mpegts.Track{e.track}}
err = e.w.Initialize()
if err != nil {
return err
}
return nil
}

View File

@@ -19,10 +19,10 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a H264 stream
// 3. decode the H264 stream into RGBA frames
// 4. convert RGBA frames to JPEG images and save them on disk
// 1. connect to a RTSP server.
// 2. check if there's a H264 stream.
// 3. decode the H264 stream into RGBA frames.
// 4. convert RGBA frames to JPEG images and save them on disk.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -14,9 +14,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's an H264 stream
// 3. decode the H264 stream into RGBA frames
// 1. connect to a RTSP server.
// 2. check if there's an H264 stream.
// 3. decode the H264 stream into RGBA frames.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -11,9 +11,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a H265 stream
// 3. save the content of the format in a file in MPEG-TS format
// 1. connect to a RTSP server.
// 2. check if there's a H265 stream.
// 3. save the content of the format in a file in MPEG-TS format.
func main() {
c := gortsplib.Client{}

View File

@@ -35,7 +35,11 @@ func (e *mpegtsMuxer) initialize() error {
Codec: &mpegts.CodecH265{},
}
e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.track})
e.w = &mpegts.Writer{W: e.b, Tracks: []*mpegts.Track{e.track}}
err = e.w.Initialize()
if err != nil {
return err
}
return nil
}

View File

@@ -19,10 +19,10 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a H265 stream
// 3. decode the H265 stream into RGBA frames
// 4. convert RGBA frames to JPEG images and save them on disk
// 1. connect to a RTSP server.
// 2. check if there's a H265 stream.
// 3. decode the H265 stream into RGBA frames.
// 4. convert RGBA frames to JPEG images and save them on disk.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -14,9 +14,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a H265 stream
// 3. decode the H265 stream into RGBA frames
// 1. connect to a RTSP server.
// 2. check if there's a H265 stream.
// 3. decode the H265 stream into RGBA frames.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -10,9 +10,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a LPCM stream
// 3. get LPCM samples of that format
// 1. connect to a RTSP server.
// 2. check if there's a LPCM stream.
// 3. get LPCM samples of that format.
func main() {
c := gortsplib.Client{}

View File

@@ -13,10 +13,10 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a M-JPEG stream
// 3. get JPEG images of that format
// 4. decode JPEG images into RGBA frames
// 1. connect to a RTSP server.
// 2. check if there's a M-JPEG stream.
// 3. get JPEG images of that format.
// 4. decode JPEG images into RGBA frames.
func main() {
c := gortsplib.Client{}

View File

@@ -11,9 +11,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a MPEG-4 audio stream
// 3. save the content of the format in a file in MPEG-TS format
// 1. connect to a RTSP server.
// 2. check if there's a MPEG-4 audio stream.
// 3. save the content of the format in a file in MPEG-TS format.
func main() {
c := gortsplib.Client{}

View File

@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
}
e.b = bufio.NewWriter(e.f)
e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.track})
e.w = &mpegts.Writer{W: e.b, Tracks: []*mpegts.Track{e.track}}
err = e.w.Initialize()
if err != nil {
return err
}
return nil
}

View File

@@ -10,9 +10,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a MPEG-4 audio stream
// 3. get access units of that format
// 1. connect to a RTSP server.
// 2. check if there's a MPEG-4 audio stream.
// 3. get access units of that format.
func main() {
c := gortsplib.Client{}

View File

@@ -11,9 +11,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a Opus stream
// 3. save the content of the format in a file in MPEG-TS format
// 1. connect to a RTSP server.
// 2. check if there's a Opus stream.
// 3. save the content of the format in a file in MPEG-TS format.
func main() {
c := gortsplib.Client{}

View File

@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
}
e.b = bufio.NewWriter(e.f)
e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.track})
e.w = &mpegts.Writer{W: e.b, Tracks: []*mpegts.Track{e.track}}
err = e.w.Initialize()
if err != nil {
return err
}
return nil
}

View File

@@ -10,9 +10,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's an Opus stream
// 3. get Opus packets of that format
// 1. connect to a RTSP server.
// 2. check if there's an Opus stream.
// 3. get Opus packets of that format.
func main() {
c := gortsplib.Client{}

View File

@@ -13,9 +13,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a VP8 stream
// 3. decode the VP8 stream into RGBA frames
// 1. connect to a RTSP server.
// 2. check if there's a VP8 stream.
// 3. decode the VP8 stream into RGBA frames.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -13,9 +13,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. check if there's a VP9 stream
// 3. decode the VP9 stream into RGBA frames
// 1. connect to a RTSP server.
// 2. check if there's a VP9 stream.
// 3. decode the VP9 stream into RGBA frames.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -13,8 +13,8 @@ import (
)
// This example shows how to
// 1. set additional client options
// 2. connect to a RTSP server and read all medias on a path
// 1. set additional client options.
// 2. connect to a RTSP server and read all medias on a path.
func main() {
// Client allows to set additional client options

View File

@@ -13,10 +13,10 @@ import (
)
// 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
// 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() {
c := gortsplib.Client{}

View File

@@ -11,9 +11,9 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. read all media streams on a path
// 3. Get the PTS and NTP timestamp of incoming RTP packets
// 1. connect to a RTSP server.
// 2. read all media streams on a path.
// 3. Get the PTS and NTP timestamp of incoming RTP packets.
func main() {
c := gortsplib.Client{}

View File

@@ -11,8 +11,8 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 2. read all medias on a path
// 1. connect to a RTSP server.
// 2. read all medias on a path.
// 3. re-publish all medias on another path.
func main() {

View File

@@ -12,7 +12,7 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 1. connect to a RTSP server.
// 2. read all media streams on a path.
func main() {

View File

@@ -8,7 +8,7 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server
// 1. connect to a RTSP server.
// 2. get and print informations about medias published on a path.
func main() {

View File

@@ -13,11 +13,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce an AV1 format
// 2. generate dummy RGBA images
// 3. encode images with AV1
// 4. generate RTP packets from AV1
// 5. write RTP packets to the server
// 1. connect to a RTSP server, announce an AV1 format.
// 2. generate dummy RGBA images.
// 3. encode images with AV1.
// 4. generate RTP packets from AV1.
// 5. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -12,11 +12,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce a G711 format
// 2. generate dummy LPCM audio samples
// 3. encode audio samples with G711
// 3. generate RTP packets from G711 samples
// 4. write RTP packets to the server
// 1. connect to a RTSP server, announce a G711 format.
// 2. generate dummy LPCM audio samples.
// 3. encode audio samples with G711.
// 3. generate RTP packets from G711 samples.
// 4. write RTP packets to the server.
func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d

View File

@@ -17,10 +17,10 @@ import (
)
// This example shows how to
// 1. read H264 frames from a video file in MPEG-TS format
// 2. connect to a RTSP server, announce a H264 format
// 3. wrap frames into RTP packets
// 4. write RTP packets to the server
// 1. read H264 frames from a video file in MPEG-TS format.
// 2. connect to a RTSP server, announce a H264 format.
// 3. wrap frames into RTP packets.
// 4. write RTP packets to the server.
func findTrack(r *mpegts.Reader) (*mpegts.Track, error) {
for _, track := range r.Tracks() {

View File

@@ -13,11 +13,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce an H264 format
// 2. generate dummy RGBA images
// 3. encode images with H264
// 4. generate RTP packets from H264
// 5. write RTP packets to the server
// 1. connect to a RTSP server, announce an H264 format.
// 2. generate dummy RGBA images.
// 3. encode images with H264.
// 4. generate RTP packets from H264.
// 5. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -13,11 +13,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce an H265 format
// 2. generate dummy RGBA images
// 3. encode images with H265
// 4. generate RTP packets from H265
// 5. write RTP packets to the server
// 1. connect to a RTSP server, announce an H265 format.
// 2. generate dummy RGBA images.
// 3. encode images with H265.
// 4. generate RTP packets from H265.
// 5. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -11,10 +11,10 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce an LPCM format
// 2. generate dummy LPCM audio samples
// 3. generate RTP packets from LPCM audio samples
// 4. write RTP packets to the server
// 1. connect to a RTSP server, announce an LPCM format.
// 2. generate dummy LPCM audio samples.
// 3. generate RTP packets from LPCM audio samples.
// 4. write RTP packets to the server.
func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d

View File

@@ -13,11 +13,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce a M-JPEG format
// 2. generate dummy RGBA images
// 3. encode images with JPEG
// 4. generate RTP packets from JPEG
// 5. write RTP packets to the server
// 1. connect to a RTSP server, announce a M-JPEG format.
// 2. generate dummy RGBA images.
// 3. encode images with JPEG.
// 4. generate RTP packets from JPEG.
// 5. write RTP packets to the server.
func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d

View File

@@ -14,11 +14,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce a MPEG-4 Audio (AAC) format
// 2. generate dummy LPCM audio samples
// 3. encode audio samples with MPEG-4 Audio (AAC)
// 3. generate RTP packets from MPEG-4 Audio units
// 4. write RTP packets to the server
// 1. connect to a RTSP server, announce a MPEG-4 Audio (AAC) format.
// 2. generate dummy LPCM audio samples.
// 3. encode audio samples with MPEG-4 Audio (AAC).
// 3. generate RTP packets from MPEG-4 Audio units.
// 4. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev gcc pkg-config

View File

@@ -16,11 +16,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce a Opus format
// 2. generate dummy LPCM audio samples
// 3. encode audio samples with Opus
// 3. generate RTP packets from Opus packets
// 4. write RTP packets to the server
// 1. connect to a RTSP server, announce a Opus format.
// 2. generate dummy LPCM audio samples.
// 3. encode audio samples with Opus.
// 3. generate RTP packets from Opus packets.
// 4. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev gcc pkg-config

View File

@@ -13,11 +13,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce a VP8 format
// 2. generate dummy RGBA images
// 3. encode images with VP8
// 4. generate RTP packets from VP8
// 5. write RTP packets to the server
// 1. connect to a RTSP server, announce a VP8 format.
// 2. generate dummy RGBA images.
// 3. encode images with VP8.
// 4. generate RTP packets from VP8.
// 5. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -13,11 +13,11 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce a VP9 format
// 2. generate dummy RGBA images
// 3. encode images with VP9
// 4. generate RTP packets from VP9
// 5. write RTP packets to the server
// 1. connect to a RTSP server, announce a VP9 format.
// 2. generate dummy RGBA images.
// 3. encode images with VP9.
// 4. generate RTP packets from VP9.
// 5. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -13,12 +13,12 @@ import (
)
// This example shows how to
// 1. set additional client options
// 2. connect to a RTSP server, announce an H264 format
// 3. generate dummy RGBA images
// 4. encode images with H264
// 5. generate RTP packets from H264
// 6. write RTP packets to the server
// 1. set additional client options.
// 2. connect to a RTSP server, announce an H264 format.
// 3. generate dummy RGBA images.
// 4. encode images with H264.
// 5. generate RTP packets from H264.
// 6. write RTP packets to the server.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -13,13 +13,13 @@ import (
)
// This example shows how to
// 1. connect to a RTSP server, announce an H264 format
// 2. generate dummy RGBA images
// 3. encode images with H264
// 4. generate RTP packets from H264
// 5. write RTP packets to the server for 5 seconds
// 6. pause for 5 seconds
// 7. repeat
// 1. connect to a RTSP server, announce an H264 format.
// 2. generate dummy RGBA images.
// 3. encode images with H264.
// 4. generate RTP packets from H264.
// 5. write RTP packets to the server for 5 seconds.
// 6. pause for 5 seconds.
// 7. repeat.
// This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config

View File

@@ -62,6 +62,7 @@ func (c *client) read() error {
return err
}
// notify the server that we are ready
stream := c.server.setStreamReady(desc)
defer c.server.setStreamUnready()

View File

@@ -4,8 +4,8 @@ import "log"
// This example shows how to
// 1. create a server that serves a single stream.
// 2. create a client, read an existing stream from an external server or camera,
// pass the stream to the server in order to serve it.
// 2. create a client, that reads an existing stream from another server or camera.
// 3. route the stream from the client to the server, and from the server to all connected readers.
func main() {
// allocate the server.
@@ -13,11 +13,11 @@ func main() {
s.initialize()
// allocate the client.
// give client access to the server.
// allow client to use the server.
c := &client{server: s}
c.initialize()
// start server and wait until a fatal error
log.Printf("server is ready on %s", s.server.RTSPAddress)
s.server.StartAndWait()
panic(s.server.StartAndWait())
}

View File

@@ -19,9 +19,9 @@ func (s *server) initialize() {
// configure the server
s.server = &gortsplib.Server{
Handler: s,
RTSPAddress: ":8554",
UDPRTPAddress: ":8000",
UDPRTCPAddress: ":8001",
RTSPAddress: ":8556",
UDPRTPAddress: ":8002",
UDPRTCPAddress: ":8003",
MulticastIPRange: "224.1.0.0/16",
MulticastRTPPort: 8002,
MulticastRTCPPort: 8003,
@@ -98,20 +98,24 @@ func (s *server) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response,
func (s *server) setStreamReady(desc *description.Session) *gortsplib.ServerStream {
s.mutex.Lock()
defer s.mutex.Unlock()
s.stream = &gortsplib.ServerStream{
Server: s.server,
Desc: desc,
}
err := s.stream.Initialize()
if err != nil {
panic(err)
}
return s.stream
}
func (s *server) setStreamUnready() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.stream.Close()
s.stream = nil
}

View File

@@ -14,9 +14,9 @@ import (
)
// This example shows how to
// 1. create a RTSP server which accepts plain connections
// 2. allow a single client to publish a stream with TCP or UDP, if it provides credentials
// 3. allow multiple clients to read that stream with TCP, UDP or UDP-multicast, if they provide credentials
// 1. create a RTSP server which accepts plain connections.
// 2. allow a single client to publish a stream with TCP or UDP, if it provides credentials.
// 3. allow multiple clients to read the stream with TCP, UDP or UDP-multicast, if they provide credentials.
const (
// credentials required to publish the stream

View File

@@ -1,266 +0,0 @@
package main
import (
"crypto/rand"
"errors"
"fmt"
"io"
"log"
"os"
"sync"
"time"
"github.com/asticode/go-astits"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts"
)
// This example shows how to
// 1. create a RTSP server which accepts plain connections
// 2. read from disk a MPEG-TS file which contains a H264 track
// 3. serve the content of the file to connected readers
func findTrack(r *mpegts.Reader) (*mpegts.Track, error) {
for _, track := range r.Tracks() {
if _, ok := track.Codec.(*mpegts.CodecH264); ok {
return track, nil
}
}
return nil, fmt.Errorf("H264 track not found")
}
func randUint32() (uint32, error) {
var b [4]byte
_, err := rand.Read(b[:])
if err != nil {
return 0, err
}
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
}
func routeFrames(f *os.File, stream *gortsplib.ServerStream) {
// setup H264 -> RTP encoder
rtpEnc, err := stream.Desc.Medias[0].Formats[0].(*format.H264).CreateEncoder()
if err != nil {
panic(err)
}
randomStart, err := randUint32()
if err != nil {
panic(err)
}
for {
// setup MPEG-TS parser
r := &mpegts.Reader{R: f}
err = r.Initialize()
if err != nil {
panic(err)
}
// find the H264 track inside the file
track, err := findTrack(r)
if err != nil {
panic(err)
}
timeDecoder := mpegts.TimeDecoder{}
timeDecoder.Initialize()
var firstDTS *int64
var firstTime time.Time
var lastRTPTime uint32
// setup a callback that is called when a H264 access unit is read from the file
r.OnDataH264(track, func(pts, dts int64, au [][]byte) error {
dts = timeDecoder.Decode(dts)
pts = timeDecoder.Decode(pts)
// sleep between access units
if firstDTS != nil {
timeDrift := time.Duration(dts-*firstDTS)*time.Second/90000 - time.Since(firstTime)
if timeDrift > 0 {
time.Sleep(timeDrift)
}
} else {
firstTime = time.Now()
firstDTS = &dts
}
log.Printf("writing access unit with pts=%d dts=%d", pts, dts)
// wrap the access unit into RTP packets
packets, err := rtpEnc.Encode(au)
if err != nil {
return err
}
// set packet timestamp
// we don't have to perform any conversion
// since H264 clock rate is the same in both MPEG-TS and RTSP
lastRTPTime = uint32(int64(randomStart) + pts)
for _, packet := range packets {
packet.Timestamp = lastRTPTime
}
// write RTP packets to the server
for _, packet := range packets {
err := stream.WritePacketRTP(stream.Desc.Medias[0], packet)
if err != nil {
return err
}
}
return nil
})
// read the file
for {
err := r.Read()
if err != nil {
// file has ended
if errors.Is(err, astits.ErrNoMorePackets) {
log.Printf("file has ended, rewinding")
// rewind to start position
_, err = f.Seek(0, io.SeekStart)
if err != nil {
panic(err)
}
// keep current timestamp
randomStart = lastRTPTime + 1
break
}
panic(err)
}
}
}
}
type serverHandler struct {
server *gortsplib.Server
stream *gortsplib.ServerStream
mutex sync.RWMutex
}
// called when a connection is opened.
func (sh *serverHandler) OnConnOpen(ctx *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(ctx *gortsplib.ServerHandlerOnSessionOpenCtx) {
log.Printf("session opened")
}
// called when a session is closed.
func (sh *serverHandler) OnSessionClose(ctx *gortsplib.ServerHandlerOnSessionCloseCtx) {
log.Printf("session closed")
}
// called when receiving a DESCRIBE request.
func (sh *serverHandler) OnDescribe(ctx *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
}
// called when receiving a SETUP request.
func (sh *serverHandler) OnSetup(ctx *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
}
// called when receiving a PLAY request.
func (sh *serverHandler) OnPlay(ctx *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()
// open a file in MPEG-TS format
f, err := os.Open("myvideo.ts")
if err != nil {
panic(err)
}
defer f.Close()
// in a separate routine, route frames from file to ServerStream
go routeFrames(f, h.stream)
// 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())
}

View File

@@ -0,0 +1,159 @@
package main
import (
"crypto/rand"
"errors"
"fmt"
"io"
"log"
"os"
"time"
"github.com/asticode/go-astits"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts"
)
func randUint32() (uint32, error) {
var b [4]byte
_, err := rand.Read(b[:])
if err != nil {
return 0, err
}
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
}
func findTrack(r *mpegts.Reader) (*mpegts.Track, error) {
for _, track := range r.Tracks() {
if _, ok := track.Codec.(*mpegts.CodecH264); ok {
return track, nil
}
}
return nil, fmt.Errorf("H264 track not found")
}
type fileStreamer struct {
stream *gortsplib.ServerStream
f *os.File
}
func (r *fileStreamer) initialize() error {
// open a file in MPEG-TS format
var err error
r.f, err = os.Open("myvideo.ts")
if err != nil {
return err
}
// in a separate routine, route frames from file to ServerStream
go r.run()
return nil
}
func (r *fileStreamer) close() {
r.f.Close()
}
func (r *fileStreamer) run() {
// setup H264 -> RTP encoder
rtpEnc, err := r.stream.Desc.Medias[0].Formats[0].(*format.H264).CreateEncoder()
if err != nil {
panic(err)
}
randomStart, err := randUint32()
if err != nil {
panic(err)
}
for {
// setup MPEG-TS parser
mr := &mpegts.Reader{R: r.f}
err = mr.Initialize()
if err != nil {
panic(err)
}
// find the H264 track inside the file
track, err := findTrack(mr)
if err != nil {
panic(err)
}
timeDecoder := mpegts.TimeDecoder{}
timeDecoder.Initialize()
var firstDTS *int64
var firstTime time.Time
var lastRTPTime uint32
// setup a callback that is called when a H264 access unit is read from the file
mr.OnDataH264(track, func(pts, dts int64, au [][]byte) error {
dts = timeDecoder.Decode(dts)
pts = timeDecoder.Decode(pts)
// sleep between access units
if firstDTS != nil {
timeDrift := time.Duration(dts-*firstDTS)*time.Second/90000 - time.Since(firstTime)
if timeDrift > 0 {
time.Sleep(timeDrift)
}
} else {
firstTime = time.Now()
firstDTS = &dts
}
log.Printf("writing access unit with pts=%d dts=%d", pts, dts)
// wrap the access unit into RTP packets
packets, err := rtpEnc.Encode(au)
if err != nil {
return err
}
// set packet timestamp
// we don't have to perform any conversion
// since H264 clock rate is the same in both MPEG-TS and RTSP
lastRTPTime = uint32(int64(randomStart) + pts)
for _, packet := range packets {
packet.Timestamp = lastRTPTime
}
// write RTP packets to the server
for _, packet := range packets {
err := r.stream.WritePacketRTP(r.stream.Desc.Medias[0], packet)
if err != nil {
return err
}
}
return nil
})
// read the file
for {
err := mr.Read()
if err != nil {
// file has ended
if errors.Is(err, astits.ErrNoMorePackets) {
log.Printf("file has ended, rewinding")
// rewind to start position
_, err = r.f.Seek(0, io.SeekStart)
if err != nil {
panic(err)
}
// keep current timestamp
randomStart = lastRTPTime + 1
break
}
panic(err)
}
}
}
}

View File

@@ -0,0 +1,137 @@
package main
import (
"log"
"sync"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
)
// This example shows how to
// 1. create a RTSP server which accepts plain connections.
// 2. read from disk a MPEG-TS file which contains a H264 track.
// 3. serve the content of the file to all connected readers.
type serverHandler struct {
server *gortsplib.Server
stream *gortsplib.ServerStream
mutex sync.RWMutex
}
// called when a connection is opened.
func (sh *serverHandler) OnConnOpen(ctx *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(ctx *gortsplib.ServerHandlerOnSessionOpenCtx) {
log.Printf("session opened")
}
// called when a session is closed.
func (sh *serverHandler) OnSessionClose(ctx *gortsplib.ServerHandlerOnSessionCloseCtx) {
log.Printf("session closed")
}
// called when receiving a DESCRIBE request.
func (sh *serverHandler) OnDescribe(ctx *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
}
// called when receiving a SETUP request.
func (sh *serverHandler) OnSetup(ctx *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
}
// called when receiving a PLAY request.
func (sh *serverHandler) OnPlay(ctx *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())
}

View File

@@ -15,9 +15,9 @@ import (
)
// This example shows how to
// 1. create a RTSP server which accepts plain connections
// 2. allow a single client to publish a stream, containing a H264 media, with TCP or UDP
// 3. save the content of the H264 media in a file in MPEG-TS format
// 1. create a RTSP server which accepts plain connections.
// 2. allow a single client to publish a stream, containing a H264 format.
// 3. save the content of the H264 media in a file in MPEG-TS format.
type serverHandler struct {
server *gortsplib.Server

View File

@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
Codec: &mpegts.CodecH264{},
}
e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.track})
e.w = &mpegts.Writer{W: e.b, Tracks: []*mpegts.Track{e.track}}
err = e.w.Initialize()
if err != nil {
return err
}
return nil
}

View File

@@ -14,9 +14,9 @@ import (
)
// This example shows how to
// 1. create a RTSP server which accepts only connections encrypted with TLS (RTSPS)
// 2. allow a single client to publish a stream with TCP
// 3. allow multiple clients to read that stream with TCP
// 1. create a RTSP server which accepts only connections encrypted with TLS (RTSPS).
// 2. allow a single client to publish a stream with TCP.
// 3. allow multiple clients to read the stream with TCP.
type serverHandler struct {
server *gortsplib.Server

View File

@@ -13,9 +13,9 @@ import (
)
// This example shows how to
// 1. create a RTSP server which accepts plain connections
// 2. allow a single client to publish a stream with TCP or UDP
// 3. allow multiple clients to read that stream with TCP, UDP or UDP-multicast
// 1. create a RTSP server which accepts plain connections.
// 2. allow a single client to publish a stream with TCP or UDP.
// 3. allow multiple clients to read the stream with TCP, UDP or UDP-multicast.
type serverHandler struct {
server *gortsplib.Server