Repository: pion/webrtc (go)

Issue:
Codec ignored due to case sensitive MimeType check
<!--
  Come join our community on Slack https://pion.ly/slack for quicker support and more resources

  We also provide a Knowledge Base https://pion.ly/knowledge-base that covers Pion and general WebRTC
  
  We maintain a FAQ of common issues and questions https://pion.ly/knowledge-base/pion-basics/faq/

 Please answer these questions before submitting your issue. Thanks!
-->

### Your environment.
  - Version: 3.1.24 (can be repro'd on latest webrtc `master`)

### What did you do?
Initialised a `MediaEngine` with two codecs registered `VP8` (MimeType = `video/vp8`) and `H264` (MimeType = `video/h264`). 
Called `PeerConnection.SetRemoteDescription` with an `offer` sdp which has two codecs - VP8 and H264. 
Creating the answer sdp using `PeerConnection.CreateAnswer` we get sdp which has only `H264` codec. 

### What did you expect?
Expected both VP8 and H264 codec to be present in the Answer SDP

### What happened?
Due to the case sensitive comparison done in https://github.com/pion/webrtc/blob/master/internal/fmtp/fmtp.go#L70 -- the `vp8` codec is not matched at all. 


Pull request:
Test for incorrect case insensitive mimetype check
Relates to pion/webrtc #2241

#### Description

#### Reference issue
Fixes #2241


Patch:
diff --git a/internal/fmtp/fmtp.go b/internal/fmtp/fmtp.go
index 86057594..bbf4457c 100644
--- a/internal/fmtp/fmtp.go
+++ b/internal/fmtp/fmtp.go
@@ -67,7 +67,7 @@ func (g *genericFMTP) Match(b FMTP) bool {
 		return false
 	}
 
-	if g.mimeType != c.MimeType() {
+	if !strings.EqualFold(g.mimeType, c.MimeType()) {
 		return false
 	}
 


Test patch:
diff --git a/mediaengine_test.go b/mediaengine_test.go
index 0c89c0a0..3d91b482 100644
--- a/mediaengine_test.go
+++ b/mediaengine_test.go
@@ -4,7 +4,9 @@
 package webrtc
 
 import (
+	"fmt"
 	"regexp"
+	"strings"
 	"testing"
 
 	"github.com/pion/sdp/v3"
@@ -548,3 +550,87 @@ a=rtpmap:111 opus/48000/2
 	assert.NotEqual(t, 2, extensions[voIndex].ID)
 	assert.NotEqual(t, 5, extensions[voIndex].ID)
 }
+
+func TestCaseInsensitiveMimeType(t *testing.T) {
+	const offerSdp = `
+v=0
+o=- 8448668841136641781 4 IN IP4 127.0.0.1
+s=-
+t=0 0
+a=group:BUNDLE 0 1 2
+a=extmap-allow-mixed
+a=msid-semantic: WMS 4beea6b0-cf95-449c-a1ec-78e16b247426
+m=video 9 UDP/TLS/RTP/SAVPF 96 127
+c=IN IP4 0.0.0.0
+a=rtcp:9 IN IP4 0.0.0.0
+a=ice-ufrag:1/MvHwjAyVf27aLu
+a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
+a=ice-options:google-ice
+a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24
+a=setup:actpass
+a=mid:1
+a=sendonly
+a=rtpmap:96 VP8/90000
+a=rtcp-fb:96 goog-remb
+a=rtcp-fb:96 transport-cc
+a=rtcp-fb:96 ccm fir
+a=rtcp-fb:96 nack
+a=rtcp-fb:96 nack pli
+a=rtpmap:127 H264/90000
+a=rtcp-fb:127 goog-remb
+a=rtcp-fb:127 transport-cc
+a=rtcp-fb:127 ccm fir
+a=rtcp-fb:127 nack
+a=rtcp-fb:127 nack pli
+a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
+
+`
+
+	for _, mimeTypeVp8 := range []string{
+		"video/vp8",
+		"video/VP8",
+	} {
+		t.Run(fmt.Sprintf("MimeType: %s", mimeTypeVp8), func(t *testing.T) {
+			me := &MediaEngine{}
+			feedback := []RTCPFeedback{
+				{Type: TypeRTCPFBTransportCC},
+				{Type: TypeRTCPFBCCM, Parameter: "fir"},
+				{Type: TypeRTCPFBNACK},
+				{Type: TypeRTCPFBNACK, Parameter: "pli"},
+			}
+
+			for _, codec := range []RTPCodecParameters{
+				{
+					RTPCodecCapability: RTPCodecCapability{MimeType: mimeTypeVp8, ClockRate: 90000, RTCPFeedback: feedback},
+					PayloadType:        96,
+				},
+				{
+					RTPCodecCapability: RTPCodecCapability{MimeType: "video/h264", ClockRate: 90000, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", RTCPFeedback: feedback},
+					PayloadType:        127,
+				},
+			} {
+				assert.NoError(t, me.RegisterCodec(codec, RTPCodecTypeVideo))
+			}
+
+			api := NewAPI(WithMediaEngine(me))
+			pc, err := api.NewPeerConnection(Configuration{
+				SDPSemantics: SDPSemanticsUnifiedPlan,
+			})
+			assert.NoError(t, err)
+
+			offer := SessionDescription{
+				Type: SDPTypeOffer,
+				SDP:  offerSdp,
+			}
+
+			assert.NoError(t, pc.SetRemoteDescription(offer))
+			answer, err := pc.CreateAnswer(nil)
+			assert.NoError(t, err)
+			assert.NotNil(t, answer)
+			assert.NoError(t, pc.SetLocalDescription(answer))
+			assert.True(t, strings.Contains(answer.SDP, "VP8") || strings.Contains(answer.SDP, "vp8"))
+
+			assert.NoError(t, pc.Close())
+		})
+	}
+}