// SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT //go:build !js // +build !js package webrtc import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func Test_RTPTransceiver_SetCodecPreferences(t *testing.T) { mediaEngine := &MediaEngine{} api := NewAPI(WithMediaEngine(mediaEngine)) assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) assert.NoError(t, mediaEngine.pushCodecs(mediaEngine.videoCodecs, RTPCodecTypeVideo)) assert.NoError(t, mediaEngine.pushCodecs(mediaEngine.audioCodecs, RTPCodecTypeAudio)) tr := RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: mediaEngine.videoCodecs} assert.EqualValues(t, mediaEngine.videoCodecs, tr.getCodecs()) failTestCases := [][]RTPCodecParameters{ { { RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil}, PayloadType: 111, }, }, { { RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 96, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil}, PayloadType: 111, }, }, } for _, testCase := range failTestCases { assert.ErrorIs(t, tr.SetCodecPreferences(testCase), errRTPTransceiverCodecUnsupported) } successTestCases := [][]RTPCodecParameters{ { { RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 96, }, }, { { RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 96, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=96", nil}, PayloadType: 97, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=0", nil}, PayloadType: 98, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=98", nil}, PayloadType: 99, }, }, } for _, testCase := range successTestCases { assert.NoError(t, tr.SetCodecPreferences(testCase)) } assert.NoError(t, tr.SetCodecPreferences(nil)) assert.NotEqual(t, 0, len(tr.getCodecs())) assert.NoError(t, tr.SetCodecPreferences([]RTPCodecParameters{})) assert.NotEqual(t, 0, len(tr.getCodecs())) } // Assert that SetCodecPreferences properly filters codecs and PayloadTypes are respected. func Test_RTPTransceiver_SetCodecPreferences_PayloadType(t *testing.T) { notOfferedCodec := RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{"video/notOfferedCodec", 90000, 0, "", nil}, PayloadType: 50, } offeredCodec := RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{"video/offeredCodec", 90000, 0, "", nil}, PayloadType: 52, } offeredCodecRTX := RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=52", nil}, PayloadType: 53, } mediaEngine := &MediaEngine{} assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) assert.NoError(t, mediaEngine.RegisterCodec(offeredCodec, RTPCodecTypeVideo)) assert.NoError(t, mediaEngine.RegisterCodec(offeredCodecRTX, RTPCodecTypeVideo)) offerPC, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) assert.NoError(t, mediaEngine.RegisterCodec(notOfferedCodec, RTPCodecTypeVideo)) answerPC, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo) assert.NoError(t, err) track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") assert.NoError(t, err) answerTransceiver, err := answerPC.AddTransceiverFromTrack( track, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}, ) assert.NoError(t, err) assert.NoError(t, answerTransceiver.SetCodecPreferences([]RTPCodecParameters{ notOfferedCodec, offeredCodec, offeredCodecRTX, { RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 54, }, })) offer, err := offerPC.CreateOffer(nil) assert.NoError(t, err) assert.NoError(t, offerPC.SetLocalDescription(offer)) assert.NoError(t, answerPC.SetRemoteDescription(offer)) answer, err := answerPC.CreateAnswer(nil) assert.NoError(t, err) // VP8 with proper PayloadType assert.NotEqual(t, -1, strings.Index(answer.SDP, "a=rtpmap:54 VP8/90000")) // testCodec1 and testCodec1RTX should be included as they are in the offer assert.NotEqual(t, -1, strings.Index(answer.SDP, "a=rtpmap:52 offeredCodec/90000")) assert.NotEqual(t, -1, strings.Index(answer.SDP, "a=rtpmap:53 rtx/90000")) assert.NotEqual(t, -1, strings.Index(answer.SDP, "a=fmtp:53 apt=52")) // testCodec is ignored since offerer doesn't support assert.Equal(t, -1, strings.Index(answer.SDP, "notOfferedCodec")) closePairNow(t, offerPC, answerPC) } // Assert that SetCodecPreferences and getCodecs properly filters unattached RTX. func Test_RTPTransceiver_UnattachedRTX(t *testing.T) { testCodec := RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{"video/testCodec", 90000, 0, "", nil}, PayloadType: 50, } testCodecRTX := RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=50", nil}, PayloadType: 51, } mediaEngine := &MediaEngine{} assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) offerPC, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) assert.NoError(t, mediaEngine.RegisterCodec(testCodec, RTPCodecTypeVideo)) assert.NoError(t, mediaEngine.RegisterCodec(testCodecRTX, RTPCodecTypeVideo)) answerPC, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo) assert.NoError(t, err) answerTransceiver, err := answerPC.AddTransceiverFromKind(RTPCodecTypeVideo) assert.NoError(t, err) assert.NoError(t, answerTransceiver.SetCodecPreferences([]RTPCodecParameters{ testCodecRTX, { RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 52, }, })) // rtx should not be in the list of transceiver codecs as testCodec (primary) is // not given to SetCodecPreferences answerTransceiver.mu.RLock() foundRTX := false for _, codec := range answerTransceiver.codecs { if strings.EqualFold(codec.RTPCodecCapability.MimeType, MimeTypeRTX) { foundRTX = true break } } assert.False(t, foundRTX) answerTransceiver.mu.RUnlock() assert.NoError(t, answerTransceiver.SetCodecPreferences([]RTPCodecParameters{ testCodec, testCodecRTX, { RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 52, }, })) // rtx should be in the list of transceiver codecs as testCodec (primary) is // given to SetCodecPreferences answerTransceiver.mu.RLock() foundRTX = false for _, codec := range answerTransceiver.codecs { if strings.EqualFold(codec.RTPCodecCapability.MimeType, MimeTypeRTX) { foundRTX = true break } } assert.True(t, foundRTX) answerTransceiver.mu.RUnlock() // getCodecs() should have RTX as remote offer has not been processed codecs := answerTransceiver.getCodecs() foundRTX = false for _, codec := range codecs { if strings.EqualFold(codec.RTPCodecCapability.MimeType, MimeTypeRTX) { foundRTX = true break } } assert.True(t, foundRTX) offer, err := offerPC.CreateOffer(nil) assert.NoError(t, err) assert.NoError(t, offerPC.SetLocalDescription(offer)) assert.NoError(t, answerPC.SetRemoteDescription(offer)) // getCodecs() should filter out RTX as remote does not offer testCodec (primary) codecs = answerTransceiver.getCodecs() foundRTX = false for _, codec := range codecs { if strings.EqualFold(codec.RTPCodecCapability.MimeType, MimeTypeRTX) { foundRTX = true break } } assert.False(t, foundRTX) answer, err := answerPC.CreateAnswer(nil) assert.NoError(t, err) // VP8 with proper PayloadType assert.NotEqual(t, -1, strings.Index(answer.SDP, "a=rtpmap:52 VP8/90000")) // testCodec is ignored since offerer doesn't support assert.Equal(t, -1, strings.Index(answer.SDP, "testCodec")) assert.Equal(t, -1, strings.Index(answer.SDP, "rtx")) closePairNow(t, offerPC, answerPC) }