| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
- // SPDX-License-Identifier: MIT
- //go:build !js
- // +build !js
- package webrtc
- import (
- "fmt"
- "regexp"
- "strings"
- "testing"
- "github.com/pion/sdp/v3"
- "github.com/pion/transport/v2/test"
- "github.com/stretchr/testify/assert"
- )
- // pion/webrtc#1078
- func TestOpusCase(t *testing.T) {
- pc, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- _, err = pc.AddTransceiverFromKind(RTPCodecTypeAudio)
- assert.NoError(t, err)
- offer, err := pc.CreateOffer(nil)
- assert.NoError(t, err)
- assert.True(t, regexp.MustCompile(`(?m)^a=rtpmap:\d+ opus/48000/2`).MatchString(offer.SDP))
- assert.NoError(t, pc.Close())
- }
- // pion/example-webrtc-applications#89
- func TestVideoCase(t *testing.T) {
- pc, err := NewPeerConnection(Configuration{})
- assert.NoError(t, err)
- _, err = pc.AddTransceiverFromKind(RTPCodecTypeVideo)
- assert.NoError(t, err)
- offer, err := pc.CreateOffer(nil)
- assert.NoError(t, err)
- assert.True(t, regexp.MustCompile(`(?m)^a=rtpmap:\d+ H264/90000`).MatchString(offer.SDP))
- assert.True(t, regexp.MustCompile(`(?m)^a=rtpmap:\d+ VP8/90000`).MatchString(offer.SDP))
- assert.True(t, regexp.MustCompile(`(?m)^a=rtpmap:\d+ VP9/90000`).MatchString(offer.SDP))
- assert.NoError(t, pc.Close())
- }
- func TestMediaEngineRemoteDescription(t *testing.T) {
- mustParse := func(raw string) sdp.SessionDescription {
- s := sdp.SessionDescription{}
- assert.NoError(t, s.Unmarshal([]byte(raw)))
- return s
- }
- t.Run("No Media", func(t *testing.T) {
- const noMedia = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(noMedia)))
- assert.False(t, m.negotiatedVideo)
- assert.False(t, m.negotiatedAudio)
- })
- t.Run("Enable Opus", func(t *testing.T) {
- const opusSamePayload = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=audio 9 UDP/TLS/RTP/SAVPF 111
- a=rtpmap:111 opus/48000/2
- a=fmtp:111 minptime=10; useinbandfec=1
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusSamePayload)))
- assert.False(t, m.negotiatedVideo)
- assert.True(t, m.negotiatedAudio)
- opusCodec, _, err := m.getCodecByPayload(111)
- assert.NoError(t, err)
- assert.Equal(t, opusCodec.MimeType, MimeTypeOpus)
- })
- t.Run("Change Payload Type", func(t *testing.T) {
- const opusSamePayload = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=audio 9 UDP/TLS/RTP/SAVPF 112
- a=rtpmap:112 opus/48000/2
- a=fmtp:112 minptime=10; useinbandfec=1
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusSamePayload)))
- assert.False(t, m.negotiatedVideo)
- assert.True(t, m.negotiatedAudio)
- _, _, err := m.getCodecByPayload(111)
- assert.Error(t, err)
- opusCodec, _, err := m.getCodecByPayload(112)
- assert.NoError(t, err)
- assert.Equal(t, opusCodec.MimeType, MimeTypeOpus)
- })
- t.Run("Ambiguous Payload Type", func(t *testing.T) {
- const opusSamePayload = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=audio 9 UDP/TLS/RTP/SAVPF 96
- a=rtpmap:96 opus/48000/2
- a=fmtp:96 minptime=10; useinbandfec=1
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusSamePayload)))
- assert.False(t, m.negotiatedVideo)
- assert.True(t, m.negotiatedAudio)
- opusCodec, _, err := m.getCodecByPayload(96)
- assert.NoError(t, err)
- assert.Equal(t, opusCodec.MimeType, MimeTypeOpus)
- })
- t.Run("Case Insensitive", func(t *testing.T) {
- const opusUpcase = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=audio 9 UDP/TLS/RTP/SAVPF 111
- a=rtpmap:111 OPUS/48000/2
- a=fmtp:111 minptime=10; useinbandfec=1
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusUpcase)))
- assert.False(t, m.negotiatedVideo)
- assert.True(t, m.negotiatedAudio)
- opusCodec, _, err := m.getCodecByPayload(111)
- assert.NoError(t, err)
- assert.Equal(t, opusCodec.MimeType, "audio/OPUS")
- })
- t.Run("Handle different fmtp", func(t *testing.T) {
- const opusNoFmtp = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=audio 9 UDP/TLS/RTP/SAVPF 111
- a=rtpmap:111 opus/48000/2
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusNoFmtp)))
- assert.False(t, m.negotiatedVideo)
- assert.True(t, m.negotiatedAudio)
- opusCodec, _, err := m.getCodecByPayload(111)
- assert.NoError(t, err)
- assert.Equal(t, opusCodec.MimeType, MimeTypeOpus)
- })
- t.Run("Header Extensions", func(t *testing.T) {
- const headerExtensions = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=audio 9 UDP/TLS/RTP/SAVPF 111
- a=extmap:7 urn:ietf:params:rtp-hdrext:sdes:mid
- a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
- a=rtpmap:111 opus/48000/2
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- registerSimulcastHeaderExtensions(&m, RTPCodecTypeAudio)
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(headerExtensions)))
- assert.False(t, m.negotiatedVideo)
- assert.True(t, m.negotiatedAudio)
- absID, absAudioEnabled, absVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.ABSSendTimeURI})
- assert.Equal(t, absID, 0)
- assert.False(t, absAudioEnabled)
- assert.False(t, absVideoEnabled)
- midID, midAudioEnabled, midVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI})
- assert.Equal(t, midID, 7)
- assert.True(t, midAudioEnabled)
- assert.False(t, midVideoEnabled)
- })
- t.Run("Prefers exact codec matches", func(t *testing.T) {
- const profileLevels = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=video 60323 UDP/TLS/RTP/SAVPF 96 98
- a=rtpmap:96 H264/90000
- a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f
- a=rtpmap:98 H264/90000
- a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil},
- PayloadType: 127,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels)))
- assert.True(t, m.negotiatedVideo)
- assert.False(t, m.negotiatedAudio)
- supportedH264, _, err := m.getCodecByPayload(98)
- assert.NoError(t, err)
- assert.Equal(t, supportedH264.MimeType, MimeTypeH264)
- _, _, err = m.getCodecByPayload(96)
- assert.Error(t, err)
- })
- t.Run("Does not match when fmtpline is set and does not match", func(t *testing.T) {
- const profileLevels = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=video 60323 UDP/TLS/RTP/SAVPF 96 98
- a=rtpmap:96 H264/90000
- a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil},
- PayloadType: 127,
- }, RTPCodecTypeVideo))
- assert.Error(t, m.updateFromRemoteDescription(mustParse(profileLevels)))
- _, _, err := m.getCodecByPayload(96)
- assert.Error(t, err)
- })
- t.Run("Matches when fmtpline is not set in offer, but exists in mediaengine", func(t *testing.T) {
- const profileLevels = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=video 60323 UDP/TLS/RTP/SAVPF 96
- a=rtpmap:96 VP9/90000
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=0", nil},
- PayloadType: 98,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels)))
- assert.True(t, m.negotiatedVideo)
- _, _, err := m.getCodecByPayload(96)
- assert.NoError(t, err)
- })
- t.Run("Matches when fmtpline exists in neither", func(t *testing.T) {
- const profileLevels = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=video 60323 UDP/TLS/RTP/SAVPF 96
- a=rtpmap:96 VP8/90000
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
- PayloadType: 96,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels)))
- assert.True(t, m.negotiatedVideo)
- _, _, err := m.getCodecByPayload(96)
- assert.NoError(t, err)
- })
- t.Run("Matches when rtx apt for exact match codec", func(t *testing.T) {
- const profileLevels = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=video 60323 UDP/TLS/RTP/SAVPF 94 96 97
- a=rtpmap:94 VP8/90000
- a=rtpmap:96 VP9/90000
- a=fmtp:96 profile-id=2
- a=rtpmap:97 rtx/90000
- a=fmtp:97 apt=96
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
- PayloadType: 94,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=2", nil},
- PayloadType: 96,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
- PayloadType: 97,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels)))
- assert.True(t, m.negotiatedVideo)
- _, _, err := m.getCodecByPayload(97)
- assert.NoError(t, err)
- })
- t.Run("Matches when rtx apt for partial match codec", func(t *testing.T) {
- const profileLevels = `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=video 60323 UDP/TLS/RTP/SAVPF 94 96 97
- a=rtpmap:94 VP8/90000
- a=rtpmap:96 VP9/90000
- a=fmtp:96 profile-id=2
- a=rtpmap:97 rtx/90000
- a=fmtp:97 apt=96
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
- PayloadType: 94,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=1", nil},
- PayloadType: 96,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.RegisterCodec(RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
- PayloadType: 97,
- }, RTPCodecTypeVideo))
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels)))
- assert.True(t, m.negotiatedVideo)
- _, _, err := m.getCodecByPayload(97)
- assert.ErrorIs(t, err, ErrCodecNotFound)
- })
- }
- func TestMediaEngineHeaderExtensionDirection(t *testing.T) {
- report := test.CheckRoutines(t)
- defer report()
- registerCodec := func(m *MediaEngine) {
- assert.NoError(t, m.RegisterCodec(
- RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 0, "", nil},
- PayloadType: 111,
- }, RTPCodecTypeAudio))
- }
- t.Run("No Direction", func(t *testing.T) {
- m := &MediaEngine{}
- registerCodec(m)
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio))
- params := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
- assert.Equal(t, 1, len(params.HeaderExtensions))
- })
- t.Run("Same Direction", func(t *testing.T) {
- m := &MediaEngine{}
- registerCodec(m)
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionRecvonly))
- params := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
- assert.Equal(t, 1, len(params.HeaderExtensions))
- })
- t.Run("Different Direction", func(t *testing.T) {
- m := &MediaEngine{}
- registerCodec(m)
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionSendonly))
- params := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
- assert.Equal(t, 0, len(params.HeaderExtensions))
- })
- t.Run("Invalid Direction", func(t *testing.T) {
- m := &MediaEngine{}
- registerCodec(m)
- assert.ErrorIs(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv), ErrRegisterHeaderExtensionInvalidDirection)
- assert.ErrorIs(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionInactive), ErrRegisterHeaderExtensionInvalidDirection)
- assert.ErrorIs(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirection(0)), ErrRegisterHeaderExtensionInvalidDirection)
- })
- t.Run("Unique extmapid with different codec", func(t *testing.T) {
- m := &MediaEngine{}
- registerCodec(m)
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio))
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test2"}, RTPCodecTypeVideo))
- audio := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
- video := m.getRTPParametersByKind(RTPCodecTypeVideo, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
- assert.Equal(t, 1, len(audio.HeaderExtensions))
- assert.Equal(t, 1, len(video.HeaderExtensions))
- assert.NotEqual(t, audio.HeaderExtensions[0].ID, video.HeaderExtensions[0].ID)
- })
- }
- // If a user attempts to register a codec twice we should just discard duplicate calls
- func TestMediaEngineDoubleRegister(t *testing.T) {
- m := MediaEngine{}
- assert.NoError(t, m.RegisterCodec(
- RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 0, "", nil},
- PayloadType: 111,
- }, RTPCodecTypeAudio))
- assert.NoError(t, m.RegisterCodec(
- RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 0, "", nil},
- PayloadType: 111,
- }, RTPCodecTypeAudio))
- assert.Equal(t, len(m.audioCodecs), 1)
- }
- // The cloned MediaEngine instance should be able to update negotiated header extensions.
- func TestUpdateHeaderExtenstionToClonedMediaEngine(t *testing.T) {
- src := MediaEngine{}
- assert.NoError(t, src.RegisterCodec(
- RTPCodecParameters{
- RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 0, "", nil},
- PayloadType: 111,
- }, RTPCodecTypeAudio))
- assert.NoError(t, src.RegisterHeaderExtension(RTPHeaderExtensionCapability{"test-extension"}, RTPCodecTypeAudio))
- validate := func(m *MediaEngine) {
- assert.NoError(t, m.updateHeaderExtension(2, "test-extension", RTPCodecTypeAudio))
- id, audioNegotiated, videoNegotiated := m.getHeaderExtensionID(RTPHeaderExtensionCapability{URI: "test-extension"})
- assert.Equal(t, 2, id)
- assert.True(t, audioNegotiated)
- assert.False(t, videoNegotiated)
- }
- validate(&src)
- validate(src.copy())
- }
- func TestExtensionIdCollision(t *testing.T) {
- mustParse := func(raw string) sdp.SessionDescription {
- s := sdp.SessionDescription{}
- assert.NoError(t, s.Unmarshal([]byte(raw)))
- return s
- }
- sdpSnippet := `v=0
- o=- 4596489990601351948 2 IN IP4 127.0.0.1
- s=-
- t=0 0
- m=audio 9 UDP/TLS/RTP/SAVPF 111
- a=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid
- a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
- a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
- a=rtpmap:111 opus/48000/2
- `
- m := MediaEngine{}
- assert.NoError(t, m.RegisterDefaultCodecs())
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.SDESMidURI}, RTPCodecTypeVideo))
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"urn:3gpp:video-orientation"}, RTPCodecTypeVideo))
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.SDESMidURI}, RTPCodecTypeAudio))
- assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.AudioLevelURI}, RTPCodecTypeAudio))
- assert.NoError(t, m.updateFromRemoteDescription(mustParse(sdpSnippet)))
- assert.True(t, m.negotiatedAudio)
- assert.False(t, m.negotiatedVideo)
- id, audioNegotiated, videoNegotiated := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.ABSSendTimeURI})
- assert.Equal(t, id, 0)
- assert.False(t, audioNegotiated)
- assert.False(t, videoNegotiated)
- id, audioNegotiated, videoNegotiated = m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI})
- assert.Equal(t, id, 2)
- assert.True(t, audioNegotiated)
- assert.False(t, videoNegotiated)
- id, audioNegotiated, videoNegotiated = m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.AudioLevelURI})
- assert.Equal(t, id, 1)
- assert.True(t, audioNegotiated)
- assert.False(t, videoNegotiated)
- params := m.getRTPParametersByKind(RTPCodecTypeVideo, []RTPTransceiverDirection{RTPTransceiverDirectionSendonly})
- extensions := params.HeaderExtensions
- assert.Equal(t, 2, len(extensions))
- midIndex := -1
- if extensions[0].URI == sdp.SDESMidURI {
- midIndex = 0
- } else if extensions[1].URI == sdp.SDESMidURI {
- midIndex = 1
- }
- voIndex := -1
- if extensions[0].URI == "urn:3gpp:video-orientation" {
- voIndex = 0
- } else if extensions[1].URI == "urn:3gpp:video-orientation" {
- voIndex = 1
- }
- assert.NotEqual(t, midIndex, -1)
- assert.NotEqual(t, voIndex, -1)
- assert.Equal(t, 2, extensions[midIndex].ID)
- assert.NotEqual(t, 1, extensions[voIndex].ID)
- 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())
- })
- }
- }
|