streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

commit 896f60249033051fe531557d83031f3d0e2fa1c1
parent 035ab79449be5db836ae24e00d8bfe3317f14ea5
Author: Oliver Lowe <o@olowe.co>
Date:   Fri,  4 Jul 2025 21:28:03 +1000

sdp: use encoding.TextMarshaler for Session encoding

fmt.Stringer is not really meant for larger multi-line text.
It sucks a bit that we may return an error, but this lets us
put some validation logic in; we don't have any validation yet.

Diffstat:
Msdp/encode.go | 14+++++++-------
Msdp/encode_test.go | 15+++++++++------
Msdp/example_test.go | 7++++---
Msdp/sdp.go | 8++++++++
4 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/sdp/encode.go b/sdp/encode.go @@ -1,21 +1,21 @@ package sdp import ( + "bytes" "fmt" "strings" ) -func (s Session) String() string { +func (s Session) MarshalText() ([]byte, error) { buf := &strings.Builder{} fmt.Fprintln(buf, "v=0") + if s.Origin.Username == "" { s.Origin.Username = NoUsername } - ipv := "IP6" - if s.Origin.Address.Is4() { - ipv = "IP4" - } - fmt.Fprintf(buf, "o=%s %d %d IN %s %s\n", s.Origin.Username, s.Origin.ID, s.Origin.Version, ipv, s.Origin.Address) + + fmt.Fprintln(buf, s.Origin) + fmt.Fprintf(buf, "s=%s\n", s.Name) if s.Info != "" { @@ -65,5 +65,5 @@ func (s Session) String() string { fmt.Fprintln(buf, m) } - return strings.TrimSpace(buf.String()) + return bytes.TrimSpace([]byte(buf.String())), nil } diff --git a/sdp/encode_test.go b/sdp/encode_test.go @@ -12,18 +12,21 @@ func TestWriteSession(t *testing.T) { if err != nil { t.Fatal(err) } - b, err := io.ReadAll(f) + want, err := io.ReadAll(f) if err != nil { t.Fatal(err) } - want := string(b) - session, err := ReadSession(bytes.NewReader(b)) + session, err := ReadSession(bytes.NewReader(want)) if err != nil { t.Fatal(err) } - if want != session.String() { + got, err := session.MarshalText() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, got) { t.Errorf("mismatched sdp text") - t.Log("want", want) - t.Log("got", session.String()) + t.Log("want", string(want)) + t.Log("got", string(got)) } } diff --git a/sdp/example_test.go b/sdp/example_test.go @@ -12,8 +12,8 @@ import ( // audio in RTP starts by setting the mandatory fields Origin and Name. // The Media field contains information about the audio such as the sample rate // and the number of audio channels. -// The Session type implements fmt.Stringer; -// to encode a Session in the SDP text format, use Session.String(). +// Session implements encoding.TextMarshaler; +// to encode a Session in the SDP text format, use Session.MarshalText(). func Example() { session := sdp.Session{ Origin: sdp.Origin{ @@ -35,7 +35,8 @@ func Example() { }, }, } - fmt.Printf("%s", session) + text, _ := session.MarshalText() + fmt.Printf("%s", text) // Output: // v=0 // o=- 3930287268 3930287268 IN IP6 2001:db8::1 diff --git a/sdp/sdp.go b/sdp/sdp.go @@ -60,6 +60,14 @@ type Origin struct { Address netip.Addr } +func (o Origin) String() string { + ipv := "IP6" + if o.Address.Is4() { + ipv = "IP4" + } + return fmt.Sprintf("o=%s %d %d IN %s %s", o.Username, o.ID, o.Version, ipv, o.Address) +} + func ReadSession(rd io.Reader) (*Session, error) { parser := &parser{Scanner: bufio.NewScanner(rd)} if err := parser.parse(); err != nil {