|
@@ -17,27 +17,37 @@ function SDP(sdp) {
|
17
|
17
|
SDP.prototype.getMediaSsrcMap = function() {
|
18
|
18
|
var self = this;
|
19
|
19
|
var media_ssrcs = {};
|
20
|
|
- for (channelNum = 0; channelNum < self.media.length; channelNum++) {
|
21
|
|
- modified = true;
|
22
|
|
- tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc:');
|
23
|
|
- var type = SDPUtil.parse_mid(SDPUtil.find_line(self.media[channelNum], 'a=mid:'));
|
24
|
|
- var channel = new MediaChannel(channelNum, type);
|
25
|
|
- media_ssrcs[channelNum] = channel;
|
|
20
|
+ var tmp;
|
|
21
|
+ for (var mediaindex = 0; mediaindex < self.media.length; mediaindex++) {
|
|
22
|
+ tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc:');
|
|
23
|
+ var mid = SDPUtil.parse_mid(SDPUtil.find_line(self.media[mediaindex], 'a=mid:'));
|
|
24
|
+ var media = {
|
|
25
|
+ mediaindex: mediaindex,
|
|
26
|
+ mid: mid,
|
|
27
|
+ ssrcs: {},
|
|
28
|
+ ssrcGroups: []
|
|
29
|
+ };
|
|
30
|
+ media_ssrcs[mediaindex] = media;
|
26
|
31
|
tmp.forEach(function (line) {
|
27
|
32
|
var linessrc = line.substring(7).split(' ')[0];
|
28
|
33
|
// allocate new ChannelSsrc
|
29
|
|
- if(!channel.ssrcs[linessrc]) {
|
30
|
|
- channel.ssrcs[linessrc] = new ChannelSsrc(linessrc, type);
|
|
34
|
+ if(!media.ssrcs[linessrc]) {
|
|
35
|
+ media.ssrcs[linessrc] = {
|
|
36
|
+ ssrc: linessrc,
|
|
37
|
+ lines: []
|
|
38
|
+ };
|
31
|
39
|
}
|
32
|
|
- channel.ssrcs[linessrc].lines.push(line);
|
|
40
|
+ media.ssrcs[linessrc].lines.push(line);
|
33
|
41
|
});
|
34
|
|
- tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc-group:');
|
|
42
|
+ tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc-group:');
|
35
|
43
|
tmp.forEach(function(line){
|
36
|
44
|
var semantics = line.substr(0, idx).substr(13);
|
37
|
45
|
var ssrcs = line.substr(14 + semantics.length).split(' ');
|
38
|
46
|
if (ssrcs.length != 0) {
|
39
|
|
- var ssrcGroup = new ChannelSsrcGroup(semantics, ssrcs);
|
40
|
|
- channel.ssrcGroups.push(ssrcGroup);
|
|
47
|
+ media.ssrcGroups.push({
|
|
48
|
+ semantics: semantics,
|
|
49
|
+ ssrcs: ssrcs
|
|
50
|
+ });
|
41
|
51
|
}
|
42
|
52
|
});
|
43
|
53
|
}
|
|
@@ -49,23 +59,28 @@ SDP.prototype.getMediaSsrcMap = function() {
|
49
|
59
|
* @returns {boolean} <tt>true</tt> if this SDP contains given SSRC.
|
50
|
60
|
*/
|
51
|
61
|
SDP.prototype.containsSSRC = function(ssrc) {
|
52
|
|
- var channels = this.getMediaSsrcMap();
|
|
62
|
+ var medias = this.getMediaSsrcMap();
|
53
|
63
|
var contains = false;
|
54
|
|
- Object.keys(channels).forEach(function(chNumber){
|
55
|
|
- var channel = channels[chNumber];
|
|
64
|
+ Object.keys(medias).forEach(function(mediaindex){
|
|
65
|
+ var media = medias[mediaindex];
|
56
|
66
|
//console.log("Check", channel, ssrc);
|
57
|
|
- if(Object.keys(channel.ssrcs).indexOf(ssrc) != -1){
|
|
67
|
+ if(Object.keys(media.ssrcs).indexOf(ssrc) != -1){
|
58
|
68
|
contains = true;
|
59
|
69
|
}
|
60
|
70
|
});
|
61
|
71
|
return contains;
|
62
|
72
|
};
|
63
|
73
|
|
|
74
|
+function SDPDiffer(mySDP, otherSDP) {
|
|
75
|
+ this.mySDP = new SDP(mySDP);
|
|
76
|
+ this.otherSDP = new SDP(otherSDP);
|
|
77
|
+}
|
|
78
|
+
|
64
|
79
|
/**
|
65
|
80
|
* Returns map of MediaChannel that contains only media not contained in <tt>otherSdp</tt>. Mapped by channel idx.
|
66
|
81
|
* @param otherSdp the other SDP to check ssrc with.
|
67
|
82
|
*/
|
68
|
|
-SDP.prototype.getNewMedia = function(otherSdp) {
|
|
83
|
+SDPDiffer.prototype.getNewMedia = function() {
|
69
|
84
|
|
70
|
85
|
// this could be useful in Array.prototype.
|
71
|
86
|
function arrayEquals(array) {
|
|
@@ -92,35 +107,40 @@ SDP.prototype.getNewMedia = function(otherSdp) {
|
92
|
107
|
return true;
|
93
|
108
|
}
|
94
|
109
|
|
95
|
|
- var myMedia = this.getMediaSsrcMap();
|
96
|
|
- var othersMedia = otherSdp.getMediaSsrcMap();
|
|
110
|
+ var myMedias = this.mySDP.getMediaSsrcMap();
|
|
111
|
+ var othersMedias = this.otherSDP.getMediaSsrcMap();
|
97
|
112
|
var newMedia = {};
|
98
|
|
- Object.keys(othersMedia).forEach(function(channelNum) {
|
99
|
|
- var myChannel = myMedia[channelNum];
|
100
|
|
- var othersChannel = othersMedia[channelNum];
|
101
|
|
- if(!myChannel && othersChannel) {
|
|
113
|
+ Object.keys(othersMedias).forEach(function(othersMediaIdx) {
|
|
114
|
+ var myMedia = myMedias[othersMediaIdx];
|
|
115
|
+ var othersMedia = othersMedias[othersMediaIdx];
|
|
116
|
+ if(!myMedia && othersMedia) {
|
102
|
117
|
// Add whole channel
|
103
|
|
- newMedia[channelNum] = othersChannel;
|
|
118
|
+ newMedia[othersMediaIdx] = othersMedia;
|
104
|
119
|
return;
|
105
|
120
|
}
|
106
|
121
|
// Look for new ssrcs accross the channel
|
107
|
|
- Object.keys(othersChannel.ssrcs).forEach(function(ssrc) {
|
108
|
|
- if(Object.keys(myChannel.ssrcs).indexOf(ssrc) === -1) {
|
|
122
|
+ Object.keys(othersMedia.ssrcs).forEach(function(ssrc) {
|
|
123
|
+ if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {
|
109
|
124
|
// Allocate channel if we've found ssrc that doesn't exist in our channel
|
110
|
|
- if(!newMedia[channelNum]){
|
111
|
|
- newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType);
|
|
125
|
+ if(!newMedia[othersMediaIdx]){
|
|
126
|
+ newMedia[othersMediaIdx] = {
|
|
127
|
+ mediaindex: othersMedia.mediaindex,
|
|
128
|
+ mid: othersMedia.mid,
|
|
129
|
+ ssrcs: {},
|
|
130
|
+ ssrcGroups: []
|
|
131
|
+ };
|
112
|
132
|
}
|
113
|
|
- newMedia[channelNum].ssrcs[ssrc] = othersChannel.ssrcs[ssrc];
|
|
133
|
+ newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
|
114
|
134
|
}
|
115
|
135
|
});
|
116
|
136
|
|
117
|
137
|
// Look for new ssrc groups across the channels
|
118
|
|
- othersChannel.ssrcGroups.forEach(function(otherSsrcGroup){
|
|
138
|
+ othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){
|
119
|
139
|
|
120
|
140
|
// try to match the other ssrc-group with an ssrc-group of ours
|
121
|
141
|
var matched = false;
|
122
|
|
- for (var i = 0; i < myChannel.ssrcGroups.length; i++) {
|
123
|
|
- var mySsrcGroup = myChannel.ssrcGroups[i];
|
|
142
|
+ for (var i = 0; i < myMedia.ssrcGroups.length; i++) {
|
|
143
|
+ var mySsrcGroup = myMedia.ssrcGroups[i];
|
124
|
144
|
if (otherSsrcGroup.semantics == mySsrcGroup.semantics
|
125
|
145
|
&& arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
|
126
|
146
|
|
|
@@ -133,16 +153,88 @@ SDP.prototype.getNewMedia = function(otherSdp) {
|
133
|
153
|
// Allocate channel if we've found an ssrc-group that doesn't
|
134
|
154
|
// exist in our channel
|
135
|
155
|
|
136
|
|
- if(!newMedia[channelNum]){
|
137
|
|
- newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType);
|
|
156
|
+ if(!newMedia[othersMediaIdx]){
|
|
157
|
+ newMedia[othersMediaIdx] = {
|
|
158
|
+ mediaindex: othersMedia.mediaindex,
|
|
159
|
+ mid: othersMedia.mid,
|
|
160
|
+ ssrcs: {},
|
|
161
|
+ ssrcGroups: []
|
|
162
|
+ };
|
138
|
163
|
}
|
139
|
|
- newMedia[channelNum].ssrcGroups.push(otherSsrcGroup);
|
|
164
|
+ newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
|
140
|
165
|
}
|
141
|
166
|
});
|
142
|
167
|
});
|
143
|
168
|
return newMedia;
|
144
|
169
|
};
|
145
|
170
|
|
|
171
|
+/**
|
|
172
|
+ * Sends SSRC update IQ.
|
|
173
|
+ * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove.
|
|
174
|
+ * @param sid session identifier that will be put into the IQ.
|
|
175
|
+ * @param initiator initiator identifier.
|
|
176
|
+ * @param toJid destination Jid
|
|
177
|
+ * @param isAdd indicates if this is remove or add operation.
|
|
178
|
+ */
|
|
179
|
+SDPDiffer.prototype.toJingle = function(modify) {
|
|
180
|
+ var sdpMediaSsrcs = this.getNewMedia();
|
|
181
|
+ var self = this;
|
|
182
|
+
|
|
183
|
+ // FIXME: only announce video ssrcs since we mix audio and dont need
|
|
184
|
+ // the audio ssrcs therefore
|
|
185
|
+ var modified = false;
|
|
186
|
+ Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){
|
|
187
|
+ modified = true;
|
|
188
|
+ var media = sdpMediaSsrcs[mediaindex];
|
|
189
|
+ modify.c('content', {name: media.mid});
|
|
190
|
+
|
|
191
|
+ modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid});
|
|
192
|
+ // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly
|
|
193
|
+ // generate sources from lines
|
|
194
|
+ Object.keys(media.ssrcs).forEach(function(ssrcNum) {
|
|
195
|
+ var mediaSsrc = media.ssrcs[ssrcNum];
|
|
196
|
+ modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
|
197
|
+ modify.attrs({ssrc: mediaSsrc.ssrc});
|
|
198
|
+ // iterate over ssrc lines
|
|
199
|
+ mediaSsrc.lines.forEach(function (line) {
|
|
200
|
+ var idx = line.indexOf(' ');
|
|
201
|
+ var kv = line.substr(idx + 1);
|
|
202
|
+ modify.c('parameter');
|
|
203
|
+ if (kv.indexOf(':') == -1) {
|
|
204
|
+ modify.attrs({ name: kv });
|
|
205
|
+ } else {
|
|
206
|
+ modify.attrs({ name: kv.split(':', 2)[0] });
|
|
207
|
+ modify.attrs({ value: kv.split(':', 2)[1] });
|
|
208
|
+ }
|
|
209
|
+ modify.up(); // end of parameter
|
|
210
|
+ });
|
|
211
|
+ modify.up(); // end of source
|
|
212
|
+ });
|
|
213
|
+
|
|
214
|
+ // generate source groups from lines
|
|
215
|
+ media.ssrcGroups.forEach(function(ssrcGroup) {
|
|
216
|
+ if (ssrcGroup.ssrcs.length != 0) {
|
|
217
|
+
|
|
218
|
+ modify.c('ssrc-group', {
|
|
219
|
+ semantics: ssrcGroup.semantics,
|
|
220
|
+ xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
|
|
221
|
+ });
|
|
222
|
+
|
|
223
|
+ ssrcGroup.ssrcs.forEach(function (ssrc) {
|
|
224
|
+ modify.c('source', { ssrc: ssrc })
|
|
225
|
+ .up(); // end of source
|
|
226
|
+ });
|
|
227
|
+ modify.up(); // end of ssrc-group
|
|
228
|
+ }
|
|
229
|
+ });
|
|
230
|
+
|
|
231
|
+ modify.up(); // end of description
|
|
232
|
+ modify.up(); // end of content
|
|
233
|
+ });
|
|
234
|
+
|
|
235
|
+ return modified;
|
|
236
|
+};
|
|
237
|
+
|
146
|
238
|
// remove iSAC and CN from SDP
|
147
|
239
|
SDP.prototype.mangle = function () {
|
148
|
240
|
var i, j, mline, lines, rtpmap, newdesc;
|
|
@@ -683,62 +775,6 @@ SDP.prototype.jingle2media = function (content) {
|
683
|
775
|
|
684
|
776
|
return media;
|
685
|
777
|
};
|
686
|
|
-/**
|
687
|
|
- * Contains utility classes used in SDP class.
|
688
|
|
- *
|
689
|
|
- */
|
690
|
|
-
|
691
|
|
-/**
|
692
|
|
- * Class holds a=ssrc lines and media type a=mid
|
693
|
|
- * @param ssrc synchronization source identifier number(a=ssrc lines from SDP)
|
694
|
|
- * @param type media type eg. "audio" or "video"(a=mid frm SDP)
|
695
|
|
- * @constructor
|
696
|
|
- */
|
697
|
|
-function ChannelSsrc(ssrc, type) {
|
698
|
|
- this.ssrc = ssrc;
|
699
|
|
- this.type = type;
|
700
|
|
- this.lines = [];
|
701
|
|
-}
|
702
|
|
-
|
703
|
|
-/**
|
704
|
|
- * Class holds a=ssrc-group: lines
|
705
|
|
- * @param semantics
|
706
|
|
- * @param ssrcs
|
707
|
|
- * @constructor
|
708
|
|
- */
|
709
|
|
-function ChannelSsrcGroup(semantics, ssrcs, line) {
|
710
|
|
- this.semantics = semantics;
|
711
|
|
- this.ssrcs = ssrcs;
|
712
|
|
-}
|
713
|
|
-
|
714
|
|
-/**
|
715
|
|
- * Helper class represents media channel. Is a container for ChannelSsrc, holds channel idx and media type.
|
716
|
|
- * @param channelNumber channel idx in SDP media array.
|
717
|
|
- * @param mediaType media type(a=mid)
|
718
|
|
- * @constructor
|
719
|
|
- */
|
720
|
|
-function MediaChannel(channelNumber, mediaType) {
|
721
|
|
- /**
|
722
|
|
- * SDP channel number
|
723
|
|
- * @type {*}
|
724
|
|
- */
|
725
|
|
- this.chNumber = channelNumber;
|
726
|
|
- /**
|
727
|
|
- * Channel media type(a=mid)
|
728
|
|
- * @type {*}
|
729
|
|
- */
|
730
|
|
- this.mediaType = mediaType;
|
731
|
|
- /**
|
732
|
|
- * The maps of ssrc numbers to ChannelSsrc objects.
|
733
|
|
- */
|
734
|
|
- this.ssrcs = {};
|
735
|
|
-
|
736
|
|
- /**
|
737
|
|
- * The array of ChannelSsrcGroup objects.
|
738
|
|
- * @type {Array}
|
739
|
|
- */
|
740
|
|
- this.ssrcGroups = [];
|
741
|
|
-}
|
742
|
778
|
|
743
|
779
|
SDPUtil = {
|
744
|
780
|
iceparams: function (mediadesc, sessiondesc) {
|