From cc0c96626c61eebef871918f8ed05912bc7b7341 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Sun, 4 May 2025 17:00:18 +0200 Subject: [PATCH] improve examples (#778) --- README.md | 4 +- examples/client-play-backchannel/main.go | 10 +- .../client-play-format-av1-to-jpeg/main.go | 8 +- examples/client-play-format-av1/main.go | 6 +- examples/client-play-format-g711/main.go | 6 +- .../main.go | 6 +- .../mpegts_muxer.go | 6 +- .../client-play-format-h264-to-disk/main.go | 6 +- .../mpegts_muxer.go | 6 +- .../client-play-format-h264-to-jpeg/main.go | 8 +- examples/client-play-format-h264/main.go | 6 +- .../client-play-format-h265-to-disk/main.go | 6 +- .../mpegts_muxer.go | 6 +- .../client-play-format-h265-to-jpeg/main.go | 8 +- examples/client-play-format-h265/main.go | 6 +- examples/client-play-format-lpcm/main.go | 6 +- examples/client-play-format-mjpeg/main.go | 8 +- .../main.go | 6 +- .../mpegts_muxer.go | 6 +- .../client-play-format-mpeg4audio/main.go | 6 +- .../client-play-format-opus-to-disk/main.go | 6 +- .../mpegts_muxer.go | 6 +- examples/client-play-format-opus/main.go | 6 +- examples/client-play-format-vp8/main.go | 6 +- examples/client-play-format-vp9/main.go | 6 +- examples/client-play-options/main.go | 4 +- examples/client-play-pause/main.go | 8 +- examples/client-play-timestamp/main.go | 6 +- examples/client-play-to-record/main.go | 4 +- examples/client-play/main.go | 2 +- examples/client-query/main.go | 2 +- examples/client-record-format-av1/main.go | 10 +- examples/client-record-format-g711/main.go | 10 +- .../main.go | 8 +- examples/client-record-format-h264/main.go | 10 +- examples/client-record-format-h265/main.go | 10 +- examples/client-record-format-lpcm/main.go | 8 +- examples/client-record-format-mjpeg/main.go | 10 +- .../client-record-format-mpeg4audio/main.go | 10 +- examples/client-record-format-opus/main.go | 10 +- examples/client-record-format-vp8/main.go | 10 +- examples/client-record-format-vp9/main.go | 10 +- examples/client-record-options/main.go | 12 +- examples/client-record-pause/main.go | 14 +- examples/proxy/client.go | 1 + examples/proxy/main.go | 8 +- examples/proxy/server.go | 10 +- examples/server-auth/main.go | 6 +- examples/server-h264-from-disk/main.go | 266 ------------------ .../file_streamer.go | 159 +++++++++++ .../server-play-format-h264-from-disk/main.go | 137 +++++++++ .../main.go | 6 +- .../mpegts_muxer.go | 6 +- examples/server-tls/main.go | 6 +- examples/server/main.go | 6 +- 55 files changed, 494 insertions(+), 435 deletions(-) delete mode 100644 examples/server-h264-from-disk/main.go create mode 100644 examples/server-play-format-h264-from-disk/file_streamer.go create mode 100644 examples/server-play-format-h264-from-disk/main.go rename examples/{server-h264-to-disk => server-record-format-h264-to-disk}/main.go (97%) rename examples/{server-h264-to-disk => server-record-format-h264-to-disk}/mpegts_muxer.go (94%) diff --git a/README.md b/README.md index d0ca2760..348e714b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/examples/client-play-backchannel/main.go b/examples/client-play-backchannel/main.go index e511cd34..6527888d 100644 --- a/examples/client-play-backchannel/main.go +++ b/examples/client-play-backchannel/main.go @@ -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) } diff --git a/examples/client-play-format-av1-to-jpeg/main.go b/examples/client-play-format-av1-to-jpeg/main.go index fa385763..19c0a210 100644 --- a/examples/client-play-format-av1-to-jpeg/main.go +++ b/examples/client-play-format-av1-to-jpeg/main.go @@ -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 diff --git a/examples/client-play-format-av1/main.go b/examples/client-play-format-av1/main.go index 978a1a7d..90534d4c 100644 --- a/examples/client-play-format-av1/main.go +++ b/examples/client-play-format-av1/main.go @@ -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 diff --git a/examples/client-play-format-g711/main.go b/examples/client-play-format-g711/main.go index dda7f8c8..fa350fbe 100644 --- a/examples/client-play-format-g711/main.go +++ b/examples/client-play-format-g711/main.go @@ -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{} diff --git a/examples/client-play-format-h264-mpeg4audio-to-disk/main.go b/examples/client-play-format-h264-mpeg4audio-to-disk/main.go index a23fd041..47e4cd09 100644 --- a/examples/client-play-format-h264-mpeg4audio-to-disk/main.go +++ b/examples/client-play-format-h264-mpeg4audio-to-disk/main.go @@ -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{} diff --git a/examples/client-play-format-h264-mpeg4audio-to-disk/mpegts_muxer.go b/examples/client-play-format-h264-mpeg4audio-to-disk/mpegts_muxer.go index 7bbc5ae2..d4c64e8b 100644 --- a/examples/client-play-format-h264-mpeg4audio-to-disk/mpegts_muxer.go +++ b/examples/client-play-format-h264-mpeg4audio-to-disk/mpegts_muxer.go @@ -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 } diff --git a/examples/client-play-format-h264-to-disk/main.go b/examples/client-play-format-h264-to-disk/main.go index 75b472d6..3ee85d4b 100644 --- a/examples/client-play-format-h264-to-disk/main.go +++ b/examples/client-play-format-h264-to-disk/main.go @@ -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{} diff --git a/examples/client-play-format-h264-to-disk/mpegts_muxer.go b/examples/client-play-format-h264-to-disk/mpegts_muxer.go index 1bc8a928..576f14ab 100644 --- a/examples/client-play-format-h264-to-disk/mpegts_muxer.go +++ b/examples/client-play-format-h264-to-disk/mpegts_muxer.go @@ -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 } diff --git a/examples/client-play-format-h264-to-jpeg/main.go b/examples/client-play-format-h264-to-jpeg/main.go index 829e1fa8..c599ac01 100644 --- a/examples/client-play-format-h264-to-jpeg/main.go +++ b/examples/client-play-format-h264-to-jpeg/main.go @@ -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 diff --git a/examples/client-play-format-h264/main.go b/examples/client-play-format-h264/main.go index 43947301..2ae32d1f 100644 --- a/examples/client-play-format-h264/main.go +++ b/examples/client-play-format-h264/main.go @@ -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 diff --git a/examples/client-play-format-h265-to-disk/main.go b/examples/client-play-format-h265-to-disk/main.go index 0fa40f35..78ee0539 100644 --- a/examples/client-play-format-h265-to-disk/main.go +++ b/examples/client-play-format-h265-to-disk/main.go @@ -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{} diff --git a/examples/client-play-format-h265-to-disk/mpegts_muxer.go b/examples/client-play-format-h265-to-disk/mpegts_muxer.go index 98d50728..7c6f801a 100644 --- a/examples/client-play-format-h265-to-disk/mpegts_muxer.go +++ b/examples/client-play-format-h265-to-disk/mpegts_muxer.go @@ -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 } diff --git a/examples/client-play-format-h265-to-jpeg/main.go b/examples/client-play-format-h265-to-jpeg/main.go index 30a6caf2..b77e42bf 100644 --- a/examples/client-play-format-h265-to-jpeg/main.go +++ b/examples/client-play-format-h265-to-jpeg/main.go @@ -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 diff --git a/examples/client-play-format-h265/main.go b/examples/client-play-format-h265/main.go index 053cf448..1bc352ff 100644 --- a/examples/client-play-format-h265/main.go +++ b/examples/client-play-format-h265/main.go @@ -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 diff --git a/examples/client-play-format-lpcm/main.go b/examples/client-play-format-lpcm/main.go index 6f13a407..93f33fa1 100644 --- a/examples/client-play-format-lpcm/main.go +++ b/examples/client-play-format-lpcm/main.go @@ -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{} diff --git a/examples/client-play-format-mjpeg/main.go b/examples/client-play-format-mjpeg/main.go index 455f74d4..2d223ed5 100644 --- a/examples/client-play-format-mjpeg/main.go +++ b/examples/client-play-format-mjpeg/main.go @@ -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{} diff --git a/examples/client-play-format-mpeg4audio-to-disk/main.go b/examples/client-play-format-mpeg4audio-to-disk/main.go index cec96a4d..59f8baf3 100644 --- a/examples/client-play-format-mpeg4audio-to-disk/main.go +++ b/examples/client-play-format-mpeg4audio-to-disk/main.go @@ -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{} diff --git a/examples/client-play-format-mpeg4audio-to-disk/mpegts_muxer.go b/examples/client-play-format-mpeg4audio-to-disk/mpegts_muxer.go index c9726db6..4ff3a6d7 100644 --- a/examples/client-play-format-mpeg4audio-to-disk/mpegts_muxer.go +++ b/examples/client-play-format-mpeg4audio-to-disk/mpegts_muxer.go @@ -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 } diff --git a/examples/client-play-format-mpeg4audio/main.go b/examples/client-play-format-mpeg4audio/main.go index e3c1c88f..1ad07e13 100644 --- a/examples/client-play-format-mpeg4audio/main.go +++ b/examples/client-play-format-mpeg4audio/main.go @@ -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{} diff --git a/examples/client-play-format-opus-to-disk/main.go b/examples/client-play-format-opus-to-disk/main.go index 8bdfecb3..97699969 100644 --- a/examples/client-play-format-opus-to-disk/main.go +++ b/examples/client-play-format-opus-to-disk/main.go @@ -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{} diff --git a/examples/client-play-format-opus-to-disk/mpegts_muxer.go b/examples/client-play-format-opus-to-disk/mpegts_muxer.go index ce65c179..1ca0bdb9 100644 --- a/examples/client-play-format-opus-to-disk/mpegts_muxer.go +++ b/examples/client-play-format-opus-to-disk/mpegts_muxer.go @@ -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 } diff --git a/examples/client-play-format-opus/main.go b/examples/client-play-format-opus/main.go index 316ff727..0cf4a235 100644 --- a/examples/client-play-format-opus/main.go +++ b/examples/client-play-format-opus/main.go @@ -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{} diff --git a/examples/client-play-format-vp8/main.go b/examples/client-play-format-vp8/main.go index a9d04721..9a1c0316 100644 --- a/examples/client-play-format-vp8/main.go +++ b/examples/client-play-format-vp8/main.go @@ -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 diff --git a/examples/client-play-format-vp9/main.go b/examples/client-play-format-vp9/main.go index 58f5134d..ede9a5bb 100644 --- a/examples/client-play-format-vp9/main.go +++ b/examples/client-play-format-vp9/main.go @@ -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 diff --git a/examples/client-play-options/main.go b/examples/client-play-options/main.go index 27818eb1..9f203927 100644 --- a/examples/client-play-options/main.go +++ b/examples/client-play-options/main.go @@ -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 diff --git a/examples/client-play-pause/main.go b/examples/client-play-pause/main.go index c3f7bf67..eab21658 100644 --- a/examples/client-play-pause/main.go +++ b/examples/client-play-pause/main.go @@ -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{} diff --git a/examples/client-play-timestamp/main.go b/examples/client-play-timestamp/main.go index 5a620e62..56c45023 100644 --- a/examples/client-play-timestamp/main.go +++ b/examples/client-play-timestamp/main.go @@ -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{} diff --git a/examples/client-play-to-record/main.go b/examples/client-play-to-record/main.go index 46dae931..54cc7201 100644 --- a/examples/client-play-to-record/main.go +++ b/examples/client-play-to-record/main.go @@ -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() { diff --git a/examples/client-play/main.go b/examples/client-play/main.go index 12e213ea..21ac7cb8 100644 --- a/examples/client-play/main.go +++ b/examples/client-play/main.go @@ -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() { diff --git a/examples/client-query/main.go b/examples/client-query/main.go index 1905d552..a10d9d02 100644 --- a/examples/client-query/main.go +++ b/examples/client-query/main.go @@ -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() { diff --git a/examples/client-record-format-av1/main.go b/examples/client-record-format-av1/main.go index f1defaf2..75bef012 100644 --- a/examples/client-record-format-av1/main.go +++ b/examples/client-record-format-av1/main.go @@ -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 diff --git a/examples/client-record-format-g711/main.go b/examples/client-record-format-g711/main.go index b4773593..4e68bf7f 100644 --- a/examples/client-record-format-g711/main.go +++ b/examples/client-record-format-g711/main.go @@ -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 diff --git a/examples/client-record-format-h264-from-disk/main.go b/examples/client-record-format-h264-from-disk/main.go index 477ff2a2..f4399e08 100644 --- a/examples/client-record-format-h264-from-disk/main.go +++ b/examples/client-record-format-h264-from-disk/main.go @@ -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() { diff --git a/examples/client-record-format-h264/main.go b/examples/client-record-format-h264/main.go index 6fdc631e..ed684108 100644 --- a/examples/client-record-format-h264/main.go +++ b/examples/client-record-format-h264/main.go @@ -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 diff --git a/examples/client-record-format-h265/main.go b/examples/client-record-format-h265/main.go index ae5f9538..f8b47279 100644 --- a/examples/client-record-format-h265/main.go +++ b/examples/client-record-format-h265/main.go @@ -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 diff --git a/examples/client-record-format-lpcm/main.go b/examples/client-record-format-lpcm/main.go index 4f31276b..bcb78062 100644 --- a/examples/client-record-format-lpcm/main.go +++ b/examples/client-record-format-lpcm/main.go @@ -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 diff --git a/examples/client-record-format-mjpeg/main.go b/examples/client-record-format-mjpeg/main.go index 5689660a..a3033e91 100644 --- a/examples/client-record-format-mjpeg/main.go +++ b/examples/client-record-format-mjpeg/main.go @@ -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 diff --git a/examples/client-record-format-mpeg4audio/main.go b/examples/client-record-format-mpeg4audio/main.go index f2d4af9a..173f5a34 100644 --- a/examples/client-record-format-mpeg4audio/main.go +++ b/examples/client-record-format-mpeg4audio/main.go @@ -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 diff --git a/examples/client-record-format-opus/main.go b/examples/client-record-format-opus/main.go index aea62319..2363b054 100644 --- a/examples/client-record-format-opus/main.go +++ b/examples/client-record-format-opus/main.go @@ -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 diff --git a/examples/client-record-format-vp8/main.go b/examples/client-record-format-vp8/main.go index 7a8c9e13..fbf4f9a3 100644 --- a/examples/client-record-format-vp8/main.go +++ b/examples/client-record-format-vp8/main.go @@ -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 diff --git a/examples/client-record-format-vp9/main.go b/examples/client-record-format-vp9/main.go index aa14a56c..6f1e4964 100644 --- a/examples/client-record-format-vp9/main.go +++ b/examples/client-record-format-vp9/main.go @@ -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 diff --git a/examples/client-record-options/main.go b/examples/client-record-options/main.go index f80c54e1..abf926c2 100644 --- a/examples/client-record-options/main.go +++ b/examples/client-record-options/main.go @@ -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 diff --git a/examples/client-record-pause/main.go b/examples/client-record-pause/main.go index a76772f4..f51ea0e6 100644 --- a/examples/client-record-pause/main.go +++ b/examples/client-record-pause/main.go @@ -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 diff --git a/examples/proxy/client.go b/examples/proxy/client.go index be38dd32..56b28a86 100644 --- a/examples/proxy/client.go +++ b/examples/proxy/client.go @@ -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() diff --git a/examples/proxy/main.go b/examples/proxy/main.go index b251230a..9cbc751e 100644 --- a/examples/proxy/main.go +++ b/examples/proxy/main.go @@ -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()) } diff --git a/examples/proxy/server.go b/examples/proxy/server.go index 52980065..b7558161 100644 --- a/examples/proxy/server.go +++ b/examples/proxy/server.go @@ -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 } diff --git a/examples/server-auth/main.go b/examples/server-auth/main.go index 828c8990..b63fb21c 100644 --- a/examples/server-auth/main.go +++ b/examples/server-auth/main.go @@ -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 diff --git a/examples/server-h264-from-disk/main.go b/examples/server-h264-from-disk/main.go deleted file mode 100644 index b7096424..00000000 --- a/examples/server-h264-from-disk/main.go +++ /dev/null @@ -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()) -} diff --git a/examples/server-play-format-h264-from-disk/file_streamer.go b/examples/server-play-format-h264-from-disk/file_streamer.go new file mode 100644 index 00000000..b63c8e3f --- /dev/null +++ b/examples/server-play-format-h264-from-disk/file_streamer.go @@ -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) + } + } + } +} diff --git a/examples/server-play-format-h264-from-disk/main.go b/examples/server-play-format-h264-from-disk/main.go new file mode 100644 index 00000000..f3b108a1 --- /dev/null +++ b/examples/server-play-format-h264-from-disk/main.go @@ -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()) +} diff --git a/examples/server-h264-to-disk/main.go b/examples/server-record-format-h264-to-disk/main.go similarity index 97% rename from examples/server-h264-to-disk/main.go rename to examples/server-record-format-h264-to-disk/main.go index 057b416c..7198606f 100644 --- a/examples/server-h264-to-disk/main.go +++ b/examples/server-record-format-h264-to-disk/main.go @@ -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 diff --git a/examples/server-h264-to-disk/mpegts_muxer.go b/examples/server-record-format-h264-to-disk/mpegts_muxer.go similarity index 94% rename from examples/server-h264-to-disk/mpegts_muxer.go rename to examples/server-record-format-h264-to-disk/mpegts_muxer.go index d0318f25..28cc9788 100644 --- a/examples/server-h264-to-disk/mpegts_muxer.go +++ b/examples/server-record-format-h264-to-disk/mpegts_muxer.go @@ -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 } diff --git a/examples/server-tls/main.go b/examples/server-tls/main.go index dfafb214..cbe5039b 100644 --- a/examples/server-tls/main.go +++ b/examples/server-tls/main.go @@ -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 diff --git a/examples/server/main.go b/examples/server/main.go index f9b0a14a..65e5adac 100644 --- a/examples/server/main.go +++ b/examples/server/main.go @@ -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