allow setting additional properties of streams through description.Stream

This commit is contained in:
aler9
2023-08-16 19:02:49 +02:00
committed by Alessandro Ros
parent 4e000eb2dd
commit cdbecb1f5d
54 changed files with 943 additions and 893 deletions

128
client.go
View File

@@ -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)

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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
}

View File

@@ -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,

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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)

View File

@@ -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)
})

View File

@@ -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()
}

View File

@@ -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)
}

View File

@@ -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
}
}

View File

@@ -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.

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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,

View File

@@ -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)