diff --git a/README.md b/README.md index b374b9ba..5dd75c5d 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ Features: * [client-play-format-mpeg4audio](examples/client-play-format-mpeg4audio/main.go) * [client-play-format-mpeg4audio-save-to-disk](examples/client-play-format-mpeg4audio-save-to-disk/main.go) * [client-play-format-opus](examples/client-play-format-opus/main.go) +* [client-play-format-opus-save-to-disk](examples/client-play-format-opus-save-to-disk/main.go) * [client-play-format-vp8](examples/client-play-format-vp8/main.go) * [client-play-format-vp9](examples/client-play-format-vp9/main.go) * [client-record-options](examples/client-record-options/main.go) diff --git a/examples/client-play-format-av1/main.go b/examples/client-play-format-av1/main.go index 5bc4b0bd..96db9065 100644 --- a/examples/client-play-format-av1/main.go +++ b/examples/client-play-format-av1/main.go @@ -12,8 +12,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a AV1 media -// 3. get access units of that media +// 2. check if there's a AV1 format +// 3. get access units of that format func main() { c := gortsplib.Client{} diff --git a/examples/client-play-format-g711/main.go b/examples/client-play-format-g711/main.go index 4a62d226..a2ed8e19 100644 --- a/examples/client-play-format-g711/main.go +++ b/examples/client-play-format-g711/main.go @@ -11,8 +11,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a G711 media -// 3. get G711 frames of that media +// 2. check if there's a G711 format +// 3. get G711 frames of that format func main() { c := gortsplib.Client{} diff --git a/examples/client-play-format-g722/main.go b/examples/client-play-format-g722/main.go index 055cad30..93b87f91 100644 --- a/examples/client-play-format-g722/main.go +++ b/examples/client-play-format-g722/main.go @@ -11,8 +11,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a G722 media -// 3. get G722 frames of that media +// 2. check if there's a G722 format +// 3. get G722 frames of that format func main() { c := gortsplib.Client{} diff --git a/examples/client-play-format-h264-convert-to-jpeg/main.go b/examples/client-play-format-h264-convert-to-jpeg/main.go index 423acd40..b87bef32 100644 --- a/examples/client-play-format-h264-convert-to-jpeg/main.go +++ b/examples/client-play-format-h264-convert-to-jpeg/main.go @@ -18,8 +18,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a H264 media stream -// 3. decode the H264 media stream into RGBA frames +// 2. check if there's a H264 format +// 3. decode the H264 format into RGBA frames // 4. convert frames to JPEG images and save them on disk // This example requires the FFmpeg libraries, that can be installed with this command: diff --git a/examples/client-play-format-h264-save-to-disk/main.go b/examples/client-play-format-h264-save-to-disk/main.go index c81e6e0e..b9beb41a 100644 --- a/examples/client-play-format-h264-save-to-disk/main.go +++ b/examples/client-play-format-h264-save-to-disk/main.go @@ -12,8 +12,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a H264 media -// 3. save the content of the media into a file in MPEG-TS format +// 2. check if there's a H264 format +// 3. save the content of the format into a file in MPEG-TS format func main() { c := gortsplib.Client{} diff --git a/examples/client-play-format-h264/main.go b/examples/client-play-format-h264/main.go index 82f3e7da..4be907a4 100644 --- a/examples/client-play-format-h264/main.go +++ b/examples/client-play-format-h264/main.go @@ -12,8 +12,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's an H264 media stream -// 3. decode the H264 media stream into RGBA frames +// 2. check if there's an H264 format +// 3. decode the H264 format into RGBA frames // This example requires the FFmpeg libraries, that can be installed with this command: // apt install -y libavformat-dev libswscale-dev gcc pkg-config diff --git a/examples/client-play-format-h265-convert-to-jpeg/main.go b/examples/client-play-format-h265-convert-to-jpeg/main.go index d48556ed..e706b347 100644 --- a/examples/client-play-format-h265-convert-to-jpeg/main.go +++ b/examples/client-play-format-h265-convert-to-jpeg/main.go @@ -18,8 +18,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a H265 media stream -// 3. decode the H265 media stream into RGBA frames +// 2. check if there's a H265 format +// 3. decode the H265 format into RGBA frames // 4. convert frames to JPEG images and save them on disk // This example requires the FFmpeg libraries, that can be installed with this command: diff --git a/examples/client-play-format-h265-save-to-disk/main.go b/examples/client-play-format-h265-save-to-disk/main.go index e670b7a9..d9567d93 100644 --- a/examples/client-play-format-h265-save-to-disk/main.go +++ b/examples/client-play-format-h265-save-to-disk/main.go @@ -12,8 +12,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a H265 media -// 3. save the content of the media into a file in MPEG-TS format +// 2. check if there's a H265 format +// 3. save the content of the format into a file in MPEG-TS format func main() { c := gortsplib.Client{} diff --git a/examples/client-play-format-h265/main.go b/examples/client-play-format-h265/main.go index 5145e61d..2891d8a0 100644 --- a/examples/client-play-format-h265/main.go +++ b/examples/client-play-format-h265/main.go @@ -12,8 +12,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's an H265 media stream -// 3. decode the H264 media stream into RGBA frames +// 2. check if there's an H265 format +// 3. decode the H265 format into RGBA frames // This example requires the FFmpeg libraries, that can be installed with this command: // apt install -y libavformat-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 19946a50..f9879036 100644 --- a/examples/client-play-format-lpcm/main.go +++ b/examples/client-play-format-lpcm/main.go @@ -11,8 +11,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's an LPCM media -// 3. get LPCM packets of that media +// 2. check if there's an LPCM format +// 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 4e8f3be1..b5ab1712 100644 --- a/examples/client-play-format-mjpeg/main.go +++ b/examples/client-play-format-mjpeg/main.go @@ -14,8 +14,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a M-JPEG media -// 3. get JPEG images of that media +// 2. check if there's a M-JPEG format +// 3. get JPEG images of that format // 4. decode JPEG images into raw images func main() { diff --git a/examples/client-play-format-mpeg4audio-save-to-disk/main.go b/examples/client-play-format-mpeg4audio-save-to-disk/main.go index 129299f2..64d29598 100644 --- a/examples/client-play-format-mpeg4audio-save-to-disk/main.go +++ b/examples/client-play-format-mpeg4audio-save-to-disk/main.go @@ -6,13 +6,14 @@ import ( "github.com/bluenviron/gortsplib/v4" "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/format" + "github.com/bluenviron/mediacommon/pkg/formats/mpegts" "github.com/pion/rtp" ) // This example shows how to // 1. connect to a RTSP server -// 2. check if there's an MPEG-4 audio media -// 3. save the content of the media into a file in MPEG-TS format +// 2. check if there's an MPEG-4 audio format +// 3. save the content of the format into a file in MPEG-TS format func main() { c := gortsplib.Client{} @@ -52,7 +53,11 @@ func main() { // setup MPEG-4 audio -> MPEG-TS muxer mpegtsMuxer := &mpegtsMuxer{ fileName: "mystream.ts", - config: forma.Config, + track: &mpegts.Track{ + Codec: &mpegts.CodecMPEG4Audio{ + Config: *forma.Config, + }, + }, } mpegtsMuxer.initialize() if err != nil { diff --git a/examples/client-play-format-mpeg4audio-save-to-disk/mpegts_muxer.go b/examples/client-play-format-mpeg4audio-save-to-disk/mpegts_muxer.go index 347ffd16..3d055bdd 100644 --- a/examples/client-play-format-mpeg4audio-save-to-disk/mpegts_muxer.go +++ b/examples/client-play-format-mpeg4audio-save-to-disk/mpegts_muxer.go @@ -5,7 +5,6 @@ import ( "os" "time" - "github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio" "github.com/bluenviron/mediacommon/pkg/formats/mpegts" ) @@ -13,15 +12,14 @@ func durationGoToMPEGTS(v time.Duration) int64 { return int64(v.Seconds() * 90000) } -// mpegtsMuxer allows to save a MPEG4-audio stream into a MPEG-TS file. +// mpegtsMuxer allows to save a MPEG-4 audio stream into a MPEG-TS file. type mpegtsMuxer struct { fileName string - config *mpeg4audio.Config + track *mpegts.Track - f *os.File - b *bufio.Writer - w *mpegts.Writer - track *mpegts.Track + f *os.File + b *bufio.Writer + w *mpegts.Writer } // initialize initializes a mpegtsMuxer. @@ -33,12 +31,6 @@ func (e *mpegtsMuxer) initialize() error { } e.b = bufio.NewWriter(e.f) - e.track = &mpegts.Track{ - Codec: &mpegts.CodecMPEG4Audio{ - Config: *e.config, - }, - } - e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.track}) return nil diff --git a/examples/client-play-format-mpeg4audio/main.go b/examples/client-play-format-mpeg4audio/main.go index f2f88f7a..6631f200 100644 --- a/examples/client-play-format-mpeg4audio/main.go +++ b/examples/client-play-format-mpeg4audio/main.go @@ -11,8 +11,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's an MPEG4-audio media -// 3. get access units of that media +// 2. check if there's an MPEG-4 audio format +// 3. get access units of that format func main() { c := gortsplib.Client{} @@ -36,7 +36,7 @@ func main() { panic(err) } - // find the MPEG4-audio media and format + // find the MPEG-4 audio media and format var forma *format.MPEG4Audio medi := desc.FindFormat(&forma) if medi == nil { diff --git a/examples/client-play-format-opus-save-to-disk/main.go b/examples/client-play-format-opus-save-to-disk/main.go new file mode 100644 index 00000000..7097ad54 --- /dev/null +++ b/examples/client-play-format-opus-save-to-disk/main.go @@ -0,0 +1,112 @@ +package main + +import ( + "log" + + "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/base" + "github.com/bluenviron/gortsplib/v4/pkg/format" + "github.com/bluenviron/mediacommon/pkg/formats/mpegts" + "github.com/pion/rtp" +) + +// This example shows how to +// 1. connect to a RTSP server +// 2. check if there's a Opus format +// 3. save the content of the format into a file in MPEG-TS format + +func main() { + c := gortsplib.Client{} + + // parse URL + u, err := base.ParseURL("rtsp://localhost:8554/mystream") + if err != nil { + panic(err) + } + + // connect to the server + err = c.Start(u.Scheme, u.Host) + if err != nil { + panic(err) + } + defer c.Close() + + // find available medias + desc, _, err := c.Describe(u) + if err != nil { + panic(err) + } + + // find the Opus media and format + var forma *format.Opus + medi := desc.FindFormat(&forma) + if medi == nil { + panic("media not found") + } + + // setup RTP/Opus -> Opus decoder + rtpDec, err := forma.CreateDecoder() + if err != nil { + panic(err) + } + + // setup Opus -> MPEG-TS muxer + mpegtsMuxer := &mpegtsMuxer{ + fileName: "mystream.ts", + track: &mpegts.Track{ + Codec: &mpegts.CodecOpus{ + ChannelCount: func() int { + if forma.IsStereo { + return 2 + } + return 1 + }(), + }, + }, + } + mpegtsMuxer.initialize() + if err != nil { + panic(err) + } + + // setup a single media + _, err = c.Setup(desc.BaseURL, medi, 0, 0) + if err != nil { + panic(err) + } + + // called when a RTP packet arrives + c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) { + // decode timestamp + pts, ok := c.PacketPTS(medi, pkt) + if !ok { + log.Printf("waiting for timestamp") + return + } + + // extract Opus packets from RTP packets + opkt, err := rtpDec.Decode(pkt) + if err != nil { + log.Printf("ERR: %v", err) + return + } + + // encode Opus packets into MPEG-TS + err = mpegtsMuxer.writeOpus(opkt, pts) + if err != nil { + log.Printf("ERR: %v", err) + return + } + + log.Printf("saved TS packet") + }) + + // start playing + _, err = c.Play(nil) + if err != nil { + panic(err) + } + + // wait until a fatal error + panic(c.Wait()) +} diff --git a/examples/client-play-format-opus-save-to-disk/mpegts_muxer.go b/examples/client-play-format-opus-save-to-disk/mpegts_muxer.go new file mode 100644 index 00000000..c709d72a --- /dev/null +++ b/examples/client-play-format-opus-save-to-disk/mpegts_muxer.go @@ -0,0 +1,48 @@ +package main + +import ( + "bufio" + "os" + "time" + + "github.com/bluenviron/mediacommon/pkg/formats/mpegts" +) + +func durationGoToMPEGTS(v time.Duration) int64 { + return int64(v.Seconds() * 90000) +} + +// mpegtsMuxer allows to save a MPEG-4 audio stream into a MPEG-TS file. +type mpegtsMuxer struct { + fileName string + track *mpegts.Track + + f *os.File + b *bufio.Writer + w *mpegts.Writer +} + +// initialize initializes a mpegtsMuxer. +func (e *mpegtsMuxer) initialize() error { + var err error + e.f, err = os.Create(e.fileName) + if err != nil { + return err + } + e.b = bufio.NewWriter(e.f) + + e.w = mpegts.NewWriter(e.b, []*mpegts.Track{e.track}) + + return nil +} + +// close closes all the mpegtsMuxer resources. +func (e *mpegtsMuxer) close() { + e.b.Flush() + e.f.Close() +} + +// writeOpus writes Opus packets into MPEG-TS. +func (e *mpegtsMuxer) writeOpus(pkt []byte, pts time.Duration) error { + return e.w.WriteOpus(e.track, durationGoToMPEGTS(pts), [][]byte{pkt}) +} diff --git a/examples/client-play-format-opus/main.go b/examples/client-play-format-opus/main.go index 86e64763..de89067c 100644 --- a/examples/client-play-format-opus/main.go +++ b/examples/client-play-format-opus/main.go @@ -11,8 +11,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's an Opus media -// 3. get Opus packets of that media +// 2. check if there's an Opus format +// 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 8ebd73b2..77ea3dcf 100644 --- a/examples/client-play-format-vp8/main.go +++ b/examples/client-play-format-vp8/main.go @@ -12,8 +12,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a VP8 media -// 3. get access units of that media +// 2. check if there's a VP8 format +// 3. get access units of that format func main() { c := gortsplib.Client{} diff --git a/examples/client-play-format-vp9/main.go b/examples/client-play-format-vp9/main.go index be297acb..75b79c14 100644 --- a/examples/client-play-format-vp9/main.go +++ b/examples/client-play-format-vp9/main.go @@ -12,8 +12,8 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's a VP9 media -// 3. get access units of that media +// 2. check if there's a VP9 format +// 3. get access units of that format func main() { c := gortsplib.Client{} diff --git a/examples/client-record-format-mpeg4audio/main.go b/examples/client-record-format-mpeg4audio/main.go index 0e340d95..91d0f60d 100644 --- a/examples/client-record-format-mpeg4audio/main.go +++ b/examples/client-record-format-mpeg4audio/main.go @@ -12,12 +12,12 @@ import ( ) // This example shows how to -// 1. generate RTP/MPEG4-audio packets with GStreamer -// 2. connect to a RTSP server, announce an MPEG4-audio media +// 1. generate RTP/MPEG-4 audio packets with GStreamer +// 2. connect to a RTSP server, announce an MPEG-4 audio media // 3. route the packets from GStreamer to the server func main() { - // open a listener to receive RTP/MPEG4-audio packets + // open a listener to receive RTP/MPEG-4 audio packets pc, err := net.ListenPacket("udp", "localhost:9000") if err != nil { panic(err) diff --git a/pkg/format/rtpmpeg4audio/encoder.go b/pkg/format/rtpmpeg4audio/encoder.go index b1f442cb..9558033a 100644 --- a/pkg/format/rtpmpeg4audio/encoder.go +++ b/pkg/format/rtpmpeg4audio/encoder.go @@ -20,7 +20,7 @@ func randUint32() (uint32, error) { return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil } -// Encoder is a RTP/MPEG4-audio encoder. +// Encoder is a RTP/MPEG-4 audio encoder. // Specification: https://datatracker.ietf.org/doc/html/rfc3640 // Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3 type Encoder struct {