mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
sdp: fix multiple crashes in SDP parser (#170)
* sdp: support parsing example SDP In ONVIF specification * sdp: fix crash in SDP parser; add fuzz tests * sdp: improve coverage
This commit is contained in:
2
go.mod
2
go.mod
@@ -7,7 +7,7 @@ require (
|
|||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/pion/rtcp v1.2.9
|
github.com/pion/rtcp v1.2.9
|
||||||
github.com/pion/rtp v0.0.0-20230107162714-c3ea6851e25b
|
github.com/pion/rtp v0.0.0-20230107162714-c3ea6851e25b
|
||||||
github.com/pion/sdp/v3 v3.0.5
|
github.com/pion/sdp/v3 v3.0.6
|
||||||
github.com/stretchr/testify v1.7.1
|
github.com/stretchr/testify v1.7.1
|
||||||
golang.org/x/net v0.0.0-20220526153639-5463443f8c37
|
golang.org/x/net v0.0.0-20220526153639-5463443f8c37
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@@ -12,8 +12,8 @@ github.com/pion/rtcp v1.2.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U=
|
|||||||
github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
|
github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
|
||||||
github.com/pion/rtp v0.0.0-20230107162714-c3ea6851e25b h1:1+fosfmaXgaSehusvtDoH/DtpAnojUTHkqsfhmeDJ3I=
|
github.com/pion/rtp v0.0.0-20230107162714-c3ea6851e25b h1:1+fosfmaXgaSehusvtDoH/DtpAnojUTHkqsfhmeDJ3I=
|
||||||
github.com/pion/rtp v0.0.0-20230107162714-c3ea6851e25b/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
github.com/pion/rtp v0.0.0-20230107162714-c3ea6851e25b/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||||
github.com/pion/sdp/v3 v3.0.5 h1:ouvI7IgGl+V4CrqskVtr3AaTrPvPisEOxwgpdktctkU=
|
github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw=
|
||||||
github.com/pion/sdp/v3 v3.0.5/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw=
|
github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw=
|
||||||
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
|
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
378
pkg/sdp/sdp.go
378
pkg/sdp/sdp.go
@@ -62,10 +62,11 @@ func parsePort(value string) (int, error) {
|
|||||||
return port, nil
|
return port, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SessionDescription) unmarshalVersion(value string) error {
|
func (s *SessionDescription) unmarshalProtocolVersion(value string) error {
|
||||||
if value != "0" {
|
if value != "0" {
|
||||||
return fmt.Errorf("invalid version")
|
return fmt.Errorf("invalid version")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,67 +75,57 @@ func (s *SessionDescription) unmarshalSessionName(value string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is rewritten from scratch to guarantee compatibility with most RTSP
|
||||||
|
// implementations.
|
||||||
func (s *SessionDescription) unmarshalOrigin(value string) error {
|
func (s *SessionDescription) unmarshalOrigin(value string) error {
|
||||||
// special case for live reporter app
|
value = strings.TrimSpace(value)
|
||||||
if value[:3] == "-0 " {
|
|
||||||
|
if strings.HasPrefix(value, "-0 ") { // live reporter app
|
||||||
value = "- 0 " + value[3:]
|
value = "- 0 " + value[3:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// find spaces from end to beginning, to support multiple spaces
|
i := strings.Index(value, " IN ")
|
||||||
// in the first field
|
if i < 0 {
|
||||||
fields := func() []string {
|
return fmt.Errorf("%w `o=%v`", errSDPInvalidSyntax, value)
|
||||||
values := strings.Split(strings.TrimSpace(value), " ")
|
|
||||||
|
|
||||||
// special case for some onvif2 cameras
|
|
||||||
if strings.Compare(values[len(values)-1], "IP4") == 0 {
|
|
||||||
values = append(values, "127.0.0.1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return append([]string{strings.Join(values[0:len(values)-5], " ")}, values[len(values)-5:]...)
|
fields := strings.SplitN(value[i+1:], " ", 3)
|
||||||
}()
|
|
||||||
|
|
||||||
if len(fields) != 6 {
|
s.Origin.NetworkType = "IN"
|
||||||
return fmt.Errorf("%w `o=%v`", errSDPInvalidSyntax, fields)
|
|
||||||
|
s.Origin.AddressType = fields[1]
|
||||||
|
if i2 := indexOf(s.Origin.AddressType, []string{"IP4", "IP6"}); i2 == -1 {
|
||||||
|
return fmt.Errorf("%w `%v`", errSDPInvalidValue, s.Origin.AddressType)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionID uint64
|
if len(fields) >= 3 {
|
||||||
|
s.Origin.UnicastAddress = fields[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = strings.SplitN(value[:i], " ", 4)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
s.Origin.SessionVersion, err = strconv.ParseUint(fields[len(fields)-1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, fields[len(fields)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fields) >= 2 {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(fields[1], "0x") || strings.HasPrefix(fields[1], "0X"):
|
case strings.HasPrefix(fields[len(fields)-2], "0x"), strings.HasPrefix(fields[len(fields)-2], "0X"):
|
||||||
sessionID, err = strconv.ParseUint(fields[1][2:], 16, 64)
|
s.Origin.SessionID, err = strconv.ParseUint(fields[len(fields)-2][2:], 16, 64)
|
||||||
case strings.ContainsAny(fields[1], "abcdefABCDEF"):
|
case strings.ContainsAny(fields[len(fields)-2], "abcdefABCDEF"):
|
||||||
sessionID, err = strconv.ParseUint(fields[1], 16, 64)
|
s.Origin.SessionID, err = strconv.ParseUint(fields[len(fields)-2], 16, 64)
|
||||||
default:
|
default:
|
||||||
sessionID, err = strconv.ParseUint(fields[1], 10, 64)
|
s.Origin.SessionID, err = strconv.ParseUint(fields[len(fields)-2], 10, 64)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, fields[1])
|
return fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, fields[len(fields)-2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionVersion, err := strconv.ParseUint(fields[2], 10, 64)
|
if len(fields) >= 3 {
|
||||||
if err != nil {
|
s.Origin.Username = strings.Join(fields[:len(fields)-2], " ")
|
||||||
return fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, fields[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set according to currently registered with IANA
|
|
||||||
// https://tools.ietf.org/html/rfc4566#section-8.2.6
|
|
||||||
if i := indexOf(fields[3], []string{"IN"}); i == -1 {
|
|
||||||
return fmt.Errorf("%w `%v`", errSDPInvalidValue, fields[3])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set according to currently registered with IANA
|
|
||||||
// https://tools.ietf.org/html/rfc4566#section-8.2.7
|
|
||||||
if i := indexOf(fields[4], []string{"IP4", "IP6"}); i == -1 {
|
|
||||||
return fmt.Errorf("%w `%v`", errSDPInvalidValue, fields[3])
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Origin = psdp.Origin{
|
|
||||||
Username: fields[0],
|
|
||||||
SessionID: sessionID,
|
|
||||||
SessionVersion: sessionVersion,
|
|
||||||
NetworkType: fields[3],
|
|
||||||
AddressType: fields[4],
|
|
||||||
UnicastAddress: fields[5],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -209,6 +200,7 @@ func (s *SessionDescription) unmarshalSessionConnectionInformation(value string)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w `c=%v`", errSDPInvalidSyntax, value)
|
return fmt.Errorf("%w `c=%v`", errSDPInvalidSyntax, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,7 +358,7 @@ func parseTimeUnits(value string) (int64, error) {
|
|||||||
func (s *SessionDescription) unmarshalRepeatTimes(value string) error {
|
func (s *SessionDescription) unmarshalRepeatTimes(value string) error {
|
||||||
fields := strings.Fields(value)
|
fields := strings.Fields(value)
|
||||||
if len(fields) < 3 {
|
if len(fields) < 3 {
|
||||||
return fmt.Errorf("%w `r=%v`", errSDPInvalidSyntax, fields)
|
return fmt.Errorf("%w `r=%v`", errSDPInvalidSyntax, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
latestTimeDesc := &s.TimeDescriptions[len(s.TimeDescriptions)-1]
|
latestTimeDesc := &s.TimeDescriptions[len(s.TimeDescriptions)-1]
|
||||||
@@ -503,20 +495,155 @@ func (s *SessionDescription) unmarshalMediaAttribute(value string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unmarshalState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateInitial unmarshalState = iota
|
||||||
|
stateSession
|
||||||
|
stateMedia
|
||||||
|
stateTimeDescription
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *SessionDescription) unmarshalSession(state *unmarshalState, key byte, val string) error {
|
||||||
|
switch key {
|
||||||
|
case 'o':
|
||||||
|
err := s.unmarshalOrigin(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
err := s.unmarshalSessionName(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
err := s.unmarshalSessionInformation(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
err := s.unmarshalURI(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
err := s.unmarshalEmail(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
err := s.unmarshalPhone(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
err := s.unmarshalSessionConnectionInformation(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
err := s.unmarshalSessionBandwidth(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'z':
|
||||||
|
err := s.unmarshalTimeZones(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
err := s.unmarshalSessionEncryptionKey(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
err := s.unmarshalSessionAttribute(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
err := s.unmarshalTiming(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*state = stateTimeDescription
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
err := s.unmarshalMediaDescription(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*state = stateMedia
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid key: %c", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionDescription) unmarshalMedia(key byte, val string) error {
|
||||||
|
switch key {
|
||||||
|
case 'm':
|
||||||
|
err := s.unmarshalMediaDescription(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
err := s.unmarshalMediaTitle(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
err := s.unmarshalMediaConnectionInformation(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
err := s.unmarshalMediaBandwidth(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
err := s.unmarshalMediaEncryptionKey(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
err := s.unmarshalMediaAttribute(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid key: %c", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Unmarshal decodes a SessionDescription.
|
// Unmarshal decodes a SessionDescription.
|
||||||
// This is rewritten from scratch to guarantee compatibility with most RTSP
|
// This is rewritten from scratch to guarantee compatibility with most RTSP
|
||||||
// implementations.
|
// implementations.
|
||||||
func (s *SessionDescription) Unmarshal(byts []byte) error {
|
func (s *SessionDescription) Unmarshal(byts []byte) error {
|
||||||
str := string(byts)
|
str := string(byts)
|
||||||
|
|
||||||
type stateVal int
|
|
||||||
|
|
||||||
const (
|
|
||||||
stateInitial stateVal = iota
|
|
||||||
stateSession
|
|
||||||
stateMedia
|
|
||||||
)
|
|
||||||
|
|
||||||
state := stateInitial
|
state := stateInitial
|
||||||
|
|
||||||
for _, line := range strings.Split(strings.ReplaceAll(str, "\r", ""), "\n") {
|
for _, line := range strings.Split(strings.ReplaceAll(str, "\r", ""), "\n") {
|
||||||
@@ -535,9 +662,9 @@ func (s *SessionDescription) Unmarshal(byts []byte) error {
|
|||||||
case stateInitial:
|
case stateInitial:
|
||||||
switch key {
|
switch key {
|
||||||
case 'v':
|
case 'v':
|
||||||
err := s.unmarshalVersion(val)
|
err := s.unmarshalProtocolVersion(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
return err
|
||||||
}
|
}
|
||||||
state = stateSession
|
state = stateSession
|
||||||
|
|
||||||
@@ -546,136 +673,31 @@ func (s *SessionDescription) Unmarshal(byts []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case stateSession:
|
case stateSession:
|
||||||
switch key {
|
err := s.unmarshalSession(&state, key, val)
|
||||||
case 'o':
|
|
||||||
err := s.unmarshalOrigin(val)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
case 's':
|
|
||||||
err := s.unmarshalSessionName(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'i':
|
|
||||||
err := s.unmarshalSessionInformation(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'u':
|
|
||||||
err := s.unmarshalURI(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'e':
|
|
||||||
err := s.unmarshalEmail(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'p':
|
|
||||||
err := s.unmarshalPhone(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'c':
|
|
||||||
err := s.unmarshalSessionConnectionInformation(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
err := s.unmarshalSessionBandwidth(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'z':
|
|
||||||
err := s.unmarshalTimeZones(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'k':
|
|
||||||
err := s.unmarshalSessionEncryptionKey(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'a':
|
|
||||||
err := s.unmarshalSessionAttribute(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 't':
|
|
||||||
err := s.unmarshalTiming(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'r':
|
|
||||||
err := s.unmarshalRepeatTimes(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'm':
|
|
||||||
err := s.unmarshalMediaDescription(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
state = stateMedia
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid key: %c (%s)", key, line)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case stateMedia:
|
case stateMedia:
|
||||||
|
err := s.unmarshalMedia(key, val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case stateTimeDescription:
|
||||||
switch key {
|
switch key {
|
||||||
case 'm':
|
case 'r':
|
||||||
err := s.unmarshalMediaDescription(val)
|
err := s.unmarshalRepeatTimes(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
case 'i':
|
|
||||||
err := s.unmarshalMediaTitle(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'c':
|
|
||||||
err := s.unmarshalMediaConnectionInformation(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
err := s.unmarshalMediaBandwidth(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'k':
|
|
||||||
err := s.unmarshalMediaEncryptionKey(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'a':
|
|
||||||
err := s.unmarshalMediaAttribute(val)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s (%s)", err, line)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid key: %c (%s)", key, line)
|
state = stateSession
|
||||||
|
err := s.unmarshalSession(&state, key, val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
//nolint:govet
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
package sdp
|
package sdp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -43,7 +45,7 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{3034423619, 3042462419}, nil},
|
{Timing: psdp.Timing{StartTime: 3034423619, StopTime: 3042462419}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -74,7 +76,7 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{3034423619, 3042462419}, nil},
|
{Timing: psdp.Timing{StartTime: 3034423619, StopTime: 3042462419}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -109,7 +111,7 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{3034423619, 3042462419}, nil},
|
{Timing: psdp.Timing{StartTime: 3034423619, StopTime: 3042462419}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -208,20 +210,29 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{2873397496, 2873404696}, nil},
|
{Timing: psdp.Timing{StartTime: 2873397496, StopTime: 2873404696}},
|
||||||
{psdp.Timing{3034423619, 3042462419}, []psdp.RepeatTime{{604800, 3600, []int64{0, 90000}}}},
|
{
|
||||||
|
Timing: psdp.Timing{StartTime: 3034423619, StopTime: 3042462419},
|
||||||
|
RepeatTimes: []psdp.RepeatTime{{Interval: 604800, Duration: 3600, Offsets: []int64{0, 90000}}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
TimeZones: []psdp.TimeZone{
|
TimeZones: []psdp.TimeZone{
|
||||||
{2882844526, -3600},
|
{AdjustmentTime: 2882844526, Offset: -3600},
|
||||||
{2898848070, 0},
|
{AdjustmentTime: 2898848070},
|
||||||
},
|
},
|
||||||
EncryptionKey: func() *psdp.EncryptionKey {
|
EncryptionKey: func() *psdp.EncryptionKey {
|
||||||
v := psdp.EncryptionKey("prompt")
|
v := psdp.EncryptionKey("prompt")
|
||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"candidate", "0 1 UDP 2113667327 203.0.113.1 54400 typ host"},
|
{
|
||||||
{"recvonly", ""},
|
Key: "candidate",
|
||||||
|
Value: "0 1 UDP 2113667327 203.0.113.1 54400 typ host",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "recvonly",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -252,7 +263,9 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"sendrecv", ""},
|
{
|
||||||
|
Key: "sendrecv",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -263,7 +276,10 @@ var cases = []struct {
|
|||||||
Formats: []string{"99"},
|
Formats: []string{"99"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "99 h263-1998/90000"},
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "99 h263-1998/90000",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -317,12 +333,21 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
SessionName: "RTSP Server",
|
SessionName: "RTSP Server",
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{0, 0}, nil},
|
{Timing: psdp.Timing{StartTime: 0, StopTime: 0}},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"control", "*"},
|
{
|
||||||
{"source-filter", " incl IN IP4 * 10.175.31.17"},
|
Key: "control",
|
||||||
{"range", "npt=0-"},
|
Value: "*",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "source-filter",
|
||||||
|
Value: " incl IN IP4 * 10.175.31.17",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "range",
|
||||||
|
Value: "npt=0-",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -338,10 +363,22 @@ var cases = []struct {
|
|||||||
Address: &psdp.Address{Address: "0.0.0.0"},
|
Address: &psdp.Address{Address: "0.0.0.0"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 H264/90000"},
|
{
|
||||||
{"fmtp", "96 profile-level-id=4D001E; packetization-mode=1; sprop-parameter-sets=Z00AHpWoKAv+VA==,aO48gA=="},
|
Key: "rtpmap",
|
||||||
{"control", "?ctype=video"},
|
Value: "96 H264/90000",
|
||||||
{"recvonly", ""},
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 profile-level-id=4D001E; packetization-mode=1; sprop-parameter-sets=Z00AHpWoKAv+VA==,aO48gA==",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "?ctype=video",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "recvonly",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -352,9 +389,18 @@ var cases = []struct {
|
|||||||
Formats: []string{"106"},
|
Formats: []string{"106"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "106 vnd.onvif.metadata/90000"},
|
{
|
||||||
{"control", "?ctype=app106"},
|
Key: "rtpmap",
|
||||||
{"sendonly", ""},
|
Value: "106 vnd.onvif.metadata/90000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "?ctype=app106",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "sendonly",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -405,10 +451,19 @@ var cases = []struct {
|
|||||||
Formats: []string{"96"},
|
Formats: []string{"96"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 H265/90000"},
|
{
|
||||||
{"fmtp", "96 sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwB4FwJA; " +
|
Key: "rtpmap",
|
||||||
"sprop-sps=QgEBAWAAAAMAsAAAAwAAAwB4oAKggC8c1YgXuRZFL/y5/E/qbgQEBAE=; sprop-pps=RAHAcvBTJA==;"},
|
Value: "96 H265/90000",
|
||||||
{"control", "streamid=0"},
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwB4FwJA; " +
|
||||||
|
"sprop-sps=QgEBAWAAAAMAsAAAAwAAAwB4oAKggC8c1YgXuRZFL/y5/E/qbgQEBAE=; sprop-pps=RAHAcvBTJA==;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "streamid=0",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -419,9 +474,18 @@ var cases = []struct {
|
|||||||
Formats: []string{"97"},
|
Formats: []string{"97"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "97 mpeg4-generic/44100/2"},
|
{
|
||||||
{"fmtp", "97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210"},
|
Key: "rtpmap",
|
||||||
{"control", "streamid=1"},
|
Value: "97 mpeg4-generic/44100/2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "streamid=1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -463,10 +527,19 @@ var cases = []struct {
|
|||||||
Formats: []string{"96"},
|
Formats: []string{"96"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 H265/90000"},
|
{
|
||||||
{"fmtp", "96 sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwB4FwJA; " +
|
Key: "rtpmap",
|
||||||
"sprop-sps=QgEBAWAAAAMAsAAAAwAAAwB4oAKggC8c1YgXuRZFL/y5/E/qbgQEBAE=; sprop-pps=RAHAcvBTJA==;"},
|
Value: "96 H265/90000",
|
||||||
{"control", "streamid=0"},
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwB4FwJA; " +
|
||||||
|
"sprop-sps=QgEBAWAAAAMAsAAAAwAAAwB4oAKggC8c1YgXuRZFL/y5/E/qbgQEBAE=; sprop-pps=RAHAcvBTJA==;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "streamid=0",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -477,9 +550,18 @@ var cases = []struct {
|
|||||||
Formats: []string{"97"},
|
Formats: []string{"97"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "97 mpeg4-generic/44100/2"},
|
{
|
||||||
{"fmtp", "97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210"},
|
Key: "rtpmap",
|
||||||
{"control", "streamid=1"},
|
Value: "97 mpeg4-generic/44100/2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "streamid=1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -534,9 +616,12 @@ var cases = []struct {
|
|||||||
AddressType: "IP4",
|
AddressType: "IP4",
|
||||||
Address: &psdp.Address{Address: "0.0.0.0"},
|
Address: &psdp.Address{Address: "0.0.0.0"},
|
||||||
},
|
},
|
||||||
TimeDescriptions: []psdp.TimeDescription{{psdp.Timing{0, 0}, nil}},
|
TimeDescriptions: []psdp.TimeDescription{{Timing: psdp.Timing{StartTime: 0, StopTime: 0}}},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"control", "*"},
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "*",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -553,9 +638,18 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 H264/90000"},
|
{
|
||||||
{"fmtp", "96 packetization-mode=1; sprop-parameter-sets=J2QAHqxWgKA9pqAgIMBA,KO48sA==; profile-level-id=64001E"},
|
Key: "rtpmap",
|
||||||
{"control", "streamid=0"},
|
Value: "96 H264/90000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 packetization-mode=1; sprop-parameter-sets=J2QAHqxWgKA9pqAgIMBA,KO48sA==; profile-level-id=64001E",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "streamid=0",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -572,9 +666,18 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "97 MPEG4-GENERIC/48000/1"},
|
{
|
||||||
{"fmtp", "97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexLength=3;indexDeltaLength=3;config=118856E500"},
|
Key: "rtpmap",
|
||||||
{"control", "streamid=1"},
|
Value: "97 MPEG4-GENERIC/48000/1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexLength=3;indexDeltaLength=3;config=118856E500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "streamid=1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -623,9 +726,12 @@ var cases = []struct {
|
|||||||
AddressType: "IP4",
|
AddressType: "IP4",
|
||||||
Address: &psdp.Address{Address: "0.0.0.0"},
|
Address: &psdp.Address{Address: "0.0.0.0"},
|
||||||
},
|
},
|
||||||
TimeDescriptions: []psdp.TimeDescription{{psdp.Timing{0, 0}, nil}},
|
TimeDescriptions: []psdp.TimeDescription{{Timing: psdp.Timing{StartTime: 0, StopTime: 0}}},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"range", "npt=now-"},
|
{
|
||||||
|
Key: "range",
|
||||||
|
Value: "npt=now-",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -636,12 +742,27 @@ var cases = []struct {
|
|||||||
Formats: []string{"105"},
|
Formats: []string{"105"},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "105 H264/90000"},
|
{
|
||||||
{"control", "trackID=1"},
|
Key: "rtpmap",
|
||||||
{"recvonly", ""},
|
Value: "105 H264/90000",
|
||||||
{"framerate", "25.0"},
|
},
|
||||||
{"fmtp", "105 packetization-mode=1; profile-level-id=640028; " +
|
{
|
||||||
"sprop-parameter-sets=Z2QAKKwa0A8ARPy4CIAAAAMAgAAADLWgAtwAHJ173CPFCKg=,KO4ESSJAAAAAAAAAAA=="},
|
Key: "control",
|
||||||
|
Value: "trackID=1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "recvonly",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "framerate",
|
||||||
|
Value: "25.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "105 packetization-mode=1; profile-level-id=640028; " +
|
||||||
|
"sprop-parameter-sets=Z2QAKKwa0A8ARPy4CIAAAAMAgAAADLWgAtwAHJ173CPFCKg=,KO4ESSJAAAAAAAAAAA==",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -706,12 +827,21 @@ var cases = []struct {
|
|||||||
AddressType: "IP4",
|
AddressType: "IP4",
|
||||||
Address: &psdp.Address{Address: "0.0.0.0"},
|
Address: &psdp.Address{Address: "0.0.0.0"},
|
||||||
},
|
},
|
||||||
TimeDescriptions: []psdp.TimeDescription{{psdp.Timing{0, 0}, nil}},
|
TimeDescriptions: []psdp.TimeDescription{{Timing: psdp.Timing{StartTime: 0, StopTime: 0}}},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"tool", "vlc 3.0.11"},
|
{
|
||||||
{"recvonly", ""},
|
Key: "tool",
|
||||||
{"type", "broadcast"},
|
Value: "vlc 3.0.11",
|
||||||
{"charset", "UTF-8"},
|
},
|
||||||
|
{Key: "recvonly"},
|
||||||
|
{
|
||||||
|
Key: "type",
|
||||||
|
Value: "broadcast",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "charset",
|
||||||
|
Value: "UTF-8",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -727,9 +857,15 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 mpeg4-generic/22050"},
|
{
|
||||||
{"fmtp", "96 streamtype=5; profile-level-id=15; " +
|
Key: "rtpmap",
|
||||||
"mode=AAC-hbr; config=1388; SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;"},
|
Value: "96 mpeg4-generic/22050",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 streamtype=5; profile-level-id=15; " +
|
||||||
|
"mode=AAC-hbr; config=1388; SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -745,8 +881,14 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 H264/90000"},
|
{
|
||||||
{"fmtp", "96 packetization-mode=1;profile-level-id=640028;sprop-parameter-sets=J2QAKKwrQCgDzQDxImo=,KO4CXLA=;"},
|
Key: "rtpmap",
|
||||||
|
Value: "96 H264/90000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 packetization-mode=1;profile-level-id=640028;sprop-parameter-sets=J2QAKKwrQCgDzQDxImo=,KO4CXLA=;",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -785,9 +927,12 @@ var cases = []struct {
|
|||||||
AddressType: "IP4",
|
AddressType: "IP4",
|
||||||
Address: &psdp.Address{Address: "239.3.1.142"},
|
Address: &psdp.Address{Address: "239.3.1.142"},
|
||||||
},
|
},
|
||||||
TimeDescriptions: []psdp.TimeDescription{{psdp.Timing{0, 0}, nil}},
|
TimeDescriptions: []psdp.TimeDescription{{Timing: psdp.Timing{StartTime: 0, StopTime: 0}}},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"range", "clock=0-"},
|
{
|
||||||
|
Key: "range",
|
||||||
|
Value: "clock=0-",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -830,7 +975,7 @@ var cases = []struct {
|
|||||||
"b=AS:5000\r\n" +
|
"b=AS:5000\r\n" +
|
||||||
"a=control:rtsp://10.10.1.30:8554/onvif2/audio/trackID=1\r\n"),
|
"a=control:rtsp://10.10.1.30:8554/onvif2/audio/trackID=1\r\n"),
|
||||||
[]byte("v=0\r\n" +
|
[]byte("v=0\r\n" +
|
||||||
"o=RTSP 16381778200090761968 16381778200090839277 IN IP4 127.0.0.1\r\n" +
|
"o=RTSP 16381778200090761968 16381778200090839277 IN IP4 \r\n" +
|
||||||
"s=RTSP Server\r\n" +
|
"s=RTSP Server\r\n" +
|
||||||
"e=NONE\r\n" +
|
"e=NONE\r\n" +
|
||||||
"t=0 0\r\n" +
|
"t=0 0\r\n" +
|
||||||
@@ -856,17 +1001,20 @@ var cases = []struct {
|
|||||||
SessionVersion: 16381778200090839277,
|
SessionVersion: 16381778200090839277,
|
||||||
NetworkType: "IN",
|
NetworkType: "IN",
|
||||||
AddressType: "IP4",
|
AddressType: "IP4",
|
||||||
UnicastAddress: "127.0.0.1",
|
UnicastAddress: "",
|
||||||
},
|
},
|
||||||
SessionName: psdp.SessionName("RTSP Server"),
|
SessionName: psdp.SessionName("RTSP Server"),
|
||||||
EmailAddress: func() *psdp.EmailAddress {
|
EmailAddress: func() *psdp.EmailAddress {
|
||||||
v := psdp.EmailAddress("NONE")
|
v := psdp.EmailAddress("NONE")
|
||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{{psdp.Timing{0, 0}, nil}},
|
TimeDescriptions: []psdp.TimeDescription{{Timing: psdp.Timing{StartTime: 0, StopTime: 0}}},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"recvonly", ""},
|
{Key: "recvonly"},
|
||||||
{"x-dimensions", "1920,1080"},
|
{
|
||||||
|
Key: "x-dimensions",
|
||||||
|
Value: "1920,1080",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -888,11 +1036,26 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 H264/90000"},
|
{
|
||||||
{"fmtp", "96 packetization-mode=1;profile-level-id=64001e;sprop-parameter-sets=Z2QAHqwsaoMg5puAgICB,aO4xshs="},
|
Key: "rtpmap",
|
||||||
{"Media_header", "MEDIAINFO=494D4B48010100000400010000000000000000000000000000000000000000000000000000000000;"},
|
Value: "96 H264/90000",
|
||||||
{"appversion", "1.0"},
|
},
|
||||||
{"control", "rtsp://10.10.1.30:8554/onvif2/audio/trackID=0"},
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 packetization-mode=1;profile-level-id=64001e;sprop-parameter-sets=Z2QAHqwsaoMg5puAgICB,aO4xshs=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "Media_header",
|
||||||
|
Value: "MEDIAINFO=494D4B48010100000400010000000000000000000000000000000000000000000000000000000000;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "appversion",
|
||||||
|
Value: "1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "rtsp://10.10.1.30:8554/onvif2/audio/trackID=0",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -914,8 +1077,14 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "0 PCMU/8000/1"},
|
{
|
||||||
{"control", "rtsp://10.10.1.30:8554/onvif2/audio/trackID=1"},
|
Key: "rtpmap",
|
||||||
|
Value: "0 PCMU/8000/1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "rtsp://10.10.1.30:8554/onvif2/audio/trackID=1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -996,14 +1165,29 @@ var cases = []struct {
|
|||||||
Bandwidth: 104,
|
Bandwidth: 104,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TimeDescriptions: []psdp.TimeDescription{{psdp.Timing{0, 0}, nil}},
|
TimeDescriptions: []psdp.TimeDescription{{Timing: psdp.Timing{StartTime: 0, StopTime: 0}}},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"maxps", "1250"},
|
{
|
||||||
{"control", "rtsp://61.135.88.175:554/refuse/unavailable_media.wmv/"},
|
Key: "maxps",
|
||||||
{"etag", "{CCEE392D-83DF-F4AA-130B-E8A05562CE63}"},
|
Value: "1250",
|
||||||
{"range", "npt=3.000-6.185"},
|
},
|
||||||
{"type", "notstridable"},
|
{
|
||||||
{"recvonly", ""},
|
Key: "control",
|
||||||
|
Value: "rtsp://61.135.88.175:554/refuse/unavailable_media.wmv/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "etag",
|
||||||
|
Value: "{CCEE392D-83DF-F4AA-130B-E8A05562CE63}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "range",
|
||||||
|
Value: "npt=3.000-6.185",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "type",
|
||||||
|
Value: "notstridable",
|
||||||
|
},
|
||||||
|
{Key: "recvonly"},
|
||||||
},
|
},
|
||||||
MediaDescriptions: []*psdp.MediaDescription{
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
{
|
{
|
||||||
@@ -1033,9 +1217,18 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 x-asf-pf/1000"},
|
{
|
||||||
{"control", "video"},
|
Key: "rtpmap",
|
||||||
{"stream", "1"},
|
Value: "96 x-asf-pf/1000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "video",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "stream",
|
||||||
|
Value: "1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1056,9 +1249,18 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Attributes: []psdp.Attribute{
|
Attributes: []psdp.Attribute{
|
||||||
{"rtpmap", "96 x-wms-rtx/1000"},
|
{
|
||||||
{"control", "rtx"},
|
Key: "rtpmap",
|
||||||
{"stream", "65536"},
|
Value: "96 x-wms-rtx/1000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "rtx",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "stream",
|
||||||
|
Value: "65536",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1223,7 +1425,7 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{3034423619, 3042462419}, nil},
|
{Timing: psdp.Timing{StartTime: 3034423619, StopTime: 3042462419}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1292,7 +1494,7 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{0, 0}, nil},
|
{Timing: psdp.Timing{StartTime: 0, StopTime: 0}},
|
||||||
},
|
},
|
||||||
ConnectionInformation: &psdp.ConnectionInformation{
|
ConnectionInformation: &psdp.ConnectionInformation{
|
||||||
NetworkType: "IN",
|
NetworkType: "IN",
|
||||||
@@ -1419,7 +1621,7 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
SessionName: "RTP session",
|
SessionName: "RTP session",
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{0, 0}, nil},
|
{Timing: psdp.Timing{StartTime: 0, StopTime: 0}},
|
||||||
},
|
},
|
||||||
EmailAddress: func() *psdp.EmailAddress {
|
EmailAddress: func() *psdp.EmailAddress {
|
||||||
e := psdp.EmailAddress("NONE")
|
e := psdp.EmailAddress("NONE")
|
||||||
@@ -1489,7 +1691,7 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{3034423619, 3042462419}, nil},
|
{Timing: psdp.Timing{StartTime: 3034423619, StopTime: 3042462419}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1520,7 +1722,7 @@ var cases = []struct {
|
|||||||
return &v
|
return &v
|
||||||
}(),
|
}(),
|
||||||
TimeDescriptions: []psdp.TimeDescription{
|
TimeDescriptions: []psdp.TimeDescription{
|
||||||
{psdp.Timing{3034423619, 3042462419}, nil},
|
{Timing: psdp.Timing{StartTime: 3034423619, StopTime: 3042462419}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1826,6 +2028,98 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"onvif specification example",
|
||||||
|
[]byte("v=0\r\n" +
|
||||||
|
"o= 2890842807 IN IP4 192.168.0.1\r\n" +
|
||||||
|
"s=RTSP Session with audiobackchannel\r\n" +
|
||||||
|
"m=video 0 RTP/AVP 26\r\n" +
|
||||||
|
"a=control:rtsp://192.168.0.1/video\r\n" +
|
||||||
|
"a=recvonly\r\n" +
|
||||||
|
"m=audio 0 RTP/AVP 0\r\n" +
|
||||||
|
"a=control:rtsp://192.168.0.1/audio\r\n" +
|
||||||
|
"a=recvonly\r\n" +
|
||||||
|
"m=audio 0 RTP/AVP 0\r\n" +
|
||||||
|
"a=control:rtsp://192.168.0.1/audioback\r\n" +
|
||||||
|
"a=rtpmap:0 PCMU/8000\r\n" +
|
||||||
|
"a=sendonly\r\n"),
|
||||||
|
[]byte("v=0\r\n" +
|
||||||
|
"o= 0 2890842807 IN IP4 192.168.0.1\r\n" +
|
||||||
|
"s=RTSP Session with audiobackchannel\r\n" +
|
||||||
|
"m=video 0 RTP/AVP 26\r\n" +
|
||||||
|
"a=control:rtsp://192.168.0.1/video\r\n" +
|
||||||
|
"a=recvonly\r\n" +
|
||||||
|
"m=audio 0 RTP/AVP 0\r\n" +
|
||||||
|
"a=control:rtsp://192.168.0.1/audio\r\n" +
|
||||||
|
"a=recvonly\r\n" +
|
||||||
|
"m=audio 0 RTP/AVP 0\r\n" +
|
||||||
|
"a=control:rtsp://192.168.0.1/audioback\r\n" +
|
||||||
|
"a=rtpmap:0 PCMU/8000\r\n" +
|
||||||
|
"a=sendonly\r\n"),
|
||||||
|
SessionDescription{
|
||||||
|
Origin: psdp.Origin{
|
||||||
|
SessionVersion: 2890842807,
|
||||||
|
NetworkType: "IN",
|
||||||
|
AddressType: "IP4",
|
||||||
|
UnicastAddress: "192.168.0.1",
|
||||||
|
},
|
||||||
|
SessionName: "RTSP Session with audiobackchannel",
|
||||||
|
MediaDescriptions: []*psdp.MediaDescription{
|
||||||
|
{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "video",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"26"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "rtsp://192.168.0.1/video",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "recvonly",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "audio",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"0"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "rtsp://192.168.0.1/audio",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "recvonly",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "audio",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"0"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "rtsp://192.168.0.1/audioback",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "0 PCMU/8000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "sendonly",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshal(t *testing.T) {
|
func TestUnmarshal(t *testing.T) {
|
||||||
@@ -1848,3 +2142,31 @@ func TestMarshal(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FuzzUnmarshal(f *testing.F) {
|
||||||
|
f.Add("v=0\r\n" +
|
||||||
|
"t=2873397496 2873404696\r\n" +
|
||||||
|
"t=3034423619 3042462419\r\n" +
|
||||||
|
"r=aa bb 0 90000\r\n")
|
||||||
|
|
||||||
|
f.Add("v=0\r\n" +
|
||||||
|
"t=2873397496 2873404696\r\n" +
|
||||||
|
"t=3034423619 3042462419\r\n" +
|
||||||
|
"r=123 bb 0 90000\r\n")
|
||||||
|
|
||||||
|
f.Add("v=0\r\n" +
|
||||||
|
"m=audio 49170 RTP/AVP 80000\r\n" +
|
||||||
|
"i=Vivamus a posuere nisl\r\n" +
|
||||||
|
"c=IN IP4 203.0.113.1\r\n" +
|
||||||
|
"b=X-YZ:128\r\n" +
|
||||||
|
"k=prompt\r\n" +
|
||||||
|
"a=sendrecv\r\n")
|
||||||
|
|
||||||
|
f.Add("v=0\r\n" +
|
||||||
|
"o = IN \r\n")
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, b string) {
|
||||||
|
desc := SessionDescription{}
|
||||||
|
desc.Unmarshal([]byte(b))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nu=%\xf3\x96\x830")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nt=0 A")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=audio 70000 0 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nc=0 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=0 0 0 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nz=000")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=audio 0 AVP 0\nb=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nb=X-:")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nc=IN 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nz=0 m")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nb=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=audio A 0 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nr=0 0 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nc=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\no=A IN IP4")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=audio 0 0 00")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nz=0 h")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nb=:")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\no=0 IN 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nz=A 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nt=0 0\nr=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nz=0 A")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nt=0 0\nr=0 0 A")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nz=0 d")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\no=X 0 IN IP4")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=audio 0 AVP 0\nc=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nt=A 0")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nt=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\no=")
|
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("v=0\nm=audio 0/ 0 0")
|
@@ -25,6 +25,18 @@ func getSessionID(header base.Header) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterMedias(medias media.Medias, streamMedias map[*media.Media]*serverStreamMedia) media.Medias {
|
||||||
|
copy := make(media.Medias, len(medias))
|
||||||
|
for i, medi := range medias {
|
||||||
|
copy[i] = &media.Media{
|
||||||
|
Type: medi.Type,
|
||||||
|
Formats: medi.Formats,
|
||||||
|
Control: "mediaUUID=" + streamMedias[medi].uuid.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
type readReq struct {
|
type readReq struct {
|
||||||
req *base.Request
|
req *base.Request
|
||||||
res chan error
|
res chan error
|
||||||
@@ -378,17 +390,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediasCopy := make(media.Medias, len(stream.medias))
|
|
||||||
for i, medi := range stream.medias {
|
|
||||||
mediasCopy[i] = &media.Media{
|
|
||||||
Type: medi.Type,
|
|
||||||
Formats: medi.Formats,
|
|
||||||
Control: "mediaUUID=" + stream.streamMedias[medi].uuid.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if stream != nil {
|
if stream != nil {
|
||||||
byts, _ := mediasCopy.Marshal(multicast).Marshal()
|
byts, _ := filterMedias(stream.medias, stream.streamMedias).Marshal(multicast).Marshal()
|
||||||
res.Body = byts
|
res.Body = byts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user