Repository: bluenviron/gortsplib
Pull request #253 (2023-04-15 11:34:39)

replace Format.Marshal() with RTPMap() and FMTP()

codecov-commenter: ## [Codecov](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron) Report
> Merging [#253](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron) (8fd32cd) into [main](https://codecov.io/gh/bluenviron/gortsplib/commit/fee147222e2d9971de4d49c38fd25fbaab7a00e7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron) (fee1472) will **increase** coverage by `0.08%`.
> The diff coverage is `97.76%`.

:mega: This organization is not using Codecov’s [GitHub App Integration](https://github.com/apps/codecov). We recommend you install it so Codecov can continue to function properly for your repositories. [Learn more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron)

```diff
@@            Coverage Diff             @@
##             main     #253      +/-   ##
==========================================
+ Coverage   86.20%   86.29%   +0.08%     
==========================================
  Files          86       86              
  Lines        8963     9001      +38     
==========================================
+ Hits         7727     7767      +40     
+ Misses        958      957       -1     
+ Partials      278      277       -1     
```


| [Impacted Files](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron) | Coverage Δ | |
|---|---|---|
| [pkg/formats/format.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvZm9ybWF0Lmdv) | `100.00% <ø> (ø)` | |
| [pkg/formats/mpeg4\_audio\_generic.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvbXBlZzRfYXVkaW9fZ2VuZXJpYy5nbw==) | `92.17% <72.72%> (+0.13%)` | :arrow_up: |
| [pkg/formats/av1.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvYXYxLmdv) | `100.00% <100.00%> (ø)` | |
| [pkg/formats/g711.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvZzcxMS5nbw==) | `100.00% <100.00%> (ø)` | |
| [pkg/formats/g722.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvZzcyMi5nbw==) | `100.00% <100.00%> (ø)` | |
| [pkg/formats/generic.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvZ2VuZXJpYy5nbw==) | `75.51% <100.00%> (+1.04%)` | :arrow_up: |
| [pkg/formats/h264.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvaDI2NC5nbw==) | `72.13% <100.00%> (+0.46%)` | :arrow_up: |
| [pkg/formats/h265.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvaDI2NS5nbw==) | `96.29% <100.00%> (+3.89%)` | :arrow_up: |
| [pkg/formats/lpcm.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvbHBjbS5nbw==) | `100.00% <100.00%> (ø)` | |
| [pkg/formats/mjpeg.go](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron#diff-cGtnL2Zvcm1hdHMvbWpwZWcuZ28=) | `100.00% <100.00%> (ø)` | |
| ... and [10 more](https://codecov.io/gh/bluenviron/gortsplib/pull/253?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron) | |

... and [1 file with indirect coverage changes](https://codecov.io/gh/bluenviron/gortsplib/pull/253/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron)

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bluenviron)


Patch:
diff --git a/pkg/formats/av1.go b/pkg/formats/av1.go
index 1bad66bf..4d21135c 100644
--- a/pkg/formats/av1.go
+++ b/pkg/formats/av1.go
@@ -16,21 +16,6 @@ type AV1 struct {
 	Tier       *int
 }
 
-// String implements Format.
-func (f *AV1) String() string {
-	return "AV1"
-}
-
-// ClockRate implements Format.
-func (f *AV1) ClockRate() int {
-	return 90000
-}
-
-// PayloadType implements Format.
-func (f *AV1) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *AV1) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -68,8 +53,28 @@ func (f *AV1) unmarshal(payloadType uint8, clock string, codec string, rtpmap st
 	return nil
 }
 
-// Marshal implements Format.
-func (f *AV1) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *AV1) String() string {
+	return "AV1"
+}
+
+// ClockRate implements Format.
+func (f *AV1) ClockRate() int {
+	return 90000
+}
+
+// PayloadType implements Format.
+func (f *AV1) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *AV1) RTPMap() string {
+	return "AV1/90000"
+}
+
+// FMTP implements Format.
+func (f *AV1) FMTP() map[string]string {
 	fmtp := make(map[string]string)
 
 	if f.LevelIdx != nil {
@@ -82,7 +87,7 @@ func (f *AV1) Marshal() (string, map[string]string) {
 		fmtp["tier"] = strconv.FormatInt(int64(*f.Tier), 10)
 	}
 
-	return "AV1/90000", fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/format.go b/pkg/formats/format.go
index 2d6853c5..de079ec5 100644
--- a/pkg/formats/format.go
+++ b/pkg/formats/format.go
@@ -19,6 +19,8 @@ func getCodecAndClock(rtpMap string) (string, string) {
 // Format is a RTP format of a media.
 // It defines a codec and a payload type used to transmit the media.
 type Format interface {
+	unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error
+
 	// String returns a description of the format.
 	String() string
 
@@ -28,10 +30,11 @@ type Format interface {
 	// PayloadType returns the payload type.
 	PayloadType() uint8
 
-	unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error
+	// RTPMap returns the rtpmap attribute.
+	RTPMap() string
 
-	// Marshal encodes the format in SDP format.
-	Marshal() (string, map[string]string)
+	// FMTP returns the fmtp attribute.
+	FMTP() map[string]string
 
 	// PTSEqualsDTS checks whether PTS is equal to DTS in RTP packets.
 	PTSEqualsDTS(*rtp.Packet) bool
diff --git a/pkg/formats/g711.go b/pkg/formats/g711.go
index c70c3985..da8427d4 100644
--- a/pkg/formats/g711.go
+++ b/pkg/formats/g711.go
@@ -13,6 +13,11 @@ type G711 struct {
 	MULaw bool
 }
 
+func (f *G711) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
+	f.MULaw = (payloadType == 0)
+	return nil
+}
+
 // String implements Format.
 func (f *G711) String() string {
 	return "G711"
@@ -31,17 +36,17 @@ func (f *G711) PayloadType() uint8 {
 	return 8
 }
 
-func (f *G711) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
-	f.MULaw = (payloadType == 0)
-	return nil
-}
-
-// Marshal implements Format.
-func (f *G711) Marshal() (string, map[string]string) {
+// RTPMap implements Format.
+func (f *G711) RTPMap() string {
 	if f.MULaw {
-		return "PCMU/8000", nil
+		return "PCMU/8000"
 	}
-	return "PCMA/8000", nil
+	return "PCMA/8000"
+}
+
+// FMTP implements Format.
+func (f *G711) FMTP() map[string]string {
+	return nil
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/g722.go b/pkg/formats/g722.go
index 76d175db..43ae702d 100644
--- a/pkg/formats/g722.go
+++ b/pkg/formats/g722.go
@@ -10,6 +10,10 @@ import (
 // Specification: https://datatracker.ietf.org/doc/html/rfc3551
 type G722 struct{}
 
+func (f *G722) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
+	return nil
+}
+
 // String implements Format.
 func (f *G722) String() string {
 	return "G722"
@@ -25,13 +29,14 @@ func (f *G722) PayloadType() uint8 {
 	return 9
 }
 
-func (f *G722) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
-	return nil
+// RTPMap implements Format.
+func (f *G722) RTPMap() string {
+	return "G722/8000"
 }
 
-// Marshal implements Format.
-func (f *G722) Marshal() (string, map[string]string) {
-	return "G722/8000", nil
+// FMTP implements Format.
+func (f *G722) FMTP() map[string]string {
+	return nil
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/generic.go b/pkg/formats/generic.go
index e7f913ee..66b55e97 100644
--- a/pkg/formats/generic.go
+++ b/pkg/formats/generic.go
@@ -54,8 +54,8 @@ func findClockRate(payloadType uint8, rtpMap string) (int, error) {
 // Generic is a generic RTP format.
 type Generic struct {
 	PayloadTyp uint8
-	RTPMap     string
-	FMTP       map[string]string
+	RTPMa      string
+	FMT        map[string]string
 
 	// clock rate of the format. Filled automatically.
 	ClockRat int
@@ -63,10 +63,21 @@ type Generic struct {
 
 // Init computes the clock rate of the format. It it mandatory to call it.
 func (f *Generic) Init() error {
-	f.ClockRat, _ = findClockRate(f.PayloadTyp, f.RTPMap)
+	f.ClockRat, _ = findClockRate(f.PayloadTyp, f.RTPMa)
 	return nil
 }
 
+func (f *Generic) unmarshal(
+	payloadType uint8, clock string, codec string,
+	rtpmap string, fmtp map[string]string,
+) error {
+	f.PayloadTyp = payloadType
+	f.RTPMa = rtpmap
+	f.FMT = fmtp
+
+	return f.Init()
+}
+
 // String returns a description of the format.
 func (f *Generic) String() string {
 	return "Generic"
@@ -82,20 +93,14 @@ func (f *Generic) PayloadType() uint8 {
 	return f.PayloadTyp
 }
 
-func (f *Generic) unmarshal(
-	payloadType uint8, clock string, codec string,
-	rtpmap string, fmtp map[string]string,
-) error {
-	f.PayloadTyp = payloadType
-	f.RTPMap = rtpmap
-	f.FMTP = fmtp
-
-	return f.Init()
+// RTPMap implements Format.
+func (f *Generic) RTPMap() string {
+	return f.RTPMa
 }
 
-// Marshal implements Format.
-func (f *Generic) Marshal() (string, map[string]string) {
-	return f.RTPMap, f.FMTP
+// FMTP implements Format.
+func (f *Generic) FMTP() map[string]string {
+	return f.FMT
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/h264.go b/pkg/formats/h264.go
index e733659d..da8d47c7 100644
--- a/pkg/formats/h264.go
+++ b/pkg/formats/h264.go
@@ -81,21 +81,6 @@ type H264 struct {
 	mutex sync.RWMutex
 }
 
-// String implements Formaf.
-func (f *H264) String() string {
-	return "H264"
-}
-
-// ClockRate implements Formaf.
-func (f *H264) ClockRate() int {
-	return 90000
-}
-
-// PayloadType implements Formaf.
-func (f *H264) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *H264) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -131,8 +116,28 @@ func (f *H264) unmarshal(payloadType uint8, clock string, codec string, rtpmap s
 	return nil
 }
 
-// Marshal implements Formaf.
-func (f *H264) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *H264) String() string {
+	return "H264"
+}
+
+// ClockRate implements Format.
+func (f *H264) ClockRate() int {
+	return 90000
+}
+
+// PayloadType implements Format.
+func (f *H264) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *H264) RTPMap() string {
+	return "H264/90000"
+}
+
+// FMTP implements Format.
+func (f *H264) FMTP() map[string]string {
 	f.mutex.RLock()
 	defer f.mutex.RUnlock()
 
@@ -156,10 +161,10 @@ func (f *H264) Marshal() (string, map[string]string) {
 		fmtp["profile-level-id"] = strings.ToUpper(hex.EncodeToString(f.SPS[1:4]))
 	}
 
-	return "H264/90000", fmtp
+	return fmtp
 }
 
-// PTSEqualsDTS implements Formaf.
+// PTSEqualsDTS implements Format.
 func (f *H264) PTSEqualsDTS(pkt *rtp.Packet) bool {
 	return rtpH264ContainsIDR(pkt)
 }
diff --git a/pkg/formats/h265.go b/pkg/formats/h265.go
index 707261a1..910a32e8 100644
--- a/pkg/formats/h265.go
+++ b/pkg/formats/h265.go
@@ -23,21 +23,6 @@ type H265 struct {
 	mutex sync.RWMutex
 }
 
-// String implements Formaf.
-func (f *H265) String() string {
-	return "H265"
-}
-
-// ClockRate implements Formaf.
-func (f *H265) ClockRate() int {
-	return 90000
-}
-
-// PayloadType implements Formaf.
-func (f *H265) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *H265) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -76,8 +61,28 @@ func (f *H265) unmarshal(payloadType uint8, clock string, codec string, rtpmap s
 	return nil
 }
 
-// Marshal implements Formaf.
-func (f *H265) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *H265) String() string {
+	return "H265"
+}
+
+// ClockRate implements Format.
+func (f *H265) ClockRate() int {
+	return 90000
+}
+
+// PayloadType implements Format.
+func (f *H265) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *H265) RTPMap() string {
+	return "H265/90000"
+}
+
+// FMTP implements Format.
+func (f *H265) FMTP() map[string]string {
 	f.mutex.RLock()
 	defer f.mutex.RUnlock()
 
@@ -95,10 +100,10 @@ func (f *H265) Marshal() (string, map[string]string) {
 		fmtp["sprop-max-don-diff"] = strconv.FormatInt(int64(f.MaxDONDiff), 10)
 	}
 
-	return "H265/90000", fmtp
+	return fmtp
 }
 
-// PTSEqualsDTS implements Formaf.
+// PTSEqualsDTS implements Format.
 func (f *H265) PTSEqualsDTS(*rtp.Packet) bool {
 	return true
 }
diff --git a/pkg/formats/lpcm.go b/pkg/formats/lpcm.go
index 0f95d97a..5fa3a63c 100644
--- a/pkg/formats/lpcm.go
+++ b/pkg/formats/lpcm.go
@@ -18,21 +18,6 @@ type LPCM struct {
 	ChannelCount int
 }
 
-// String implements Format.
-func (f *LPCM) String() string {
-	return "LPCM"
-}
-
-// ClockRate implements Format.
-func (f *LPCM) ClockRate() int {
-	return f.SampleRate
-}
-
-// PayloadType implements Format.
-func (f *LPCM) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *LPCM) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -68,8 +53,23 @@ func (f *LPCM) unmarshal(payloadType uint8, clock string, codec string, rtpmap s
 	return nil
 }
 
-// Marshal implements Format.
-func (f *LPCM) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *LPCM) String() string {
+	return "LPCM"
+}
+
+// ClockRate implements Format.
+func (f *LPCM) ClockRate() int {
+	return f.SampleRate
+}
+
+// PayloadType implements Format.
+func (f *LPCM) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *LPCM) RTPMap() string {
 	var codec string
 	switch f.BitDepth {
 	case 8:
@@ -83,7 +83,12 @@ func (f *LPCM) Marshal() (string, map[string]string) {
 	}
 
 	return codec + "/" + strconv.FormatInt(int64(f.SampleRate), 10) +
-		"/" + strconv.FormatInt(int64(f.ChannelCount), 10), nil
+		"/" + strconv.FormatInt(int64(f.ChannelCount), 10)
+}
+
+// FMTP implements Format.
+func (f *LPCM) FMTP() map[string]string {
+	return nil
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/mjpeg.go b/pkg/formats/mjpeg.go
index 4b2e16e8..8da5b5f3 100644
--- a/pkg/formats/mjpeg.go
+++ b/pkg/formats/mjpeg.go
@@ -10,6 +10,10 @@ import (
 // Specification: https://datatracker.ietf.org/doc/html/rfc2435
 type MJPEG struct{}
 
+func (f *MJPEG) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
+	return nil
+}
+
 // String implements Format.
 func (f *MJPEG) String() string {
 	return "M-JPEG"
@@ -25,13 +29,14 @@ func (f *MJPEG) PayloadType() uint8 {
 	return 26
 }
 
-func (f *MJPEG) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
-	return nil
+// RTPMap implements Format.
+func (f *MJPEG) RTPMap() string {
+	return "JPEG/90000"
 }
 
-// Marshal implements Format.
-func (f *MJPEG) Marshal() (string, map[string]string) {
-	return "JPEG/90000", nil
+// FMTP implements Format.
+func (f *MJPEG) FMTP() map[string]string {
+	return nil
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/mpeg2_audio.go b/pkg/formats/mpeg2_audio.go
index 625f73e9..51cab41b 100644
--- a/pkg/formats/mpeg2_audio.go
+++ b/pkg/formats/mpeg2_audio.go
@@ -8,6 +8,13 @@ import (
 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
 type MPEG2Audio struct{}
 
+func (f *MPEG2Audio) unmarshal(
+	payloadType uint8, clock string, codec string,
+	rtpmap string, fmtp map[string]string,
+) error {
+	return nil
+}
+
 // String implements Format.
 func (f *MPEG2Audio) String() string {
 	return "MPEG2-audio"
@@ -23,16 +30,14 @@ func (f *MPEG2Audio) PayloadType() uint8 {
 	return 14
 }
 
-func (f *MPEG2Audio) unmarshal(
-	payloadType uint8, clock string, codec string,
-	rtpmap string, fmtp map[string]string,
-) error {
-	return nil
+// RTPMap implements Format.
+func (f *MPEG2Audio) RTPMap() string {
+	return ""
 }
 
-// Marshal implements Format.
-func (f *MPEG2Audio) Marshal() (string, map[string]string) {
-	return "", nil
+// FMTP implements Format.
+func (f *MPEG2Audio) FMTP() map[string]string {
+	return nil
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/mpeg2_video.go b/pkg/formats/mpeg2_video.go
index da1729d3..645652d6 100644
--- a/pkg/formats/mpeg2_video.go
+++ b/pkg/formats/mpeg2_video.go
@@ -8,6 +8,13 @@ import (
 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
 type MPEG2Video struct{}
 
+func (f *MPEG2Video) unmarshal(
+	payloadType uint8, clock string, codec string,
+	rtpmap string, fmtp map[string]string,
+) error {
+	return nil
+}
+
 // String implements Format.
 func (f *MPEG2Video) String() string {
 	return "MPEG2-video"
@@ -23,16 +30,14 @@ func (f *MPEG2Video) PayloadType() uint8 {
 	return 32
 }
 
-func (f *MPEG2Video) unmarshal(
-	payloadType uint8, clock string, codec string,
-	rtpmap string, fmtp map[string]string,
-) error {
-	return nil
+// RTPMap implements Format.
+func (f *MPEG2Video) RTPMap() string {
+	return ""
 }
 
-// Marshal implements Format.
-func (f *MPEG2Video) Marshal() (string, map[string]string) {
-	return "", nil
+// FMTP implements Format.
+func (f *MPEG2Video) FMTP() map[string]string {
+	return nil
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/mpeg4_audio_generic.go b/pkg/formats/mpeg4_audio_generic.go
index 4ddcb52e..f196422c 100644
--- a/pkg/formats/mpeg4_audio_generic.go
+++ b/pkg/formats/mpeg4_audio_generic.go
@@ -26,21 +26,6 @@ type MPEG4AudioGeneric struct {
 	IndexDeltaLength int
 }
 
-// String implements Format.
-func (f *MPEG4AudioGeneric) String() string {
-	return "MPEG4-audio-gen"
-}
-
-// ClockRate implements Format.
-func (f *MPEG4AudioGeneric) ClockRate() int {
-	return f.Config.SampleRate
-}
-
-// PayloadType implements Format.
-func (f *MPEG4AudioGeneric) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *MPEG4AudioGeneric) unmarshal(
 	payloadType uint8, clock string, codec string,
 	rtpmap string, fmtp map[string]string,
@@ -113,18 +98,39 @@ func (f *MPEG4AudioGeneric) unmarshal(
 	return nil
 }
 
-// Marshal implements Format.
-func (f *MPEG4AudioGeneric) Marshal() (string, map[string]string) {
-	enc, err := f.Config.Marshal()
-	if err != nil {
-		return "", nil
-	}
+// String implements Format.
+func (f *MPEG4AudioGeneric) String() string {
+	return "MPEG4-audio-gen"
+}
+
+// ClockRate implements Format.
+func (f *MPEG4AudioGeneric) ClockRate() int {
+	return f.Config.SampleRate
+}
+
+// PayloadType implements Format.
+func (f *MPEG4AudioGeneric) PayloadType() uint8 {
+	return f.PayloadTyp
+}
 
+// RTPMap implements Format.
+func (f *MPEG4AudioGeneric) RTPMap() string {
 	sampleRate := f.Config.SampleRate
 	if f.Config.ExtensionSampleRate != 0 {
 		sampleRate = f.Config.ExtensionSampleRate
 	}
 
+	return "mpeg4-generic/" + strconv.FormatInt(int64(sampleRate), 10) +
+		"/" + strconv.FormatInt(int64(f.Config.ChannelCount), 10)
+}
+
+// FMTP implements Format.
+func (f *MPEG4AudioGeneric) FMTP() map[string]string {
+	enc, err := f.Config.Marshal()
+	if err != nil {
+		return nil
+	}
+
 	profileLevelID := f.ProfileLevelID
 	if profileLevelID == 0 { // support legacy definition which didn't include profile-level-id
 		profileLevelID = 1
@@ -150,8 +156,7 @@ func (f *MPEG4AudioGeneric) Marshal() (string, map[string]string) {
 
 	fmtp["config"] = hex.EncodeToString(enc)
 
-	return "mpeg4-generic/" + strconv.FormatInt(int64(sampleRate), 10) +
-		"/" + strconv.FormatInt(int64(f.Config.ChannelCount), 10), fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/mpeg4_audio_latm.go b/pkg/formats/mpeg4_audio_latm.go
index 2cfca285..55a5a680 100644
--- a/pkg/formats/mpeg4_audio_latm.go
+++ b/pkg/formats/mpeg4_audio_latm.go
@@ -24,21 +24,6 @@ type MPEG4AudioLATM struct {
 	SBREnabled     *bool
 }
 
-// String implements Format.
-func (f *MPEG4AudioLATM) String() string {
-	return "MPEG4-audio-latm"
-}
-
-// ClockRate implements Format.
-func (f *MPEG4AudioLATM) ClockRate() int {
-	return f.SampleRate
-}
-
-// PayloadType implements Format.
-func (f *MPEG4AudioLATM) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *MPEG4AudioLATM) unmarshal(
 	payloadType uint8, clock string, codec string,
 	rtpmap string, fmtp map[string]string,
@@ -135,8 +120,29 @@ func (f *MPEG4AudioLATM) unmarshal(
 	return nil
 }
 
-// Marshal implements Format.
-func (f *MPEG4AudioLATM) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *MPEG4AudioLATM) String() string {
+	return "MPEG4-audio-latm"
+}
+
+// ClockRate implements Format.
+func (f *MPEG4AudioLATM) ClockRate() int {
+	return f.SampleRate
+}
+
+// PayloadType implements Format.
+func (f *MPEG4AudioLATM) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *MPEG4AudioLATM) RTPMap() string {
+	return "MP4A-LATM/" + strconv.FormatInt(int64(f.SampleRate), 10) +
+		"/" + strconv.FormatInt(int64(f.Channels), 10)
+}
+
+// FMTP implements Format.
+func (f *MPEG4AudioLATM) FMTP() map[string]string {
 	fmtp := map[string]string{
 		"profile-level-id": strconv.FormatInt(int64(f.ProfileLevelID), 10),
 		"config":           hex.EncodeToString(f.Config),
@@ -163,8 +169,7 @@ func (f *MPEG4AudioLATM) Marshal() (string, map[string]string) {
 		}
 	}
 
-	return "MP4A-LATM/" + strconv.FormatInt(int64(f.SampleRate), 10) +
-		"/" + strconv.FormatInt(int64(f.Channels), 10), fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/mpeg4_video_es.go b/pkg/formats/mpeg4_video_es.go
index 4b1b7af0..3c308714 100644
--- a/pkg/formats/mpeg4_video_es.go
+++ b/pkg/formats/mpeg4_video_es.go
@@ -20,21 +20,6 @@ type MPEG4VideoES struct {
 	Config         []byte
 }
 
-// String implements Format.
-func (f *MPEG4VideoES) String() string {
-	return "MPEG4-video-es"
-}
-
-// ClockRate implements Format.
-func (f *MPEG4VideoES) ClockRate() int {
-	return 90000
-}
-
-// PayloadType implements Format.
-func (f *MPEG4VideoES) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *MPEG4VideoES) unmarshal(
 	payloadType uint8, clock string, codec string,
 	rtpmap string, fmtp map[string]string,
@@ -64,14 +49,34 @@ func (f *MPEG4VideoES) unmarshal(
 	return nil
 }
 
-// Marshal implements Format.
-func (f *MPEG4VideoES) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *MPEG4VideoES) String() string {
+	return "MPEG4-video-es"
+}
+
+// ClockRate implements Format.
+func (f *MPEG4VideoES) ClockRate() int {
+	return 90000
+}
+
+// PayloadType implements Format.
+func (f *MPEG4VideoES) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *MPEG4VideoES) RTPMap() string {
+	return "MP4V-ES/90000"
+}
+
+// FMTP implements Format.
+func (f *MPEG4VideoES) FMTP() map[string]string {
 	fmtp := map[string]string{
 		"profile-level-id": strconv.FormatInt(int64(f.ProfileLevelID), 10),
 		"config":           strings.ToUpper(hex.EncodeToString(f.Config)),
 	}
 
-	return "MP4V-ES/90000", fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/mpegts.go b/pkg/formats/mpegts.go
index 84856db0..d057517c 100644
--- a/pkg/formats/mpegts.go
+++ b/pkg/formats/mpegts.go
@@ -8,6 +8,10 @@ import (
 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
 type MPEGTS struct{}
 
+func (f *MPEGTS) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
+	return nil
+}
+
 // String implements Format.
 func (f *MPEGTS) String() string {
 	return "MPEG-TS"
@@ -23,13 +27,14 @@ func (f *MPEGTS) PayloadType() uint8 {
 	return 33
 }
 
-func (f *MPEGTS) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
-	return nil
+// RTPMap implements Format.
+func (f *MPEGTS) RTPMap() string {
+	return "MP2T/90000"
 }
 
-// Marshal implements Format.
-func (f *MPEGTS) Marshal() (string, map[string]string) {
-	return "MP2T/90000", nil
+// FMTP implements Format.
+func (f *MPEGTS) FMTP() map[string]string {
+	return nil
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/opus.go b/pkg/formats/opus.go
index 45226a5b..c7c4f67d 100644
--- a/pkg/formats/opus.go
+++ b/pkg/formats/opus.go
@@ -17,23 +17,6 @@ type Opus struct {
 	IsStereo   bool
 }
 
-// String implements Format.
-func (f *Opus) String() string {
-	return "Opus"
-}
-
-// ClockRate implements Format.
-func (f *Opus) ClockRate() int {
-	// RFC7587: the RTP timestamp is incremented with a 48000 Hz
-	// clock rate for all modes of Opus and all sampling rates.
-	return 48000
-}
-
-// PayloadType implements Format.
-func (f *Opus) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *Opus) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -61,8 +44,32 @@ func (f *Opus) unmarshal(payloadType uint8, clock string, codec string, rtpmap s
 	return nil
 }
 
-// Marshal implements Format.
-func (f *Opus) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *Opus) String() string {
+	return "Opus"
+}
+
+// ClockRate implements Format.
+func (f *Opus) ClockRate() int {
+	// RFC7587: the RTP timestamp is incremented with a 48000 Hz
+	// clock rate for all modes of Opus and all sampling rates.
+	return 48000
+}
+
+// PayloadType implements Format.
+func (f *Opus) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *Opus) RTPMap() string {
+	// RFC7587: The RTP clock rate in "a=rtpmap" MUST be 48000, and the
+	// number of channels MUST be 2.
+	return "opus/48000/2"
+}
+
+// FMTP implements Format.
+func (f *Opus) FMTP() map[string]string {
 	fmtp := map[string]string{
 		"sprop-stereo": func() string {
 			if f.IsStereo {
@@ -71,10 +78,7 @@ func (f *Opus) Marshal() (string, map[string]string) {
 			return "0"
 		}(),
 	}
-
-	// RFC7587: The RTP clock rate in "a=rtpmap" MUST be 48000, and the
-	// number of channels MUST be 2.
-	return "opus/48000/2", fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/vorbis.go b/pkg/formats/vorbis.go
index 6e5cd08f..6c8a75c0 100644
--- a/pkg/formats/vorbis.go
+++ b/pkg/formats/vorbis.go
@@ -18,21 +18,6 @@ type Vorbis struct {
 	Configuration []byte
 }
 
-// String implements Format.
-func (f *Vorbis) String() string {
-	return "Vorbis"
-}
-
-// ClockRate implements Format.
-func (f *Vorbis) ClockRate() int {
-	return f.SampleRate
-}
-
-// PayloadType implements Format.
-func (f *Vorbis) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *Vorbis) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -71,15 +56,34 @@ func (f *Vorbis) unmarshal(payloadType uint8, clock string, codec string, rtpmap
 	return nil
 }
 
-// Marshal implements Format.
-func (f *Vorbis) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *Vorbis) String() string {
+	return "Vorbis"
+}
+
+// ClockRate implements Format.
+func (f *Vorbis) ClockRate() int {
+	return f.SampleRate
+}
+
+// PayloadType implements Format.
+func (f *Vorbis) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *Vorbis) RTPMap() string {
+	return "VORBIS/" + strconv.FormatInt(int64(f.SampleRate), 10) +
+		"/" + strconv.FormatInt(int64(f.ChannelCount), 10)
+}
+
+// FMTP implements Format.
+func (f *Vorbis) FMTP() map[string]string {
 	fmtp := map[string]string{
 		"configuration": base64.StdEncoding.EncodeToString(f.Configuration),
 	}
 
-	return "VORBIS/" + strconv.FormatInt(int64(f.SampleRate), 10) +
-			"/" + strconv.FormatInt(int64(f.ChannelCount), 10),
-		fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/vp8.go b/pkg/formats/vp8.go
index f02e9297..8a1e6db1 100644
--- a/pkg/formats/vp8.go
+++ b/pkg/formats/vp8.go
@@ -17,21 +17,6 @@ type VP8 struct {
 	MaxFS      *int
 }
 
-// String implements Format.
-func (f *VP8) String() string {
-	return "VP8"
-}
-
-// ClockRate implements Format.
-func (f *VP8) ClockRate() int {
-	return 90000
-}
-
-// PayloadType implements Format.
-func (f *VP8) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *VP8) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -60,8 +45,28 @@ func (f *VP8) unmarshal(payloadType uint8, clock string, codec string, rtpmap st
 	return nil
 }
 
-// Marshal implements Format.
-func (f *VP8) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *VP8) String() string {
+	return "VP8"
+}
+
+// ClockRate implements Format.
+func (f *VP8) ClockRate() int {
+	return 90000
+}
+
+// PayloadType implements Format.
+func (f *VP8) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *VP8) RTPMap() string {
+	return "VP8/90000"
+}
+
+// FMTP implements Format.
+func (f *VP8) FMTP() map[string]string {
 	fmtp := make(map[string]string)
 
 	if f.MaxFR != nil {
@@ -72,7 +77,7 @@ func (f *VP8) Marshal() (string, map[string]string) {
 		fmtp["max-fs"] = strconv.FormatInt(int64(*f.MaxFS), 10)
 	}
 
-	return "VP8/90000", fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/formats/vp9.go b/pkg/formats/vp9.go
index e3ab2da4..5521fce8 100644
--- a/pkg/formats/vp9.go
+++ b/pkg/formats/vp9.go
@@ -18,21 +18,6 @@ type VP9 struct {
 	ProfileID  *int
 }
 
-// String implements Format.
-func (f *VP9) String() string {
-	return "VP9"
-}
-
-// ClockRate implements Format.
-func (f *VP9) ClockRate() int {
-	return 90000
-}
-
-// PayloadType implements Format.
-func (f *VP9) PayloadType() uint8 {
-	return f.PayloadTyp
-}
-
 func (f *VP9) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
 	f.PayloadTyp = payloadType
 
@@ -70,8 +55,28 @@ func (f *VP9) unmarshal(payloadType uint8, clock string, codec string, rtpmap st
 	return nil
 }
 
-// Marshal implements Format.
-func (f *VP9) Marshal() (string, map[string]string) {
+// String implements Format.
+func (f *VP9) String() string {
+	return "VP9"
+}
+
+// ClockRate implements Format.
+func (f *VP9) ClockRate() int {
+	return 90000
+}
+
+// PayloadType implements Format.
+func (f *VP9) PayloadType() uint8 {
+	return f.PayloadTyp
+}
+
+// RTPMap implements Format.
+func (f *VP9) RTPMap() string {
+	return "VP9/90000"
+}
+
+// FMTP implements Format.
+func (f *VP9) FMTP() map[string]string {
 	fmtp := make(map[string]string)
 
 	if f.MaxFR != nil {
@@ -84,7 +89,7 @@ func (f *VP9) Marshal() (string, map[string]string) {
 		fmtp["profile-id"] = strconv.FormatInt(int64(*f.ProfileID), 10)
 	}
 
-	return "VP9/90000", fmtp
+	return fmtp
 }
 
 // PTSEqualsDTS implements Format.
diff --git a/pkg/media/media.go b/pkg/media/media.go
index 22fc86e5..10262957 100644
--- a/pkg/media/media.go
+++ b/pkg/media/media.go
@@ -78,6 +78,17 @@ func decodeFMTP(enc string) map[string]string {
 	return ret
 }
 
+func sortedKeys(fmtp map[string]string) []string {
+	keys := make([]string, len(fmtp))
+	i := 0
+	for key := range fmtp {
+		keys[i] = key
+		i++
+	}
+	sort.Strings(keys)
+	return keys
+}
+
 // Direction is the direction of a media stream.
 type Direction string
 
@@ -182,8 +193,7 @@ func (m Media) Marshal() *psdp.MediaDescription {
 		typ := strconv.FormatUint(uint64(forma.PayloadType()), 10)
 		md.MediaName.Formats = append(md.MediaName.Formats, typ)
 
-		rtpmap, fmtp := forma.Marshal()
-
+		rtpmap := forma.RTPMap()
 		if rtpmap != "" {
 			md.Attributes = append(md.Attributes, psdp.Attribute{
 				Key:   "rtpmap",
@@ -191,17 +201,10 @@ func (m Media) Marshal() *psdp.MediaDescription {
 			})
 		}
 
+		fmtp := forma.FMTP()
 		if len(fmtp) != 0 {
-			keys := make([]string, len(fmtp))
-			i := 0
-			for key := range fmtp {
-				keys[i] = key
-				i++
-			}
-			sort.Strings(keys)
-
 			tmp := make([]string, len(fmtp))
-			for i, key := range keys {
+			for i, key := range sortedKeys(fmtp) {
 				tmp[i] = key + "=" + fmtp[key]
 			}
 


Test patch:
diff --git a/client_play_test.go b/client_play_test.go
index 8cedb949..ef9d56fa 100644
--- a/client_play_test.go
+++ b/client_play_test.go
@@ -269,7 +269,7 @@ func TestClientPlay(t *testing.T) {
 
 				forma := &formats.Generic{
 					PayloadTyp: 96,
-					RTPMap:     "private/90000",
+					RTPMa:      "private/90000",
 				}
 				err = forma.Init()
 				require.NoError(t, err)
@@ -543,7 +543,7 @@ func TestClientPlayPartial(t *testing.T) {
 
 		forma := &formats.Generic{
 			PayloadTyp: 96,
-			RTPMap:     "private/90000",
+			RTPMa:      "private/90000",
 		}
 		err = forma.Init()
 		require.NoError(t, err)
@@ -2932,7 +2932,7 @@ func TestClientPlayDecodeErrors(t *testing.T) {
 					Type: media.TypeApplication,
 					Formats: []formats.Format{&formats.Generic{
 						PayloadTyp: 97,
-						RTPMap:     "private/90000",
+						RTPMa:      "private/90000",
 					}},
 				}}
 				resetMediaControls(medias)
diff --git a/pkg/formats/av1_test.go b/pkg/formats/av1_test.go
index 68e69d5e..eaa2f1b1 100644
--- a/pkg/formats/av1_test.go
+++ b/pkg/formats/av1_test.go
@@ -13,6 +13,5 @@ func TestAV1Attributes(t *testing.T) {
 	}
 	require.Equal(t, "AV1", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(100), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/format_test.go b/pkg/formats/format_test.go
index f8da6f59..4a79cb61 100644
--- a/pkg/formats/format_test.go
+++ b/pkg/formats/format_test.go
@@ -609,7 +609,7 @@ var casesFormat = []struct {
 		nil,
 		&Generic{
 			PayloadTyp: 98,
-			RTPMap:     "MetaData/80000",
+			RTPMa:      "MetaData/80000",
 			ClockRat:   80000,
 		},
 		"MetaData/80000",
@@ -635,7 +635,7 @@ var casesFormat = []struct {
 		nil,
 		&Generic{
 			PayloadTyp: 98,
-			RTPMap:     "custom",
+			RTPMa:      "custom",
 		},
 		"custom",
 		nil,
@@ -648,7 +648,7 @@ var casesFormat = []struct {
 		nil,
 		&Generic{
 			PayloadTyp: 98,
-			RTPMap:     "custom/aaa",
+			RTPMa:      "custom/aaa",
 		},
 		"custom/aaa",
 		nil,
@@ -668,10 +668,9 @@ func TestUnmarshal(t *testing.T) {
 func TestMarshal(t *testing.T) {
 	for _, ca := range casesFormat {
 		t.Run(ca.name, func(t *testing.T) {
-			rtpMap, fmtp := ca.dec.Marshal()
 			require.Equal(t, ca.payloadType, ca.dec.PayloadType())
-			require.Equal(t, ca.encRtpMap, rtpMap)
-			require.Equal(t, ca.encFmtp, fmtp)
+			require.Equal(t, ca.encRtpMap, ca.dec.RTPMap())
+			require.Equal(t, ca.encFmtp, ca.dec.FMTP())
 		})
 	}
 }
diff --git a/pkg/formats/g711_test.go b/pkg/formats/g711_test.go
index 63fd51cc..bbfc3a15 100644
--- a/pkg/formats/g711_test.go
+++ b/pkg/formats/g711_test.go
@@ -11,7 +11,6 @@ func TestG711Attributes(t *testing.T) {
 	format := &G711{}
 	require.Equal(t, "G711", format.String())
 	require.Equal(t, 8000, format.ClockRate())
-	require.Equal(t, uint8(8), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 
 	format = &G711{
@@ -19,7 +18,6 @@ func TestG711Attributes(t *testing.T) {
 	}
 	require.Equal(t, "G711", format.String())
 	require.Equal(t, 8000, format.ClockRate())
-	require.Equal(t, uint8(0), format.PayloadType())
 }
 
 func TestG711DecEncoder(t *testing.T) {
diff --git a/pkg/formats/g722_test.go b/pkg/formats/g722_test.go
index c51f40b8..4827a7fc 100644
--- a/pkg/formats/g722_test.go
+++ b/pkg/formats/g722_test.go
@@ -11,7 +11,6 @@ func TestG722Attributes(t *testing.T) {
 	format := &G722{}
 	require.Equal(t, "G722", format.String())
 	require.Equal(t, 8000, format.ClockRate())
-	require.Equal(t, uint8(9), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
 
diff --git a/pkg/formats/generic_test.go b/pkg/formats/generic_test.go
index 7ccec9b6..ba316f14 100644
--- a/pkg/formats/generic_test.go
+++ b/pkg/formats/generic_test.go
@@ -10,8 +10,8 @@ import (
 func TestGenericAttributes(t *testing.T) {
 	format := &Generic{
 		PayloadTyp: 98,
-		RTPMap:     "H265/90000",
-		FMTP: map[string]string{
+		RTPMa:      "H265/90000",
+		FMT: map[string]string{
 			"profile-id": "1",
 			"sprop-vps":  "QAEMAf//AWAAAAMAAAMAAAMAAAMAlqwJ",
 			"sprop-sps":  "QgEBAWAAAAMAAAMAAAMAAAMAlqADwIAQ5Za5JMmuWcBSSgAAB9AAAHUwgkA=",
@@ -23,6 +23,5 @@ func TestGenericAttributes(t *testing.T) {
 
 	require.Equal(t, "Generic", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(98), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/h264_test.go b/pkg/formats/h264_test.go
index 4bd32c7d..8e947035 100644
--- a/pkg/formats/h264_test.go
+++ b/pkg/formats/h264_test.go
@@ -16,7 +16,6 @@ func TestH264Attributes(t *testing.T) {
 	}
 	require.Equal(t, "H264", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 
 	sps, pps := format.SafeParams()
 	require.Equal(t, []byte{0x01, 0x02}, sps)
diff --git a/pkg/formats/h265_test.go b/pkg/formats/h265_test.go
index 8f4e5747..c96f30fc 100644
--- a/pkg/formats/h265_test.go
+++ b/pkg/formats/h265_test.go
@@ -16,7 +16,6 @@ func TestH265Attributes(t *testing.T) {
 	}
 	require.Equal(t, "H265", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 
 	vps, sps, pps := format.SafeParams()
diff --git a/pkg/formats/lpcm_test.go b/pkg/formats/lpcm_test.go
index 246d77a1..605c8e7d 100644
--- a/pkg/formats/lpcm_test.go
+++ b/pkg/formats/lpcm_test.go
@@ -16,7 +16,6 @@ func TestLPCMAttributes(t *testing.T) {
 	}
 	require.Equal(t, "LPCM", format.String())
 	require.Equal(t, 44100, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
 
diff --git a/pkg/formats/mjpeg_test.go b/pkg/formats/mjpeg_test.go
index b733192b..e5fb970d 100644
--- a/pkg/formats/mjpeg_test.go
+++ b/pkg/formats/mjpeg_test.go
@@ -11,7 +11,6 @@ func TestMJPEGAttributes(t *testing.T) {
 	format := &MJPEG{}
 	require.Equal(t, "M-JPEG", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(26), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
 
diff --git a/pkg/formats/mpeg2_audio_test.go b/pkg/formats/mpeg2_audio_test.go
index 5b8fc986..afcc38bc 100644
--- a/pkg/formats/mpeg2_audio_test.go
+++ b/pkg/formats/mpeg2_audio_test.go
@@ -11,6 +11,5 @@ func TestMPEG2AudioAttributes(t *testing.T) {
 	format := &MPEG2Audio{}
 	require.Equal(t, "MPEG2-audio", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(14), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/mpeg2_video_test.go b/pkg/formats/mpeg2_video_test.go
index ce054d8c..d9ef1598 100644
--- a/pkg/formats/mpeg2_video_test.go
+++ b/pkg/formats/mpeg2_video_test.go
@@ -11,6 +11,5 @@ func TestMPEG2VideoAttributes(t *testing.T) {
 	format := &MPEG2Video{}
 	require.Equal(t, "MPEG2-video", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(32), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/mpeg4_audio_generic_test.go b/pkg/formats/mpeg4_audio_generic_test.go
index 3759be26..32d7e649 100644
--- a/pkg/formats/mpeg4_audio_generic_test.go
+++ b/pkg/formats/mpeg4_audio_generic_test.go
@@ -23,7 +23,6 @@ func TestMPEG4AudioGenericAttributes(t *testing.T) {
 	}
 	require.Equal(t, "MPEG4-audio-gen", format.String())
 	require.Equal(t, 48000, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
 
diff --git a/pkg/formats/mpeg4_audio_latm_test.go b/pkg/formats/mpeg4_audio_latm_test.go
index 45ddd32e..386b9ab9 100644
--- a/pkg/formats/mpeg4_audio_latm_test.go
+++ b/pkg/formats/mpeg4_audio_latm_test.go
@@ -18,6 +18,5 @@ func TestMPEG4AudioLATMAttributes(t *testing.T) {
 	}
 	require.Equal(t, "MPEG4-audio-latm", format.String())
 	require.Equal(t, 48000, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/mpeg4_video_es_test.go b/pkg/formats/mpeg4_video_es_test.go
index d3c0104f..14a81bf3 100644
--- a/pkg/formats/mpeg4_video_es_test.go
+++ b/pkg/formats/mpeg4_video_es_test.go
@@ -15,6 +15,5 @@ func TestMPEG4VideoESAttributes(t *testing.T) {
 	}
 	require.Equal(t, "MPEG4-video-es", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/mpegts_test.go b/pkg/formats/mpegts_test.go
index eb5198f3..cc39329a 100644
--- a/pkg/formats/mpegts_test.go
+++ b/pkg/formats/mpegts_test.go
@@ -11,6 +11,5 @@ func TestMPEGTSAttributes(t *testing.T) {
 	format := &MPEGTS{}
 	require.Equal(t, "MPEG-TS", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(33), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/opus_test.go b/pkg/formats/opus_test.go
index 964a49a2..edd10c91 100644
--- a/pkg/formats/opus_test.go
+++ b/pkg/formats/opus_test.go
@@ -14,7 +14,6 @@ func TestOpusAttributes(t *testing.T) {
 	}
 	require.Equal(t, "Opus", format.String())
 	require.Equal(t, 48000, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
 
diff --git a/pkg/formats/vorbis_test.go b/pkg/formats/vorbis_test.go
index d98c5fba..d2f18a2a 100644
--- a/pkg/formats/vorbis_test.go
+++ b/pkg/formats/vorbis_test.go
@@ -16,6 +16,5 @@ func TestVorbisAttributes(t *testing.T) {
 	}
 	require.Equal(t, "Vorbis", format.String())
 	require.Equal(t, 48000, format.ClockRate())
-	require.Equal(t, uint8(96), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
diff --git a/pkg/formats/vp8_test.go b/pkg/formats/vp8_test.go
index f54182cb..259e8c74 100644
--- a/pkg/formats/vp8_test.go
+++ b/pkg/formats/vp8_test.go
@@ -13,7 +13,6 @@ func TestVP8ttributes(t *testing.T) {
 	}
 	require.Equal(t, "VP8", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(99), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
 
diff --git a/pkg/formats/vp9_test.go b/pkg/formats/vp9_test.go
index 202f52c7..fb4cff35 100644
--- a/pkg/formats/vp9_test.go
+++ b/pkg/formats/vp9_test.go
@@ -13,7 +13,6 @@ func TestVP9Attributes(t *testing.T) {
 	}
 	require.Equal(t, "VP9", format.String())
 	require.Equal(t, 90000, format.ClockRate())
-	require.Equal(t, uint8(100), format.PayloadType())
 	require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
 }
 
diff --git a/pkg/media/medias_test.go b/pkg/media/medias_test.go
index 8fb0580f..075b991b 100644
--- a/pkg/media/medias_test.go
+++ b/pkg/media/medias_test.go
@@ -302,18 +302,18 @@ var casesMedias = []struct {
 					},
 					&formats.Generic{
 						PayloadTyp: 103,
-						RTPMap:     "ISAC/16000",
+						RTPMa:      "ISAC/16000",
 						ClockRat:   16000,
 					},
 					&formats.Generic{
 						PayloadTyp: 104,
-						RTPMap:     "ISAC/32000",
+						RTPMa:      "ISAC/32000",
 						ClockRat:   32000,
 					},
 					&formats.G722{},
 					&formats.Generic{
 						PayloadTyp: 102,
-						RTPMap:     "ILBC/8000",
+						RTPMa:      "ILBC/8000",
 						ClockRat:   8000,
 					},
 					&formats.G711{
@@ -324,37 +324,37 @@ var casesMedias = []struct {
 					},
 					&formats.Generic{
 						PayloadTyp: 106,
-						RTPMap:     "CN/32000",
+						RTPMa:      "CN/32000",
 						ClockRat:   32000,
 					},
 					&formats.Generic{
 						PayloadTyp: 105,
-						RTPMap:     "CN/16000",
+						RTPMa:      "CN/16000",
 						ClockRat:   16000,
 					},
 					&formats.Generic{
 						PayloadTyp: 13,
-						RTPMap:     "CN/8000",
+						RTPMa:      "CN/8000",
 						ClockRat:   8000,
 					},
 					&formats.Generic{
 						PayloadTyp: 110,
-						RTPMap:     "telephone-event/48000",
+						RTPMa:      "telephone-event/48000",
 						ClockRat:   48000,
 					},
 					&formats.Generic{
 						PayloadTyp: 112,
-						RTPMap:     "telephone-event/32000",
+						RTPMa:      "telephone-event/32000",
 						ClockRat:   32000,
 					},
 					&formats.Generic{
 						PayloadTyp: 113,
-						RTPMap:     "telephone-event/16000",
+						RTPMa:      "telephone-event/16000",
 						ClockRat:   16000,
 					},
 					&formats.Generic{
 						PayloadTyp: 126,
-						RTPMap:     "telephone-event/8000",
+						RTPMa:      "telephone-event/8000",
 						ClockRat:   8000,
 					},
 				},
@@ -368,8 +368,8 @@ var casesMedias = []struct {
 					},
 					&formats.Generic{
 						PayloadTyp: 97,
-						RTPMap:     "rtx/90000",
-						FMTP: map[string]string{
+						RTPMa:      "rtx/90000",
+						FMT: map[string]string{
 							"apt": "96",
 						},
 						ClockRat: 90000,
@@ -379,8 +379,8 @@ var casesMedias = []struct {
 					},
 					&formats.Generic{
 						PayloadTyp: 99,
-						RTPMap:     "rtx/90000",
-						FMTP: map[string]string{
+						RTPMa:      "rtx/90000",
+						FMT: map[string]string{
 							"apt": "98",
 						},
 						ClockRat: 90000,
@@ -391,28 +391,28 @@ var casesMedias = []struct {
 					},
 					&formats.Generic{
 						PayloadTyp: 101,
-						RTPMap:     "rtx/90000",
-						FMTP: map[string]string{
+						RTPMa:      "rtx/90000",
+						FMT: map[string]string{
 							"apt": "100",
 						},
 						ClockRat: 90000,
 					},
 					&formats.Generic{
 						PayloadTyp: 127,
-						RTPMap:     "red/90000",
+						RTPMa:      "red/90000",
 						ClockRat:   90000,
 					},
 					&formats.Generic{
 						PayloadTyp: 124,
-						RTPMap:     "rtx/90000",
-						FMTP: map[string]string{
+						RTPMa:      "rtx/90000",
+						FMT: map[string]string{
 							"apt": "127",
 						},
 						ClockRat: 90000,
 					},
 					&formats.Generic{
 						PayloadTyp: 125,
-						RTPMap:     "ulpfec/90000",
+						RTPMa:      "ulpfec/90000",
 						ClockRat:   90000,
 					},
 				},
@@ -457,7 +457,7 @@ var casesMedias = []struct {
 					},
 					&formats.Generic{
 						PayloadTyp: 98,
-						RTPMap:     "MetaData",
+						RTPMa:      "MetaData",
 					},
 				},
 			},
@@ -537,7 +537,7 @@ var casesMedias = []struct {
 				Type: "application",
 				Formats: []formats.Format{&formats.Generic{
 					PayloadTyp: 95,
-					RTPMap:     "TP-LINK/90000",
+					RTPMa:      "TP-LINK/90000",
 					ClockRat:   90000,
 				}},
 			},
@@ -638,8 +638,8 @@ func TestMediasMarshal(t *testing.T) {
 func TestMediasFindFormat(t *testing.T) {
 	tr := &formats.Generic{
 		PayloadTyp: 97,
-		RTPMap:     "rtx/90000",
-		FMTP: map[string]string{
+		RTPMa:      "rtx/90000",
+		FMT: map[string]string{
 			"apt": "96",
 		},
 		ClockRat: 90000,
diff --git a/server_play_test.go b/server_play_test.go
index be82ffa6..ead9d82e 100644
--- a/server_play_test.go
+++ b/server_play_test.go
@@ -1833,7 +1833,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) {
 
 	forma := &formats.Generic{
 		PayloadTyp: 96,
-		RTPMap:     "private/90000",
+		RTPMa:      "private/90000",
 	}
 	err := forma.Init()
 	require.NoError(t, err)
@@ -1964,7 +1964,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) {
 func TestServerPlayNoInterleavedIDs(t *testing.T) {
 	forma := &formats.Generic{
 		PayloadTyp: 96,
-		RTPMap:     "private/90000",
+		RTPMa:      "private/90000",
 	}
 	err := forma.Init()
 	require.NoError(t, err)
diff --git a/server_record_test.go b/server_record_test.go
index 631903ae..5d7b94b8 100644
--- a/server_record_test.go
+++ b/server_record_test.go
@@ -431,7 +431,7 @@ func TestServerRecordErrorRecordPartialMedias(t *testing.T) {
 
 	forma := &formats.Generic{
 		PayloadTyp: 96,
-		RTPMap:     "private/90000",
+		RTPMa:      "private/90000",
 	}
 	err = forma.Init()
 	require.NoError(t, err)
@@ -1263,7 +1263,7 @@ func TestServerRecordDecodeErrors(t *testing.T) {
 				Type: media.TypeApplication,
 				Formats: []formats.Format{&formats.Generic{
 					PayloadTyp: 97,
-					RTPMap:     "private/90000",
+					RTPMa:      "private/90000",
 				}},
 			}}
 			resetMediaControls(medias)