From ab0c9e64c95ab8b1490c9b9962e67495c18ebfef Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Sun, 27 Nov 2022 19:36:46 +0100 Subject: [PATCH] merge TrackPCMA and TrackPCMU into Track G711 --- README.md | 5 +- .../main.go | 2 +- examples/client-publish-codec-pcmu/main.go | 69 --------------- .../main.go | 4 +- examples/client-read-codec-pcmu/main.go | 73 --------------- track.go | 7 +- track_pcma.go => track_g711.go | 59 +++++++++---- track_g711_test.go | 71 +++++++++++++++ track_pcma_test.go | 45 ---------- track_pcmu.go | 88 ------------------- track_pcmu_test.go | 45 ---------- track_test.go | 6 +- tracks_test.go | 3 +- 13 files changed, 124 insertions(+), 353 deletions(-) rename examples/{client-publish-codec-pcma => client-publish-codec-g711}/main.go (97%) delete mode 100644 examples/client-publish-codec-pcmu/main.go rename examples/{client-read-codec-pcma => client-read-codec-g711}/main.go (93%) delete mode 100644 examples/client-read-codec-pcmu/main.go rename track_pcma.go => track_g711.go (51%) create mode 100644 track_g711_test.go delete mode 100644 track_pcma_test.go delete mode 100644 track_pcmu.go delete mode 100644 track_pcmu_test.go diff --git a/README.md b/README.md index ec1b4dfc..c261a082 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,11 @@ Features: * VP8 * VP9 * Audio + * G711 * G722 * LPCM * MPEG4-audio (AAC) * Opus - * PCMA - * PCMU * Parse RTSP elements: requests, responses, SDP * Parse H264 elements and formats: Annex-B, AVCC, anti-competition, DTS * Parse MPEG4-audio (AAC) element and formats: ADTS, MPEG4-audio configurations @@ -79,7 +78,6 @@ Features: * [client-read-codec-mpeg4audio](examples/client-read-codec-mpeg4audio/main.go) * [client-read-codec-opus](examples/client-read-codec-opus/main.go) * [client-read-codec-pcma](examples/client-read-codec-pcma/main.go) -* [client-read-codec-pcmu](examples/client-read-codec-pcmu/main.go) * [client-read-codec-vp8](examples/client-read-codec-vp8/main.go) * [client-read-codec-vp9](examples/client-read-codec-vp9/main.go) * [client-read-partial](examples/client-read-partial/main.go) @@ -93,7 +91,6 @@ Features: * [client-publish-codec-mpeg4audio](examples/client-publish-codec-mpeg4audio/main.go) * [client-publish-codec-opus](examples/client-publish-codec-opus/main.go) * [client-publish-codec-pcma](examples/client-publish-codec-pcma/main.go) -* [client-publish-codec-pcmu](examples/client-publish-codec-pcmu/main.go) * [client-publish-codec-vp8](examples/client-publish-codec-vp8/main.go) * [client-publish-codec-vp9](examples/client-publish-codec-vp9/main.go) * [client-publish-options](examples/client-publish-options/main.go) diff --git a/examples/client-publish-codec-pcma/main.go b/examples/client-publish-codec-g711/main.go similarity index 97% rename from examples/client-publish-codec-pcma/main.go rename to examples/client-publish-codec-g711/main.go index becafd2e..ddbbcba3 100644 --- a/examples/client-publish-codec-pcma/main.go +++ b/examples/client-publish-codec-g711/main.go @@ -34,7 +34,7 @@ func main() { log.Println("stream connected") // create a PCMA track - track := &gortsplib.TrackPCMA{} + track := &gortsplib.TrackG711{} c := gortsplib.Client{} diff --git a/examples/client-publish-codec-pcmu/main.go b/examples/client-publish-codec-pcmu/main.go deleted file mode 100644 index 13e1adb5..00000000 --- a/examples/client-publish-codec-pcmu/main.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import ( - "log" - "net" - - "github.com/aler9/gortsplib" - "github.com/pion/rtp" -) - -// This example shows how to -// 1. generate RTP/PCMU packets with GStreamer -// 2. connect to a RTSP server, announce a PCMU track -// 3. route the packets from GStreamer to the server - -func main() { - // open a listener to receive RTP/PCMU packets - pc, err := net.ListenPacket("udp", "localhost:9000") - if err != nil { - panic(err) - } - defer pc.Close() - - log.Println("Waiting for a RTP/PCMU stream on UDP port 9000 - you can send one with GStreamer:\n" + - "gst-launch-1.0 audiotestsrc freq=300 ! audioconvert ! audioresample ! audio/x-raw,rate=8000" + - " ! mulawenc ! rtppcmupay ! udpsink host=127.0.0.1 port=9000") - - // wait for first packet - buf := make([]byte, 2048) - n, _, err := pc.ReadFrom(buf) - if err != nil { - panic(err) - } - log.Println("stream connected") - - // create a PCMU track - track := &gortsplib.TrackPCMU{} - - c := gortsplib.Client{} - - // connect to the server and start publishing the track - err = c.StartPublishing("rtsp://localhost:8554/mystream", - gortsplib.Tracks{track}) - if err != nil { - panic(err) - } - defer c.Close() - - var pkt rtp.Packet - for { - // parse RTP packet - err = pkt.Unmarshal(buf[:n]) - if err != nil { - panic(err) - } - - // route RTP packet to the server - err = c.WritePacketRTP(0, &pkt) - if err != nil { - panic(err) - } - - // read another RTP packet from source - n, _, err = pc.ReadFrom(buf) - if err != nil { - panic(err) - } - } -} diff --git a/examples/client-read-codec-pcma/main.go b/examples/client-read-codec-g711/main.go similarity index 93% rename from examples/client-read-codec-pcma/main.go rename to examples/client-read-codec-g711/main.go index 1bcb6d09..1f971fc5 100644 --- a/examples/client-read-codec-pcma/main.go +++ b/examples/client-read-codec-g711/main.go @@ -35,9 +35,9 @@ func main() { } // find the PCMA track - track := func() *gortsplib.TrackPCMA { + track := func() *gortsplib.TrackG711 { for _, track := range tracks { - if tt, ok := track.(*gortsplib.TrackPCMA); ok { + if tt, ok := track.(*gortsplib.TrackG711); ok { return tt } } diff --git a/examples/client-read-codec-pcmu/main.go b/examples/client-read-codec-pcmu/main.go deleted file mode 100644 index f1ae14cc..00000000 --- a/examples/client-read-codec-pcmu/main.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "log" - - "github.com/aler9/gortsplib" - "github.com/aler9/gortsplib/pkg/url" -) - -// This example shows how to -// 1. connect to a RTSP server and read all tracks on a path -// 2. check if there's a PCMU track -// 3. get PCMU frames of that track - -func main() { - c := gortsplib.Client{} - - // parse URL - u, err := url.Parse("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 published tracks - tracks, baseURL, _, err := c.Describe(u) - if err != nil { - panic(err) - } - - // find the PCMU track - track := func() *gortsplib.TrackPCMU { - for _, track := range tracks { - if tt, ok := track.(*gortsplib.TrackPCMU); ok { - return tt - } - } - return nil - }() - if track == nil { - panic("PCMU track not found") - } - - // setup decoder - dec := track.CreateDecoder() - - // called when a RTP packet arrives - c.OnPacketRTP = func(ctx *gortsplib.ClientOnPacketRTPCtx) { - // decode an PCMU packet from the RTP packet - op, _, err := dec.Decode(ctx.Packet) - if err != nil { - return - } - - // print - log.Printf("received PCMU frame of size %d\n", len(op)) - } - - // setup and read the PCMU track only - err = c.SetupAndPlay(gortsplib.Tracks{track}, baseURL) - if err != nil { - panic(err) - } - - // wait until a fatal error - panic(c.Wait()) -} diff --git a/track.go b/track.go index 393789f5..3a2830dc 100644 --- a/track.go +++ b/track.go @@ -122,11 +122,8 @@ func newTrackFromMediaDescription(md *psdp.MediaDescription) (Track, error) { case md.MediaName.Media == "audio": switch { - case payloadType == 0: - return newTrackPCMUFromMediaDescription(control, clock) - - case payloadType == 8: - return newTrackPCMAFromMediaDescription(control, clock) + case payloadType == 0, payloadType == 8: + return newTrackG711FromMediaDescription(control, payloadType, clock) case payloadType == 9: return newTrackG722FromMediaDescription(control, clock) diff --git a/track_pcma.go b/track_g711.go similarity index 51% rename from track_pcma.go rename to track_g711.go index 38f5dc0b..0b104baa 100644 --- a/track_pcma.go +++ b/track_g711.go @@ -1,4 +1,4 @@ -package gortsplib //nolint:dupl +package gortsplib import ( "fmt" @@ -9,22 +9,27 @@ import ( "github.com/aler9/gortsplib/pkg/rtpcodecs/rtpsimpleaudio" ) -// TrackPCMA is a PCMA track. -type TrackPCMA struct { +// TrackG711 is a PCMA track. +type TrackG711 struct { + // whether to use mu-law. Otherwise, A-law is used. + MULaw bool + trackBase } -func newTrackPCMAFromMediaDescription( +func newTrackG711FromMediaDescription( control string, + payloadType uint8, clock string, -) (*TrackPCMA, error, +) (*TrackG711, error, ) { tmp := strings.Split(clock, "/") if len(tmp) == 2 && tmp[1] != "1" { - return nil, fmt.Errorf("PCMA tracks can have only one channel") + return nil, fmt.Errorf("G711 tracks can have only one channel") } - return &TrackPCMA{ + return &TrackG711{ + MULaw: (payloadType == 0), trackBase: trackBase{ control: control, }, @@ -32,27 +37,37 @@ func newTrackPCMAFromMediaDescription( } // String returns the track codec. -func (t *TrackPCMA) String() string { - return "PCMA" +func (t *TrackG711) String() string { + return "G711" } // ClockRate returns the track clock rate. -func (t *TrackPCMA) ClockRate() int { +func (t *TrackG711) ClockRate() int { return 8000 } // MediaDescription returns the track media description in SDP format. -func (t *TrackPCMA) MediaDescription() *psdp.MediaDescription { +func (t *TrackG711) MediaDescription() *psdp.MediaDescription { + var formats []string + var rtpmap string + if t.MULaw { + formats = []string{"0"} + rtpmap = "0 PCMU/8000" + } else { + formats = []string{"8"} + rtpmap = "8 PCMA/8000" + } + return &psdp.MediaDescription{ MediaName: psdp.MediaName{ Media: "audio", Protos: []string{"RTP", "AVP"}, - Formats: []string{"8"}, + Formats: formats, }, Attributes: []psdp.Attribute{ { Key: "rtpmap", - Value: "8 PCMA/8000", + Value: rtpmap, }, { Key: "control", @@ -62,14 +77,15 @@ func (t *TrackPCMA) MediaDescription() *psdp.MediaDescription { } } -func (t *TrackPCMA) clone() Track { - return &TrackPCMA{ +func (t *TrackG711) clone() Track { + return &TrackG711{ + MULaw: t.MULaw, trackBase: t.trackBase, } } // CreateDecoder creates a decoder able to decode the content of the track. -func (t *TrackPCMA) CreateDecoder() *rtpsimpleaudio.Decoder { +func (t *TrackG711) CreateDecoder() *rtpsimpleaudio.Decoder { d := &rtpsimpleaudio.Decoder{ SampleRate: 8000, } @@ -78,9 +94,16 @@ func (t *TrackPCMA) CreateDecoder() *rtpsimpleaudio.Decoder { } // CreateEncoder creates an encoder able to encode the content of the track. -func (t *TrackPCMA) CreateEncoder() *rtpsimpleaudio.Encoder { +func (t *TrackG711) CreateEncoder() *rtpsimpleaudio.Encoder { + var payloadType uint8 + if t.MULaw { + payloadType = 0 + } else { + payloadType = 8 + } + e := &rtpsimpleaudio.Encoder{ - PayloadType: 8, + PayloadType: payloadType, SampleRate: 8000, } e.Init() diff --git a/track_g711_test.go b/track_g711_test.go new file mode 100644 index 00000000..0402b08d --- /dev/null +++ b/track_g711_test.go @@ -0,0 +1,71 @@ +package gortsplib + +import ( + "testing" + + psdp "github.com/pion/sdp/v3" + "github.com/stretchr/testify/require" +) + +func TestTrackG711Attributes(t *testing.T) { + track := &TrackG711{} + require.Equal(t, "G711", track.String()) + require.Equal(t, 8000, track.ClockRate()) + require.Equal(t, "", track.GetControl()) +} + +func TestTrackG711Clone(t *testing.T) { + track := &TrackG711{} + + clone := track.clone() + require.NotSame(t, track, clone) + require.Equal(t, track, clone) +} + +func TestTrackG711MediaDescription(t *testing.T) { + t.Run("pcma", func(t *testing.T) { + track := &TrackG711{} + + require.Equal(t, &psdp.MediaDescription{ + MediaName: psdp.MediaName{ + Media: "audio", + Protos: []string{"RTP", "AVP"}, + Formats: []string{"8"}, + }, + Attributes: []psdp.Attribute{ + { + Key: "rtpmap", + Value: "8 PCMA/8000", + }, + { + Key: "control", + Value: "", + }, + }, + }, track.MediaDescription()) + }) + + t.Run("pcmu", func(t *testing.T) { + track := &TrackG711{ + MULaw: true, + } + + require.Equal(t, &psdp.MediaDescription{ + MediaName: psdp.MediaName{ + Media: "audio", + Protos: []string{"RTP", "AVP"}, + Formats: []string{"0"}, + }, + Attributes: []psdp.Attribute{ + { + Key: "rtpmap", + Value: "0 PCMU/8000", + }, + { + Key: "control", + Value: "", + }, + }, + }, track.MediaDescription()) + }) +} diff --git a/track_pcma_test.go b/track_pcma_test.go deleted file mode 100644 index 967cfa64..00000000 --- a/track_pcma_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package gortsplib //nolint:dupl - -import ( - "testing" - - psdp "github.com/pion/sdp/v3" - "github.com/stretchr/testify/require" -) - -func TestTrackPCMAAttributes(t *testing.T) { - track := &TrackPCMA{} - require.Equal(t, "PCMA", track.String()) - require.Equal(t, 8000, track.ClockRate()) - require.Equal(t, "", track.GetControl()) -} - -func TestTrackPCMAClone(t *testing.T) { - track := &TrackPCMA{} - - clone := track.clone() - require.NotSame(t, track, clone) - require.Equal(t, track, clone) -} - -func TestTrackPCMAMediaDescription(t *testing.T) { - track := &TrackPCMA{} - - require.Equal(t, &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"8"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "8 PCMA/8000", - }, - { - Key: "control", - Value: "", - }, - }, - }, track.MediaDescription()) -} diff --git a/track_pcmu.go b/track_pcmu.go deleted file mode 100644 index c48b4a43..00000000 --- a/track_pcmu.go +++ /dev/null @@ -1,88 +0,0 @@ -package gortsplib //nolint:dupl - -import ( - "fmt" - "strings" - - psdp "github.com/pion/sdp/v3" - - "github.com/aler9/gortsplib/pkg/rtpcodecs/rtpsimpleaudio" -) - -// TrackPCMU is a PCMU track. -type TrackPCMU struct { - trackBase -} - -func newTrackPCMUFromMediaDescription( - control string, - clock string, -) (*TrackPCMU, error, -) { - tmp := strings.SplitN(clock, "/", 2) - if len(tmp) == 2 && tmp[1] != "1" { - return nil, fmt.Errorf("PCMU tracks can have only one channel") - } - - return &TrackPCMU{ - trackBase: trackBase{ - control: control, - }, - }, nil -} - -// String returns the track codec. -func (t *TrackPCMU) String() string { - return "PCMU" -} - -// ClockRate returns the track clock rate. -func (t *TrackPCMU) ClockRate() int { - return 8000 -} - -// MediaDescription returns the track media description in SDP format. -func (t *TrackPCMU) MediaDescription() *psdp.MediaDescription { - return &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"0"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "0 PCMU/8000", - }, - { - Key: "control", - Value: t.control, - }, - }, - } -} - -func (t *TrackPCMU) clone() Track { - return &TrackPCMU{ - trackBase: t.trackBase, - } -} - -// CreateDecoder creates a decoder able to decode the content of the track. -func (t *TrackPCMU) CreateDecoder() *rtpsimpleaudio.Decoder { - d := &rtpsimpleaudio.Decoder{ - SampleRate: 8000, - } - d.Init() - return d -} - -// CreateEncoder creates an encoder able to encode the content of the track. -func (t *TrackPCMU) CreateEncoder() *rtpsimpleaudio.Encoder { - e := &rtpsimpleaudio.Encoder{ - PayloadType: 0, - SampleRate: 8000, - } - e.Init() - return e -} diff --git a/track_pcmu_test.go b/track_pcmu_test.go deleted file mode 100644 index 03f5ca8d..00000000 --- a/track_pcmu_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package gortsplib //nolint:dupl - -import ( - "testing" - - psdp "github.com/pion/sdp/v3" - "github.com/stretchr/testify/require" -) - -func TestTrackPCMUAttributes(t *testing.T) { - track := &TrackPCMU{} - require.Equal(t, "PCMU", track.String()) - require.Equal(t, 8000, track.ClockRate()) - require.Equal(t, "", track.GetControl()) -} - -func TestTrackPCMUClone(t *testing.T) { - track := &TrackPCMU{} - - clone := track.clone() - require.NotSame(t, track, clone) - require.Equal(t, track, clone) -} - -func TestTrackPCMUMediaDescription(t *testing.T) { - track := &TrackPCMU{} - - require.Equal(t, &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"0"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "0 PCMU/8000", - }, - { - Key: "control", - Value: "", - }, - }, - }, track.MediaDescription()) -} diff --git a/track_test.go b/track_test.go index 459548f0..3ac59a2d 100644 --- a/track_test.go +++ b/track_test.go @@ -25,7 +25,7 @@ func TestTrackNewFromMediaDescription(t *testing.T) { Formats: []string{"8"}, }, }, - &TrackPCMA{}, + &TrackG711{}, }, { "pcmu", @@ -36,7 +36,9 @@ func TestTrackNewFromMediaDescription(t *testing.T) { Formats: []string{"0"}, }, }, - &TrackPCMU{}, + &TrackG711{ + MULaw: true, + }, }, { "g722", diff --git a/tracks_test.go b/tracks_test.go index adfce999..88134f9d 100644 --- a/tracks_test.go +++ b/tracks_test.go @@ -81,7 +81,8 @@ func TestTracksReadSkipGenericTracksWithoutClockRate(t *testing.T) { PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, PacketizationMode: 1, }, - &TrackPCMU{ + &TrackG711{ + MULaw: true, trackBase: trackBase{ control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", },