Merge branch 'main' into v4

This commit is contained in:
aler9
2023-08-25 19:06:02 +02:00
22 changed files with 240 additions and 236 deletions

View File

@@ -18,10 +18,10 @@ type AV1 struct {
Tier *int Tier *int
} }
func (f *AV1) unmarshal(payloadType uint8, _ string, _ string, _ string, fmtp map[string]string) error { func (f *AV1) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "level-idx": case "level-idx":
n, err := strconv.ParseUint(val, 10, 31) n, err := strconv.ParseUint(val, 10, 31)

View File

@@ -16,10 +16,19 @@ func getCodecAndClock(rtpMap string) (string, string) {
return strings.ToLower(parts2[0]), parts2[1] return strings.ToLower(parts2[0]), parts2[1]
} }
type unmarshalContext struct {
mediaType string
payloadType uint8
clock string
codec string
rtpMap string
fmtp map[string]string
}
// Format is a media format. // Format is a media format.
// It defines the payload type of RTP packets and how to encode/decode them. // It defines the payload type of RTP packets and how to encode/decode them.
type Format interface { type Format interface {
unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error unmarshal(ctx *unmarshalContext) error
// Codec returns the codec name. // Codec returns the codec name.
Codec() string Codec() string
@@ -46,81 +55,86 @@ func Unmarshal(mediaType string, payloadType uint8, rtpMap string, fmtp map[stri
format := func() Format { format := func() Format {
switch { switch {
case mediaType == "video": // video
switch {
case payloadType == 26:
return &MJPEG{}
case payloadType == 32: case payloadType == 26:
return &MPEG1Video{} return &MJPEG{}
case payloadType == 33: case payloadType == 32:
return &MPEGTS{} return &MPEG1Video{}
case codec == "mp4v-es" && clock == "90000": case payloadType == 33:
return &MPEG4Video{} return &MPEGTS{}
case codec == "h264" && clock == "90000": case codec == "mp4v-es" && clock == "90000":
return &H264{} return &MPEG4Video{}
case codec == "h265" && clock == "90000": case codec == "h264" && clock == "90000":
return &H265{} return &H264{}
case codec == "vp8" && clock == "90000": case codec == "h265" && clock == "90000":
return &VP8{} return &H265{}
case codec == "vp9" && clock == "90000": case codec == "vp8" && clock == "90000":
return &VP9{} return &VP8{}
case codec == "av1" && clock == "90000": case codec == "vp9" && clock == "90000":
return &AV1{} return &VP9{}
}
case mediaType == "audio": case codec == "av1" && clock == "90000":
switch { return &AV1{}
case payloadType == 0, payloadType == 8:
return &G711{}
case payloadType == 9: // audio
return &G722{}
case (codec == "g726-16" || case payloadType == 0, payloadType == 8:
codec == "g726-24" || return &G711{}
codec == "g726-32" ||
codec == "g726-40" ||
codec == "aal2-g726-16" ||
codec == "aal2-g726-24" ||
codec == "aal2-g726-32" ||
codec == "aal2-g726-40") && clock == "8000":
return &G726{}
case payloadType == 14: case payloadType == 9:
return &MPEG1Audio{} return &G722{}
case codec == "l8", codec == "l16", codec == "l24": case (codec == "g726-16" ||
return &LPCM{} codec == "g726-24" ||
codec == "g726-32" ||
codec == "g726-40" ||
codec == "aal2-g726-16" ||
codec == "aal2-g726-24" ||
codec == "aal2-g726-32" ||
codec == "aal2-g726-40") && clock == "8000":
return &G726{}
case codec == "mpeg4-generic": case payloadType == 14:
return &MPEG4AudioGeneric{} return &MPEG1Audio{}
case codec == "mp4a-latm": case codec == "l8", codec == "l16", codec == "l24":
return &MPEG4AudioLATM{} return &LPCM{}
case codec == "speex": case codec == "mpeg4-generic":
return &Speex{} return &MPEG4AudioGeneric{}
case codec == "vorbis": case codec == "mp4a-latm":
return &Vorbis{} return &MPEG4AudioLATM{}
case codec == "opus": case codec == "speex":
return &Opus{} return &Speex{}
}
case codec == "vorbis":
return &Vorbis{}
case codec == "opus":
return &Opus{}
} }
return &Generic{} return &Generic{}
}() }()
err := format.unmarshal(payloadType, clock, codec, rtpMap, fmtp) err := format.unmarshal(&unmarshalContext{
mediaType: mediaType,
payloadType: payloadType,
clock: clock,
codec: codec,
rtpMap: rtpMap,
fmtp: fmtp,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -890,19 +890,6 @@ var casesFormat = []struct {
"custom", "custom",
nil, nil,
}, },
{
"application invalid rtpmap 2",
"application",
98,
"custom/aaa",
nil,
&Generic{
PayloadTyp: 98,
RTPMa: "custom/aaa",
},
"custom/aaa",
nil,
},
} }
func TestUnmarshal(t *testing.T) { func TestUnmarshal(t *testing.T) {
@@ -925,109 +912,116 @@ func TestMarshal(t *testing.T) {
} }
} }
func TestUnmarshalMPEG4AudioGenericErrors(t *testing.T) { func TestUnmarshalErrors(t *testing.T) {
_, err := Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ t.Run("invalid video", func(t *testing.T) {
"streamtype": "10", _, err := Unmarshal("video", 96, "", map[string]string{})
require.Error(t, err)
}) })
require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ t.Run("mpeg-4 audio generic", func(t *testing.T) {
"mode": "asd", _, err := Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
}) "streamtype": "10",
require.Error(t, err) })
require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"profile-level-id": "aaa", "mode": "asd",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"config": "aaa", "profile-level-id": "aaa",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"config": "0ab2", "config": "aaa",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"sizelength": "aaa", "config": "0ab2",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"indexlength": "aaa", "sizelength": "aaa",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"indexdeltalength": "aaa", "indexlength": "aaa",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"profile-level-id": "1", "indexdeltalength": "aaa",
"sizelength": "13", })
"indexlength": "3", require.Error(t, err)
"indexdeltalength": "3",
})
require.Error(t, err)
_, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"profile-level-id": "1", "profile-level-id": "1",
"config": "1190", "sizelength": "13",
"indexlength": "3", "indexlength": "3",
"indexdeltalength": "3", "indexdeltalength": "3",
}) })
require.Error(t, err) require.Error(t, err)
}
func TestUnmarshalMPEG4AudioLATMErrors(t *testing.T) { _, err = Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
_, err := Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{ "profile-level-id": "1",
"profile-level-id": "aaa", "config": "1190",
"indexlength": "3",
"indexdeltalength": "3",
})
require.Error(t, err)
}) })
require.Error(t, err)
_, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{ t.Run("mpeg-4 audio latm", func(t *testing.T) {
"bitrate": "aaa", _, err := Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{
}) "profile-level-id": "aaa",
require.Error(t, err) })
require.Error(t, err)
_, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{
"cpresent": "0", "bitrate": "aaa",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{
"config": "aaa", "cpresent": "0",
}) })
require.Error(t, err) require.Error(t, err)
_, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{ _, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{
"profile-level-id": "15", "config": "aaa",
"object": "2", })
"cpresent": "0", require.Error(t, err)
"sbr-enabled": "1",
})
require.Error(t, err)
}
func TestUnmarshalAV1Errors(t *testing.T) { _, err = Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{
_, err := Unmarshal("video", 96, "AV1/90000", map[string]string{ "profile-level-id": "15",
"level-idx": "aaa", "object": "2",
"cpresent": "0",
"sbr-enabled": "1",
})
require.Error(t, err)
}) })
require.Error(t, err)
_, err = Unmarshal("video", 96, "AV1/90000", map[string]string{ t.Run("av1", func(t *testing.T) {
"profile": "aaa", _, err := Unmarshal("video", 96, "AV1/90000", map[string]string{
}) "level-idx": "aaa",
require.Error(t, err) })
require.Error(t, err)
_, err = Unmarshal("video", 96, "AV1/90000", map[string]string{ _, err = Unmarshal("video", 96, "AV1/90000", map[string]string{
"tier": "aaa", "profile": "aaa",
})
require.Error(t, err)
_, err = Unmarshal("video", 96, "AV1/90000", map[string]string{
"tier": "aaa",
})
require.Error(t, err)
}) })
require.Error(t, err)
} }
func FuzzUnmarshalH264(f *testing.F) { func FuzzUnmarshalH264(f *testing.F) {

View File

@@ -13,8 +13,8 @@ type G711 struct {
MULaw bool MULaw bool
} }
func (f *G711) unmarshal(payloadType uint8, _ string, _ string, _ string, _ map[string]string) error { func (f *G711) unmarshal(ctx *unmarshalContext) error {
f.MULaw = (payloadType == 0) f.MULaw = (ctx.payloadType == 0)
return nil return nil
} }

View File

@@ -10,7 +10,7 @@ import (
// Specification: https://datatracker.ietf.org/doc/html/rfc3551 // Specification: https://datatracker.ietf.org/doc/html/rfc3551
type G722 struct{} type G722 struct{}
func (f *G722) unmarshal(_ uint8, _ string, _ string, _ string, _ map[string]string) error { func (f *G722) unmarshal(_ *unmarshalContext) error {
return nil return nil
} }

View File

@@ -15,19 +15,19 @@ type G726 struct {
BigEndian bool BigEndian bool
} }
func (f *G726) unmarshal(payloadType uint8, _ string, codec string, _ string, _ map[string]string) error { func (f *G726) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
if strings.HasPrefix(codec, "aal2-") { if strings.HasPrefix(ctx.codec, "aal2-") {
f.BigEndian = true f.BigEndian = true
} }
switch { switch {
case strings.HasSuffix(codec, "-16"): case strings.HasSuffix(ctx.codec, "-16"):
f.BitRate = 16 f.BitRate = 16
case strings.HasSuffix(codec, "-24"): case strings.HasSuffix(ctx.codec, "-24"):
f.BitRate = 24 f.BitRate = 24
case strings.HasSuffix(codec, "-32"): case strings.HasSuffix(ctx.codec, "-32"):
f.BitRate = 32 f.BitRate = 32
default: default:
f.BitRate = 40 f.BitRate = 40

View File

@@ -8,7 +8,7 @@ import (
"github.com/pion/rtp" "github.com/pion/rtp"
) )
func findClockRate(payloadType uint8, rtpMap string) (int, error) { func findClockRate(payloadType uint8, rtpMap string, isApplication bool) (int, error) {
// get clock rate from payload type // get clock rate from payload type
// https://en.wikipedia.org/wiki/RTP_payload_formats // https://en.wikipedia.org/wiki/RTP_payload_formats
switch payloadType { switch payloadType {
@@ -34,21 +34,23 @@ func findClockRate(payloadType uint8, rtpMap string) (int, error) {
// get clock rate from rtpmap // get clock rate from rtpmap
// https://tools.ietf.org/html/rfc4566 // https://tools.ietf.org/html/rfc4566
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>] // a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
if rtpMap == "" { if rtpMap != "" {
return 0, fmt.Errorf("attribute 'rtpmap' not found") if tmp := strings.Split(rtpMap, "/"); len(tmp) >= 2 {
v, err := strconv.ParseUint(tmp[1], 10, 31)
if err != nil {
return 0, err
}
return int(v), nil
}
} }
tmp := strings.Split(rtpMap, "/") // application format without clock rate.
if len(tmp) != 2 && len(tmp) != 3 { // do not throw an error, but return zero, that disables RTCP sender and receiver reports.
return 0, fmt.Errorf("invalid rtpmap (%v)", rtpMap) if isApplication || rtpMap != "" {
return 0, nil
} }
v, err := strconv.ParseUint(tmp[1], 10, 31) return 0, fmt.Errorf("clock rate not found")
if err != nil {
return 0, err
}
return int(v), nil
} }
// Generic is a generic RTP format. // Generic is a generic RTP format.
@@ -63,16 +65,19 @@ type Generic struct {
// Init computes the clock rate of the format. It is mandatory to call it. // Init computes the clock rate of the format. It is mandatory to call it.
func (f *Generic) Init() error { func (f *Generic) Init() error {
f.ClockRat, _ = findClockRate(f.PayloadTyp, f.RTPMa) var err error
return nil f.ClockRat, err = findClockRate(f.PayloadTyp, f.RTPMa, true)
return err
} }
func (f *Generic) unmarshal(payloadType uint8, _ string, _ string, rtpmap string, fmtp map[string]string) error { func (f *Generic) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
f.RTPMa = rtpmap f.RTPMa = ctx.rtpMap
f.FMT = fmtp f.FMT = ctx.fmtp
return f.Init() var err error
f.ClockRat, err = findClockRate(f.PayloadTyp, f.RTPMa, ctx.mediaType == "application")
return err
} }
// Codec implements Format. // Codec implements Format.

View File

@@ -25,10 +25,10 @@ type H264 struct {
mutex sync.RWMutex mutex sync.RWMutex
} }
func (f *H264) unmarshal(payloadType uint8, _ string, _ string, _ string, fmtp map[string]string) error { func (f *H264) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "sprop-parameter-sets": case "sprop-parameter-sets":
tmp := strings.Split(val, ",") tmp := strings.Split(val, ",")

View File

@@ -24,36 +24,36 @@ type H265 struct {
mutex sync.RWMutex mutex sync.RWMutex
} }
func (f *H265) unmarshal(payloadType uint8, _ string, _ string, _ string, fmtp map[string]string) error { func (f *H265) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "sprop-vps": case "sprop-vps":
var err error var err error
f.VPS, err = base64.StdEncoding.DecodeString(val) f.VPS, err = base64.StdEncoding.DecodeString(val)
if err != nil { if err != nil {
return fmt.Errorf("invalid sprop-vps (%v)", fmtp) return fmt.Errorf("invalid sprop-vps (%v)", ctx.fmtp)
} }
case "sprop-sps": case "sprop-sps":
var err error var err error
f.SPS, err = base64.StdEncoding.DecodeString(val) f.SPS, err = base64.StdEncoding.DecodeString(val)
if err != nil { if err != nil {
return fmt.Errorf("invalid sprop-sps (%v)", fmtp) return fmt.Errorf("invalid sprop-sps (%v)", ctx.fmtp)
} }
case "sprop-pps": case "sprop-pps":
var err error var err error
f.PPS, err = base64.StdEncoding.DecodeString(val) f.PPS, err = base64.StdEncoding.DecodeString(val)
if err != nil { if err != nil {
return fmt.Errorf("invalid sprop-pps (%v)", fmtp) return fmt.Errorf("invalid sprop-pps (%v)", ctx.fmtp)
} }
case "sprop-max-don-diff": case "sprop-max-don-diff":
tmp, err := strconv.ParseUint(val, 10, 31) tmp, err := strconv.ParseUint(val, 10, 31)
if err != nil { if err != nil {
return fmt.Errorf("invalid sprop-max-don-diff (%v)", fmtp) return fmt.Errorf("invalid sprop-max-don-diff (%v)", ctx.fmtp)
} }
f.MaxDONDiff = int(tmp) f.MaxDONDiff = int(tmp)
} }

View File

@@ -18,10 +18,10 @@ type LPCM struct {
ChannelCount int ChannelCount int
} }
func (f *LPCM) unmarshal(payloadType uint8, clock string, codec string, _ string, _ map[string]string) error { func (f *LPCM) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
switch codec { switch ctx.codec {
case "l8": case "l8":
f.BitDepth = 8 f.BitDepth = 8
@@ -32,7 +32,7 @@ func (f *LPCM) unmarshal(payloadType uint8, clock string, codec string, _ string
f.BitDepth = 24 f.BitDepth = 24
} }
tmp := strings.SplitN(clock, "/", 2) tmp := strings.SplitN(ctx.clock, "/", 2)
tmp1, err := strconv.ParseUint(tmp[0], 10, 31) tmp1, err := strconv.ParseUint(tmp[0], 10, 31)
if err != nil { if err != nil {

View File

@@ -10,7 +10,7 @@ import (
// Specification: https://datatracker.ietf.org/doc/html/rfc2435 // Specification: https://datatracker.ietf.org/doc/html/rfc2435
type MJPEG struct{} type MJPEG struct{}
func (f *MJPEG) unmarshal(_ uint8, _ string, _ string, _ string, _ map[string]string) error { func (f *MJPEG) unmarshal(_ *unmarshalContext) error {
return nil return nil
} }

View File

@@ -10,7 +10,7 @@ import (
// Specification: https://datatracker.ietf.org/doc/html/rfc2250 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
type MPEG1Audio struct{} type MPEG1Audio struct{}
func (f *MPEG1Audio) unmarshal(_ uint8, _ string, _ string, _ string, _ map[string]string) error { func (f *MPEG1Audio) unmarshal(_ *unmarshalContext) error {
return nil return nil
} }

View File

@@ -8,7 +8,7 @@ import (
// Specification: https://datatracker.ietf.org/doc/html/rfc2250 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
type MPEG1Video struct{} type MPEG1Video struct{}
func (f *MPEG1Video) unmarshal(_ uint8, _ string, _ string, _ string, _ map[string]string) error { func (f *MPEG1Video) unmarshal(_ *unmarshalContext) error {
return nil return nil
} }

View File

@@ -26,13 +26,10 @@ type MPEG4AudioGeneric struct {
IndexDeltaLength int IndexDeltaLength int
} }
func (f *MPEG4AudioGeneric) unmarshal( func (f *MPEG4AudioGeneric) unmarshal(ctx *unmarshalContext) error {
payloadType uint8, _ string, _ string, f.PayloadTyp = ctx.payloadType
_ string, fmtp map[string]string,
) error {
f.PayloadTyp = payloadType
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "streamtype": case "streamtype":
if val != "5" { // AudioStream in ISO 14496-1 if val != "5" { // AudioStream in ISO 14496-1

View File

@@ -22,17 +22,14 @@ type MPEG4AudioLATM struct {
SBREnabled *bool SBREnabled *bool
} }
func (f *MPEG4AudioLATM) unmarshal( func (f *MPEG4AudioLATM) unmarshal(ctx *unmarshalContext) error {
payloadType uint8, _ string, _ string, f.PayloadTyp = ctx.payloadType
_ string, fmtp map[string]string,
) error {
f.PayloadTyp = payloadType
// default value set by specification // default value set by specification
f.ProfileLevelID = 30 f.ProfileLevelID = 30
f.CPresent = true f.CPresent = true
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "profile-level-id": case "profile-level-id":
tmp, err := strconv.ParseUint(val, 10, 31) tmp, err := strconv.ParseUint(val, 10, 31)

View File

@@ -22,14 +22,11 @@ type MPEG4VideoES struct {
Config []byte Config []byte
} }
func (f *MPEG4VideoES) unmarshal( func (f *MPEG4VideoES) unmarshal(ctx *unmarshalContext) error {
payloadType uint8, _ string, _ string, f.PayloadTyp = ctx.payloadType
_ string, fmtp map[string]string, f.ProfileLevelID = 1 // default value imposed by specification
) error {
f.PayloadTyp = payloadType
f.ProfileLevelID = 1 // default value defined by specification
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "profile-level-id": case "profile-level-id":
tmp, err := strconv.ParseUint(val, 10, 31) tmp, err := strconv.ParseUint(val, 10, 31)

View File

@@ -8,7 +8,7 @@ import (
// Specification: https://datatracker.ietf.org/doc/html/rfc2250 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
type MPEGTS struct{} type MPEGTS struct{}
func (f *MPEGTS) unmarshal(_ uint8, _ string, _ string, _ string, _ map[string]string) error { func (f *MPEGTS) unmarshal(_ *unmarshalContext) error {
return nil return nil
} }

View File

@@ -17,12 +17,12 @@ type Opus struct {
IsStereo bool IsStereo bool
} }
func (f *Opus) unmarshal(payloadType uint8, clock string, _ string, _ string, fmtp map[string]string) error { func (f *Opus) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
tmp := strings.SplitN(clock, "/", 2) tmp := strings.SplitN(ctx.clock, "/", 2)
if len(tmp) != 2 { if len(tmp) != 2 {
return fmt.Errorf("invalid clock (%v)", clock) return fmt.Errorf("invalid clock (%v)", ctx.clock)
} }
sampleRate, err := strconv.ParseUint(tmp[0], 10, 31) sampleRate, err := strconv.ParseUint(tmp[0], 10, 31)
@@ -35,7 +35,7 @@ func (f *Opus) unmarshal(payloadType uint8, clock string, _ string, _ string, fm
return fmt.Errorf("invalid channel count: %d", channelCount) return fmt.Errorf("invalid channel count: %d", channelCount)
} }
for key, val := range fmtp { for key, val := range ctx.fmtp {
if key == "sprop-stereo" { if key == "sprop-stereo" {
f.IsStereo = (val == "1") f.IsStereo = (val == "1")
} }

View File

@@ -15,16 +15,16 @@ type Speex struct {
VBR *bool VBR *bool
} }
func (f *Speex) unmarshal(payloadType uint8, clock string, _ string, _ string, fmtp map[string]string) error { func (f *Speex) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
sampleRate, err := strconv.ParseUint(clock, 10, 31) sampleRate, err := strconv.ParseUint(ctx.clock, 10, 31)
if err != nil { if err != nil {
return err return err
} }
f.SampleRate = int(sampleRate) f.SampleRate = int(sampleRate)
for key, val := range fmtp { for key, val := range ctx.fmtp {
if key == "vbr" { if key == "vbr" {
if val != "on" && val != "off" { if val != "on" && val != "off" {
return fmt.Errorf("invalid vbr value: %v", val) return fmt.Errorf("invalid vbr value: %v", val)

View File

@@ -18,12 +18,12 @@ type Vorbis struct {
Configuration []byte Configuration []byte
} }
func (f *Vorbis) unmarshal(payloadType uint8, clock string, _ string, _ string, fmtp map[string]string) error { func (f *Vorbis) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
tmp := strings.SplitN(clock, "/", 2) tmp := strings.SplitN(ctx.clock, "/", 2)
if len(tmp) != 2 { if len(tmp) != 2 {
return fmt.Errorf("invalid clock (%v)", clock) return fmt.Errorf("invalid clock (%v)", ctx.clock)
} }
sampleRate, err := strconv.ParseUint(tmp[0], 10, 31) sampleRate, err := strconv.ParseUint(tmp[0], 10, 31)
@@ -38,7 +38,7 @@ func (f *Vorbis) unmarshal(payloadType uint8, clock string, _ string, _ string,
} }
f.ChannelCount = int(channelCount) f.ChannelCount = int(channelCount)
for key, val := range fmtp { for key, val := range ctx.fmtp {
if key == "configuration" { if key == "configuration" {
conf, err := base64.StdEncoding.DecodeString(val) conf, err := base64.StdEncoding.DecodeString(val)
if err != nil { if err != nil {

View File

@@ -17,10 +17,10 @@ type VP8 struct {
MaxFS *int MaxFS *int
} }
func (f *VP8) unmarshal(payloadType uint8, _ string, _ string, _ string, fmtp map[string]string) error { func (f *VP8) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "max-fr": case "max-fr":
n, err := strconv.ParseUint(val, 10, 31) n, err := strconv.ParseUint(val, 10, 31)

View File

@@ -18,10 +18,10 @@ type VP9 struct {
ProfileID *int ProfileID *int
} }
func (f *VP9) unmarshal(payloadType uint8, _ string, _ string, _ string, fmtp map[string]string) error { func (f *VP9) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = payloadType f.PayloadTyp = ctx.payloadType
for key, val := range fmtp { for key, val := range ctx.fmtp {
switch key { switch key {
case "max-fr": case "max-fr":
n, err := strconv.ParseUint(val, 10, 31) n, err := strconv.ParseUint(val, 10, 31)