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
}
func (f *AV1) unmarshal(payloadType uint8, _ string, _ string, _ string, fmtp map[string]string) error {
f.PayloadTyp = payloadType
func (f *AV1) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = ctx.payloadType
for key, val := range fmtp {
for key, val := range ctx.fmtp {
switch key {
case "level-idx":
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]
}
type unmarshalContext struct {
mediaType string
payloadType uint8
clock string
codec string
rtpMap string
fmtp map[string]string
}
// Format is a media format.
// It defines the payload type of RTP packets and how to encode/decode them.
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() string
@@ -46,8 +55,8 @@ func Unmarshal(mediaType string, payloadType uint8, rtpMap string, fmtp map[stri
format := func() Format {
switch {
case mediaType == "video":
switch {
// video
case payloadType == 26:
return &MJPEG{}
@@ -74,10 +83,9 @@ func Unmarshal(mediaType string, payloadType uint8, rtpMap string, fmtp map[stri
case codec == "av1" && clock == "90000":
return &AV1{}
}
case mediaType == "audio":
switch {
// audio
case payloadType == 0, payloadType == 8:
return &G711{}
@@ -115,12 +123,18 @@ func Unmarshal(mediaType string, payloadType uint8, rtpMap string, fmtp map[stri
case codec == "opus":
return &Opus{}
}
}
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 {
return nil, err
}

View File

@@ -890,19 +890,6 @@ var casesFormat = []struct {
"custom",
nil,
},
{
"application invalid rtpmap 2",
"application",
98,
"custom/aaa",
nil,
&Generic{
PayloadTyp: 98,
RTPMa: "custom/aaa",
},
"custom/aaa",
nil,
},
}
func TestUnmarshal(t *testing.T) {
@@ -925,7 +912,13 @@ func TestMarshal(t *testing.T) {
}
}
func TestUnmarshalMPEG4AudioGenericErrors(t *testing.T) {
func TestUnmarshalErrors(t *testing.T) {
t.Run("invalid video", func(t *testing.T) {
_, err := Unmarshal("video", 96, "", map[string]string{})
require.Error(t, err)
})
t.Run("mpeg-4 audio generic", func(t *testing.T) {
_, err := Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{
"streamtype": "10",
})
@@ -981,9 +974,9 @@ func TestUnmarshalMPEG4AudioGenericErrors(t *testing.T) {
"indexdeltalength": "3",
})
require.Error(t, err)
}
})
func TestUnmarshalMPEG4AudioLATMErrors(t *testing.T) {
t.Run("mpeg-4 audio latm", func(t *testing.T) {
_, err := Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{
"profile-level-id": "aaa",
})
@@ -1011,9 +1004,9 @@ func TestUnmarshalMPEG4AudioLATMErrors(t *testing.T) {
"sbr-enabled": "1",
})
require.Error(t, err)
}
})
func TestUnmarshalAV1Errors(t *testing.T) {
t.Run("av1", func(t *testing.T) {
_, err := Unmarshal("video", 96, "AV1/90000", map[string]string{
"level-idx": "aaa",
})
@@ -1028,6 +1021,7 @@ func TestUnmarshalAV1Errors(t *testing.T) {
"tier": "aaa",
})
require.Error(t, err)
})
}
func FuzzUnmarshalH264(f *testing.F) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,12 +17,12 @@ type Opus struct {
IsStereo bool
}
func (f *Opus) unmarshal(payloadType uint8, clock string, _ string, _ string, fmtp map[string]string) error {
f.PayloadTyp = payloadType
func (f *Opus) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = ctx.payloadType
tmp := strings.SplitN(clock, "/", 2)
tmp := strings.SplitN(ctx.clock, "/", 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)
@@ -35,7 +35,7 @@ func (f *Opus) unmarshal(payloadType uint8, clock string, _ string, _ string, fm
return fmt.Errorf("invalid channel count: %d", channelCount)
}
for key, val := range fmtp {
for key, val := range ctx.fmtp {
if key == "sprop-stereo" {
f.IsStereo = (val == "1")
}

View File

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

View File

@@ -18,12 +18,12 @@ type Vorbis struct {
Configuration []byte
}
func (f *Vorbis) unmarshal(payloadType uint8, clock string, _ string, _ string, fmtp map[string]string) error {
f.PayloadTyp = payloadType
func (f *Vorbis) unmarshal(ctx *unmarshalContext) error {
f.PayloadTyp = ctx.payloadType
tmp := strings.SplitN(clock, "/", 2)
tmp := strings.SplitN(ctx.clock, "/", 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)
@@ -38,7 +38,7 @@ func (f *Vorbis) unmarshal(payloadType uint8, clock string, _ string, _ string,
}
f.ChannelCount = int(channelCount)
for key, val := range fmtp {
for key, val := range ctx.fmtp {
if key == "configuration" {
conf, err := base64.StdEncoding.DecodeString(val)
if err != nil {

View File

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

View File

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