1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var sdputils = {
checkSdpAfterEndOfTrickle: function(sdp, testOptions, label) {
info("EOC-SDP: " + JSON.stringify(sdp));
ok(sdp.sdp.includes("a=end-of-candidates"), label + ": SDP contains end-of-candidates");
sdputils.checkSdpCLineNotDefault(sdp.sdp, label);
if (testOptions.rtcpmux) {
ok(sdp.sdp.includes("a=rtcp-mux"), label + ": SDP contains rtcp-mux");
} else {
ok(sdp.sdp.includes("a=rtcp:"), label + ": SDP contains rtcp port");
}
},
// takes sdp in string form (or possibly a fragment, say an m-section), and
// verifies that the default 0.0.0.0 addr is not present.
checkSdpCLineNotDefault: function(sdpStr, label) {
info("CLINE-NO-DEFAULT-ADDR-SDP: " + JSON.stringify(sdpStr));
ok(!sdpStr.includes("c=IN IP4 0.0.0.0"), label + ": SDP contains non-zero IP c line");
},
// Note, we don't bother removing the fmtp lines, which makes a good test
// for some SDP parsing issues. It would be good to have a "removeAllButCodec(sdp, codec)" too.
removeCodec: function(sdp, codec) {
var updated_sdp = sdp.replace(new RegExp("a=rtpmap:" + codec + ".*\\/90000\\r\\n",""),"");
updated_sdp = updated_sdp.replace(new RegExp("(RTP\\/SAVPF.*)( " + codec + ")(.*\\r\\n)",""),"$1$3");
updated_sdp = updated_sdp.replace(new RegExp("a=rtcp-fb:" + codec + " nack\\r\\n",""),"");
updated_sdp = updated_sdp.replace(new RegExp("a=rtcp-fb:" + codec + " nack pli\\r\\n",""),"");
updated_sdp = updated_sdp.replace(new RegExp("a=rtcp-fb:" + codec + " ccm fir\\r\\n",""),"");
return updated_sdp;
},
removeRtcpMux: function(sdp) {
return sdp.replace(/a=rtcp-mux\r\n/g,"");
},
removeBundle: function(sdp) {
return sdp.replace(/a=group:BUNDLE .*\r\n/g, "");
},
reduceAudioMLineToPcmuPcma: function(sdp) {
return sdp.replace(/m=audio .*\r\n/g, "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n");
},
setAllMsectionsInactive: function(sdp) {
return sdp.replace(/\r\na=sendrecv/g, "\r\na=inactive")
.replace(/\r\na=sendonly/g, "\r\na=inactive")
.replace(/\r\na=recvonly/g, "\r\na=inactive");
},
removeAllRtpMaps: function(sdp) {
return sdp.replace(/a=rtpmap:.*\r\n/g, "");
},
reduceAudioMLineToDynamicPtAndOpus: function(sdp) {
return sdp.replace(/m=audio .*\r\n/g, "m=audio 9 UDP/TLS/RTP/SAVPF 101 109\r\n");
},
transferSimulcastProperties: function(offer_sdp, answer_sdp) {
if (!offer_sdp.includes("a=simulcast:")) {
return answer_sdp;
}
var o_simul = offer_sdp.match(/simulcast: send rid=(.*)([\n$])*/i);
var o_rids = offer_sdp.match(/a=rid:(.*)/ig);
var new_answer_sdp = answer_sdp + "a=simulcast: recv rid=" + o_simul[1] + "\r\n";
o_rids.forEach((o_rid) => {
new_answer_sdp = new_answer_sdp + o_rid.replace(/send/, "recv") + "\r\n";
});
return new_answer_sdp;
},
verifySdp: function(desc, expectedType, offerConstraintsList, offerOptions,
testOptions) {
info("Examining this SessionDescription: " + JSON.stringify(desc));
info("offerConstraintsList: " + JSON.stringify(offerConstraintsList));
info("offerOptions: " + JSON.stringify(offerOptions));
ok(desc, "SessionDescription is not null");
is(desc.type, expectedType, "SessionDescription type is " + expectedType);
ok(desc.sdp.length > 10, "SessionDescription body length is plausible");
ok(desc.sdp.includes("a=ice-ufrag"), "ICE username is present in SDP");
ok(desc.sdp.includes("a=ice-pwd"), "ICE password is present in SDP");
ok(desc.sdp.includes("a=fingerprint"), "ICE fingerprint is present in SDP");
//TODO: update this for loopback support bug 1027350
ok(!desc.sdp.includes(LOOPBACK_ADDR), "loopback interface is absent from SDP");
var requiresTrickleIce = !desc.sdp.includes("a=candidate");
if (requiresTrickleIce) {
info("No ICE candidate in SDP -> requiring trickle ICE");
} else {
info("at least one ICE candidate is present in SDP");
}
//TODO: how can we check for absence/presence of m=application?
var audioTracks =
sdputils.countTracksInConstraint('audio', offerConstraintsList) ||
((offerOptions && offerOptions.offerToReceiveAudio) ? 1 : 0);
info("expected audio tracks: " + audioTracks);
if (audioTracks == 0) {
ok(!desc.sdp.includes("m=audio"), "audio m-line is absent from SDP");
} else {
ok(desc.sdp.includes("m=audio"), "audio m-line is present in SDP");
is(testOptions.opus, desc.sdp.includes("a=rtpmap:109 opus/48000/2"), "OPUS codec is present in SDP");
//TODO: ideally the rtcp-mux should be for the m=audio, and not just
// anywhere in the SDP (JS SDP parser bug 1045429)
is(testOptions.rtcpmux, desc.sdp.includes("a=rtcp-mux"), "RTCP Mux is offered in SDP");
}
var videoTracks =
sdputils.countTracksInConstraint('video', offerConstraintsList) ||
((offerOptions && offerOptions.offerToReceiveVideo) ? 1 : 0);
info("expected video tracks: " + videoTracks);
if (videoTracks == 0) {
ok(!desc.sdp.includes("m=video"), "video m-line is absent from SDP");
} else {
ok(desc.sdp.includes("m=video"), "video m-line is present in SDP");
if (testOptions.h264) {
ok(desc.sdp.includes("a=rtpmap:126 H264/90000"), "H.264 codec is present in SDP");
} else {
ok(desc.sdp.includes("a=rtpmap:120 VP8/90000") ||
desc.sdp.includes("a=rtpmap:121 VP9/90000"), "VP8 or VP9 codec is present in SDP");
}
is(testOptions.rtcpmux, desc.sdp.includes("a=rtcp-mux"), "RTCP Mux is offered in SDP");
}
return requiresTrickleIce;
},
/**
* Counts the amount of audio tracks in a given media constraint.
*
* @param constraints
* The contraint to be examined.
*/
countTracksInConstraint: function(type, constraints) {
if (!Array.isArray(constraints)) {
return 0;
}
return constraints.reduce((sum, c) => sum + (c[type] ? 1 : 0), 0);
},
};
|