diff --git a/client.go b/client.go index 3f4c6578..7158dfd8 100644 --- a/client.go +++ b/client.go @@ -23,10 +23,10 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/bytecounter" "github.com/bluenviron/gortsplib/v4/pkg/conn" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/headers" "github.com/bluenviron/gortsplib/v4/pkg/liberrors" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/rtptime" "github.com/bluenviron/gortsplib/v4/pkg/sdp" "github.com/bluenviron/gortsplib/v4/pkg/url" @@ -91,7 +91,7 @@ func findBaseURL(sd *sdp.SessionDescription, res *base.Response, u *url.URL) (*u return u, nil } -func resetMediaControls(ms media.Medias) { +func resetMediaControls(ms []*description.Media) { for i, media := range ms { media.Control = "trackID=" + strconv.FormatInt(int64(i), 10) } @@ -148,14 +148,14 @@ type describeReq struct { } type announceReq struct { - url *url.URL - medias media.Medias - res chan clientRes + url *url.URL + desc *description.Session + res chan clientRes } type setupReq struct { - media *media.Media baseURL *url.URL + media *description.Media rtpPort int rtcpPort int res chan clientRes @@ -175,10 +175,9 @@ type pauseReq struct { } type clientRes struct { - medias media.Medias - baseURL *url.URL - res *base.Response - err error + sd *description.Session // describe only + res *base.Response + err error } // ClientOnRequestFunc is the prototype of Client.OnRequest. @@ -200,13 +199,13 @@ type ClientOnDecodeErrorFunc func(err error) type OnPacketRTPFunc func(*rtp.Packet) // OnPacketRTPAnyFunc is the prototype of the callback passed to OnPacketRTP(Any). -type OnPacketRTPAnyFunc func(*media.Media, format.Format, *rtp.Packet) +type OnPacketRTPAnyFunc func(*description.Media, format.Format, *rtp.Packet) // OnPacketRTCPFunc is the prototype of the callback passed to OnPacketRTCP(). type OnPacketRTCPFunc func(rtcp.Packet) // OnPacketRTCPAnyFunc is the prototype of the callback passed to OnPacketRTCPAny(). -type OnPacketRTCPAnyFunc func(*media.Media, rtcp.Packet) +type OnPacketRTCPAnyFunc func(*description.Media, rtcp.Packet) // Client is a RTSP client. type Client struct { @@ -306,7 +305,7 @@ type Client struct { lastDescribeURL *url.URL baseURL *url.URL effectiveTransport *Transport - medias map[*media.Media]*clientMedia + medias map[*description.Media]*clientMedia tcpCallbackByChannel map[int]readFunc lastRange *headers.Range checkTimeoutTimer *time.Timer @@ -442,7 +441,7 @@ func (c *Client) Start(scheme string, host string) error { } // StartRecording connects to the address and starts publishing given media. -func (c *Client) StartRecording(address string, medias media.Medias) error { +func (c *Client) StartRecording(address string, desc *description.Session) error { u, err := url.Parse(address) if err != nil { return err @@ -453,13 +452,13 @@ func (c *Client) StartRecording(address string, medias media.Medias) error { return err } - _, err = c.Announce(u, medias) + _, err = c.Announce(u, desc) if err != nil { c.Close() return err } - err = c.SetupAll(u, medias) + err = c.SetupAll(u, desc.Medias) if err != nil { c.Close() return err @@ -505,11 +504,11 @@ func (c *Client) runInner() error { req.res <- clientRes{res: res, err: err} case req := <-c.describe: - medias, baseURL, res, err := c.doDescribe(req.url) - req.res <- clientRes{medias: medias, baseURL: baseURL, res: res, err: err} + sd, res, err := c.doDescribe(req.url) + req.res <- clientRes{sd: sd, res: res, err: err} case req := <-c.announce: - res, err := c.doAnnounce(req.url, req.medias) + res, err := c.doAnnounce(req.url, req.desc) req.res <- clientRes{res: res, err: err} case req := <-c.setup: @@ -624,7 +623,7 @@ func (c *Client) trySwitchingProtocol() error { c.connURL = prevConnURL // some Hikvision cameras require a describe before a setup - _, _, _, err := c.doDescribe(c.lastDescribeURL) + _, _, err := c.doDescribe(c.lastDescribeURL) if err != nil { return err } @@ -649,7 +648,7 @@ func (c *Client) trySwitchingProtocol() error { return nil } -func (c *Client) trySwitchingProtocol2(medi *media.Media, baseURL *url.URL) (*base.Response, error) { +func (c *Client) trySwitchingProtocol2(medi *description.Media, baseURL *url.URL) (*base.Response, error) { c.OnTransportSwitch(fmt.Errorf("switching to TCP because server requested it")) prevConnURL := c.connURL @@ -661,7 +660,7 @@ func (c *Client) trySwitchingProtocol2(medi *media.Media, baseURL *url.URL) (*ba c.connURL = prevConnURL // some Hikvision cameras require a describe before a setup - _, _, _, err := c.doDescribe(c.lastDescribeURL) + _, _, err := c.doDescribe(c.lastDescribeURL) if err != nil { return nil, err } @@ -970,7 +969,7 @@ func (c *Client) doOptions(u *url.URL) (*base.Response, error) { return res, nil } -// Options writes an OPTIONS request and reads a response. +// Options sends an OPTIONS request. func (c *Client) Options(u *url.URL) (*base.Response, error) { cres := make(chan clientRes) select { @@ -983,14 +982,14 @@ func (c *Client) Options(u *url.URL) (*base.Response, error) { } } -func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response, error) { +func (c *Client) doDescribe(u *url.URL) (*description.Session, *base.Response, error) { err := c.checkState(map[clientState]struct{}{ clientStateInitial: {}, clientStatePrePlay: {}, clientStatePreRecord: {}, }) if err != nil { - return nil, nil, nil, err + return nil, nil, err } res, err := c.do(&base.Request{ @@ -1001,7 +1000,7 @@ func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response, }, }, false, false) if err != nil { - return nil, nil, nil, err + return nil, nil, err } if res.StatusCode != base.StatusOK { @@ -1013,7 +1012,7 @@ func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response, ru, err := url.Parse(res.Header["Location"][0]) if err != nil { - return nil, nil, nil, err + return nil, nil, err } if u.User != nil { @@ -1028,57 +1027,58 @@ func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response, return c.doDescribe(ru) } - return nil, nil, res, liberrors.ErrClientBadStatusCode{Code: res.StatusCode, Message: res.StatusMessage} + return nil, res, liberrors.ErrClientBadStatusCode{Code: res.StatusCode, Message: res.StatusMessage} } ct, ok := res.Header["Content-Type"] if !ok || len(ct) != 1 { - return nil, nil, nil, liberrors.ErrClientContentTypeMissing{} + return nil, nil, liberrors.ErrClientContentTypeMissing{} } // strip encoding information from Content-Type header ct = base.HeaderValue{strings.Split(ct[0], ";")[0]} if ct[0] != "application/sdp" { - return nil, nil, nil, liberrors.ErrClientContentTypeUnsupported{CT: ct} + return nil, nil, liberrors.ErrClientContentTypeUnsupported{CT: ct} } - var sd sdp.SessionDescription - err = sd.Unmarshal(res.Body) + var ssd sdp.SessionDescription + err = ssd.Unmarshal(res.Body) if err != nil { - return nil, nil, nil, err + return nil, nil, err } - var medias media.Medias - err = medias.Unmarshal(sd.MediaDescriptions) + var desc description.Session + err = desc.Unmarshal(&ssd) if err != nil { - return nil, nil, nil, err + return nil, nil, err } - baseURL, err := findBaseURL(&sd, res, u) + baseURL, err := findBaseURL(&ssd, res, u) if err != nil { - return nil, nil, nil, err + return nil, nil, err } + desc.BaseURL = baseURL c.lastDescribeURL = u - return medias, baseURL, res, nil + return &desc, res, nil } -// Describe writes a DESCRIBE request and reads a Response. -func (c *Client) Describe(u *url.URL) (media.Medias, *url.URL, *base.Response, error) { +// Describe sends a DESCRIBE request. +func (c *Client) Describe(u *url.URL) (*description.Session, *base.Response, error) { cres := make(chan clientRes) select { case c.describe <- describeReq{url: u, res: cres}: res := <-cres - return res.medias, res.baseURL, res.res, res.err + return res.sd, res.res, res.err case <-c.ctx.Done(): - return nil, nil, nil, liberrors.ErrClientTerminated{} + return nil, nil, liberrors.ErrClientTerminated{} } } -func (c *Client) doAnnounce(u *url.URL, medias media.Medias) (*base.Response, error) { +func (c *Client) doAnnounce(u *url.URL, desc *description.Session) (*base.Response, error) { err := c.checkState(map[clientState]struct{}{ clientStateInitial: {}, }) @@ -1086,9 +1086,9 @@ func (c *Client) doAnnounce(u *url.URL, medias media.Medias) (*base.Response, er return nil, err } - resetMediaControls(medias) + resetMediaControls(desc.Medias) - byts, err := medias.Marshal(false).Marshal() + byts, err := desc.Marshal(false) if err != nil { return nil, err } @@ -1117,11 +1117,11 @@ func (c *Client) doAnnounce(u *url.URL, medias media.Medias) (*base.Response, er return res, nil } -// Announce writes an ANNOUNCE request and reads a Response. -func (c *Client) Announce(u *url.URL, medias media.Medias) (*base.Response, error) { +// Announce sends an ANNOUNCE request. +func (c *Client) Announce(u *url.URL, desc *description.Session) (*base.Response, error) { cres := make(chan clientRes) select { - case c.announce <- announceReq{url: u, medias: medias, res: cres}: + case c.announce <- announceReq{url: u, desc: desc, res: cres}: res := <-cres return res.res, res.err @@ -1132,7 +1132,7 @@ func (c *Client) Announce(u *url.URL, medias media.Medias) (*base.Response, erro func (c *Client) doSetup( baseURL *url.URL, - medi *media.Media, + medi *description.Media, rtpPort int, rtcpPort int, ) (*base.Response, error) { @@ -1384,7 +1384,7 @@ func (c *Client) doSetup( } if c.medias == nil { - c.medias = make(map[*media.Media]*clientMedia) + c.medias = make(map[*description.Media]*clientMedia) } c.medias[medi] = cm @@ -1417,20 +1417,20 @@ func (c *Client) findFreeChannelPair() int { } } -// Setup writes a SETUP request and reads a Response. +// Setup sends a SETUP request. // rtpPort and rtcpPort are used only if transport is UDP. // if rtpPort and rtcpPort are zero, they are chosen automatically. func (c *Client) Setup( baseURL *url.URL, - media *media.Media, + media *description.Media, rtpPort int, rtcpPort int, ) (*base.Response, error) { cres := make(chan clientRes) select { case c.setup <- setupReq{ - media: media, baseURL: baseURL, + media: media, rtpPort: rtpPort, rtcpPort: rtcpPort, res: cres, @@ -1444,7 +1444,7 @@ func (c *Client) Setup( } // SetupAll setups all the given medias. -func (c *Client) SetupAll(baseURL *url.URL, medias media.Medias) error { +func (c *Client) SetupAll(baseURL *url.URL, medias []*description.Media) error { for _, m := range medias { _, err := c.Setup(baseURL, m, 0, 0) if err != nil { @@ -1509,7 +1509,7 @@ func (c *Client) doPlay(ra *headers.Range) (*base.Response, error) { return res, nil } -// Play writes a PLAY request and reads a Response. +// Play sends a PLAY request. // This can be called only after Setup(). func (c *Client) Play(ra *headers.Range) (*base.Response, error) { cres := make(chan clientRes) @@ -1551,7 +1551,7 @@ func (c *Client) doRecord() (*base.Response, error) { return nil, nil } -// Record writes a RECORD request and reads a Response. +// Record sends a RECORD request. // This can be called only after Announce() and Setup(). func (c *Client) Record() (*base.Response, error) { cres := make(chan clientRes) @@ -1601,7 +1601,7 @@ func (c *Client) doPause() (*base.Response, error) { return res, nil } -// Pause writes a PAUSE request and reads a Response. +// Pause sends a PAUSE request. // This can be called only after Play() or Record(). func (c *Client) Pause() (*base.Response, error) { cres := make(chan clientRes) @@ -1648,26 +1648,26 @@ func (c *Client) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) { } // OnPacketRTP sets the callback that is called when a RTP packet is read. -func (c *Client) OnPacketRTP(medi *media.Media, forma format.Format, cb OnPacketRTPFunc) { +func (c *Client) OnPacketRTP(medi *description.Media, forma format.Format, cb OnPacketRTPFunc) { cm := c.medias[medi] ct := cm.formats[forma.PayloadType()] ct.onPacketRTP = cb } // OnPacketRTCP sets the callback that is called when a RTCP packet is read. -func (c *Client) OnPacketRTCP(medi *media.Media, cb OnPacketRTCPFunc) { +func (c *Client) OnPacketRTCP(medi *description.Media, cb OnPacketRTCPFunc) { cm := c.medias[medi] cm.onPacketRTCP = cb } // WritePacketRTP writes a RTP packet to the server. -func (c *Client) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error { +func (c *Client) WritePacketRTP(medi *description.Media, pkt *rtp.Packet) error { return c.WritePacketRTPWithNTP(medi, pkt, c.timeNow()) } // WritePacketRTPWithNTP writes a RTP packet to the server. // ntp is the absolute time of the packet, and is sent with periodic RTCP sender reports. -func (c *Client) WritePacketRTPWithNTP(medi *media.Media, pkt *rtp.Packet, ntp time.Time) error { +func (c *Client) WritePacketRTPWithNTP(medi *description.Media, pkt *rtp.Packet, ntp time.Time) error { byts := make([]byte, c.MaxPacketSize) n, err := pkt.MarshalTo(byts) if err != nil { @@ -1688,7 +1688,7 @@ func (c *Client) WritePacketRTPWithNTP(medi *media.Media, pkt *rtp.Packet, ntp t } // WritePacketRTCP writes a RTCP packet to the server. -func (c *Client) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) error { +func (c *Client) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet) error { byts, err := pkt.Marshal() if err != nil { return err @@ -1713,7 +1713,7 @@ func (c *Client) PacketPTS(forma format.Format, pkt *rtp.Packet) (time.Duration, // PacketNTP returns the NTP timestamp of an incoming RTP packet. // The NTP timestamp is computed from sender reports. -func (c *Client) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) { +func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time, bool) { cm := c.medias[medi] ct := cm.formats[pkt.PayloadType] return ct.rtcpReceiver.PacketNTP(pkt.Timestamp) diff --git a/client_media.go b/client_media.go index 044311da..9e49b403 100644 --- a/client_media.go +++ b/client_media.go @@ -9,12 +9,12 @@ import ( "github.com/pion/rtp" "github.com/bluenviron/gortsplib/v4/pkg/base" - "github.com/bluenviron/gortsplib/v4/pkg/media" + "github.com/bluenviron/gortsplib/v4/pkg/description" ) type clientMedia struct { c *Client - media *media.Media + media *description.Media formats map[uint8]*clientFormat tcpChannel int udpRTPListener *clientUDPListener @@ -71,7 +71,7 @@ func (cm *clientMedia) allocateUDPListeners(multicast bool, rtpAddress string, r return err } -func (cm *clientMedia) setMedia(medi *media.Media) { +func (cm *clientMedia) setMedia(medi *description.Media) { cm.media = medi cm.formats = make(map[uint8]*clientFormat) diff --git a/client_play_test.go b/client_play_test.go index ea8887f2..2a4fdb57 100644 --- a/client_play_test.go +++ b/client_play_test.go @@ -12,15 +12,16 @@ import ( "github.com/pion/rtcp" "github.com/pion/rtp" + psdp "github.com/pion/sdp/v3" "github.com/stretchr/testify/require" "golang.org/x/net/ipv4" "github.com/bluenviron/gortsplib/v4/pkg/auth" "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/conn" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/headers" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/url" "github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio" ) @@ -29,8 +30,34 @@ func ipPtr(v net.IP) *net.IP { return &v } -func mustMarshalMedias(medias media.Medias) []byte { - byts, err := medias.Marshal(false).Marshal() +func mediasToSDP(medias []*description.Media) []byte { + resetMediaControls(medias) + + sout := &psdp.SessionDescription{ + SessionName: psdp.SessionName("Stream"), + Origin: psdp.Origin{ + Username: "-", + NetworkType: "IN", + AddressType: "IP4", + UnicastAddress: "127.0.0.1", + }, + // required by Darwin Streaming Server + ConnectionInformation: &psdp.ConnectionInformation{ + NetworkType: "IN", + AddressType: "IP4", + Address: &psdp.Address{Address: "0.0.0.0"}, + }, + TimeDescriptions: []psdp.TimeDescription{ + {Timing: psdp.Timing{StartTime: 0, StopTime: 0}}, + }, + MediaDescriptions: make([]*psdp.MediaDescription, len(medias)), + } + + for i, media := range medias { + sout.MediaDescriptions[i] = media.Marshal() + } + + byts, err := sout.Marshal() if err != nil { panic(err) } @@ -53,7 +80,7 @@ func mustMarshalPacketRTCP(pkt rtcp.Packet) []byte { return byts } -func readAll(c *Client, ur string, cb func(*media.Media, format.Format, *rtp.Packet)) error { +func readAll(c *Client, ur string, cb func(*description.Media, format.Format, *rtp.Packet)) error { u, err := url.Parse(ur) if err != nil { return err @@ -64,13 +91,13 @@ func readAll(c *Client, ur string, cb func(*media.Media, format.Format, *rtp.Pac return err } - medias, baseURL, _, err := c.Describe(u) + sd, _, err := c.Describe(u) if err != nil { c.Close() return err } - err = c.SetupAll(baseURL, medias) + err = c.SetupAll(sd.BaseURL, sd.Medias) if err != nil { c.Close() return err @@ -92,8 +119,8 @@ func readAll(c *Client, ur string, cb func(*media.Media, format.Format, *rtp.Pac func TestClientPlayFormats(t *testing.T) { media1 := testH264Media - media2 := &media.Media{ - Type: media.TypeAudio, + media2 := &description.Media{ + Type: description.MediaTypeAudio, Formats: []format.Format{&format.MPEG4Audio{ PayloadTyp: 96, Config: &mpeg4audio.Config{ @@ -107,8 +134,8 @@ func TestClientPlayFormats(t *testing.T) { }}, } - media3 := &media.Media{ - Type: media.TypeAudio, + media3 := &description.Media{ + Type: description.MediaTypeAudio, Formats: []format.Format{&format.MPEG4Audio{ PayloadTyp: 96, Config: &mpeg4audio.Config{ @@ -157,8 +184,7 @@ func TestClientPlayFormats(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) - medias := media.Medias{media1, media2, media3} - resetMediaControls(medias) + medias := []*description.Media{media1, media2, media3} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -166,7 +192,7 @@ func TestClientPlayFormats(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -293,17 +319,16 @@ func TestClientPlay(t *testing.T) { err = forma.Init() require.NoError(t, err) - medias := media.Medias{ - &media.Media{ + medias := []*description.Media{ + { Type: "application", Formats: []format.Format{forma}, }, - &media.Media{ + { Type: "application", Formats: []format.Format{forma}, }, } - resetMediaControls(medias) err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -311,7 +336,7 @@ func TestClientPlay(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{scheme + "://" + listenIP + ":8554/test/stream?param=value/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -505,13 +530,13 @@ func TestClientPlay(t *testing.T) { require.NoError(t, err) defer c.Close() - medias, baseURL, _, err := c.Describe(u) + sd, _, err := c.Describe(u) require.NoError(t, err) - err = c.SetupAll(baseURL, medias) + err = c.SetupAll(sd.BaseURL, sd.Medias) require.NoError(t, err) - c.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + c.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { require.Equal(t, &testRTPPacket, pkt) err := c.WritePacketRTCP(medi, &testRTCPPacket) require.NoError(t, err) @@ -569,17 +594,16 @@ func TestClientPlayPartial(t *testing.T) { err = forma.Init() require.NoError(t, err) - medias := media.Medias{ - &media.Media{ + medias := []*description.Media{ + { Type: "application", Formats: []format.Format{forma}, }, - &media.Media{ + { Type: "application", Formats: []format.Format{forma}, }, } - resetMediaControls(medias) err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -587,7 +611,7 @@ func TestClientPlayPartial(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://" + listenIP + ":8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -661,15 +685,15 @@ func TestClientPlayPartial(t *testing.T) { require.NoError(t, err) defer c.Close() - medias, baseURL, _, err := c.Describe(u) + sd, _, err := c.Describe(u) require.NoError(t, err) - _, err = c.Setup(baseURL, medias[1], 0, 0) + _, err = c.Setup(sd.BaseURL, sd.Medias[1], 0, 0) require.NoError(t, err) - c.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { - require.Equal(t, medias[1], medi) - require.Equal(t, medias[1].Formats[0], forma) + c.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { + require.Equal(t, sd.Medias[1], medi) + require.Equal(t, sd.Medias[1].Formats[0], forma) require.Equal(t, &testRTPPacket, pkt) close(packetRecv) }) @@ -721,8 +745,7 @@ func TestClientPlayContentBase(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} switch ca { case "absent": @@ -731,12 +754,12 @@ func TestClientPlayContentBase(t *testing.T) { Header: base.Header{ "Content-Type": base.HeaderValue{"application/sdp"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) case "inside control attribute": - body := string(mustMarshalMedias(medias)) + body := string(mediasToSDP(medias)) body = strings.Replace(body, "t=0 0", "t=0 0\r\na=control:rtsp://localhost:8554/teststream", 1) err = conn.WriteResponse(&base.Response{ @@ -851,8 +874,7 @@ func TestClientPlayAnyPort(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -860,7 +882,7 @@ func TestClientPlayAnyPort(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -950,9 +972,9 @@ func TestClientPlayAnyPort(t *testing.T) { AnyPortEnable: true, } - var med *media.Media + var med *description.Media err = readAll(&c, "rtsp://localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { require.Equal(t, &testRTPPacket, pkt) med = medi close(packetRecv) @@ -1007,8 +1029,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -1016,7 +1037,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -1080,7 +1101,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { } err = readAll(&c, "rtsp://localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { close(packetRecv) }) require.NoError(t, err) @@ -1100,8 +1121,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { go func() { defer close(serverDone) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} func() { nconn, err := l.Accept() @@ -1135,7 +1155,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -1204,7 +1224,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -1260,7 +1280,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { } err = readAll(&c, "rtsp://localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { close(packetRecv) }) require.NoError(t, err) @@ -1280,8 +1300,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { go func() { defer close(serverDone) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} func() { nconn, err := l.Accept() @@ -1333,7 +1352,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -1415,7 +1434,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -1501,7 +1520,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { } err = readAll(&c, "rtsp://myuser:mypass@localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { close(packetRecv) }) require.NoError(t, err) @@ -1548,8 +1567,7 @@ func TestClientPlayDifferentInterleavedIDs(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -1557,7 +1575,7 @@ func TestClientPlayDifferentInterleavedIDs(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -1624,7 +1642,7 @@ func TestClientPlayDifferentInterleavedIDs(t *testing.T) { } err = readAll(&c, "rtsp://localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { close(packetRecv) }) require.NoError(t, err) @@ -1743,8 +1761,7 @@ func TestClientPlayRedirect(t *testing.T) { require.Equal(t, base.Describe, req.Method) } - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -1752,7 +1769,7 @@ func TestClientPlayRedirect(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -1812,7 +1829,7 @@ func TestClientPlayRedirect(t *testing.T) { ru = "rtsp://testusr:testpwd@localhost:8554/path1" } err = readAll(&c, ru, - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { close(packetRecv) }) require.NoError(t, err) @@ -1907,8 +1924,7 @@ func TestClientPlayPause(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -1916,7 +1932,7 @@ func TestClientPlayPause(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -2014,7 +2030,7 @@ func TestClientPlayPause(t *testing.T) { } err = readAll(&c, "rtsp://localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { if atomic.SwapInt32(&firstFrame, 1) == 0 { close(packetRecv) } @@ -2075,8 +2091,7 @@ func TestClientPlayRTCPReport(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -2084,7 +2099,7 @@ func TestClientPlayRTCPReport(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -2249,8 +2264,7 @@ func TestClientPlayErrorTimeout(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -2258,7 +2272,7 @@ func TestClientPlayErrorTimeout(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -2396,8 +2410,7 @@ func TestClientPlayIgnoreTCPInvalidMedia(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -2405,7 +2418,7 @@ func TestClientPlayIgnoreTCPInvalidMedia(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -2475,7 +2488,7 @@ func TestClientPlayIgnoreTCPInvalidMedia(t *testing.T) { } err = readAll(&c, "rtsp://localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { close(recv) }) require.NoError(t, err) @@ -2519,8 +2532,7 @@ func TestClientPlaySeek(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -2528,7 +2540,7 @@ func TestClientPlaySeek(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -2625,10 +2637,10 @@ func TestClientPlaySeek(t *testing.T) { require.NoError(t, err) defer c.Close() - medias, baseURL, _, err := c.Describe(u) + sd, _, err := c.Describe(u) require.NoError(t, err) - err = c.SetupAll(baseURL, medias) + err = c.SetupAll(sd.BaseURL, sd.Medias) require.NoError(t, err) _, err = c.Play(&headers.Range{ @@ -2683,8 +2695,7 @@ func TestClientPlayKeepaliveFromSession(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -2692,7 +2703,7 @@ func TestClientPlayKeepaliveFromSession(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -2803,8 +2814,7 @@ func TestClientPlayDifferentSource(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/test/stream?param=value"), req.URL) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -2812,7 +2822,7 @@ func TestClientPlayDifferentSource(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/test/stream?param=value/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -2888,7 +2898,7 @@ func TestClientPlayDifferentSource(t *testing.T) { } err = readAll(&c, "rtsp://localhost:8554/test/stream?param=value", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { require.Equal(t, &testRTPPacket, pkt) close(packetRecv) }) @@ -2953,14 +2963,13 @@ func TestClientPlayDecodeErrors(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{&media.Media{ - Type: media.TypeApplication, + medias := []*description.Media{{ + Type: description.MediaTypeApplication, Formats: []format.Format{&format.Generic{ PayloadTyp: 97, RTPMa: "private/90000", }}, }} - resetMediaControls(medias) err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -2968,7 +2977,7 @@ func TestClientPlayDecodeErrors(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/stream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -3217,8 +3226,7 @@ func TestClientPlayPacketNTP(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -3226,7 +3234,7 @@ func TestClientPlayPacketNTP(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) @@ -3341,7 +3349,7 @@ func TestClientPlayPacketNTP(t *testing.T) { first := false err = readAll(&c, "rtsp://localhost:8554/teststream", - func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { if !first { first = true } else { diff --git a/client_record_test.go b/client_record_test.go index 02251942..34015d8e 100644 --- a/client_record_test.go +++ b/client_record_test.go @@ -15,15 +15,15 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/conn" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/headers" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/sdp" "github.com/bluenviron/gortsplib/v4/pkg/url" ) -var testH264Media = &media.Media{ - Type: media.TypeVideo, +var testH264Media = &description.Media{ + Type: description.MediaTypeVideo, Formats: []format.Format{&format.H264{ PayloadTyp: 96, SPS: []byte{0x01, 0x02, 0x03, 0x04}, @@ -65,7 +65,7 @@ func ntpTimeGoToRTCP(v time.Time) uint64 { return (s/1000000000)<<32 | (s % 1000000000) } -func record(c *Client, ur string, medias media.Medias, cb func(*media.Media, rtcp.Packet)) error { +func record(c *Client, ur string, medias []*description.Media, cb func(*description.Media, rtcp.Packet)) error { u, err := url.Parse(ur) if err != nil { return err @@ -76,7 +76,7 @@ func record(c *Client, ur string, medias media.Medias, cb func(*media.Media, rtc return err } - _, err = c.Announce(u, medias) + _, err = c.Announce(u, &description.Session{Medias: medias}) if err != nil { c.Close() return err @@ -285,10 +285,10 @@ func TestClientRecordSerial(t *testing.T) { } medi := testH264Media - medias := media.Medias{medi} + medias := []*description.Media{medi} err = record(&c, scheme+"://localhost:8554/teststream", medias, - func(medi *media.Media, pkt rtcp.Packet) { + func(medi *description.Media, pkt rtcp.Packet) { require.Equal(t, &testRTCPPacket, pkt) close(recvDone) }) @@ -440,7 +440,7 @@ func TestClientRecordParallel(t *testing.T) { defer func() { <-writerDone }() medi := testH264Media - medias := media.Medias{medi} + medias := []*description.Media{medi} err = record(&c, scheme+"://localhost:8554/teststream", medias, nil) require.NoError(t, err) @@ -592,7 +592,7 @@ func TestClientRecordPauseSerial(t *testing.T) { } medi := testH264Media - medias := media.Medias{medi} + medias := []*description.Media{medi} err = record(&c, "rtsp://localhost:8554/teststream", medias, nil) require.NoError(t, err) @@ -722,7 +722,7 @@ func TestClientRecordPauseParallel(t *testing.T) { } medi := testH264Media - medias := media.Medias{medi} + medias := []*description.Media{medi} err = record(&c, "rtsp://localhost:8554/teststream", medias, nil) require.NoError(t, err) @@ -861,7 +861,7 @@ func TestClientRecordAutomaticProtocol(t *testing.T) { c := Client{} medi := testH264Media - medias := media.Medias{medi} + medias := []*description.Media{medi} err = record(&c, "rtsp://localhost:8554/teststream", medias, nil) require.NoError(t, err) @@ -1041,7 +1041,7 @@ func TestClientRecordDecodeErrors(t *testing.T) { }, } - medias := media.Medias{testH264Media} + medias := []*description.Media{testH264Media} err = record(&c, "rtsp://localhost:8554/stream", medias, nil) require.NoError(t, err) @@ -1207,7 +1207,7 @@ func TestClientRecordRTCPReport(t *testing.T) { } medi := testH264Media - medias := media.Medias{medi} + medias := []*description.Media{medi} err = record(&c, "rtsp://localhost:8554/teststream", medias, nil) require.NoError(t, err) @@ -1345,10 +1345,10 @@ func TestClientRecordIgnoreTCPRTPPackets(t *testing.T) { }(), } - medias := media.Medias{testH264Media} + medias := []*description.Media{testH264Media} err = record(&c, "rtsp://localhost:8554/teststream", medias, - func(medi *media.Media, pkt rtcp.Packet) { + func(medi *description.Media, pkt rtcp.Packet) { close(rtcpReceived) }) require.NoError(t, err) diff --git a/client_test.go b/client_test.go index 459ff933..24c98bea 100644 --- a/client_test.go +++ b/client_test.go @@ -11,7 +11,7 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/auth" "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/conn" - "github.com/bluenviron/gortsplib/v4/pkg/media" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/url" ) @@ -134,8 +134,7 @@ func TestClientSession(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, base.HeaderValue{"123456"}, req.Header["Session"]) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -143,7 +142,7 @@ func TestClientSession(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp"}, "Session": base.HeaderValue{"123456"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) }() @@ -157,7 +156,7 @@ func TestClientSession(t *testing.T) { require.NoError(t, err) defer c.Close() - _, _, _, err = c.Describe(u) + _, _, err = c.Describe(u) require.NoError(t, err) } @@ -212,15 +211,14 @@ func TestClientAuth(t *testing.T) { err = auth.Validate(req, "myuser", "mypass", nil, nil, "IPCAM", nonce) require.NoError(t, err) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, Header: base.Header{ "Content-Type": base.HeaderValue{"application/sdp"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) }() @@ -234,7 +232,7 @@ func TestClientAuth(t *testing.T) { require.NoError(t, err) defer c.Close() - _, _, _, err = c.Describe(u) + _, _, err = c.Describe(u) require.NoError(t, err) } @@ -272,7 +270,7 @@ func TestClientDescribeCharset(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) - medias := media.Medias{testH264Media} + medias := []*description.Media{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, @@ -280,7 +278,7 @@ func TestClientDescribeCharset(t *testing.T) { "Content-Type": base.HeaderValue{"application/sdp; charset=utf-8"}, "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) }() @@ -294,7 +292,7 @@ func TestClientDescribeCharset(t *testing.T) { require.NoError(t, err) defer c.Close() - _, _, _, err = c.Describe(u) + _, _, err = c.Describe(u) require.NoError(t, err) } @@ -312,7 +310,7 @@ func TestClientClose(t *testing.T) { _, err = c.Options(u) require.EqualError(t, err, "terminated") - _, _, _, err = c.Describe(u) + _, _, err = c.Describe(u) require.EqualError(t, err, "terminated") _, err = c.Announce(u, nil) diff --git a/examples/client-publish-format-g711/main.go b/examples/client-publish-format-g711/main.go index d5c66796..90ea7f46 100644 --- a/examples/client-publish-format-g711/main.go +++ b/examples/client-publish-format-g711/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -35,16 +35,18 @@ func main() { } log.Println("stream connected") - // create a media that contains a G711 format - medi := &media.Media{ - Type: media.TypeAudio, - Formats: []format.Format{&format.G711{}}, + // create a description that contains a G711 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.G711{}}, + }}, } c := gortsplib.Client{} - // connect to the server and start recording the media - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + // connect to the server and start recording + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -59,7 +61,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-g722/main.go b/examples/client-publish-format-g722/main.go index d21d7ec9..da8a05c8 100644 --- a/examples/client-publish-format-g722/main.go +++ b/examples/client-publish-format-g722/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -35,16 +35,18 @@ func main() { } log.Println("stream connected") - // create a media that contains a G722 format - medi := &media.Media{ - Type: media.TypeAudio, - Formats: []format.Format{&format.G722{}}, + // create a description that contains a G722 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.G722{}}, + }}, } c := gortsplib.Client{} - // connect to the server and start recording the media - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + // connect to the server and start recording + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -59,7 +61,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-h264/main.go b/examples/client-publish-format-h264/main.go index 6f8de274..0ed4bcdd 100644 --- a/examples/client-publish-format-h264/main.go +++ b/examples/client-publish-format-h264/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -37,18 +37,20 @@ func main() { } log.Println("stream connected") - // create a media that contains a H264 format - medi := &media.Media{ - Type: media.TypeVideo, - Formats: []format.Format{&format.H264{ - PayloadTyp: 96, - PacketizationMode: 1, + // create a stream description that contains a H264 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.H264{ + PayloadTyp: 96, + PacketizationMode: 1, + }}, }}, } - // connect to the server and start recording the media + // connect to the server and start recording c := gortsplib.Client{} - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -63,7 +65,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-h265/main.go b/examples/client-publish-format-h265/main.go index 20437569..73b9f51d 100644 --- a/examples/client-publish-format-h265/main.go +++ b/examples/client-publish-format-h265/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -36,17 +36,19 @@ func main() { } log.Println("stream connected") - // create a media that contains a H265 format - medi := &media.Media{ - Type: media.TypeVideo, - Formats: []format.Format{&format.H265{ - PayloadTyp: 96, + // create a description that contains a H265 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.H265{ + PayloadTyp: 96, + }}, }}, } - // connect to the server and start recording the media + // connect to the server and start recording c := gortsplib.Client{} - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -61,7 +63,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-lpcm/main.go b/examples/client-publish-format-lpcm/main.go index 6ef5159b..4ac0c8d9 100644 --- a/examples/client-publish-format-lpcm/main.go +++ b/examples/client-publish-format-lpcm/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -35,21 +35,23 @@ func main() { } log.Println("stream connected") - // create a media that contains a LPCM format - medi := &media.Media{ - Type: media.TypeAudio, - Formats: []format.Format{&format.LPCM{ - PayloadTyp: 96, - BitDepth: 16, - SampleRate: 44100, - ChannelCount: 1, + // create a description that contains a LPCM format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.LPCM{ + PayloadTyp: 96, + BitDepth: 16, + SampleRate: 44100, + ChannelCount: 1, + }}, }}, } c := gortsplib.Client{} - // connect to the server and start recording the media - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + // connect to the server and start recording + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -64,7 +66,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-mjpeg/main.go b/examples/client-publish-format-mjpeg/main.go index c9e30edb..843cf9e3 100644 --- a/examples/client-publish-format-mjpeg/main.go +++ b/examples/client-publish-format-mjpeg/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -35,15 +35,17 @@ func main() { } log.Println("stream connected") - // create a media that contains a M-JPEG format - medi := &media.Media{ - Type: media.TypeVideo, - Formats: []format.Format{&format.MJPEG{}}, + // create a description that contains a M-JPEG format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.MJPEG{}}, + }}, } - // connect to the server and start recording the media + // connect to the server and start recording c := gortsplib.Client{} - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -58,7 +60,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-mpeg4audio/main.go b/examples/client-publish-format-mpeg4audio/main.go index db2732d9..0e340d95 100644 --- a/examples/client-publish-format-mpeg4audio/main.go +++ b/examples/client-publish-format-mpeg4audio/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio" "github.com/pion/rtp" ) @@ -36,25 +36,27 @@ func main() { } log.Println("stream connected") - // create a media that contains a MPEG-4 audio format - medi := &media.Media{ - Type: media.TypeAudio, - Formats: []format.Format{&format.MPEG4Audio{ - PayloadTyp: 96, - Config: &mpeg4audio.Config{ - Type: mpeg4audio.ObjectTypeAACLC, - SampleRate: 48000, - ChannelCount: 2, - }, - SizeLength: 13, - IndexLength: 3, - IndexDeltaLength: 3, + // create a description that contains a MPEG-4 audio format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.MPEG4Audio{ + PayloadTyp: 96, + Config: &mpeg4audio.Config{ + Type: mpeg4audio.ObjectTypeAACLC, + SampleRate: 48000, + ChannelCount: 2, + }, + SizeLength: 13, + IndexLength: 3, + IndexDeltaLength: 3, + }}, }}, } - // connect to the server and start recording the media + // connect to the server and start recording c := gortsplib.Client{} - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -69,7 +71,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-opus/main.go b/examples/client-publish-format-opus/main.go index 5cda7324..8f88f346 100644 --- a/examples/client-publish-format-opus/main.go +++ b/examples/client-publish-format-opus/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -35,19 +35,20 @@ func main() { } log.Println("stream connected") - // create a media that contains a Opus format - medi := &media.Media{ - Type: media.TypeAudio, - Formats: []format.Format{&format.Opus{ - PayloadTyp: 96, - IsStereo: false, + // create a description that contains a Opus format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.Opus{ + PayloadTyp: 96, + IsStereo: false, + }}, }}, } + // connect to the server and start recording c := gortsplib.Client{} - - // connect to the server and start recording the media - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -62,7 +63,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-vp8/main.go b/examples/client-publish-format-vp8/main.go index ed2ad00e..d2e647e6 100644 --- a/examples/client-publish-format-vp8/main.go +++ b/examples/client-publish-format-vp8/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -36,17 +36,19 @@ func main() { } log.Println("stream connected") - // create a media that contains a VP8 format - medi := &media.Media{ - Type: media.TypeVideo, - Formats: []format.Format{&format.VP8{ - PayloadTyp: 96, + // create a description that contains a VP8 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.VP8{ + PayloadTyp: 96, + }}, }}, } - // connect to the server and start recording the media + // connect to the server and start recording c := gortsplib.Client{} - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -61,7 +63,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-format-vp9/main.go b/examples/client-publish-format-vp9/main.go index 3ba463b7..496238a5 100644 --- a/examples/client-publish-format-vp9/main.go +++ b/examples/client-publish-format-vp9/main.go @@ -5,8 +5,8 @@ import ( "net" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -36,17 +36,19 @@ func main() { } log.Println("stream connected") - // create a media that contains a VP9 format - medi := &media.Media{ - Type: media.TypeVideo, - Formats: []format.Format{&format.VP9{ - PayloadTyp: 96, + // create a description that contains a VP9 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.VP9{ + PayloadTyp: 96, + }}, }}, } - // connect to the server and start recording the media + // connect to the server and start recording c := gortsplib.Client{} - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -61,7 +63,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-options/main.go b/examples/client-publish-options/main.go index d25f3699..78a16697 100644 --- a/examples/client-publish-options/main.go +++ b/examples/client-publish-options/main.go @@ -6,8 +6,8 @@ import ( "time" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -37,12 +37,14 @@ func main() { } log.Println("stream connected") - // create a media that contains a H264 media - medi := &media.Media{ - Type: media.TypeVideo, - Formats: []format.Format{&format.H264{ - PayloadTyp: 96, - PacketizationMode: 1, + // create a stream description that contains a H264 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.H264{ + PayloadTyp: 96, + PacketizationMode: 1, + }}, }}, } @@ -56,8 +58,8 @@ func main() { WriteTimeout: 10 * time.Second, } - // connect to the server and start recording the media - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + // connect to the server and start recording + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -72,7 +74,7 @@ func main() { } // route RTP packet to the server - err = c.WritePacketRTP(medi, &pkt) + err = c.WritePacketRTP(desc.Medias[0], &pkt) if err != nil { panic(err) } diff --git a/examples/client-publish-pause/main.go b/examples/client-publish-pause/main.go index 54d2024a..ec631db5 100644 --- a/examples/client-publish-pause/main.go +++ b/examples/client-publish-pause/main.go @@ -6,8 +6,8 @@ import ( "time" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/pion/rtp" ) @@ -38,18 +38,20 @@ func main() { } log.Println("stream connected") - // create a media that contains a H264 format - medi := &media.Media{ - Type: media.TypeVideo, - Formats: []format.Format{&format.H264{ - PayloadTyp: 96, - PacketizationMode: 1, + // create a stream description that contains a H264 format + desc := &description.Session{ + Medias: []*description.Media{{ + Type: description.MediaTypeVideo, + Formats: []format.Format{&format.H264{ + PayloadTyp: 96, + PacketizationMode: 1, + }}, }}, } - // connect to the server and start recording the media + // connect to the server and start recording c := gortsplib.Client{} - err = c.StartRecording("rtsp://localhost:8554/mystream", media.Medias{medi}) + err = c.StartRecording("rtsp://localhost:8554/mystream", desc) if err != nil { panic(err) } @@ -66,7 +68,7 @@ func main() { } // route RTP packet to the server - c.WritePacketRTP(medi, &pkt) + c.WritePacketRTP(desc.Medias[0], &pkt) // read another RTP packet from source n, _, err = pc.ReadFrom(buf) diff --git a/examples/client-query/main.go b/examples/client-query/main.go index a66b0668..d199dc60 100644 --- a/examples/client-query/main.go +++ b/examples/client-query/main.go @@ -25,10 +25,10 @@ func main() { } defer c.Close() - medias, _, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } - log.Printf("available medias: %v\n", medias) + log.Printf("available medias: %v\n", desc.Medias) } diff --git a/examples/client-read-format-g711/main.go b/examples/client-read-format-g711/main.go index fd875f3f..d6deadf7 100644 --- a/examples/client-read-format-g711/main.go +++ b/examples/client-read-format-g711/main.go @@ -31,14 +31,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the G711 media and format var forma *format.G711 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -50,7 +50,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-g722/main.go b/examples/client-read-format-g722/main.go index de852b58..1d11ccf2 100644 --- a/examples/client-read-format-g722/main.go +++ b/examples/client-read-format-g722/main.go @@ -31,14 +31,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the G722 media and format var forma *format.G722 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -50,7 +50,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-h264-convert-to-jpeg/main.go b/examples/client-read-format-h264-convert-to-jpeg/main.go index 5e1f4d0d..e4d41625 100644 --- a/examples/client-read-format-h264-convert-to-jpeg/main.go +++ b/examples/client-read-format-h264-convert-to-jpeg/main.go @@ -58,14 +58,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the H264 media and format var forma *format.H264 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -92,7 +92,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-h264-save-to-disk/main.go b/examples/client-read-format-h264-save-to-disk/main.go index 1d8c3ca9..fbda0cf3 100644 --- a/examples/client-read-format-h264-save-to-disk/main.go +++ b/examples/client-read-format-h264-save-to-disk/main.go @@ -32,14 +32,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the H264 media and format var forma *format.H264 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -57,7 +57,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-h264/main.go b/examples/client-read-format-h264/main.go index 776f91a2..12c535f4 100644 --- a/examples/client-read-format-h264/main.go +++ b/examples/client-read-format-h264/main.go @@ -35,14 +35,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the H264 media and format var forma *format.H264 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -69,7 +69,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-h265/main.go b/examples/client-read-format-h265/main.go index 074f4d18..06b8901d 100644 --- a/examples/client-read-format-h265/main.go +++ b/examples/client-read-format-h265/main.go @@ -32,14 +32,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the H265 media and format var forma *format.H265 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -51,7 +51,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-lpcm/main.go b/examples/client-read-format-lpcm/main.go index 125cdc22..60c70f75 100644 --- a/examples/client-read-format-lpcm/main.go +++ b/examples/client-read-format-lpcm/main.go @@ -31,14 +31,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the LPCM media and format var forma *format.LPCM - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -50,7 +50,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-mjpeg/main.go b/examples/client-read-format-mjpeg/main.go index 1df37f7e..e473840a 100644 --- a/examples/client-read-format-mjpeg/main.go +++ b/examples/client-read-format-mjpeg/main.go @@ -35,14 +35,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the M-JPEG media and format var forma *format.MJPEG - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -54,7 +54,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-mpeg4audio-save-to-disk/main.go b/examples/client-read-format-mpeg4audio-save-to-disk/main.go index 0028546d..b66938e3 100644 --- a/examples/client-read-format-mpeg4audio-save-to-disk/main.go +++ b/examples/client-read-format-mpeg4audio-save-to-disk/main.go @@ -11,7 +11,7 @@ import ( // This example shows how to // 1. connect to a RTSP server -// 2. check if there's an MPEG4-audio media +// 2. check if there's an MPEG-4 audio media // 3. save the content of the media into a file in MPEG-TS format func main() { @@ -31,32 +31,32 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } - // find the MPEG4-audio media and format + // find the MPEG-4 audio media and format var forma *format.MPEG4Audio - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } - // setup RTP/MPEG4-audio -> MPEG4-audio decoder + // setup RTP/MPEG-4 audio -> MPEG-4 audio decoder rtpDec, err := forma.CreateDecoder() if err != nil { panic(err) } - // setup MPEG4-audio -> MPEG-TS muxer + // setup MPEG-4 audio -> MPEG-TS muxer mpegtsMuxer, err := newMPEGTSMuxer(forma.Config) if err != nil { panic(err) } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-mpeg4audio/main.go b/examples/client-read-format-mpeg4audio/main.go index 995d3d80..a85a72e0 100644 --- a/examples/client-read-format-mpeg4audio/main.go +++ b/examples/client-read-format-mpeg4audio/main.go @@ -31,14 +31,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the MPEG4-audio media and format var forma *format.MPEG4Audio - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -50,7 +50,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-opus/main.go b/examples/client-read-format-opus/main.go index e0444de9..e9729649 100644 --- a/examples/client-read-format-opus/main.go +++ b/examples/client-read-format-opus/main.go @@ -31,14 +31,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the Opus media and format var forma *format.Opus - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -50,7 +50,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-vp8/main.go b/examples/client-read-format-vp8/main.go index 2d3b03b1..194ebd44 100644 --- a/examples/client-read-format-vp8/main.go +++ b/examples/client-read-format-vp8/main.go @@ -32,14 +32,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the VP8 media and format var forma *format.VP8 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -51,7 +51,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-format-vp9/main.go b/examples/client-read-format-vp9/main.go index 2a81bc91..655cc9af 100644 --- a/examples/client-read-format-vp9/main.go +++ b/examples/client-read-format-vp9/main.go @@ -32,14 +32,14 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // find the VP9 media and format var forma *format.VP9 - medi := medias.FindFormat(&forma) + medi := desc.FindFormat(&forma) if medi == nil { panic("media not found") } @@ -51,7 +51,7 @@ func main() { } // setup a single media - _, err = c.Setup(baseURL, medi, 0, 0) + _, err = c.Setup(desc.BaseURL, medi, 0, 0) if err != nil { panic(err) } diff --git a/examples/client-read-options/main.go b/examples/client-read-options/main.go index 172f8939..c1c1a251 100644 --- a/examples/client-read-options/main.go +++ b/examples/client-read-options/main.go @@ -5,8 +5,8 @@ import ( "time" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/url" "github.com/pion/rtcp" "github.com/pion/rtp" @@ -41,24 +41,24 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // setup all medias - err = c.SetupAll(baseURL, medias) + err = c.SetupAll(desc.BaseURL, desc.Medias) if err != nil { panic(err) } // called when a RTP packet arrives - c.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + c.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { log.Printf("RTP packet from media %v\n", medi) }) // called when a RTCP packet arrives - c.OnPacketRTCPAny(func(medi *media.Media, pkt rtcp.Packet) { + c.OnPacketRTCPAny(func(medi *description.Media, pkt rtcp.Packet) { log.Printf("RTCP packet from media %v, type %T\n", medi, pkt) }) diff --git a/examples/client-read-pause/main.go b/examples/client-read-pause/main.go index 78e15cc6..faa9aa17 100644 --- a/examples/client-read-pause/main.go +++ b/examples/client-read-pause/main.go @@ -5,8 +5,8 @@ import ( "time" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/url" "github.com/pion/rtcp" "github.com/pion/rtp" @@ -35,24 +35,24 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // setup all medias - err = c.SetupAll(baseURL, medias) + err = c.SetupAll(desc.BaseURL, desc.Medias) if err != nil { panic(err) } // called when a RTP packet arrives - c.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + c.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { log.Printf("RTP packet from media %v\n", medi) }) // called when a RTCP packet arrives - c.OnPacketRTCPAny(func(medi *media.Media, pkt rtcp.Packet) { + c.OnPacketRTCPAny(func(medi *description.Media, pkt rtcp.Packet) { log.Printf("RTCP packet from media %v, type %T\n", medi, pkt) }) diff --git a/examples/client-read-republish/main.go b/examples/client-read-republish/main.go index d8565339..92b719a4 100644 --- a/examples/client-read-republish/main.go +++ b/examples/client-read-republish/main.go @@ -4,8 +4,8 @@ import ( "log" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/url" "github.com/pion/rtp" ) @@ -31,31 +31,31 @@ func main() { defer reader.Close() // find published medias - medias, baseURL, _, err := reader.Describe(sourceURL) + desc, _, err := reader.Describe(sourceURL) if err != nil { panic(err) } - log.Printf("republishing %d medias", len(medias)) + log.Printf("republishing %d medias", len(desc.Medias)) // setup all medias // this must be called before StartRecording(), since it overrides the control attribute. - err = reader.SetupAll(baseURL, medias) + err = reader.SetupAll(desc.BaseURL, desc.Medias) if err != nil { panic(err) } // connect to the server and start recording the same medias publisher := gortsplib.Client{} - err = publisher.StartRecording("rtsp://localhost:8554/mystream2", medias) + err = publisher.StartRecording("rtsp://localhost:8554/mystream2", desc) if err != nil { panic(err) } defer publisher.Close() // read RTP packets from the reader and route them to the publisher - reader.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { - publisher.WritePacketRTP(medi, pkt) + reader.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { + publisher.WritePacketRTP(desc.Medias[0], pkt) }) // start playing diff --git a/examples/client-read/main.go b/examples/client-read/main.go index 0522787d..057baeab 100644 --- a/examples/client-read/main.go +++ b/examples/client-read/main.go @@ -4,8 +4,8 @@ import ( "log" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/url" "github.com/pion/rtcp" "github.com/pion/rtp" @@ -32,24 +32,24 @@ func main() { defer c.Close() // find published medias - medias, baseURL, _, err := c.Describe(u) + desc, _, err := c.Describe(u) if err != nil { panic(err) } // setup all medias - err = c.SetupAll(baseURL, medias) + err = c.SetupAll(desc.BaseURL, desc.Medias) if err != nil { panic(err) } // called when a RTP packet arrives - c.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + c.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { log.Printf("RTP packet from media %v\n", medi) }) // called when a RTCP packet arrives - c.OnPacketRTCPAny(func(medi *media.Media, pkt rtcp.Packet) { + c.OnPacketRTCPAny(func(medi *description.Media, pkt rtcp.Packet) { log.Printf("RTCP packet from media %v, type %T\n", medi, pkt) }) diff --git a/examples/proxy/client.go b/examples/proxy/client.go index 8e205ea4..e6b1023c 100644 --- a/examples/proxy/client.go +++ b/examples/proxy/client.go @@ -5,8 +5,8 @@ import ( "time" "github.com/bluenviron/gortsplib/v4" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/url" "github.com/pion/rtp" ) @@ -57,24 +57,24 @@ func (c *client) read() error { defer rc.Close() // find published medias - medias, baseURL, _, err := rc.Describe(u) + desc, _, err := rc.Describe(u) if err != nil { return err } // setup all medias - err = rc.SetupAll(baseURL, medias) + err = rc.SetupAll(desc.BaseURL, desc.Medias) if err != nil { return err } - stream := c.s.setStreamReady(medias) + stream := c.s.setStreamReady(desc) defer c.s.setStreamUnready() log.Printf("stream is ready and can be read from the server at rtsp://localhost:8554/stream\n") // called when a RTP packet arrives - rc.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + rc.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { // route incoming packets to the server stream stream.WritePacketRTP(medi, pkt) }) diff --git a/examples/proxy/server.go b/examples/proxy/server.go index 5cf0234e..660f9c7a 100644 --- a/examples/proxy/server.go +++ b/examples/proxy/server.go @@ -6,7 +6,7 @@ import ( "github.com/bluenviron/gortsplib/v4" "github.com/bluenviron/gortsplib/v4/pkg/base" - "github.com/bluenviron/gortsplib/v4/pkg/media" + "github.com/bluenviron/gortsplib/v4/pkg/description" ) type server struct { @@ -99,10 +99,10 @@ func (s *server) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, }, nil } -func (s *server) setStreamReady(medias media.Medias) *gortsplib.ServerStream { +func (s *server) setStreamReady(desc *description.Session) *gortsplib.ServerStream { s.mutex.Lock() defer s.mutex.Unlock() - s.stream = gortsplib.NewServerStream(s.s, medias) + s.stream = gortsplib.NewServerStream(s.s, desc) return s.stream } diff --git a/examples/server-h264-save-to-disk/main.go b/examples/server-h264-save-to-disk/main.go index 14258dbf..0ddeaaac 100644 --- a/examples/server-h264-save-to-disk/main.go +++ b/examples/server-h264-save-to-disk/main.go @@ -9,9 +9,9 @@ import ( "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/gortsplib/v4/pkg/format/rtph264" - "github.com/bluenviron/gortsplib/v4/pkg/media" ) // This example shows how to @@ -23,7 +23,7 @@ type serverHandler struct { s *gortsplib.Server mutex sync.Mutex publisher *gortsplib.ServerSession - media *media.Media + media *description.Media format *format.H264 rtpDec *rtph264.Decoder mpegtsMuxer *mpegtsMuxer @@ -69,7 +69,7 @@ func (sh *serverHandler) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) ( // find the H264 media and format var forma *format.H264 - medi := ctx.Medias.FindFormat(&forma) + medi := ctx.Description.FindFormat(&forma) if medi == nil { return &base.Response{ StatusCode: base.StatusBadRequest, diff --git a/examples/server-tls/main.go b/examples/server-tls/main.go index bcc971c2..7af7102a 100644 --- a/examples/server-tls/main.go +++ b/examples/server-tls/main.go @@ -9,8 +9,8 @@ import ( "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/gortsplib/v4/pkg/media" ) // This example shows how to @@ -89,7 +89,7 @@ func (sh *serverHandler) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) ( } // create the stream and save the publisher - sh.stream = gortsplib.NewServerStream(sh.s, ctx.Medias) + sh.stream = gortsplib.NewServerStream(sh.s, ctx.Description) sh.publisher = ctx.Session return &base.Response{ @@ -127,7 +127,7 @@ func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*bas log.Printf("record request") // called when receiving a RTP packet - ctx.Session.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + ctx.Session.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { // route the RTP packet to all readers sh.stream.WritePacketRTP(medi, pkt) }) diff --git a/examples/server/main.go b/examples/server/main.go index e4df20c2..1714c60b 100644 --- a/examples/server/main.go +++ b/examples/server/main.go @@ -8,8 +8,8 @@ import ( "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/gortsplib/v4/pkg/media" ) // This example shows how to @@ -88,7 +88,7 @@ func (sh *serverHandler) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) ( } // create the stream and save the publisher - sh.stream = gortsplib.NewServerStream(sh.s, ctx.Medias) + sh.stream = gortsplib.NewServerStream(sh.s, ctx.Description) sh.publisher = ctx.Session return &base.Response{ @@ -126,7 +126,7 @@ func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*bas log.Printf("record request") // called when receiving a RTP packet - ctx.Session.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + ctx.Session.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { // route the RTP packet to all readers sh.stream.WritePacketRTP(medi, pkt) }) diff --git a/internal/highleveltests/server_test.go b/internal/highleveltests/server_test.go index a7a5b484..91bb2a37 100644 --- a/internal/highleveltests/server_test.go +++ b/internal/highleveltests/server_test.go @@ -19,8 +19,8 @@ import ( "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/gortsplib/v4/pkg/media" ) var serverCert = []byte(`-----BEGIN CERTIFICATE----- @@ -329,7 +329,7 @@ func TestServerRecordRead(t *testing.T) { }, fmt.Errorf("someone is already publishing") } - stream = gortsplib.NewServerStream(s, ctx.Medias) + stream = gortsplib.NewServerStream(s, ctx.Description) publisher = ctx.Session return &base.Response{ @@ -386,7 +386,7 @@ func TestServerRecordRead(t *testing.T) { }, fmt.Errorf("invalid query (%s)", ctx.Query) } - ctx.Session.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + ctx.Session.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { stream.WritePacketRTP(medi, pkt) }) diff --git a/pkg/media/media.go b/pkg/description/media.go similarity index 85% rename from pkg/media/media.go rename to pkg/description/media.go index c67e056e..1f92ee03 100644 --- a/pkg/media/media.go +++ b/pkg/description/media.go @@ -1,5 +1,5 @@ -// Package media contains the media stream definition. -package media +// Package description contains objects to describe streams. +package description import ( "fmt" @@ -26,17 +26,17 @@ func getControlAttribute(attributes []psdp.Attribute) string { return "" } -func getDirection(attributes []psdp.Attribute) Direction { +func getDirection(attributes []psdp.Attribute) MediaDirection { for _, attr := range attributes { switch attr.Key { case "sendonly": - return DirectionSendonly + return MediaDirectionSendonly case "recvonly": - return DirectionRecvonly + return MediaDirectionRecvonly case "sendrecv": - return DirectionSendrecv + return MediaDirectionSendrecv } } return "" @@ -92,34 +92,34 @@ func sortedKeys(fmtp map[string]string) []string { return keys } -// Direction is the direction of a media stream. -type Direction string +// MediaDirection is the direction of a media stream. +type MediaDirection string // standard directions. const ( - DirectionSendonly Direction = "sendonly" - DirectionRecvonly Direction = "recvonly" - DirectionSendrecv Direction = "sendrecv" + MediaDirectionSendonly MediaDirection = "sendonly" + MediaDirectionRecvonly MediaDirection = "recvonly" + MediaDirectionSendrecv MediaDirection = "sendrecv" ) -// Type is the type of a media stream. -type Type string +// MediaType is the type of a media stream. +type MediaType string // standard media stream types. const ( - TypeVideo Type = "video" - TypeAudio Type = "audio" - TypeApplication Type = "application" + MediaTypeVideo MediaType = "video" + MediaTypeAudio MediaType = "audio" + MediaTypeApplication MediaType = "application" ) // Media is a media stream. // It contains one or more formats. type Media struct { // Media type. - Type Type + Type MediaType // Direction of the stream. - Direction Direction + Direction MediaDirection // Control attribute. Control string @@ -128,8 +128,9 @@ type Media struct { Formats []format.Format } -func (m *Media) unmarshal(md *psdp.MediaDescription) error { - m.Type = Type(md.MediaName.Media) +// Unmarshal decodes the media from the SDP format. +func (m *Media) Unmarshal(md *psdp.MediaDescription) error { + m.Type = MediaType(md.MediaName.Media) m.Direction = getDirection(md.Attributes) m.Control = getControlAttribute(md.Attributes) diff --git a/pkg/media/media_test.go b/pkg/description/media_test.go similarity index 97% rename from pkg/media/media_test.go rename to pkg/description/media_test.go index a2f3fcd3..2fa6ffd1 100644 --- a/pkg/media/media_test.go +++ b/pkg/description/media_test.go @@ -1,4 +1,4 @@ -package media +package description import ( "testing" @@ -133,11 +133,11 @@ func TestMediaURL(t *testing.T) { err := sd.Unmarshal(ca.sdp) require.NoError(t, err) - var medias Medias - err = medias.Unmarshal(sd.MediaDescriptions) + var media Media + err = media.Unmarshal(sd.MediaDescriptions[0]) require.NoError(t, err) - ur, err := medias[0].URL(ca.baseURL) + ur, err := media.URL(ca.baseURL) require.NoError(t, err) require.Equal(t, ca.ur, ur) }) diff --git a/pkg/media/medias.go b/pkg/description/session.go similarity index 52% rename from pkg/media/medias.go rename to pkg/description/session.go index 9a4ceaa5..c7f7a7ce 100644 --- a/pkg/media/medias.go +++ b/pkg/description/session.go @@ -1,4 +1,4 @@ -package media +package description import ( "fmt" @@ -6,29 +6,48 @@ import ( psdp "github.com/pion/sdp/v3" "github.com/bluenviron/gortsplib/v4/pkg/sdp" + "github.com/bluenviron/gortsplib/v4/pkg/url" ) -// Medias is a list of media streams. -type Medias []*Media +// Session is the description of a RTSP session. +type Session struct { + // base URL of the stream (read only). + BaseURL *url.URL -// Unmarshal decodes medias from the SDP format. -func (ms *Medias) Unmarshal(mds []*psdp.MediaDescription) error { - *ms = make(Medias, len(mds)) + // available media streams. + Medias []*Media +} - for i, md := range mds { +// FindFormat finds a certain format among all the formats in all the medias of the stream. +// If the format is found, it is inserted into forma, and its media is returned. +func (d *Session) FindFormat(forma interface{}) *Media { + for _, media := range d.Medias { + ok := media.FindFormat(forma) + if ok { + return media + } + } + return nil +} + +// Unmarshal decodes the description from SDP. +func (d *Session) Unmarshal(ssd *sdp.SessionDescription) error { + d.Medias = make([]*Media, len(ssd.MediaDescriptions)) + + for i, md := range ssd.MediaDescriptions { var m Media - err := m.unmarshal(md) + err := m.Unmarshal(md) if err != nil { return fmt.Errorf("media %d is invalid: %v", i+1, err) } - (*ms)[i] = &m + d.Medias[i] = &m } return nil } -// Marshal encodes the medias in SDP format. -func (ms Medias) Marshal(multicast bool) *sdp.SessionDescription { +// Marshal encodes the description in SDP. +func (d Session) Marshal(multicast bool) ([]byte, error) { var address string if multicast { address = "224.1.0.0" @@ -37,14 +56,14 @@ func (ms Medias) Marshal(multicast bool) *sdp.SessionDescription { } sout := &sdp.SessionDescription{ - SessionName: psdp.SessionName("Stream"), + SessionName: psdp.SessionName("Session"), Origin: psdp.Origin{ Username: "-", NetworkType: "IN", AddressType: "IP4", UnicastAddress: "127.0.0.1", }, - // required by Darwin Streaming Server + // required by Darwin Sessioning Server ConnectionInformation: &psdp.ConnectionInformation{ NetworkType: "IN", AddressType: "IP4", @@ -53,24 +72,12 @@ func (ms Medias) Marshal(multicast bool) *sdp.SessionDescription { TimeDescriptions: []psdp.TimeDescription{ {Timing: psdp.Timing{StartTime: 0, StopTime: 0}}, }, - MediaDescriptions: make([]*psdp.MediaDescription, len(ms)), + MediaDescriptions: make([]*psdp.MediaDescription, len(d.Medias)), } - for i, media := range ms { + for i, media := range d.Medias { sout.MediaDescriptions[i] = media.Marshal() } - return sout -} - -// FindFormat finds a certain format among all the formats in all the medias. -// If the format is found, it is inserted into forma, and its media is returned. -func (ms Medias) FindFormat(forma interface{}) *Media { - for _, media := range ms { - ok := media.FindFormat(forma) - if ok { - return media - } - } - return nil + return sout.Marshal() } diff --git a/pkg/media/medias_test.go b/pkg/description/session_test.go similarity index 66% rename from pkg/media/medias_test.go rename to pkg/description/session_test.go index e31004c0..590d875b 100644 --- a/pkg/media/medias_test.go +++ b/pkg/description/session_test.go @@ -1,4 +1,4 @@ -package media +package description import ( "testing" @@ -9,11 +9,11 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/sdp" ) -var casesMedias = []struct { - name string - in string - out string - medias Medias +var casesSession = []struct { + name string + in string + out string + desc Session }{ { "one format for each media, absolute", @@ -43,7 +43,7 @@ var casesMedias = []struct { "b=AS:8\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 97\r\n" + @@ -56,30 +56,32 @@ var casesMedias = []struct { "a=rtpmap:0 PCMU/8000\r\n" + "m=application 0 RTP/AVP 107\r\n" + "a=control\r\n", - Medias{ - { - Type: "video", - Control: "rtsp://10.0.100.50/profile5/media.smp/trackID=v", - Formats: []format.Format{&format.H264{ - PayloadTyp: 97, - PacketizationMode: 1, - SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, - PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, - }}, - }, - { - Type: "audio", - Direction: DirectionRecvonly, - Control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", - Formats: []format.Format{&format.G711{ - MULaw: true, - }}, - }, - { - Type: "application", - Formats: []format.Format{&format.Generic{ - PayloadTyp: 107, - }}, + Session{ + Medias: []*Media{ + { + Type: "video", + Control: "rtsp://10.0.100.50/profile5/media.smp/trackID=v", + Formats: []format.Format{&format.H264{ + PayloadTyp: 97, + PacketizationMode: 1, + SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, + PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, + }}, + }, + { + Type: "audio", + Direction: MediaDirectionRecvonly, + Control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", + Formats: []format.Format{&format.G711{ + MULaw: true, + }}, + }, + { + Type: "application", + Formats: []format.Format{&format.Generic{ + PayloadTyp: 107, + }}, + }, }, }, }, @@ -110,7 +112,7 @@ var casesMedias = []struct { "b=AS:8\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 97\r\n" + @@ -123,30 +125,32 @@ var casesMedias = []struct { "a=rtpmap:0 PCMU/8000\r\n" + "m=application 0 RTP/AVP 107\r\n" + "a=control\r\n", - Medias{ - { - Type: "video", - Control: "trackID=1", - Formats: []format.Format{&format.H264{ - PayloadTyp: 97, - PacketizationMode: 1, - SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, - PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, - }}, - }, - { - Type: "audio", - Direction: DirectionRecvonly, - Control: "trackID=2", - Formats: []format.Format{&format.G711{ - MULaw: true, - }}, - }, - { - Type: "application", - Formats: []format.Format{&format.Generic{ - PayloadTyp: 107, - }}, + Session{ + Medias: []*Media{ + { + Type: "video", + Control: "trackID=1", + Formats: []format.Format{&format.H264{ + PayloadTyp: 97, + PacketizationMode: 1, + SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, + PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, + }}, + }, + { + Type: "audio", + Direction: MediaDirectionRecvonly, + Control: "trackID=2", + Formats: []format.Format{&format.G711{ + MULaw: true, + }}, + }, + { + Type: "application", + Formats: []format.Format{&format.Generic{ + PayloadTyp: 107, + }}, + }, }, }, }, @@ -157,7 +161,7 @@ var casesMedias = []struct { "s=-\r\n" + "t=0 0\r\n" + "a=group:BUNDLE audio video\r\n" + - "a=msid-semantic: WMS mediaStreamLocal\r\n" + + "a=msid-semantic: WMS mediaSessionLocal\r\n" + "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + @@ -190,8 +194,8 @@ var casesMedias = []struct { "a=rtpmap:113 telephone-event/16000\r\n" + "a=rtpmap:126 telephone-event/8000\r\n" + "a=ssrc:3754810229 cname:CvU1TYqkVsjj5XOt\r\n" + - "a=ssrc:3754810229 msid:mediaStreamLocal 101\r\n" + - "a=ssrc:3754810229 mslabel:mediaStreamLocal\r\n" + + "a=ssrc:3754810229 msid:mediaSessionLocal 101\r\n" + + "a=ssrc:3754810229 mslabel:mediaSessionLocal\r\n" + "a=ssrc:3754810229 label:101\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 124 125\r\n" + "c=IN IP4 0.0.0.0\r\n" + @@ -245,16 +249,16 @@ var casesMedias = []struct { "a=rtpmap:125 ulpfec/90000\r\n" + "a=ssrc-group:FID 2712436124 1733091158\r\n" + "a=ssrc:2712436124 cname:CvU1TYqkVsjj5XOt\r\n" + - "a=ssrc:2712436124 msid:mediaStreamLocal 100\r\n" + - "a=ssrc:2712436124 mslabel:mediaStreamLocal\r\n" + + "a=ssrc:2712436124 msid:mediaSessionLocal 100\r\n" + + "a=ssrc:2712436124 mslabel:mediaSessionLocal\r\n" + "a=ssrc:2712436124 label:100\r\n" + "a=ssrc:1733091158 cname:CvU1TYqkVsjj5XOt\r\n" + - "a=ssrc:1733091158 msid:mediaStreamLocal 100\r\n" + - "a=ssrc:1733091158 mslabel:mediaStreamLocal\r\n" + + "a=ssrc:1733091158 msid:mediaSessionLocal 100\r\n" + + "a=ssrc:1733091158 mslabel:mediaSessionLocal\r\n" + "a=ssrc:1733091158 label:100\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=audio 0 RTP/AVP 111 103 104 9 102 0 8 106 105 13 110 112 113 126\r\n" + @@ -291,129 +295,131 @@ var casesMedias = []struct { "a=rtpmap:127 red/90000\r\n" + "a=rtpmap:124 rtx/90000\r\n" + "a=fmtp:124 apt=127\r\na=rtpmap:125 ulpfec/90000\r\n", - Medias{ - { - Type: "audio", - Direction: DirectionSendonly, - Formats: []format.Format{ - &format.Opus{ - PayloadTyp: 111, - IsStereo: false, - }, - &format.Generic{ - PayloadTyp: 103, - RTPMa: "ISAC/16000", - ClockRat: 16000, - }, - &format.Generic{ - PayloadTyp: 104, - RTPMa: "ISAC/32000", - ClockRat: 32000, - }, - &format.G722{}, - &format.Generic{ - PayloadTyp: 102, - RTPMa: "ILBC/8000", - ClockRat: 8000, - }, - &format.G711{ - MULaw: true, - }, - &format.G711{ - MULaw: false, - }, - &format.Generic{ - PayloadTyp: 106, - RTPMa: "CN/32000", - ClockRat: 32000, - }, - &format.Generic{ - PayloadTyp: 105, - RTPMa: "CN/16000", - ClockRat: 16000, - }, - &format.Generic{ - PayloadTyp: 13, - RTPMa: "CN/8000", - ClockRat: 8000, - }, - &format.Generic{ - PayloadTyp: 110, - RTPMa: "telephone-event/48000", - ClockRat: 48000, - }, - &format.Generic{ - PayloadTyp: 112, - RTPMa: "telephone-event/32000", - ClockRat: 32000, - }, - &format.Generic{ - PayloadTyp: 113, - RTPMa: "telephone-event/16000", - ClockRat: 16000, - }, - &format.Generic{ - PayloadTyp: 126, - RTPMa: "telephone-event/8000", - ClockRat: 8000, + Session{ + Medias: []*Media{ + { + Type: "audio", + Direction: MediaDirectionSendonly, + Formats: []format.Format{ + &format.Opus{ + PayloadTyp: 111, + IsStereo: false, + }, + &format.Generic{ + PayloadTyp: 103, + RTPMa: "ISAC/16000", + ClockRat: 16000, + }, + &format.Generic{ + PayloadTyp: 104, + RTPMa: "ISAC/32000", + ClockRat: 32000, + }, + &format.G722{}, + &format.Generic{ + PayloadTyp: 102, + RTPMa: "ILBC/8000", + ClockRat: 8000, + }, + &format.G711{ + MULaw: true, + }, + &format.G711{ + MULaw: false, + }, + &format.Generic{ + PayloadTyp: 106, + RTPMa: "CN/32000", + ClockRat: 32000, + }, + &format.Generic{ + PayloadTyp: 105, + RTPMa: "CN/16000", + ClockRat: 16000, + }, + &format.Generic{ + PayloadTyp: 13, + RTPMa: "CN/8000", + ClockRat: 8000, + }, + &format.Generic{ + PayloadTyp: 110, + RTPMa: "telephone-event/48000", + ClockRat: 48000, + }, + &format.Generic{ + PayloadTyp: 112, + RTPMa: "telephone-event/32000", + ClockRat: 32000, + }, + &format.Generic{ + PayloadTyp: 113, + RTPMa: "telephone-event/16000", + ClockRat: 16000, + }, + &format.Generic{ + PayloadTyp: 126, + RTPMa: "telephone-event/8000", + ClockRat: 8000, + }, }, }, - }, - { - Type: "video", - Direction: DirectionSendonly, - Formats: []format.Format{ - &format.VP8{ - PayloadTyp: 96, - }, - &format.Generic{ - PayloadTyp: 97, - RTPMa: "rtx/90000", - FMT: map[string]string{ - "apt": "96", + { + Type: "video", + Direction: MediaDirectionSendonly, + Formats: []format.Format{ + &format.VP8{ + PayloadTyp: 96, }, - ClockRat: 90000, - }, - &format.VP9{ - PayloadTyp: 98, - }, - &format.Generic{ - PayloadTyp: 99, - RTPMa: "rtx/90000", - FMT: map[string]string{ - "apt": "98", + &format.Generic{ + PayloadTyp: 97, + RTPMa: "rtx/90000", + FMT: map[string]string{ + "apt": "96", + }, + ClockRat: 90000, }, - ClockRat: 90000, - }, - &format.H264{ - PayloadTyp: 100, - PacketizationMode: 1, - }, - &format.Generic{ - PayloadTyp: 101, - RTPMa: "rtx/90000", - FMT: map[string]string{ - "apt": "100", + &format.VP9{ + PayloadTyp: 98, }, - ClockRat: 90000, - }, - &format.Generic{ - PayloadTyp: 127, - RTPMa: "red/90000", - ClockRat: 90000, - }, - &format.Generic{ - PayloadTyp: 124, - RTPMa: "rtx/90000", - FMT: map[string]string{ - "apt": "127", + &format.Generic{ + PayloadTyp: 99, + RTPMa: "rtx/90000", + FMT: map[string]string{ + "apt": "98", + }, + ClockRat: 90000, + }, + &format.H264{ + PayloadTyp: 100, + PacketizationMode: 1, + }, + &format.Generic{ + PayloadTyp: 101, + RTPMa: "rtx/90000", + FMT: map[string]string{ + "apt": "100", + }, + ClockRat: 90000, + }, + &format.Generic{ + PayloadTyp: 127, + RTPMa: "red/90000", + ClockRat: 90000, + }, + &format.Generic{ + PayloadTyp: 124, + RTPMa: "rtx/90000", + FMT: map[string]string{ + "apt": "127", + }, + ClockRat: 90000, + }, + &format.Generic{ + PayloadTyp: 125, + RTPMa: "ulpfec/90000", + ClockRat: 90000, }, - ClockRat: 90000, - }, - &format.Generic{ - PayloadTyp: 125, - RTPMa: "ulpfec/90000", - ClockRat: 90000, }, }, }, @@ -433,7 +439,7 @@ var casesMedias = []struct { "sprop-parameter-sets=Z00AKp2oHgCJ+WbgICAgQA==,aO48gA==\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 96 98\r\n" + @@ -442,22 +448,24 @@ var casesMedias = []struct { "a=fmtp:96 packetization-mode=1; profile-level-id=4D002A; " + "sprop-parameter-sets=Z00AKp2oHgCJ+WbgICAgQA==,aO48gA==\r\n" + "a=rtpmap:98 MetaData\r\n", - Medias{ - { - Type: "video", - Formats: []format.Format{ - &format.H264{ - PayloadTyp: 96, - SPS: []byte{ - 0x67, 0x4d, 0x00, 0x2a, 0x9d, 0xa8, 0x1e, 0x00, - 0x89, 0xf9, 0x66, 0xe0, 0x20, 0x20, 0x20, 0x40, + Session{ + Medias: []*Media{ + { + Type: "video", + Formats: []format.Format{ + &format.H264{ + PayloadTyp: 96, + SPS: []byte{ + 0x67, 0x4d, 0x00, 0x2a, 0x9d, 0xa8, 0x1e, 0x00, + 0x89, 0xf9, 0x66, 0xe0, 0x20, 0x20, 0x20, 0x40, + }, + PPS: []byte{0x68, 0xee, 0x3c, 0x80}, + PacketizationMode: 1, + }, + &format.Generic{ + PayloadTyp: 98, + RTPMa: "MetaData", }, - PPS: []byte{0x68, 0xee, 0x3c, 0x80}, - PacketizationMode: 1, - }, - &format.Generic{ - PayloadTyp: 98, - RTPMa: "MetaData", }, }, }, @@ -480,7 +488,7 @@ var casesMedias = []struct { "a=sendonly\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 26\r\n" + @@ -495,24 +503,26 @@ var casesMedias = []struct { "a=control:rtsp://192.168.0.1/audioback\r\n" + "a=sendonly\r\n" + "a=rtpmap:0 PCMU/8000\r\n", - Medias{ - { - Type: "video", - Direction: DirectionRecvonly, - Control: "rtsp://192.168.0.1/video", - Formats: []format.Format{&format.MJPEG{}}, - }, - { - Type: "audio", - Direction: DirectionRecvonly, - Control: "rtsp://192.168.0.1/audio", - Formats: []format.Format{&format.G711{MULaw: true}}, - }, - { - Type: "audio", - Direction: DirectionSendonly, - Control: "rtsp://192.168.0.1/audioback", - Formats: []format.Format{&format.G711{MULaw: true}}, + Session{ + Medias: []*Media{ + { + Type: "video", + Direction: MediaDirectionRecvonly, + Control: "rtsp://192.168.0.1/video", + Formats: []format.Format{&format.MJPEG{}}, + }, + { + Type: "audio", + Direction: MediaDirectionRecvonly, + Control: "rtsp://192.168.0.1/audio", + Formats: []format.Format{&format.G711{MULaw: true}}, + }, + { + Type: "audio", + Direction: MediaDirectionSendonly, + Control: "rtsp://192.168.0.1/audioback", + Formats: []format.Format{&format.G711{MULaw: true}}, + }, }, }, }, @@ -526,20 +536,22 @@ var casesMedias = []struct { "a=rtpmap:95 TP-LINK/90000\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=application/TP-LINK 0 RTP/AVP 95\r\n" + "a=control\r\n" + "a=rtpmap:95 TP-LINK/90000\r\n", - Medias{ - { - Type: "application/TP-LINK", - Formats: []format.Format{&format.Generic{ - PayloadTyp: 95, - RTPMa: "TP-LINK/90000", - ClockRat: 90000, - }}, + Session{ + Medias: []*Media{ + { + Type: "application/TP-LINK", + Formats: []format.Format{&format.Generic{ + PayloadTyp: 95, + RTPMa: "TP-LINK/90000", + ClockRat: 90000, + }}, + }, }, }, }, @@ -554,20 +566,22 @@ var casesMedias = []struct { "a=rtpmap:95 MERCURY/90000\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=application/MERCURY 0 RTP/AVP 95\r\n" + "a=control\r\n" + "a=rtpmap:95 MERCURY/90000\r\n", - Medias{ - { - Type: "application/MERCURY", - Formats: []format.Format{&format.Generic{ - PayloadTyp: 95, - RTPMa: "MERCURY/90000", - ClockRat: 90000, - }}, + Session{ + Medias: []*Media{ + { + Type: "application/MERCURY", + Formats: []format.Format{&format.Generic{ + PayloadTyp: 95, + RTPMa: "MERCURY/90000", + ClockRat: 90000, + }}, + }, }, }, }, @@ -582,20 +596,22 @@ var casesMedias = []struct { "a=fmtp:96 packetization-mode=1\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + - "s=Stream\r\n" + + "s=Session\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 96\r\n" + "a=control\r\n" + "a=rtpmap:96 H264/90000\r\n" + "a=fmtp:96 packetization-mode=1\r\n", - Medias{ - { - Type: "video", - Formats: []format.Format{ - &format.H264{ - PayloadTyp: 96, - PacketizationMode: 1, + Session{ + Medias: []*Media{ + { + Type: "video", + Formats: []format.Format{ + &format.H264{ + PayloadTyp: 96, + PacketizationMode: 1, + }, }, }, }, @@ -603,22 +619,22 @@ var casesMedias = []struct { }, } -func TestMediasUnmarshal(t *testing.T) { - for _, ca := range casesMedias { +func TestSessionUnmarshal(t *testing.T) { + for _, ca := range casesSession { t.Run(ca.name, func(t *testing.T) { var sdp sdp.SessionDescription err := sdp.Unmarshal([]byte(ca.in)) require.NoError(t, err) - var medias Medias - err = medias.Unmarshal(sdp.MediaDescriptions) + var desc Session + err = desc.Unmarshal(&sdp) require.NoError(t, err) - require.Equal(t, ca.medias, medias) + require.Equal(t, ca.desc, desc) }) } } -func TestMediasUnmarshalErrors(t *testing.T) { +func TestSessionUnmarshalErrors(t *testing.T) { for _, ca := range []struct { name string sdp string @@ -646,24 +662,24 @@ func TestMediasUnmarshalErrors(t *testing.T) { err := sd.Unmarshal([]byte(ca.sdp)) require.NoError(t, err) - var medias Medias - err = medias.Unmarshal(sd.MediaDescriptions) + var desc Session + err = desc.Unmarshal(&sd) require.EqualError(t, err, ca.err) }) } } -func TestMediasMarshal(t *testing.T) { - for _, ca := range casesMedias { +func TestSessionMarshal(t *testing.T) { + for _, ca := range casesSession { t.Run(ca.name, func(t *testing.T) { - byts, err := ca.medias.Marshal(false).Marshal() + byts, err := ca.desc.Marshal(false) require.NoError(t, err) require.Equal(t, ca.out, string(byts)) }) } } -func TestMediasFindFormat(t *testing.T) { +func TestSessionFindFormat(t *testing.T) { tr := &format.Generic{ PayloadTyp: 97, RTPMa: "rtx/90000", @@ -674,7 +690,7 @@ func TestMediasFindFormat(t *testing.T) { } md := &Media{ - Type: TypeVideo, + Type: MediaTypeVideo, Formats: []format.Format{ &format.VP8{ PayloadTyp: 96, @@ -686,21 +702,23 @@ func TestMediasFindFormat(t *testing.T) { }, } - ms := Medias{ - { - Type: TypeAudio, - Formats: []format.Format{ - &format.Opus{ - PayloadTyp: 111, - IsStereo: true, + desc := &Session{ + Medias: []*Media{ + { + Type: MediaTypeAudio, + Formats: []format.Format{ + &format.Opus{ + PayloadTyp: 111, + IsStereo: true, + }, }, }, + md, }, - md, } var forma *format.Generic - me := ms.FindFormat(&forma) + me := desc.FindFormat(&forma) require.Equal(t, md, me) require.Equal(t, tr, forma) } diff --git a/server_conn.go b/server_conn.go index 13b9bc9a..3ed1dc87 100644 --- a/server_conn.go +++ b/server_conn.go @@ -12,8 +12,8 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/bytecounter" "github.com/bluenviron/gortsplib/v4/pkg/conn" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/liberrors" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/url" ) @@ -24,14 +24,13 @@ func getSessionID(header base.Header) string { return "" } -func mediasForSDP( - medias media.Medias, - contentBase *url.URL, -) media.Medias { - newMedias := make(media.Medias, len(medias)) +func streamDescCopyForServer(d *description.Session, contentBase *url.URL) *description.Session { + out := &description.Session{ + Medias: make([]*description.Media, len(d.Medias)), + } - for i, medi := range medias { - mc := &media.Media{ + for i, medi := range d.Medias { + mc := &description.Media{ Type: medi.Type, // Direction: skipped for the moment Formats: medi.Formats, @@ -47,10 +46,10 @@ func mediasForSDP( u, _ := mc.URL(contentBase) mc.Control = u.String() - newMedias[i] = mc + out.Medias[i] = mc } - return newMedias + return out } type readReq struct { @@ -294,7 +293,7 @@ func (sc *ServerConn) handleRequestInner(req *base.Request) (*base.Response, err } if stream != nil { - byts, _ := mediasForSDP(stream.medias, req.URL).Marshal(multicast).Marshal() + byts, _ := streamDescCopyForServer(stream.desc, req.URL).Marshal(multicast) res.Body = byts } } diff --git a/server_handler.go b/server_handler.go index 0ed4a8b6..d0e49218 100644 --- a/server_handler.go +++ b/server_handler.go @@ -2,7 +2,7 @@ package gortsplib import ( "github.com/bluenviron/gortsplib/v4/pkg/base" - "github.com/bluenviron/gortsplib/v4/pkg/media" + "github.com/bluenviron/gortsplib/v4/pkg/description" ) // ServerHandler is the interface implemented by all the server handlers. @@ -83,12 +83,12 @@ type ServerHandlerOnDescribe interface { // ServerHandlerOnAnnounceCtx is the context of OnAnnounce. type ServerHandlerOnAnnounceCtx struct { - Session *ServerSession - Conn *ServerConn - Request *base.Request - Path string - Query string - Medias media.Medias + Session *ServerSession + Conn *ServerConn + Request *base.Request + Path string + Query string + Description *description.Session } // ServerHandlerOnAnnounce can be implemented by a ServerHandler. diff --git a/server_play_test.go b/server_play_test.go index cfbf835a..fdcc0589 100644 --- a/server_play_test.go +++ b/server_play_test.go @@ -19,9 +19,9 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/conn" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/headers" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/sdp" "github.com/bluenviron/gortsplib/v4/pkg/url" ) @@ -254,11 +254,13 @@ func TestServerPlayPath(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{ - testH264Media, - testH264Media, - testH264Media, - testH264Media, + stream = NewServerStream(s, &description.Session{ + Medias: []*description.Media{ + testH264Media, + testH264Media, + testH264Media, + testH264Media, + }, }) defer stream.Close() @@ -337,7 +339,7 @@ func TestServerPlaySetupErrors(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) if ca == "closed stream" { stream.Close() } else { @@ -463,7 +465,7 @@ func TestServerPlaySetupErrorSameUDPPortsAndIP(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() for i := 0; i < 2; i++ { @@ -553,17 +555,17 @@ func TestServerPlay(t *testing.T) { // send RTCP packets directly to the session. // these are sent after the response, only if onPlay returns StatusOK. if transport != "multicast" { - err := ctx.Session.WritePacketRTCP(stream.Medias()[0], &testRTCPPacket) + err := ctx.Session.WritePacketRTCP(stream.Description().Medias[0], &testRTCPPacket) require.NoError(t, err) } - ctx.Session.OnPacketRTCPAny(func(medi *media.Media, pkt rtcp.Packet) { + ctx.Session.OnPacketRTCPAny(func(medi *description.Media, pkt rtcp.Packet) { // ignore multicast loopback if transport == "multicast" && atomic.AddUint64(&counter, 1) <= 1 { return } - require.Equal(t, stream.Medias()[0], medi) + require.Equal(t, stream.Description().Medias[0], medi) require.Equal(t, &testRTCPPacket, pkt) close(framesReceived) }) @@ -573,9 +575,9 @@ func TestServerPlay(t *testing.T) { // ServerStream.WritePacket*() go func() { time.Sleep(500 * time.Millisecond) - err := stream.WritePacketRTCP(stream.Medias()[0], &testRTCPPacket) + err := stream.WritePacketRTCP(stream.Description().Medias[0], &testRTCPPacket) require.NoError(t, err) - err = stream.WritePacketRTP(stream.Medias()[0], &testRTPPacket) + err = stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket) require.NoError(t, err) }() @@ -612,7 +614,7 @@ func TestServerPlay(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", listenIP+":8554") @@ -890,7 +892,7 @@ func TestServerPlayDecodeErrors(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1014,7 +1016,7 @@ func TestServerPlayRTCPReport(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1066,7 +1068,7 @@ func TestServerPlayRTCPReport(t *testing.T) { curTimeMutex.Unlock() err = stream.WritePacketRTPWithNTP( - stream.Medias()[0], + stream.Description().Medias[0], &rtp.Packet{ Header: rtp.Header{ Version: 2, @@ -1138,7 +1140,7 @@ func TestServerPlayVLCMulticast(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", listenIP+":8554") @@ -1189,7 +1191,7 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { go func() { defer close(writerDone) - err := stream.WritePacketRTP(stream.Medias()[0], &testRTPPacket) + err := stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket) require.NoError(t, err) ti := time.NewTicker(50 * time.Millisecond) @@ -1198,7 +1200,7 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { for { select { case <-ti.C: - err := stream.WritePacketRTP(stream.Medias()[0], &testRTPPacket) + err := stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket) require.NoError(t, err) case <-writerTerminate: return @@ -1219,7 +1221,7 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1282,7 +1284,7 @@ func TestServerPlayPlayPlay(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1347,7 +1349,7 @@ func TestServerPlayPlayPausePlay(t *testing.T) { for { select { case <-ti.C: - err := stream.WritePacketRTP(stream.Medias()[0], &testRTPPacket) + err := stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket) require.NoError(t, err) case <-writerTerminate: return @@ -1373,7 +1375,7 @@ func TestServerPlayPlayPausePlay(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1436,7 +1438,7 @@ func TestServerPlayPlayPausePause(t *testing.T) { for { select { case <-ti.C: - err := stream.WritePacketRTP(stream.Medias()[0], &testRTPPacket) + err := stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket) require.NoError(t, err) case <-writerTerminate: return @@ -1461,7 +1463,7 @@ func TestServerPlayPlayPausePause(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1547,7 +1549,7 @@ func TestServerPlayTimeout(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1636,7 +1638,7 @@ func TestServerPlayWithoutTeardown(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1714,7 +1716,7 @@ func TestServerPlayUDPChangeConn(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() sxID := "" @@ -1786,9 +1788,9 @@ func TestServerPlayPartialMedias(t *testing.T) { onPlay: func(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) { go func() { time.Sleep(500 * time.Millisecond) - err := stream.WritePacketRTP(stream.Medias()[0], &testRTPPacket) + err := stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket) require.NoError(t, err) - err = stream.WritePacketRTP(stream.Medias()[1], &testRTPPacket) + err = stream.WritePacketRTP(stream.Description().Medias[1], &testRTPPacket) require.NoError(t, err) }() @@ -1804,7 +1806,7 @@ func TestServerPlayPartialMedias(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media, testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media, testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1928,19 +1930,21 @@ func TestServerPlayAdditionalInfos(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{ - &media.Media{ - Type: "application", - Formats: []format.Format{forma}, - }, - &media.Media{ - Type: "application", - Formats: []format.Format{forma}, + stream = NewServerStream(s, &description.Session{ + Medias: []*description.Media{ + { + Type: "application", + Formats: []format.Format{forma}, + }, + { + Type: "application", + Formats: []format.Format{forma}, + }, }, }) defer stream.Close() - err = stream.WritePacketRTP(stream.Medias()[0], &rtp.Packet{ + err = stream.WritePacketRTP(stream.Description().Medias[0], &rtp.Packet{ Header: rtp.Header{ Version: 2, PayloadType: 96, @@ -1970,7 +1974,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) { nil, }, ssrcs) - err = stream.WritePacketRTP(stream.Medias()[1], &rtp.Packet{ + err = stream.WritePacketRTP(stream.Description().Medias[1], &rtp.Packet{ Header: rtp.Header{ Version: 2, PayloadType: 96, @@ -2045,14 +2049,16 @@ func TestServerPlayNoInterleavedIDs(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{ - &media.Media{ - Type: "application", - Formats: []format.Format{forma}, - }, - &media.Media{ - Type: "application", - Formats: []format.Format{forma}, + stream = NewServerStream(s, &description.Session{ + Medias: []*description.Media{ + { + Type: "application", + Formats: []format.Format{forma}, + }, + { + Type: "application", + Formats: []format.Format{forma}, + }, }, }) defer stream.Close() @@ -2089,7 +2095,7 @@ func TestServerPlayNoInterleavedIDs(t *testing.T) { doPlay(t, conn, "rtsp://localhost:8554/teststream", session) for i := 0; i < 2; i++ { - err := stream.WritePacketRTP(stream.Medias()[i], &testRTPPacket) + err := stream.WritePacketRTP(stream.Description().Medias[i], &testRTPPacket) require.NoError(t, err) f, err := conn.ReadInterleavedFrame() diff --git a/server_record_test.go b/server_record_test.go index a9910ac5..850c28e4 100644 --- a/server_record_test.go +++ b/server_record_test.go @@ -15,13 +15,13 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/conn" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/headers" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/sdp" ) -func doAnnounce(t *testing.T, conn *conn.Conn, u string, medias media.Medias) { +func doAnnounce(t *testing.T, conn *conn.Conn, u string, medias []*description.Media) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Announce, URL: mustParseURL(u), @@ -29,7 +29,7 @@ func doAnnounce(t *testing.T, conn *conn.Conn, u string, medias media.Medias) { "CSeq": base.HeaderValue{"1"}, "Content-Type": base.HeaderValue{"application/sdp"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), }) require.NoError(t, err) require.Equal(t, base.StatusOK, res.StatusCode) @@ -224,7 +224,7 @@ func TestServerRecordPath(t *testing.T) { Handler: &testServerHandler{ onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) { // make sure that media URLs are not overridden by NewServerStream() - stream := NewServerStream(s, ctx.Medias) + stream := NewServerStream(s, ctx.Description) defer stream.Close() return &base.Response{ @@ -344,8 +344,7 @@ func TestServerRecordErrorSetupMediaTwice(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -438,17 +437,16 @@ func TestServerRecordErrorRecordPartialMedias(t *testing.T) { err = forma.Init() require.NoError(t, err) - medias := media.Medias{ - &media.Media{ + medias := []*description.Media{ + { Type: "application", Formats: []format.Format{forma}, }, - &media.Media{ + { Type: "application", Formats: []format.Format{forma}, }, } - resetMediaControls(medias) doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -523,25 +521,25 @@ func TestServerRecord(t *testing.T) { onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) { // queue sending of RTCP packets. // these are sent after the response, only if onRecord returns StatusOK. - err := ctx.Session.WritePacketRTCP(ctx.Session.AnnouncedMedias()[0], &testRTCPPacket) + err := ctx.Session.WritePacketRTCP(ctx.Session.AnnouncedDescription().Medias[0], &testRTCPPacket) require.NoError(t, err) - err = ctx.Session.WritePacketRTCP(ctx.Session.AnnouncedMedias()[1], &testRTCPPacket) + err = ctx.Session.WritePacketRTCP(ctx.Session.AnnouncedDescription().Medias[1], &testRTCPPacket) require.NoError(t, err) for i := 0; i < 2; i++ { ctx.Session.OnPacketRTP( - ctx.Session.AnnouncedMedias()[i], - ctx.Session.AnnouncedMedias()[i].Formats[0], + ctx.Session.AnnouncedDescription().Medias[i], + ctx.Session.AnnouncedDescription().Medias[i].Formats[0], func(pkt *rtp.Packet) { require.Equal(t, &testRTPPacket, pkt) }) ci := i ctx.Session.OnPacketRTCP( - ctx.Session.AnnouncedMedias()[i], + ctx.Session.AnnouncedDescription().Medias[i], func(pkt rtcp.Packet) { require.Equal(t, &testRTCPPacket, pkt) - err := ctx.Session.WritePacketRTCP(ctx.Session.AnnouncedMedias()[ci], &testRTCPPacket) + err := ctx.Session.WritePacketRTCP(ctx.Session.AnnouncedDescription().Medias[ci], &testRTCPPacket) require.NoError(t, err) }) } @@ -583,9 +581,9 @@ func TestServerRecord(t *testing.T) { <-nconnOpened - medias := media.Medias{ - &media.Media{ - Type: media.TypeVideo, + medias := []*description.Media{ + { + Type: description.MediaTypeVideo, Formats: []format.Format{&format.H264{ PayloadTyp: 96, SPS: []byte{0x01, 0x02, 0x03, 0x04}, @@ -593,8 +591,8 @@ func TestServerRecord(t *testing.T) { PacketizationMode: 1, }}, }, - &media.Media{ - Type: media.TypeVideo, + { + Type: description.MediaTypeVideo, Formats: []format.Format{&format.H264{ PayloadTyp: 96, SPS: []byte{0x01, 0x02, 0x03, 0x04}, @@ -603,7 +601,6 @@ func TestServerRecord(t *testing.T) { }}, }, } - resetMediaControls(medias) doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -766,8 +763,7 @@ func TestServerRecordErrorInvalidProtocol(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -833,8 +829,7 @@ func TestServerRecordRTCPReport(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -974,8 +969,7 @@ func TestServerRecordTimeout(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -1063,8 +1057,7 @@ func TestServerRecordWithoutTeardown(t *testing.T) { require.NoError(t, err) conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -1142,8 +1135,7 @@ func TestServerRecordUDPChangeConn(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -1273,14 +1265,13 @@ func TestServerRecordDecodeErrors(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{&media.Media{ - Type: media.TypeApplication, + medias := []*description.Media{{ + Type: description.MediaTypeApplication, Formats: []format.Format{&format.Generic{ PayloadTyp: 97, RTPMa: "private/90000", }}, }} - resetMediaControls(medias) doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) @@ -1438,7 +1429,7 @@ func TestServerRecordPacketNTP(t *testing.T) { }, nil, nil }, onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) { - ctx.Session.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) { + ctx.Session.OnPacketRTPAny(func(medi *description.Media, forma format.Format, pkt *rtp.Packet) { if !first { first = true } else { @@ -1468,8 +1459,7 @@ func TestServerRecordPacketNTP(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} - resetMediaControls(medias) + medias := []*description.Media{testH264Media} doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias) diff --git a/server_session.go b/server_session.go index 41a3d751..7257fa65 100644 --- a/server_session.go +++ b/server_session.go @@ -14,10 +14,10 @@ import ( "github.com/pion/rtp" "github.com/bluenviron/gortsplib/v4/pkg/base" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/headers" "github.com/bluenviron/gortsplib/v4/pkg/liberrors" - "github.com/bluenviron/gortsplib/v4/pkg/media" "github.com/bluenviron/gortsplib/v4/pkg/rtptime" "github.com/bluenviron/gortsplib/v4/pkg/sdp" "github.com/bluenviron/gortsplib/v4/pkg/url" @@ -58,7 +58,7 @@ func serverParseURLForPlay(u *url.URL) (string, string, string, error) { return path, query, trackID, nil } -func findMediaByURL(medias media.Medias, baseURL *url.URL, u *url.URL) *media.Media { +func findMediaByURL(medias []*description.Media, baseURL *url.URL, u *url.URL) *description.Media { for _, media := range medias { u1, err := media.URL(baseURL) if err == nil && u1.String() == u.String() { @@ -69,9 +69,9 @@ func findMediaByURL(medias media.Medias, baseURL *url.URL, u *url.URL) *media.Me return nil } -func findMediaByTrackID(st *ServerStream, trackID string) *media.Media { +func findMediaByTrackID(medias []*description.Media, trackID string) *description.Media { if trackID == "" { - return st.medias[0] + return medias[0] } tmp, err := strconv.ParseUint(trackID, 10, 31) @@ -80,11 +80,11 @@ func findMediaByTrackID(st *ServerStream, trackID string) *media.Media { } id := int(tmp) - if len(st.medias) <= id { + if len(medias) <= id { return nil } - return st.medias[id] + return medias[id] } func findFirstSupportedTransportHeader(s *Server, tsh headers.Transports) *headers.Transport { @@ -144,7 +144,7 @@ type ServerSession struct { userData interface{} conns map[*ServerConn]struct{} state ServerSessionState - setuppedMedias map[*media.Media]*serverSessionMedia + setuppedMedias map[*description.Media]*serverSessionMedia setuppedMediasOrdered []*serverSessionMedia tcpCallbackByChannel map[int]readFunc setuppedTransport *Transport @@ -153,8 +153,8 @@ type ServerSession struct { setuppedQuery string lastRequestTime time.Time tcpConn *ServerConn - announcedMedias media.Medias // publish - udpLastPacketTime *int64 // publish + announcedDesc *description.Session // publish + udpLastPacketTime *int64 // publish udpCheckStreamTimer *time.Timer writer asyncProcessor timeDecoder *rtptime.GlobalDecoder @@ -221,14 +221,14 @@ func (ss *ServerSession) SetuppedTransport() *Transport { return ss.setuppedTransport } -// AnnouncedMedias returns the announced medias. -func (ss *ServerSession) AnnouncedMedias() media.Medias { - return ss.announcedMedias +// AnnouncedDescription returns the announced stream description. +func (ss *ServerSession) AnnouncedDescription() *description.Session { + return ss.announcedDesc } // SetuppedMedias returns the setupped medias. -func (ss *ServerSession) SetuppedMedias() media.Medias { - ret := make(media.Medias, len(ss.setuppedMedias)) +func (ss *ServerSession) SetuppedMedias() []*description.Media { + ret := make([]*description.Media, len(ss.setuppedMedias)) for i, sm := range ss.setuppedMediasOrdered { ret[i] = sm.media } @@ -511,23 +511,23 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) ( }, liberrors.ErrServerContentTypeUnsupported{CT: ct} } - var sd sdp.SessionDescription - err = sd.Unmarshal(req.Body) + var ssd sdp.SessionDescription + err = ssd.Unmarshal(req.Body) if err != nil { return &base.Response{ StatusCode: base.StatusBadRequest, }, liberrors.ErrServerSDPInvalid{Err: err} } - var medias media.Medias - err = medias.Unmarshal(sd.MediaDescriptions) + var desc description.Session + err = desc.Unmarshal(&ssd) if err != nil { return &base.Response{ StatusCode: base.StatusBadRequest, }, liberrors.ErrServerSDPInvalid{Err: err} } - for _, medi := range medias { + for _, medi := range desc.Medias { mediURL, err := medi.URL(req.URL) if err != nil { return &base.Response{ @@ -551,12 +551,12 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) ( } res, err := ss.s.Handler.(ServerHandlerOnAnnounce).OnAnnounce(&ServerHandlerOnAnnounceCtx{ - Session: ss, - Conn: sc, - Request: req, - Path: path, - Query: query, - Medias: medias, + Session: ss, + Conn: sc, + Request: req, + Path: path, + Query: query, + Description: &desc, }) if res.StatusCode != base.StatusOK { @@ -566,7 +566,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) ( ss.state = ServerSessionStatePreRecord ss.setuppedPath = &path ss.setuppedQuery = query - ss.announcedMedias = medias + ss.announcedDesc = &desc return res, err @@ -706,10 +706,10 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) ( return res, err } - var medi *media.Media + var medi *description.Media switch ss.state { case ServerSessionStateInitial, ServerSessionStatePrePlay: // play - medi = findMediaByTrackID(stream, trackID) + medi = findMediaByTrackID(stream.desc.Medias, trackID) default: // record baseURL := &url.URL{ Scheme: req.URL.Scheme, @@ -724,7 +724,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) ( baseURL.Path += "/" } - medi = findMediaByURL(ss.announcedMedias, baseURL, req.URL) + medi = findMediaByURL(ss.announcedDesc.Medias, baseURL, req.URL) } if medi == nil { @@ -819,7 +819,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) ( } if ss.setuppedMedias == nil { - ss.setuppedMedias = make(map[*media.Media]*serverSessionMedia) + ss.setuppedMedias = make(map[*description.Media]*serverSessionMedia) } ss.setuppedMedias[medi] = sm ss.setuppedMediasOrdered = append(ss.setuppedMediasOrdered, sm) @@ -934,7 +934,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) ( }, err } - if len(ss.setuppedMedias) != len(ss.announcedMedias) { + if len(ss.setuppedMedias) != len(ss.announcedDesc.Medias) { return &base.Response{ StatusCode: base.StatusBadRequest, }, liberrors.ErrServerNotAllAnnouncedMediasSetup{} @@ -1148,25 +1148,25 @@ func (ss *ServerSession) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) { } // OnPacketRTP sets the callback that is called when a RTP packet is read. -func (ss *ServerSession) OnPacketRTP(medi *media.Media, forma format.Format, cb OnPacketRTPFunc) { +func (ss *ServerSession) OnPacketRTP(medi *description.Media, forma format.Format, cb OnPacketRTPFunc) { sm := ss.setuppedMedias[medi] st := sm.formats[forma.PayloadType()] st.onPacketRTP = cb } // OnPacketRTCP sets the callback that is called when a RTCP packet is read. -func (ss *ServerSession) OnPacketRTCP(medi *media.Media, cb OnPacketRTCPFunc) { +func (ss *ServerSession) OnPacketRTCP(medi *description.Media, cb OnPacketRTCPFunc) { sm := ss.setuppedMedias[medi] sm.onPacketRTCP = cb } -func (ss *ServerSession) writePacketRTP(medi *media.Media, byts []byte) { +func (ss *ServerSession) writePacketRTP(medi *description.Media, byts []byte) { sm := ss.setuppedMedias[medi] sm.writePacketRTP(byts) } // WritePacketRTP writes a RTP packet to the session. -func (ss *ServerSession) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error { +func (ss *ServerSession) WritePacketRTP(medi *description.Media, pkt *rtp.Packet) error { byts := make([]byte, ss.s.MaxPacketSize) n, err := pkt.MarshalTo(byts) if err != nil { @@ -1178,13 +1178,13 @@ func (ss *ServerSession) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) erro return nil } -func (ss *ServerSession) writePacketRTCP(medi *media.Media, byts []byte) { +func (ss *ServerSession) writePacketRTCP(medi *description.Media, byts []byte) { sm := ss.setuppedMedias[medi] sm.writePacketRTCP(byts) } // WritePacketRTCP writes a RTCP packet to the session. -func (ss *ServerSession) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) error { +func (ss *ServerSession) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet) error { byts, err := pkt.Marshal() if err != nil { return err @@ -1202,7 +1202,7 @@ func (ss *ServerSession) PacketPTS(forma format.Format, pkt *rtp.Packet) (time.D // PacketNTP returns the NTP timestamp of an incoming RTP packet. // The NTP timestamp is computed from sender reports. -func (ss *ServerSession) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) { +func (ss *ServerSession) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time, bool) { sm := ss.setuppedMedias[medi] sf := sm.formats[pkt.PayloadType] return sf.rtcpReceiver.PacketNTP(pkt.Timestamp) diff --git a/server_session_media.go b/server_session_media.go index e959929a..d979793b 100644 --- a/server_session_media.go +++ b/server_session_media.go @@ -10,12 +10,12 @@ import ( "github.com/pion/rtp" "github.com/bluenviron/gortsplib/v4/pkg/base" - "github.com/bluenviron/gortsplib/v4/pkg/media" + "github.com/bluenviron/gortsplib/v4/pkg/description" ) type serverSessionMedia struct { ss *ServerSession - media *media.Media + media *description.Media tcpChannel int udpRTPReadPort int udpRTPWriteAddr *net.UDPAddr @@ -30,7 +30,7 @@ type serverSessionMedia struct { onPacketRTCP OnPacketRTCPFunc } -func newServerSessionMedia(ss *ServerSession, medi *media.Media) *serverSessionMedia { +func newServerSessionMedia(ss *ServerSession, medi *description.Media) *serverSessionMedia { sm := &serverSessionMedia{ ss: ss, media: medi, diff --git a/server_stream.go b/server_stream.go index b90dae2e..01b986f6 100644 --- a/server_stream.go +++ b/server_stream.go @@ -8,9 +8,9 @@ import ( "github.com/pion/rtcp" "github.com/pion/rtp" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/headers" "github.com/bluenviron/gortsplib/v4/pkg/liberrors" - "github.com/bluenviron/gortsplib/v4/pkg/media" ) func firstFormat(formats map[uint8]*serverStreamFormat) *serverStreamFormat { @@ -29,27 +29,27 @@ func firstFormat(formats map[uint8]*serverStreamFormat) *serverStreamFormat { // - allocating multicast listeners // - gathering infos about the stream in order to generate SSRC and RTP-Info type ServerStream struct { - s *Server - medias media.Medias + s *Server + desc *description.Session mutex sync.RWMutex activeUnicastReaders map[*ServerSession]struct{} readers map[*ServerSession]struct{} - streamMedias map[*media.Media]*serverStreamMedia + streamMedias map[*description.Media]*serverStreamMedia closed bool } // NewServerStream allocates a ServerStream. -func NewServerStream(s *Server, medias media.Medias) *ServerStream { +func NewServerStream(s *Server, desc *description.Session) *ServerStream { st := &ServerStream{ s: s, - medias: medias, + desc: desc, activeUnicastReaders: make(map[*ServerSession]struct{}), readers: make(map[*ServerSession]struct{}), } - st.streamMedias = make(map[*media.Media]*serverStreamMedia, len(medias)) - for i, medi := range medias { + st.streamMedias = make(map[*description.Media]*serverStreamMedia, len(desc.Medias)) + for i, medi := range desc.Medias { st.streamMedias[medi] = newServerStreamMedia(st, medi, i) } @@ -71,12 +71,12 @@ func (st *ServerStream) Close() { } } -// Medias returns the medias of the stream. -func (st *ServerStream) Medias() media.Medias { - return st.medias +// Description returns the description of the stream. +func (st *ServerStream) Description() *description.Session { + return st.desc } -func (st *ServerStream) senderSSRC(medi *media.Media) (uint32, bool) { +func (st *ServerStream) senderSSRC(medi *description.Media) (uint32, bool) { st.mutex.Lock() defer st.mutex.Unlock() @@ -92,7 +92,7 @@ func (st *ServerStream) senderSSRC(medi *media.Media) (uint32, bool) { return firstFormat(sm.formats).rtcpSender.SenderSSRC() } -func (st *ServerStream) rtpInfoEntry(medi *media.Media, now time.Time) *headers.RTPInfoEntry { +func (st *ServerStream) rtpInfoEntry(medi *description.Media, now time.Time) *headers.RTPInfoEntry { st.mutex.Lock() defer st.mutex.Unlock() @@ -233,13 +233,13 @@ func (st *ServerStream) readerSetInactive(ss *ServerSession) { } // WritePacketRTP writes a RTP packet to all the readers of the stream. -func (st *ServerStream) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error { +func (st *ServerStream) WritePacketRTP(medi *description.Media, pkt *rtp.Packet) error { return st.WritePacketRTPWithNTP(medi, pkt, st.s.timeNow()) } // WritePacketRTPWithNTP writes a RTP packet to all the readers of the stream. // ntp is the absolute time of the packet, and is sent with periodic RTCP sender reports. -func (st *ServerStream) WritePacketRTPWithNTP(medi *media.Media, pkt *rtp.Packet, ntp time.Time) error { +func (st *ServerStream) WritePacketRTPWithNTP(medi *description.Media, pkt *rtp.Packet, ntp time.Time) error { byts := make([]byte, st.s.MaxPacketSize) n, err := pkt.MarshalTo(byts) if err != nil { @@ -260,7 +260,7 @@ func (st *ServerStream) WritePacketRTPWithNTP(medi *media.Media, pkt *rtp.Packet } // WritePacketRTCP writes a RTCP packet to all the readers of the stream. -func (st *ServerStream) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) error { +func (st *ServerStream) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet) error { byts, err := pkt.Marshal() if err != nil { return err diff --git a/server_stream_media.go b/server_stream_media.go index 4da3b598..c75f5d77 100644 --- a/server_stream_media.go +++ b/server_stream_media.go @@ -6,19 +6,19 @@ import ( "github.com/pion/rtcp" "github.com/pion/rtp" - "github.com/bluenviron/gortsplib/v4/pkg/media" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/rtcpsender" ) type serverStreamMedia struct { st *ServerStream - media *media.Media + media *description.Media trackID int formats map[uint8]*serverStreamFormat multicastWriter *serverMulticastWriter } -func newServerStreamMedia(st *ServerStream, medi *media.Media, trackID int) *serverStreamMedia { +func newServerStreamMedia(st *ServerStream, medi *description.Media, trackID int) *serverStreamMedia { sm := &serverStreamMedia{ st: st, media: medi, diff --git a/server_test.go b/server_test.go index fce0faf1..e8445f3c 100644 --- a/server_test.go +++ b/server_test.go @@ -11,8 +11,8 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/auth" "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/conn" + "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/headers" - "github.com/bluenviron/gortsplib/v4/pkg/media" ) var serverCert = []byte(`-----BEGIN CERTIFICATE----- @@ -353,7 +353,7 @@ func TestServerErrorMethodNotImplemented(t *testing.T) { require.NoError(t, err) defer s.Close() - stream := NewServerStream(s, media.Medias{testH264Media}) + stream := NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() h.stream = stream @@ -452,7 +452,7 @@ func TestServerErrorTCPTwoConnOneSession(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn1, err := net.Dial("tcp", "localhost:8554") @@ -545,7 +545,7 @@ func TestServerErrorTCPOneConnTwoSessions(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -620,7 +620,7 @@ func TestServerSetupMultipleTransports(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -731,7 +731,7 @@ func TestServerGetSetParameter(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -881,7 +881,7 @@ func TestServerSessionClose(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -962,7 +962,7 @@ func TestServerSessionAutoClose(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1030,7 +1030,7 @@ func TestServerSessionTeardown(t *testing.T) { require.NoError(t, err) defer s.Close() - stream = NewServerStream(s, media.Medias{testH264Media}) + stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}}) defer stream.Close() nconn, err := net.Dial("tcp", "localhost:8554") @@ -1104,7 +1104,7 @@ func TestServerAuth(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media} + medias := []*description.Media{testH264Media} req := base.Request{ Method: base.Announce, @@ -1113,7 +1113,7 @@ func TestServerAuth(t *testing.T) { "CSeq": base.HeaderValue{"1"}, "Content-Type": base.HeaderValue{"application/sdp"}, }, - Body: mustMarshalMedias(medias), + Body: mediasToSDP(medias), } res, err := writeReqReadRes(conn, req)