mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 07:06:58 +08:00
improve examples (#778)
This commit is contained in:
@@ -95,8 +95,8 @@ Features:
|
|||||||
* [server](examples/server/main.go)
|
* [server](examples/server/main.go)
|
||||||
* [server-tls](examples/server-tls/main.go)
|
* [server-tls](examples/server-tls/main.go)
|
||||||
* [server-auth](examples/server-auth/main.go)
|
* [server-auth](examples/server-auth/main.go)
|
||||||
* [server-h264-to-disk](examples/server-h264-to-disk/main.go)
|
* [server-record-format-h264-to-disk](examples/server-record-format-h264-to-disk/main.go)
|
||||||
* [server-h264-from-disk](examples/server-h264-from-disk/main.go)
|
* [server-play-format-h264-from-disk](examples/server-play-format-h264-from-disk/main.go)
|
||||||
* [proxy](examples/proxy/main.go)
|
* [proxy](examples/proxy/main.go)
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
@@ -13,9 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. generate a dummy G711 audio stream
|
// 1. generate a dummy G711 audio stream.
|
||||||
// 2. connect to a RTSP server, find a back channel that supports G711
|
// 2. connect to a RTSP server, find a back channel that supports G711.
|
||||||
// 3. route the G711 stream to the channel
|
// 3. route the G711 stream to the channel.
|
||||||
|
|
||||||
func multiplyAndDivide(v, m, d int64) int64 {
|
func multiplyAndDivide(v, m, d int64) int64 {
|
||||||
secs := v / d
|
secs := v / d
|
||||||
@@ -72,7 +72,7 @@ func main() {
|
|||||||
// find the back channel
|
// find the back channel
|
||||||
medi, forma := findG711BackChannel(desc)
|
medi, forma := findG711BackChannel(desc)
|
||||||
if medi == nil {
|
if medi == nil {
|
||||||
panic("media not found")
|
panic("back channel not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup a single media
|
// setup a single media
|
||||||
@@ -137,7 +137,7 @@ func main() {
|
|||||||
for _, pkt := range pkts {
|
for _, pkt := range pkts {
|
||||||
pkt.Timestamp += uint32(int64(randomStart) + prevPTS)
|
pkt.Timestamp += uint32(int64(randomStart) + prevPTS)
|
||||||
|
|
||||||
err = c.WritePacketRTP(desc.Medias[0], pkt)
|
err = c.WritePacketRTP(medi, pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -19,10 +19,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a AV1 stream
|
// 2. check if there's a AV1 stream.
|
||||||
// 3. decode the AV1 stream into RGBA frames
|
// 3. decode the AV1 stream into RGBA frames.
|
||||||
// 4. convert RGBA frames to JPEG images and save them on disk
|
// 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:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -14,9 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a AV1 stream
|
// 2. check if there's a AV1 stream.
|
||||||
// 3. decode the AV1 stream into RGBA frames
|
// 3. decode the AV1 stream into RGBA frames.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a G711 stream
|
// 2. check if there's a G711 stream.
|
||||||
// 3. decode the G711 stream into audio samples
|
// 3. decode the G711 stream into audio samples.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a H264 stream and a MPEG-4 audio stream
|
// 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
|
// 3. save the content of those formats in a file in MPEG-TS format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -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
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a H264 stream
|
// 2. check if there's a H264 stream.
|
||||||
// 3. save the content of the format in a file in MPEG-TS format
|
// 3. save the content of the format in a file in MPEG-TS format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
|
|||||||
Codec: &mpegts.CodecH264{},
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -19,10 +19,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a H264 stream
|
// 2. check if there's a H264 stream.
|
||||||
// 3. decode the H264 stream into RGBA frames
|
// 3. decode the H264 stream into RGBA frames.
|
||||||
// 4. convert RGBA frames to JPEG images and save them on disk
|
// 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:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -14,9 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's an H264 stream
|
// 2. check if there's an H264 stream.
|
||||||
// 3. decode the H264 stream into RGBA frames
|
// 3. decode the H264 stream into RGBA frames.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a H265 stream
|
// 2. check if there's a H265 stream.
|
||||||
// 3. save the content of the format in a file in MPEG-TS format
|
// 3. save the content of the format in a file in MPEG-TS format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -35,7 +35,11 @@ func (e *mpegtsMuxer) initialize() error {
|
|||||||
Codec: &mpegts.CodecH265{},
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -19,10 +19,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a H265 stream
|
// 2. check if there's a H265 stream.
|
||||||
// 3. decode the H265 stream into RGBA frames
|
// 3. decode the H265 stream into RGBA frames.
|
||||||
// 4. convert RGBA frames to JPEG images and save them on disk
|
// 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:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -14,9 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a H265 stream
|
// 2. check if there's a H265 stream.
|
||||||
// 3. decode the H265 stream into RGBA frames
|
// 3. decode the H265 stream into RGBA frames.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -10,9 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a LPCM stream
|
// 2. check if there's a LPCM stream.
|
||||||
// 3. get LPCM samples of that format
|
// 3. get LPCM samples of that format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -13,10 +13,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a M-JPEG stream
|
// 2. check if there's a M-JPEG stream.
|
||||||
// 3. get JPEG images of that format
|
// 3. get JPEG images of that format.
|
||||||
// 4. decode JPEG images into RGBA frames
|
// 4. decode JPEG images into RGBA frames.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a MPEG-4 audio stream
|
// 2. check if there's a MPEG-4 audio stream.
|
||||||
// 3. save the content of the format in a file in MPEG-TS format
|
// 3. save the content of the format in a file in MPEG-TS format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
|
|||||||
}
|
}
|
||||||
e.b = bufio.NewWriter(e.f)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -10,9 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a MPEG-4 audio stream
|
// 2. check if there's a MPEG-4 audio stream.
|
||||||
// 3. get access units of that format
|
// 3. get access units of that format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a Opus stream
|
// 2. check if there's a Opus stream.
|
||||||
// 3. save the content of the format in a file in MPEG-TS format
|
// 3. save the content of the format in a file in MPEG-TS format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
|
|||||||
}
|
}
|
||||||
e.b = bufio.NewWriter(e.f)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -10,9 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's an Opus stream
|
// 2. check if there's an Opus stream.
|
||||||
// 3. get Opus packets of that format
|
// 3. get Opus packets of that format.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -13,9 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a VP8 stream
|
// 2. check if there's a VP8 stream.
|
||||||
// 3. decode the VP8 stream into RGBA frames
|
// 3. decode the VP8 stream into RGBA frames.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -13,9 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. check if there's a VP9 stream
|
// 2. check if there's a VP9 stream.
|
||||||
// 3. decode the VP9 stream into RGBA frames
|
// 3. decode the VP9 stream into RGBA frames.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -13,8 +13,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. set additional client options
|
// 1. set additional client options.
|
||||||
// 2. connect to a RTSP server and read all medias on a path
|
// 2. connect to a RTSP server and read all medias on a path.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Client allows to set additional client options
|
// Client allows to set additional client options
|
||||||
|
@@ -13,10 +13,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server and read all medias on a path
|
// 1. connect to a RTSP server and read all medias on a path.
|
||||||
// 2. wait for 5 seconds
|
// 2. wait for 5 seconds.
|
||||||
// 3. pause for 5 seconds
|
// 3. pause for 5 seconds.
|
||||||
// 4. repeat
|
// 4. repeat.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// 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
|
// 2. read all media streams on a path.
|
||||||
// 3. Get the PTS and NTP timestamp of incoming RTP packets
|
// 3. Get the PTS and NTP timestamp of incoming RTP packets.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
@@ -11,8 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server
|
// 1. connect to a RTSP server.
|
||||||
// 2. read all medias on a path
|
// 2. read all medias on a path.
|
||||||
// 3. re-publish all medias on another path.
|
// 3. re-publish all medias on another path.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// 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.
|
// 2. read all media streams on a path.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// 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.
|
// 2. get and print informations about medias published on a path.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@@ -13,11 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce an AV1 format
|
// 1. connect to a RTSP server, announce an AV1 format.
|
||||||
// 2. generate dummy RGBA images
|
// 2. generate dummy RGBA images.
|
||||||
// 3. encode images with AV1
|
// 3. encode images with AV1.
|
||||||
// 4. generate RTP packets from AV1
|
// 4. generate RTP packets from AV1.
|
||||||
// 5. write RTP packets to the server
|
// 5. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -12,11 +12,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce a G711 format
|
// 1. connect to a RTSP server, announce a G711 format.
|
||||||
// 2. generate dummy LPCM audio samples
|
// 2. generate dummy LPCM audio samples.
|
||||||
// 3. encode audio samples with G711
|
// 3. encode audio samples with G711.
|
||||||
// 3. generate RTP packets from G711 samples
|
// 3. generate RTP packets from G711 samples.
|
||||||
// 4. write RTP packets to the server
|
// 4. write RTP packets to the server.
|
||||||
|
|
||||||
func multiplyAndDivide(v, m, d int64) int64 {
|
func multiplyAndDivide(v, m, d int64) int64 {
|
||||||
secs := v / d
|
secs := v / d
|
||||||
|
@@ -17,10 +17,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. read H264 frames from a video file in MPEG-TS format
|
// 1. read H264 frames from a video file in MPEG-TS format.
|
||||||
// 2. connect to a RTSP server, announce a H264 format
|
// 2. connect to a RTSP server, announce a H264 format.
|
||||||
// 3. wrap frames into RTP packets
|
// 3. wrap frames into RTP packets.
|
||||||
// 4. write RTP packets to the server
|
// 4. write RTP packets to the server.
|
||||||
|
|
||||||
func findTrack(r *mpegts.Reader) (*mpegts.Track, error) {
|
func findTrack(r *mpegts.Reader) (*mpegts.Track, error) {
|
||||||
for _, track := range r.Tracks() {
|
for _, track := range r.Tracks() {
|
||||||
|
@@ -13,11 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce an H264 format
|
// 1. connect to a RTSP server, announce an H264 format.
|
||||||
// 2. generate dummy RGBA images
|
// 2. generate dummy RGBA images.
|
||||||
// 3. encode images with H264
|
// 3. encode images with H264.
|
||||||
// 4. generate RTP packets from H264
|
// 4. generate RTP packets from H264.
|
||||||
// 5. write RTP packets to the server
|
// 5. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -13,11 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce an H265 format
|
// 1. connect to a RTSP server, announce an H265 format.
|
||||||
// 2. generate dummy RGBA images
|
// 2. generate dummy RGBA images.
|
||||||
// 3. encode images with H265
|
// 3. encode images with H265.
|
||||||
// 4. generate RTP packets from H265
|
// 4. generate RTP packets from H265.
|
||||||
// 5. write RTP packets to the server
|
// 5. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -11,10 +11,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce an LPCM format
|
// 1. connect to a RTSP server, announce an LPCM format.
|
||||||
// 2. generate dummy LPCM audio samples
|
// 2. generate dummy LPCM audio samples.
|
||||||
// 3. generate RTP packets from LPCM audio samples
|
// 3. generate RTP packets from LPCM audio samples.
|
||||||
// 4. write RTP packets to the server
|
// 4. write RTP packets to the server.
|
||||||
|
|
||||||
func multiplyAndDivide(v, m, d int64) int64 {
|
func multiplyAndDivide(v, m, d int64) int64 {
|
||||||
secs := v / d
|
secs := v / d
|
||||||
|
@@ -13,11 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce a M-JPEG format
|
// 1. connect to a RTSP server, announce a M-JPEG format.
|
||||||
// 2. generate dummy RGBA images
|
// 2. generate dummy RGBA images.
|
||||||
// 3. encode images with JPEG
|
// 3. encode images with JPEG.
|
||||||
// 4. generate RTP packets from JPEG
|
// 4. generate RTP packets from JPEG.
|
||||||
// 5. write RTP packets to the server
|
// 5. write RTP packets to the server.
|
||||||
|
|
||||||
func multiplyAndDivide(v, m, d int64) int64 {
|
func multiplyAndDivide(v, m, d int64) int64 {
|
||||||
secs := v / d
|
secs := v / d
|
||||||
|
@@ -14,11 +14,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce a MPEG-4 Audio (AAC) format
|
// 1. connect to a RTSP server, announce a MPEG-4 Audio (AAC) format.
|
||||||
// 2. generate dummy LPCM audio samples
|
// 2. generate dummy LPCM audio samples.
|
||||||
// 3. encode audio samples with MPEG-4 Audio (AAC)
|
// 3. encode audio samples with MPEG-4 Audio (AAC).
|
||||||
// 3. generate RTP packets from MPEG-4 Audio units
|
// 3. generate RTP packets from MPEG-4 Audio units.
|
||||||
// 4. write RTP packets to the server
|
// 4. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev gcc pkg-config
|
// apt install -y libavcodec-dev gcc pkg-config
|
||||||
|
@@ -16,11 +16,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce a Opus format
|
// 1. connect to a RTSP server, announce a Opus format.
|
||||||
// 2. generate dummy LPCM audio samples
|
// 2. generate dummy LPCM audio samples.
|
||||||
// 3. encode audio samples with Opus
|
// 3. encode audio samples with Opus.
|
||||||
// 3. generate RTP packets from Opus packets
|
// 3. generate RTP packets from Opus packets.
|
||||||
// 4. write RTP packets to the server
|
// 4. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev gcc pkg-config
|
// apt install -y libavcodec-dev gcc pkg-config
|
||||||
|
@@ -13,11 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce a VP8 format
|
// 1. connect to a RTSP server, announce a VP8 format.
|
||||||
// 2. generate dummy RGBA images
|
// 2. generate dummy RGBA images.
|
||||||
// 3. encode images with VP8
|
// 3. encode images with VP8.
|
||||||
// 4. generate RTP packets from VP8
|
// 4. generate RTP packets from VP8.
|
||||||
// 5. write RTP packets to the server
|
// 5. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -13,11 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce a VP9 format
|
// 1. connect to a RTSP server, announce a VP9 format.
|
||||||
// 2. generate dummy RGBA images
|
// 2. generate dummy RGBA images.
|
||||||
// 3. encode images with VP9
|
// 3. encode images with VP9.
|
||||||
// 4. generate RTP packets from VP9
|
// 4. generate RTP packets from VP9.
|
||||||
// 5. write RTP packets to the server
|
// 5. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -13,12 +13,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. set additional client options
|
// 1. set additional client options.
|
||||||
// 2. connect to a RTSP server, announce an H264 format
|
// 2. connect to a RTSP server, announce an H264 format.
|
||||||
// 3. generate dummy RGBA images
|
// 3. generate dummy RGBA images.
|
||||||
// 4. encode images with H264
|
// 4. encode images with H264.
|
||||||
// 5. generate RTP packets from H264
|
// 5. generate RTP packets from H264.
|
||||||
// 6. write RTP packets to the server
|
// 6. write RTP packets to the server.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -13,13 +13,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. connect to a RTSP server, announce an H264 format
|
// 1. connect to a RTSP server, announce an H264 format.
|
||||||
// 2. generate dummy RGBA images
|
// 2. generate dummy RGBA images.
|
||||||
// 3. encode images with H264
|
// 3. encode images with H264.
|
||||||
// 4. generate RTP packets from H264
|
// 4. generate RTP packets from H264.
|
||||||
// 5. write RTP packets to the server for 5 seconds
|
// 5. write RTP packets to the server for 5 seconds.
|
||||||
// 6. pause for 5 seconds
|
// 6. pause for 5 seconds.
|
||||||
// 7. repeat
|
// 7. repeat.
|
||||||
|
|
||||||
// This example requires the FFmpeg libraries, that can be installed with this command:
|
// This example requires the FFmpeg libraries, that can be installed with this command:
|
||||||
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
// apt install -y libavcodec-dev libswscale-dev gcc pkg-config
|
||||||
|
@@ -62,6 +62,7 @@ func (c *client) read() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify the server that we are ready
|
||||||
stream := c.server.setStreamReady(desc)
|
stream := c.server.setStreamReady(desc)
|
||||||
defer c.server.setStreamUnready()
|
defer c.server.setStreamUnready()
|
||||||
|
|
||||||
|
@@ -4,8 +4,8 @@ import "log"
|
|||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. create a server that serves a single stream.
|
// 1. create a server that serves a single stream.
|
||||||
// 2. create a client, read an existing stream from an external server or camera,
|
// 2. create a client, that reads an existing stream from another server or camera.
|
||||||
// pass the stream to the server in order to serve it.
|
// 3. route the stream from the client to the server, and from the server to all connected readers.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// allocate the server.
|
// allocate the server.
|
||||||
@@ -13,11 +13,11 @@ func main() {
|
|||||||
s.initialize()
|
s.initialize()
|
||||||
|
|
||||||
// allocate the client.
|
// allocate the client.
|
||||||
// give client access to the server.
|
// allow client to use the server.
|
||||||
c := &client{server: s}
|
c := &client{server: s}
|
||||||
c.initialize()
|
c.initialize()
|
||||||
|
|
||||||
// start server and wait until a fatal error
|
// start server and wait until a fatal error
|
||||||
log.Printf("server is ready on %s", s.server.RTSPAddress)
|
log.Printf("server is ready on %s", s.server.RTSPAddress)
|
||||||
s.server.StartAndWait()
|
panic(s.server.StartAndWait())
|
||||||
}
|
}
|
||||||
|
@@ -19,9 +19,9 @@ func (s *server) initialize() {
|
|||||||
// configure the server
|
// configure the server
|
||||||
s.server = &gortsplib.Server{
|
s.server = &gortsplib.Server{
|
||||||
Handler: s,
|
Handler: s,
|
||||||
RTSPAddress: ":8554",
|
RTSPAddress: ":8556",
|
||||||
UDPRTPAddress: ":8000",
|
UDPRTPAddress: ":8002",
|
||||||
UDPRTCPAddress: ":8001",
|
UDPRTCPAddress: ":8003",
|
||||||
MulticastIPRange: "224.1.0.0/16",
|
MulticastIPRange: "224.1.0.0/16",
|
||||||
MulticastRTPPort: 8002,
|
MulticastRTPPort: 8002,
|
||||||
MulticastRTCPPort: 8003,
|
MulticastRTCPPort: 8003,
|
||||||
@@ -98,20 +98,24 @@ func (s *server) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response,
|
|||||||
func (s *server) setStreamReady(desc *description.Session) *gortsplib.ServerStream {
|
func (s *server) setStreamReady(desc *description.Session) *gortsplib.ServerStream {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
s.stream = &gortsplib.ServerStream{
|
s.stream = &gortsplib.ServerStream{
|
||||||
Server: s.server,
|
Server: s.server,
|
||||||
Desc: desc,
|
Desc: desc,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.stream.Initialize()
|
err := s.stream.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.stream
|
return s.stream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) setStreamUnready() {
|
func (s *server) setStreamUnready() {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
s.stream.Close()
|
s.stream.Close()
|
||||||
s.stream = nil
|
s.stream = nil
|
||||||
}
|
}
|
||||||
|
@@ -14,9 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. create a RTSP server which accepts plain connections
|
// 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
|
// 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
|
// 3. allow multiple clients to read the stream with TCP, UDP or UDP-multicast, if they provide credentials.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// credentials required to publish the stream
|
// credentials required to publish the stream
|
||||||
|
@@ -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())
|
|
||||||
}
|
|
159
examples/server-play-format-h264-from-disk/file_streamer.go
Normal file
159
examples/server-play-format-h264-from-disk/file_streamer.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
137
examples/server-play-format-h264-from-disk/main.go
Normal file
137
examples/server-play-format-h264-from-disk/main.go
Normal 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())
|
||||||
|
}
|
@@ -15,9 +15,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. create a RTSP server which accepts plain connections
|
// 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
|
// 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
|
// 3. save the content of the H264 media in a file in MPEG-TS format.
|
||||||
|
|
||||||
type serverHandler struct {
|
type serverHandler struct {
|
||||||
server *gortsplib.Server
|
server *gortsplib.Server
|
@@ -34,7 +34,11 @@ func (e *mpegtsMuxer) initialize() error {
|
|||||||
Codec: &mpegts.CodecH264{},
|
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
|
return nil
|
||||||
}
|
}
|
@@ -14,9 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. create a RTSP server which accepts only connections encrypted with TLS (RTSPS)
|
// 1. create a RTSP server which accepts only connections encrypted with TLS (RTSPS).
|
||||||
// 2. allow a single client to publish a stream with TCP
|
// 2. allow a single client to publish a stream with TCP.
|
||||||
// 3. allow multiple clients to read that stream with TCP
|
// 3. allow multiple clients to read the stream with TCP.
|
||||||
|
|
||||||
type serverHandler struct {
|
type serverHandler struct {
|
||||||
server *gortsplib.Server
|
server *gortsplib.Server
|
||||||
|
@@ -13,9 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. create a RTSP server which accepts plain connections
|
// 1. create a RTSP server which accepts plain connections.
|
||||||
// 2. allow a single client to publish a stream with TCP or UDP
|
// 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
|
// 3. allow multiple clients to read the stream with TCP, UDP or UDP-multicast.
|
||||||
|
|
||||||
type serverHandler struct {
|
type serverHandler struct {
|
||||||
server *gortsplib.Server
|
server *gortsplib.Server
|
||||||
|
Reference in New Issue
Block a user