mirror of
https://github.com/aler9/gortsplib
synced 2025-09-28 03:52:12 +08:00
allow setting additional properties of streams through description.Stream
This commit is contained in:
128
client.go
128
client.go
@@ -23,10 +23,10 @@ import (
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/bytecounter"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/conn"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/media"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/url"
|
||||
@@ -91,7 +91,7 @@ func findBaseURL(sd *sdp.SessionDescription, res *base.Response, u *url.URL) (*u
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func resetMediaControls(ms media.Medias) {
|
||||
func resetMediaControls(ms []*description.Media) {
|
||||
for i, media := range ms {
|
||||
media.Control = "trackID=" + strconv.FormatInt(int64(i), 10)
|
||||
}
|
||||
@@ -148,14 +148,14 @@ type describeReq struct {
|
||||
}
|
||||
|
||||
type announceReq struct {
|
||||
url *url.URL
|
||||
medias media.Medias
|
||||
res chan clientRes
|
||||
url *url.URL
|
||||
desc *description.Session
|
||||
res chan clientRes
|
||||
}
|
||||
|
||||
type setupReq struct {
|
||||
media *media.Media
|
||||
baseURL *url.URL
|
||||
media *description.Media
|
||||
rtpPort int
|
||||
rtcpPort int
|
||||
res chan clientRes
|
||||
@@ -175,10 +175,9 @@ type pauseReq struct {
|
||||
}
|
||||
|
||||
type clientRes struct {
|
||||
medias media.Medias
|
||||
baseURL *url.URL
|
||||
res *base.Response
|
||||
err error
|
||||
sd *description.Session // describe only
|
||||
res *base.Response
|
||||
err error
|
||||
}
|
||||
|
||||
// ClientOnRequestFunc is the prototype of Client.OnRequest.
|
||||
@@ -200,13 +199,13 @@ type ClientOnDecodeErrorFunc func(err error)
|
||||
type OnPacketRTPFunc func(*rtp.Packet)
|
||||
|
||||
// OnPacketRTPAnyFunc is the prototype of the callback passed to OnPacketRTP(Any).
|
||||
type OnPacketRTPAnyFunc func(*media.Media, format.Format, *rtp.Packet)
|
||||
type OnPacketRTPAnyFunc func(*description.Media, format.Format, *rtp.Packet)
|
||||
|
||||
// OnPacketRTCPFunc is the prototype of the callback passed to OnPacketRTCP().
|
||||
type OnPacketRTCPFunc func(rtcp.Packet)
|
||||
|
||||
// OnPacketRTCPAnyFunc is the prototype of the callback passed to OnPacketRTCPAny().
|
||||
type OnPacketRTCPAnyFunc func(*media.Media, rtcp.Packet)
|
||||
type OnPacketRTCPAnyFunc func(*description.Media, rtcp.Packet)
|
||||
|
||||
// Client is a RTSP client.
|
||||
type Client struct {
|
||||
@@ -306,7 +305,7 @@ type Client struct {
|
||||
lastDescribeURL *url.URL
|
||||
baseURL *url.URL
|
||||
effectiveTransport *Transport
|
||||
medias map[*media.Media]*clientMedia
|
||||
medias map[*description.Media]*clientMedia
|
||||
tcpCallbackByChannel map[int]readFunc
|
||||
lastRange *headers.Range
|
||||
checkTimeoutTimer *time.Timer
|
||||
@@ -442,7 +441,7 @@ func (c *Client) Start(scheme string, host string) error {
|
||||
}
|
||||
|
||||
// StartRecording connects to the address and starts publishing given media.
|
||||
func (c *Client) StartRecording(address string, medias media.Medias) error {
|
||||
func (c *Client) StartRecording(address string, desc *description.Session) error {
|
||||
u, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -453,13 +452,13 @@ func (c *Client) StartRecording(address string, medias media.Medias) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.Announce(u, medias)
|
||||
_, err = c.Announce(u, desc)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.SetupAll(u, medias)
|
||||
err = c.SetupAll(u, desc.Medias)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
@@ -505,11 +504,11 @@ func (c *Client) runInner() error {
|
||||
req.res <- clientRes{res: res, err: err}
|
||||
|
||||
case req := <-c.describe:
|
||||
medias, baseURL, res, err := c.doDescribe(req.url)
|
||||
req.res <- clientRes{medias: medias, baseURL: baseURL, res: res, err: err}
|
||||
sd, res, err := c.doDescribe(req.url)
|
||||
req.res <- clientRes{sd: sd, res: res, err: err}
|
||||
|
||||
case req := <-c.announce:
|
||||
res, err := c.doAnnounce(req.url, req.medias)
|
||||
res, err := c.doAnnounce(req.url, req.desc)
|
||||
req.res <- clientRes{res: res, err: err}
|
||||
|
||||
case req := <-c.setup:
|
||||
@@ -624,7 +623,7 @@ func (c *Client) trySwitchingProtocol() error {
|
||||
c.connURL = prevConnURL
|
||||
|
||||
// some Hikvision cameras require a describe before a setup
|
||||
_, _, _, err := c.doDescribe(c.lastDescribeURL)
|
||||
_, _, err := c.doDescribe(c.lastDescribeURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -649,7 +648,7 @@ func (c *Client) trySwitchingProtocol() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) trySwitchingProtocol2(medi *media.Media, baseURL *url.URL) (*base.Response, error) {
|
||||
func (c *Client) trySwitchingProtocol2(medi *description.Media, baseURL *url.URL) (*base.Response, error) {
|
||||
c.OnTransportSwitch(fmt.Errorf("switching to TCP because server requested it"))
|
||||
|
||||
prevConnURL := c.connURL
|
||||
@@ -661,7 +660,7 @@ func (c *Client) trySwitchingProtocol2(medi *media.Media, baseURL *url.URL) (*ba
|
||||
c.connURL = prevConnURL
|
||||
|
||||
// some Hikvision cameras require a describe before a setup
|
||||
_, _, _, err := c.doDescribe(c.lastDescribeURL)
|
||||
_, _, err := c.doDescribe(c.lastDescribeURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -970,7 +969,7 @@ func (c *Client) doOptions(u *url.URL) (*base.Response, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Options writes an OPTIONS request and reads a response.
|
||||
// Options sends an OPTIONS request.
|
||||
func (c *Client) Options(u *url.URL) (*base.Response, error) {
|
||||
cres := make(chan clientRes)
|
||||
select {
|
||||
@@ -983,14 +982,14 @@ func (c *Client) Options(u *url.URL) (*base.Response, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response, error) {
|
||||
func (c *Client) doDescribe(u *url.URL) (*description.Session, *base.Response, error) {
|
||||
err := c.checkState(map[clientState]struct{}{
|
||||
clientStateInitial: {},
|
||||
clientStatePrePlay: {},
|
||||
clientStatePreRecord: {},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
res, err := c.do(&base.Request{
|
||||
@@ -1001,7 +1000,7 @@ func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response,
|
||||
},
|
||||
}, false, false)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if res.StatusCode != base.StatusOK {
|
||||
@@ -1013,7 +1012,7 @@ func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response,
|
||||
|
||||
ru, err := url.Parse(res.Header["Location"][0])
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
@@ -1028,57 +1027,58 @@ func (c *Client) doDescribe(u *url.URL) (media.Medias, *url.URL, *base.Response,
|
||||
return c.doDescribe(ru)
|
||||
}
|
||||
|
||||
return nil, nil, res, liberrors.ErrClientBadStatusCode{Code: res.StatusCode, Message: res.StatusMessage}
|
||||
return nil, res, liberrors.ErrClientBadStatusCode{Code: res.StatusCode, Message: res.StatusMessage}
|
||||
}
|
||||
|
||||
ct, ok := res.Header["Content-Type"]
|
||||
if !ok || len(ct) != 1 {
|
||||
return nil, nil, nil, liberrors.ErrClientContentTypeMissing{}
|
||||
return nil, nil, liberrors.ErrClientContentTypeMissing{}
|
||||
}
|
||||
|
||||
// strip encoding information from Content-Type header
|
||||
ct = base.HeaderValue{strings.Split(ct[0], ";")[0]}
|
||||
|
||||
if ct[0] != "application/sdp" {
|
||||
return nil, nil, nil, liberrors.ErrClientContentTypeUnsupported{CT: ct}
|
||||
return nil, nil, liberrors.ErrClientContentTypeUnsupported{CT: ct}
|
||||
}
|
||||
|
||||
var sd sdp.SessionDescription
|
||||
err = sd.Unmarshal(res.Body)
|
||||
var ssd sdp.SessionDescription
|
||||
err = ssd.Unmarshal(res.Body)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var medias media.Medias
|
||||
err = medias.Unmarshal(sd.MediaDescriptions)
|
||||
var desc description.Session
|
||||
err = desc.Unmarshal(&ssd)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
baseURL, err := findBaseURL(&sd, res, u)
|
||||
baseURL, err := findBaseURL(&ssd, res, u)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
desc.BaseURL = baseURL
|
||||
|
||||
c.lastDescribeURL = u
|
||||
|
||||
return medias, baseURL, res, nil
|
||||
return &desc, res, nil
|
||||
}
|
||||
|
||||
// Describe writes a DESCRIBE request and reads a Response.
|
||||
func (c *Client) Describe(u *url.URL) (media.Medias, *url.URL, *base.Response, error) {
|
||||
// Describe sends a DESCRIBE request.
|
||||
func (c *Client) Describe(u *url.URL) (*description.Session, *base.Response, error) {
|
||||
cres := make(chan clientRes)
|
||||
select {
|
||||
case c.describe <- describeReq{url: u, res: cres}:
|
||||
res := <-cres
|
||||
return res.medias, res.baseURL, res.res, res.err
|
||||
return res.sd, res.res, res.err
|
||||
|
||||
case <-c.ctx.Done():
|
||||
return nil, nil, nil, liberrors.ErrClientTerminated{}
|
||||
return nil, nil, liberrors.ErrClientTerminated{}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) doAnnounce(u *url.URL, medias media.Medias) (*base.Response, error) {
|
||||
func (c *Client) doAnnounce(u *url.URL, desc *description.Session) (*base.Response, error) {
|
||||
err := c.checkState(map[clientState]struct{}{
|
||||
clientStateInitial: {},
|
||||
})
|
||||
@@ -1086,9 +1086,9 @@ func (c *Client) doAnnounce(u *url.URL, medias media.Medias) (*base.Response, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resetMediaControls(medias)
|
||||
resetMediaControls(desc.Medias)
|
||||
|
||||
byts, err := medias.Marshal(false).Marshal()
|
||||
byts, err := desc.Marshal(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1117,11 +1117,11 @@ func (c *Client) doAnnounce(u *url.URL, medias media.Medias) (*base.Response, er
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Announce writes an ANNOUNCE request and reads a Response.
|
||||
func (c *Client) Announce(u *url.URL, medias media.Medias) (*base.Response, error) {
|
||||
// Announce sends an ANNOUNCE request.
|
||||
func (c *Client) Announce(u *url.URL, desc *description.Session) (*base.Response, error) {
|
||||
cres := make(chan clientRes)
|
||||
select {
|
||||
case c.announce <- announceReq{url: u, medias: medias, res: cres}:
|
||||
case c.announce <- announceReq{url: u, desc: desc, res: cres}:
|
||||
res := <-cres
|
||||
return res.res, res.err
|
||||
|
||||
@@ -1132,7 +1132,7 @@ func (c *Client) Announce(u *url.URL, medias media.Medias) (*base.Response, erro
|
||||
|
||||
func (c *Client) doSetup(
|
||||
baseURL *url.URL,
|
||||
medi *media.Media,
|
||||
medi *description.Media,
|
||||
rtpPort int,
|
||||
rtcpPort int,
|
||||
) (*base.Response, error) {
|
||||
@@ -1384,7 +1384,7 @@ func (c *Client) doSetup(
|
||||
}
|
||||
|
||||
if c.medias == nil {
|
||||
c.medias = make(map[*media.Media]*clientMedia)
|
||||
c.medias = make(map[*description.Media]*clientMedia)
|
||||
}
|
||||
|
||||
c.medias[medi] = cm
|
||||
@@ -1417,20 +1417,20 @@ func (c *Client) findFreeChannelPair() int {
|
||||
}
|
||||
}
|
||||
|
||||
// Setup writes a SETUP request and reads a Response.
|
||||
// Setup sends a SETUP request.
|
||||
// rtpPort and rtcpPort are used only if transport is UDP.
|
||||
// if rtpPort and rtcpPort are zero, they are chosen automatically.
|
||||
func (c *Client) Setup(
|
||||
baseURL *url.URL,
|
||||
media *media.Media,
|
||||
media *description.Media,
|
||||
rtpPort int,
|
||||
rtcpPort int,
|
||||
) (*base.Response, error) {
|
||||
cres := make(chan clientRes)
|
||||
select {
|
||||
case c.setup <- setupReq{
|
||||
media: media,
|
||||
baseURL: baseURL,
|
||||
media: media,
|
||||
rtpPort: rtpPort,
|
||||
rtcpPort: rtcpPort,
|
||||
res: cres,
|
||||
@@ -1444,7 +1444,7 @@ func (c *Client) Setup(
|
||||
}
|
||||
|
||||
// SetupAll setups all the given medias.
|
||||
func (c *Client) SetupAll(baseURL *url.URL, medias media.Medias) error {
|
||||
func (c *Client) SetupAll(baseURL *url.URL, medias []*description.Media) error {
|
||||
for _, m := range medias {
|
||||
_, err := c.Setup(baseURL, m, 0, 0)
|
||||
if err != nil {
|
||||
@@ -1509,7 +1509,7 @@ func (c *Client) doPlay(ra *headers.Range) (*base.Response, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Play writes a PLAY request and reads a Response.
|
||||
// Play sends a PLAY request.
|
||||
// This can be called only after Setup().
|
||||
func (c *Client) Play(ra *headers.Range) (*base.Response, error) {
|
||||
cres := make(chan clientRes)
|
||||
@@ -1551,7 +1551,7 @@ func (c *Client) doRecord() (*base.Response, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Record writes a RECORD request and reads a Response.
|
||||
// Record sends a RECORD request.
|
||||
// This can be called only after Announce() and Setup().
|
||||
func (c *Client) Record() (*base.Response, error) {
|
||||
cres := make(chan clientRes)
|
||||
@@ -1601,7 +1601,7 @@ func (c *Client) doPause() (*base.Response, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Pause writes a PAUSE request and reads a Response.
|
||||
// Pause sends a PAUSE request.
|
||||
// This can be called only after Play() or Record().
|
||||
func (c *Client) Pause() (*base.Response, error) {
|
||||
cres := make(chan clientRes)
|
||||
@@ -1648,26 +1648,26 @@ func (c *Client) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) {
|
||||
}
|
||||
|
||||
// OnPacketRTP sets the callback that is called when a RTP packet is read.
|
||||
func (c *Client) OnPacketRTP(medi *media.Media, forma format.Format, cb OnPacketRTPFunc) {
|
||||
func (c *Client) OnPacketRTP(medi *description.Media, forma format.Format, cb OnPacketRTPFunc) {
|
||||
cm := c.medias[medi]
|
||||
ct := cm.formats[forma.PayloadType()]
|
||||
ct.onPacketRTP = cb
|
||||
}
|
||||
|
||||
// OnPacketRTCP sets the callback that is called when a RTCP packet is read.
|
||||
func (c *Client) OnPacketRTCP(medi *media.Media, cb OnPacketRTCPFunc) {
|
||||
func (c *Client) OnPacketRTCP(medi *description.Media, cb OnPacketRTCPFunc) {
|
||||
cm := c.medias[medi]
|
||||
cm.onPacketRTCP = cb
|
||||
}
|
||||
|
||||
// WritePacketRTP writes a RTP packet to the server.
|
||||
func (c *Client) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error {
|
||||
func (c *Client) WritePacketRTP(medi *description.Media, pkt *rtp.Packet) error {
|
||||
return c.WritePacketRTPWithNTP(medi, pkt, c.timeNow())
|
||||
}
|
||||
|
||||
// WritePacketRTPWithNTP writes a RTP packet to the server.
|
||||
// ntp is the absolute time of the packet, and is sent with periodic RTCP sender reports.
|
||||
func (c *Client) WritePacketRTPWithNTP(medi *media.Media, pkt *rtp.Packet, ntp time.Time) error {
|
||||
func (c *Client) WritePacketRTPWithNTP(medi *description.Media, pkt *rtp.Packet, ntp time.Time) error {
|
||||
byts := make([]byte, c.MaxPacketSize)
|
||||
n, err := pkt.MarshalTo(byts)
|
||||
if err != nil {
|
||||
@@ -1688,7 +1688,7 @@ func (c *Client) WritePacketRTPWithNTP(medi *media.Media, pkt *rtp.Packet, ntp t
|
||||
}
|
||||
|
||||
// WritePacketRTCP writes a RTCP packet to the server.
|
||||
func (c *Client) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) error {
|
||||
func (c *Client) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet) error {
|
||||
byts, err := pkt.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1713,7 +1713,7 @@ func (c *Client) PacketPTS(forma format.Format, pkt *rtp.Packet) (time.Duration,
|
||||
|
||||
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
|
||||
// The NTP timestamp is computed from sender reports.
|
||||
func (c *Client) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {
|
||||
func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time, bool) {
|
||||
cm := c.medias[medi]
|
||||
ct := cm.formats[pkt.PayloadType]
|
||||
return ct.rtcpReceiver.PacketNTP(pkt.Timestamp)
|
||||
|
@@ -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)
|
||||
|
@@ -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 {
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
})
|
||||
|
||||
|
@@ -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)
|
||||
})
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
})
|
||||
|
||||
|
@@ -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)
|
||||
})
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
})
|
||||
|
@@ -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)
|
||||
})
|
||||
|
@@ -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)
|
||||
})
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
})
|
@@ -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()
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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()
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user