mirror of
				https://github.com/pion/webrtc.git
				synced 2025-10-31 18:52:55 +08:00 
			
		
		
		
	 c6ad8c78ea
			
		
	
	c6ad8c78ea
	
	
	
		
			
			If we receive an unknown SSRC and we have a single media section with no SSRCes declared fire an OnTrack with that stream Resolves #880
		
			
				
	
	
		
			309 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build !js
 | |
| 
 | |
| package webrtc
 | |
| 
 | |
| import (
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/pion/sdp/v2"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| )
 | |
| 
 | |
| func TestSDPSemantics_String(t *testing.T) {
 | |
| 	testCases := []struct {
 | |
| 		value          SDPSemantics
 | |
| 		expectedString string
 | |
| 	}{
 | |
| 		{SDPSemantics(42), unknownStr},
 | |
| 		{SDPSemanticsUnifiedPlanWithFallback, "unified-plan-with-fallback"},
 | |
| 		{SDPSemanticsPlanB, "plan-b"},
 | |
| 		{SDPSemanticsUnifiedPlan, "unified-plan"},
 | |
| 	}
 | |
| 
 | |
| 	for i, testCase := range testCases {
 | |
| 		assert.Equal(t,
 | |
| 			testCase.expectedString,
 | |
| 			testCase.value.String(),
 | |
| 			"testCase: %d %v", i, testCase,
 | |
| 		)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // The following tests are for non-standard SDP semantics
 | |
| // (i.e. not unified-unified)
 | |
| 
 | |
| func getMdNames(sdp *sdp.SessionDescription) []string {
 | |
| 	mdNames := make([]string, 0, len(sdp.MediaDescriptions))
 | |
| 	for _, media := range sdp.MediaDescriptions {
 | |
| 		mdNames = append(mdNames, media.MediaName.Media)
 | |
| 	}
 | |
| 	return mdNames
 | |
| }
 | |
| 
 | |
| func extractSsrcList(md *sdp.MediaDescription) []string {
 | |
| 	ssrcMap := map[string]struct{}{}
 | |
| 	for _, attr := range md.Attributes {
 | |
| 		if attr.Key == ssrcStr {
 | |
| 			ssrc := strings.Fields(attr.Value)[0]
 | |
| 			ssrcMap[ssrc] = struct{}{}
 | |
| 		}
 | |
| 	}
 | |
| 	ssrcList := make([]string, 0, len(ssrcMap))
 | |
| 	for ssrc := range ssrcMap {
 | |
| 		ssrcList = append(ssrcList, ssrc)
 | |
| 	}
 | |
| 	return ssrcList
 | |
| }
 | |
| 
 | |
| func TestSDPSemantics_PlanBOfferTransceivers(t *testing.T) {
 | |
| 	opc, err := NewPeerConnection(Configuration{
 | |
| 		SDPSemantics: SDPSemanticsPlanB,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		t.Errorf("NewPeerConnection failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeVideo, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionSendrecv,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("AddTransceiver failed: %v", err)
 | |
| 	}
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeVideo, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionSendrecv,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("AddTransceiver failed: %v", err)
 | |
| 	}
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeAudio, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionSendrecv,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("AddTransceiver failed: %v", err)
 | |
| 	}
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeAudio, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionSendrecv,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("AddTransceiver failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	offer, err := opc.CreateOffer(nil)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Plan B CreateOffer failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	mdNames := getMdNames(offer.parsed)
 | |
| 	assert.ObjectsAreEqual(mdNames, []string{"video", "audio", "data"})
 | |
| 
 | |
| 	// Verify that each section has 2 SSRCs (one for each transceiver)
 | |
| 	for _, section := range []string{"video", "audio"} {
 | |
| 		for _, media := range offer.parsed.MediaDescriptions {
 | |
| 			if media.MediaName.Media == section {
 | |
| 				assert.Len(t, extractSsrcList(media), 2)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	apc, err := NewPeerConnection(Configuration{
 | |
| 		SDPSemantics: SDPSemanticsPlanB,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		t.Errorf("NewPeerConnection failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if err = apc.SetRemoteDescription(offer); err != nil {
 | |
| 		t.Errorf("SetRemoteDescription failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	answer, err := apc.CreateAnswer(nil)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Plan B CreateAnswer failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	mdNames = getMdNames(answer.parsed)
 | |
| 	assert.ObjectsAreEqual(mdNames, []string{"video", "audio", "data"})
 | |
| }
 | |
| 
 | |
| func TestSDPSemantics_PlanBAnswerSenders(t *testing.T) {
 | |
| 	opc, err := NewPeerConnection(Configuration{
 | |
| 		SDPSemantics: SDPSemanticsPlanB,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		t.Errorf("NewPeerConnection failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeVideo, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionRecvonly,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("Failed to add transceiver")
 | |
| 	}
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeAudio, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionRecvonly,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("Failed to add transceiver")
 | |
| 	}
 | |
| 
 | |
| 	offer, err := opc.CreateOffer(nil)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Plan B CreateOffer failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	mdNames := getMdNames(offer.parsed)
 | |
| 	assert.ObjectsAreEqual(mdNames, []string{"video", "audio", "data"})
 | |
| 
 | |
| 	apc, err := NewPeerConnection(Configuration{
 | |
| 		SDPSemantics: SDPSemanticsPlanB,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		t.Errorf("NewPeerConnection failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	video1, err := NewTrack(DefaultPayloadTypeH264, 1, "1", "1", NewRTPH264Codec(DefaultPayloadTypeH264, 90000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create video track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(video1); err != nil {
 | |
| 		t.Errorf("Failed to add video track")
 | |
| 	}
 | |
| 	video2, err := NewTrack(DefaultPayloadTypeH264, 2, "2", "2", NewRTPH264Codec(DefaultPayloadTypeH264, 90000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create video track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(video2); err != nil {
 | |
| 		t.Errorf("Failed to add video track")
 | |
| 	}
 | |
| 	audio1, err := NewTrack(DefaultPayloadTypeOpus, 3, "3", "3", NewRTPOpusCodec(DefaultPayloadTypeOpus, 48000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create audio track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(audio1); err != nil {
 | |
| 		t.Errorf("Failed to add audio track")
 | |
| 	}
 | |
| 	audio2, err := NewTrack(DefaultPayloadTypeOpus, 4, "4", "4", NewRTPOpusCodec(DefaultPayloadTypeOpus, 48000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create audio track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(audio2); err != nil {
 | |
| 		t.Errorf("Failed to add audio track")
 | |
| 	}
 | |
| 
 | |
| 	if err = apc.SetRemoteDescription(offer); err != nil {
 | |
| 		t.Errorf("SetRemoteDescription failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	answer, err := apc.CreateAnswer(nil)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Plan B CreateAnswer failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	mdNames = getMdNames(answer.parsed)
 | |
| 	assert.ObjectsAreEqual(mdNames, []string{"video", "audio", "data"})
 | |
| 
 | |
| 	// Verify that each section has 2 SSRCs (one for each sender)
 | |
| 	for _, section := range []string{"video", "audio"} {
 | |
| 		for _, media := range answer.parsed.MediaDescriptions {
 | |
| 			if media.MediaName.Media == section {
 | |
| 				assert.Lenf(t, extractSsrcList(media), 2, "%q should have 2 SSRCs in Plan-B mode", section)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSDPSemantics_UnifiedPlanWithFallback(t *testing.T) {
 | |
| 	opc, err := NewPeerConnection(Configuration{
 | |
| 		SDPSemantics: SDPSemanticsPlanB,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		t.Errorf("NewPeerConnection failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeVideo, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionRecvonly,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("Failed to add transceiver")
 | |
| 	}
 | |
| 	if _, err = opc.AddTransceiver(RTPCodecTypeAudio, RtpTransceiverInit{
 | |
| 		Direction: RTPTransceiverDirectionRecvonly,
 | |
| 	}); err != nil {
 | |
| 		t.Errorf("Failed to add transceiver")
 | |
| 	}
 | |
| 
 | |
| 	offer, err := opc.CreateOffer(nil)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Plan B CreateOffer failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	mdNames := getMdNames(offer.parsed)
 | |
| 	assert.ObjectsAreEqual(mdNames, []string{"video", "audio", "data"})
 | |
| 
 | |
| 	apc, err := NewPeerConnection(Configuration{
 | |
| 		SDPSemantics: SDPSemanticsUnifiedPlanWithFallback,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		t.Errorf("NewPeerConnection failed: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	video1, err := NewTrack(DefaultPayloadTypeH264, 1, "1", "1", NewRTPH264Codec(DefaultPayloadTypeH264, 90000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create video track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(video1); err != nil {
 | |
| 		t.Errorf("Failed to add video track")
 | |
| 	}
 | |
| 	video2, err := NewTrack(DefaultPayloadTypeH264, 2, "2", "2", NewRTPH264Codec(DefaultPayloadTypeH264, 90000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create video track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(video2); err != nil {
 | |
| 		t.Errorf("Failed to add video track")
 | |
| 	}
 | |
| 	audio1, err := NewTrack(DefaultPayloadTypeOpus, 3, "3", "3", NewRTPOpusCodec(DefaultPayloadTypeOpus, 48000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create audio track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(audio1); err != nil {
 | |
| 		t.Errorf("Failed to add audio track")
 | |
| 	}
 | |
| 	audio2, err := NewTrack(DefaultPayloadTypeOpus, 4, "4", "4", NewRTPOpusCodec(DefaultPayloadTypeOpus, 48000))
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Failed to create audio track")
 | |
| 	}
 | |
| 	if _, err = apc.AddTrack(audio2); err != nil {
 | |
| 		t.Errorf("Failed to add audio track")
 | |
| 	}
 | |
| 
 | |
| 	if err = apc.SetRemoteDescription(offer); err != nil {
 | |
| 		t.Errorf("SetRemoteDescription failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	answer, err := apc.CreateAnswer(nil)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Plan B CreateAnswer failed: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	mdNames = getMdNames(answer.parsed)
 | |
| 	assert.ObjectsAreEqual(mdNames, []string{"video", "audio", "data"})
 | |
| 
 | |
| 	extractSsrcList := func(md *sdp.MediaDescription) []string {
 | |
| 		ssrcMap := map[string]struct{}{}
 | |
| 		for _, attr := range md.Attributes {
 | |
| 			if attr.Key == ssrcStr {
 | |
| 				ssrc := strings.Fields(attr.Value)[0]
 | |
| 				ssrcMap[ssrc] = struct{}{}
 | |
| 			}
 | |
| 		}
 | |
| 		ssrcList := make([]string, 0, len(ssrcMap))
 | |
| 		for ssrc := range ssrcMap {
 | |
| 			ssrcList = append(ssrcList, ssrc)
 | |
| 		}
 | |
| 		return ssrcList
 | |
| 	}
 | |
| 	// Verify that each section has 2 SSRCs (one for each sender)
 | |
| 	for _, section := range []string{"video", "audio"} {
 | |
| 		for _, media := range answer.parsed.MediaDescriptions {
 | |
| 			if media.MediaName.Media == section {
 | |
| 				assert.Lenf(t, extractSsrcList(media), 2, "%q should have 2 SSRCs in Plan-B fallback mode", section)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |