Browse Source

Refactors simulcast support.

master
George Politis 10 years ago
parent
commit
23f1dc174e

+ 0
- 1
app.js View File

@@ -9,7 +9,6 @@ var APP =
9 9
         this.connectionquality = require("./modules/connectionquality/connectionquality");
10 10
         this.statistics = require("./modules/statistics/statistics");
11 11
         this.RTC = require("./modules/RTC/RTC");
12
-        this.simulcast = require("./modules/simulcast/simulcast");
13 12
         this.desktopsharing = require("./modules/desktopsharing/desktopsharing");
14 13
         this.xmpp = require("./modules/xmpp/xmpp");
15 14
         this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut");

+ 0
- 18
modules/RTC/DataChannels.js View File

@@ -112,24 +112,6 @@ var DataChannels =
112 112
                     eventEmitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED,
113 113
                         lastNEndpoints, endpointsEnteringLastN, obj);
114 114
                 }
115
-                else if ("SimulcastLayersChangedEvent" === colibriClass)
116
-                {
117
-                    eventEmitter.emit(RTCEvents.SIMULCAST_LAYER_CHANGED,
118
-                        obj.endpointSimulcastLayers);
119
-                }
120
-                else if ("SimulcastLayersChangingEvent" === colibriClass)
121
-                {
122
-                    eventEmitter.emit(RTCEvents.SIMULCAST_LAYER_CHANGING,
123
-                        obj.endpointSimulcastLayers);
124
-                }
125
-                else if ("StartSimulcastLayerEvent" === colibriClass)
126
-                {
127
-                    eventEmitter.emit(RTCEvents.SIMULCAST_START, obj.simulcastLayer);
128
-                }
129
-                else if ("StopSimulcastLayerEvent" === colibriClass)
130
-                {
131
-                    eventEmitter.emit(RTCEvents.SIMULCAST_STOP, obj.simulcastLayer);
132
-                }
133 115
                 else
134 116
                 {
135 117
                     console.debug("Data channel JSON-formatted message: ", obj);

+ 15
- 39
modules/RTC/RTCUtils.js View File

@@ -131,7 +131,7 @@ function RTCUtils(RTCService)
131 131
         console.log('This appears to be Firefox');
132 132
         var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
133 133
         if (version >= 40
134
-            && !config.enableSimulcast && config.useBundle && config.useRtcpMux) {
134
+            && config.useBundle && config.useRtcpMux) {
135 135
             this.peerconnection = mozRTCPeerConnection;
136 136
             this.browser = RTCBrowserType.RTC_BROWSER_FIREFOX;
137 137
             this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
@@ -237,44 +237,20 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
237 237
     var self = this;
238 238
 
239 239
     try {
240
-        if (config.enableSimulcast
241
-            && constraints.video
242
-            && constraints.video.chromeMediaSource !== 'screen'
243
-            && constraints.video.chromeMediaSource !== 'desktop'
244
-            && !isAndroid
245
-
246
-            // We currently do not support FF, as it doesn't have multistream support.
247
-            && !isFF) {
248
-            APP.simulcast.getUserMedia(constraints, function (stream) {
249
-                    console.log('onUserMediaSuccess');
250
-                    self.setAvailableDevices(um, true);
251
-                    success_callback(stream);
252
-                },
253
-                function (error) {
254
-                    console.warn('Failed to get access to local media. Error ', error);
255
-                    self.setAvailableDevices(um, false);
256
-                    if (failure_callback) {
257
-                        failure_callback(error);
258
-                    }
259
-                });
260
-        } else {
261
-
262
-            this.getUserMedia(constraints,
263
-                function (stream) {
264
-                    console.log('onUserMediaSuccess');
265
-                    self.setAvailableDevices(um, true);
266
-                    success_callback(stream);
267
-                },
268
-                function (error) {
269
-                    self.setAvailableDevices(um, false);
270
-                    console.warn('Failed to get access to local media. Error ',
271
-                        error, constraints);
272
-                    if (failure_callback) {
273
-                        failure_callback(error);
274
-                    }
275
-                });
276
-
277
-        }
240
+        this.getUserMedia(constraints,
241
+            function (stream) {
242
+                console.log('onUserMediaSuccess');
243
+                self.setAvailableDevices(um, true);
244
+                success_callback(stream);
245
+            },
246
+            function (error) {
247
+                self.setAvailableDevices(um, false);
248
+                console.warn('Failed to get access to local media. Error ',
249
+                    error, constraints);
250
+                if (failure_callback) {
251
+                    failure_callback(error);
252
+                }
253
+            });
278 254
     } catch (e) {
279 255
         console.error('GUM failed: ', e);
280 256
         if(failure_callback) {

+ 0
- 8
modules/UI/UI.js View File

@@ -122,14 +122,6 @@ function registerListeners() {
122 122
             VideoLayout.onLastNEndpointsChanged(lastNEndpoints,
123 123
                 endpointsEnteringLastN, stream);
124 124
         });
125
-    APP.RTC.addListener(RTCEvents.SIMULCAST_LAYER_CHANGED,
126
-        function (endpointSimulcastLayers) {
127
-           VideoLayout.onSimulcastLayersChanged(endpointSimulcastLayers);
128
-        });
129
-    APP.RTC.addListener(RTCEvents.SIMULCAST_LAYER_CHANGING,
130
-        function (endpointSimulcastLayers) {
131
-            VideoLayout.onSimulcastLayersChanging(endpointSimulcastLayers);
132
-        });
133 125
     APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED,
134 126
         function (devices) {
135 127
             VideoLayout.setDeviceAvailabilityIcons(null, devices);

+ 2
- 10
modules/UI/videolayout/ConnectionIndicator.js View File

@@ -96,17 +96,9 @@ ConnectionIndicator.prototype.generateText = function () {
96 96
     if(this.resolution && this.jid != null)
97 97
     {
98 98
         var keys = Object.keys(this.resolution);
99
-        if(keys.length == 1)
99
+        for(var ssrc in this.resolution)
100 100
         {
101
-            for(var ssrc in this.resolution)
102
-            {
103
-                resolutionValue = this.resolution[ssrc];
104
-            }
105
-        }
106
-        else if(keys.length > 1)
107
-        {
108
-            var displayedSsrc = APP.simulcast.getReceivingSSRC(this.jid);
109
-            resolutionValue = this.resolution[displayedSsrc];
101
+            resolutionValue = this.resolution[ssrc];
110 102
         }
111 103
     }
112 104
 

+ 48
- 239
modules/UI/videolayout/VideoLayout.js View File

@@ -93,8 +93,7 @@ function waitForRemoteVideo(selector, ssrc, stream, jid) {
93 93
     if (stream.id === 'mixedmslabel') return;
94 94
 
95 95
     if (selector[0].currentTime > 0) {
96
-        var videoStream = APP.simulcast.getReceivingVideoStream(stream);
97
-        APP.RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
96
+        APP.RTC.attachMediaStream(selector, stream); // FIXME: why do i have to do this for FF?
98 97
         videoactive(selector);
99 98
     } else {
100 99
         setTimeout(function () {
@@ -629,8 +628,7 @@ var VideoLayout = (function (my) {
629 628
         }
630 629
 
631 630
         // Attach WebRTC stream
632
-        var videoStream = APP.simulcast.getLocalVideoStream();
633
-        APP.RTC.attachMediaStream(localVideoSelector, videoStream);
631
+        APP.RTC.attachMediaStream(localVideoSelector, stream.getOriginalStream());
634 632
 
635 633
         // Add stream ended handler
636 634
         stream.getOriginalStream().onended = function () {
@@ -824,113 +822,67 @@ var VideoLayout = (function (my) {
824 822
             // Screen stream is already rotated
825 823
             largeVideoState.flipX = (newSrc === localVideoSrc) && flipXLocalVideo;
826 824
 
827
-            var userChanged = false;
828 825
             if (largeVideoState.oldResourceJid !== largeVideoState.userResourceJid) {
829
-                userChanged = true;
830 826
                 // we want the notification to trigger even if userJid is undefined,
831 827
                 // or null.
832 828
                 eventEmitter.emit(UIEvents.SELECTED_ENDPOINT,
833 829
                     largeVideoState.userResourceJid);
834 830
             }
835 831
 
836
-            if (!largeVideoState.updateInProgress) {
837
-                largeVideoState.updateInProgress = true;
838
-
839
-                var doUpdate = function () {
840
-                    Avatar.updateActiveSpeakerAvatarSrc(
841
-                        APP.xmpp.findJidFromResource(
842
-                            largeVideoState.userResourceJid));
843
-
844
-                    if (!userChanged && largeVideoState.preload &&
845
-                        largeVideoState.preload !== null &&
846
-                        APP.RTC.getVideoSrc($(largeVideoState.preload)[0]) === newSrc)
847
-                    {
848
-
849
-                        console.info('Switching to preloaded video');
850
-                        var attributes = $('#largeVideo').prop("attributes");
851
-
852
-                        // loop through largeVideo attributes and apply them on
853
-                        // preload.
854
-                        $.each(attributes, function () {
855
-                            if (this.name !== 'id' && this.name !== 'src') {
856
-                                largeVideoState.preload.attr(this.name, this.value);
857
-                            }
858
-                        });
859
-
860
-                        largeVideoState.preload.appendTo($('#largeVideoContainer'));
861
-                        $('#largeVideo').attr('id', 'previousLargeVideo');
862
-                        largeVideoState.preload.attr('id', 'largeVideo');
863
-                        $('#previousLargeVideo').remove();
864
-
865
-                        largeVideoState.preload.on('loadedmetadata', function (e) {
866
-                            currentVideoWidth = this.videoWidth;
867
-                            currentVideoHeight = this.videoHeight;
868
-                            VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
869
-                        });
870
-                        largeVideoState.preload = null;
871
-                        largeVideoState.preload_ssrc = 0;
872
-                    } else {
873
-                        APP.RTC.setVideoSrc($('#largeVideo')[0], largeVideoState.newSrc);
874
-                    }
875
-
876
-                    var videoTransform = document.getElementById('largeVideo')
877
-                        .style.webkitTransform;
878
-
879
-                    if (largeVideoState.flipX && videoTransform !== 'scaleX(-1)') {
880
-                        document.getElementById('largeVideo').style.webkitTransform
881
-                            = "scaleX(-1)";
882
-                    }
883
-                    else if (!largeVideoState.flipX && videoTransform === 'scaleX(-1)') {
884
-                        document.getElementById('largeVideo').style.webkitTransform
885
-                            = "none";
886
-                    }
832
+            $('#largeVideo').fadeOut(300, function () {
833
+                Avatar.updateActiveSpeakerAvatarSrc(
834
+                    APP.xmpp.findJidFromResource(
835
+                        largeVideoState.userResourceJid));
887 836
 
888
-                    // Change the way we'll be measuring and positioning large video
837
+                APP.RTC.setVideoSrc($('#largeVideo')[0], largeVideoState.newSrc);
889 838
 
890
-                    VideoLayout.getVideoSize = largeVideoState.isDesktop
891
-                        ? getDesktopVideoSize
892
-                        : getCameraVideoSize;
893
-                    VideoLayout.getVideoPosition = largeVideoState.isDesktop
894
-                        ? getDesktopVideoPosition
895
-                        : getCameraVideoPosition;
839
+                var videoTransform = document.getElementById('largeVideo')
840
+                    .style.webkitTransform;
896 841
 
842
+                if (largeVideoState.flipX && videoTransform !== 'scaleX(-1)') {
843
+                    document.getElementById('largeVideo').style.webkitTransform
844
+                        = "scaleX(-1)";
845
+                }
846
+                else if (!largeVideoState.flipX && videoTransform === 'scaleX(-1)') {
847
+                    document.getElementById('largeVideo').style.webkitTransform
848
+                        = "none";
849
+                }
897 850
 
898
-                    // Only if the large video is currently visible.
899
-                    // Disable previous dominant speaker video.
900
-                    if (largeVideoState.oldResourceJid) {
901
-                        VideoLayout.enableDominantSpeaker(
902
-                            largeVideoState.oldResourceJid,
903
-                            false);
904
-                    }
851
+                // Change the way we'll be measuring and positioning large video
905 852
 
906
-                    // Enable new dominant speaker in the remote videos section.
907
-                    if (largeVideoState.userResourceJid) {
908
-                        VideoLayout.enableDominantSpeaker(
909
-                            largeVideoState.userResourceJid,
910
-                            true);
911
-                    }
853
+                VideoLayout.getVideoSize = largeVideoState.isDesktop
854
+                    ? getDesktopVideoSize
855
+                    : getCameraVideoSize;
856
+                VideoLayout.getVideoPosition = largeVideoState.isDesktop
857
+                    ? getDesktopVideoPosition
858
+                    : getCameraVideoPosition;
912 859
 
913
-                    if (userChanged && largeVideoState.isVisible) {
914
-                        // using "this" should be ok because we're called
915
-                        // from within the fadeOut event.
916
-                        $(this).fadeIn(300);
917
-                    }
918 860
 
919
-                    if(userChanged) {
920
-                        Avatar.showUserAvatar(
921
-                            APP.xmpp.findJidFromResource(
922
-                                largeVideoState.oldResourceJid));
923
-                    }
861
+                // Only if the large video is currently visible.
862
+                // Disable previous dominant speaker video.
863
+                if (largeVideoState.oldResourceJid) {
864
+                    VideoLayout.enableDominantSpeaker(
865
+                        largeVideoState.oldResourceJid,
866
+                        false);
867
+                }
924 868
 
925
-                    largeVideoState.updateInProgress = false;
926
-                };
869
+                // Enable new dominant speaker in the remote videos section.
870
+                if (largeVideoState.userResourceJid) {
871
+                    VideoLayout.enableDominantSpeaker(
872
+                        largeVideoState.userResourceJid,
873
+                        true);
874
+                }
927 875
 
928
-                if (userChanged) {
929
-                    $('#largeVideo').fadeOut(300, doUpdate);
930
-                } else {
931
-                    doUpdate();
876
+                if (largeVideoState.isVisible) {
877
+                    // using "this" should be ok because we're called
878
+                    // from within the fadeOut event.
879
+                    $(this).fadeIn(300);
932 880
                 }
933
-            }
881
+
882
+                Avatar.showUserAvatar(
883
+                    APP.xmpp.findJidFromResource(
884
+                        largeVideoState.oldResourceJid));
885
+            });
934 886
         } else {
935 887
             Avatar.showUserAvatar(
936 888
                 APP.xmpp.findJidFromResource(
@@ -1188,8 +1140,7 @@ var VideoLayout = (function (my) {
1188 1140
             // If the container is currently visible we attach the stream.
1189 1141
             if (!isVideo
1190 1142
                 || (container.offsetParent !== null && isVideo)) {
1191
-                var videoStream = APP.simulcast.getReceivingVideoStream(stream);
1192
-                APP.RTC.attachMediaStream(sel, videoStream);
1143
+                APP.RTC.attachMediaStream(sel, stream);
1193 1144
 
1194 1145
                 if (isVideo)
1195 1146
                     waitForRemoteVideo(sel, thessrc, stream, peerJid);
@@ -2025,9 +1976,7 @@ var VideoLayout = (function (my) {
2025 1976
                     var mediaStream = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
2026 1977
                     var sel = $('#participant_' + resourceJid + '>video');
2027 1978
 
2028
-                    var videoStream = APP.simulcast.getReceivingVideoStream(
2029
-                        mediaStream.stream);
2030
-                    APP.RTC.attachMediaStream(sel, videoStream);
1979
+                    APP.RTC.attachMediaStream(sel, mediaStream.stream);
2031 1980
                     if (lastNPickupJid == mediaStream.peerjid) {
2032 1981
                         // Clean up the lastN pickup jid.
2033 1982
                         lastNPickupJid = null;
@@ -2078,146 +2027,6 @@ var VideoLayout = (function (my) {
2078 2027
         }
2079 2028
     };
2080 2029
 
2081
-    my.onSimulcastLayersChanging = function (endpointSimulcastLayers) {
2082
-        endpointSimulcastLayers.forEach(function (esl) {
2083
-
2084
-            var resource = esl.endpoint;
2085
-
2086
-            // if lastN is enabled *and* the endpoint is *not* in the lastN set,
2087
-            // then ignore the event (= do not preload anything).
2088
-            //
2089
-            // The bridge could probably stop sending this message if it's for
2090
-            // an endpoint that's not in lastN.
2091
-
2092
-            if (lastNCount != -1
2093
-                && (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1)) {
2094
-                return;
2095
-            }
2096
-
2097
-            var primarySSRC = esl.simulcastLayer.primarySSRC;
2098
-
2099
-            // Get session and stream from primary ssrc.
2100
-            var res = APP.simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
2101
-            var sid = res.sid;
2102
-            var electedStream = res.stream;
2103
-
2104
-            if (sid && electedStream) {
2105
-                var msid = APP.simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
2106
-
2107
-                console.info([esl, primarySSRC, msid, sid, electedStream]);
2108
-
2109
-                var preload = (Strophe.getResourceFromJid(APP.xmpp.getJidFromSSRC(primarySSRC)) == largeVideoState.userResourceJid);
2110
-
2111
-                if (preload) {
2112
-                    if (largeVideoState.preload)
2113
-                    {
2114
-                        $(largeVideoState.preload).remove();
2115
-                    }
2116
-                    console.info('Preloading remote video');
2117
-                    largeVideoState.preload = $('<video autoplay></video>');
2118
-                    // ssrcs are unique in an rtp session
2119
-                    largeVideoState.preload_ssrc = primarySSRC;
2120
-
2121
-                    APP.RTC.attachMediaStream(largeVideoState.preload, electedStream)
2122
-                }
2123
-
2124
-            } else {
2125
-                console.error('Could not find a stream or a session.', sid, electedStream);
2126
-            }
2127
-        });
2128
-    };
2129
-
2130
-    /**
2131
-     * On simulcast layers changed event.
2132
-     */
2133
-    my.onSimulcastLayersChanged = function (endpointSimulcastLayers) {
2134
-        endpointSimulcastLayers.forEach(function (esl) {
2135
-
2136
-            var resource = esl.endpoint;
2137
-
2138
-            // if lastN is enabled *and* the endpoint is *not* in the lastN set,
2139
-            // then ignore the event (= do not change large video/thumbnail
2140
-            // SRCs).
2141
-            //
2142
-            // Note that even if we ignore the "changed" event in this event
2143
-            // handler, the bridge must continue sending these events because
2144
-            // the simulcast code in simulcast.js uses it to know what's going
2145
-            // to be streamed by the bridge when/if the endpoint gets back into
2146
-            // the lastN set.
2147
-
2148
-            if (lastNCount != -1
2149
-                && (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1)) {
2150
-                return;
2151
-            }
2152
-
2153
-            var primarySSRC = esl.simulcastLayer.primarySSRC;
2154
-
2155
-            // Get session and stream from primary ssrc.
2156
-            var res = APP.simulcast.getReceivingVideoStreamBySSRC(primarySSRC);
2157
-            var sid = res.sid;
2158
-            var electedStream = res.stream;
2159
-
2160
-            if (sid && electedStream) {
2161
-                var msid = APP.simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
2162
-
2163
-                console.info('Switching simulcast substream.');
2164
-                console.info([esl, primarySSRC, msid, sid, electedStream]);
2165
-
2166
-                var msidParts = msid.split(' ');
2167
-                var selRemoteVideo = $(['#', 'remoteVideo_', sid, '_', msidParts[0]].join(''));
2168
-
2169
-                var updateLargeVideo = (Strophe.getResourceFromJid(APP.xmpp.getJidFromSSRC(primarySSRC))
2170
-                    == largeVideoState.userResourceJid);
2171
-                var updateFocusedVideoSrc = (focusedVideoInfo && focusedVideoInfo.src && focusedVideoInfo.src != '' &&
2172
-                    (APP.RTC.getVideoSrc(selRemoteVideo[0]) == focusedVideoInfo.src));
2173
-
2174
-                var electedStreamUrl;
2175
-                if (largeVideoState.preload_ssrc == primarySSRC)
2176
-                {
2177
-                    APP.RTC.setVideoSrc(selRemoteVideo[0], APP.RTC.getVideoSrc(largeVideoState.preload[0]));
2178
-                }
2179
-                else
2180
-                {
2181
-                    if (largeVideoState.preload
2182
-                        && largeVideoState.preload != null) {
2183
-                        $(largeVideoState.preload).remove();
2184
-                    }
2185
-
2186
-                    largeVideoState.preload_ssrc = 0;
2187
-
2188
-                    APP.RTC.attachMediaStream(selRemoteVideo, electedStream);
2189
-                }
2190
-
2191
-                var jid = APP.xmpp.getJidFromSSRC(primarySSRC);
2192
-
2193
-                if (updateLargeVideo) {
2194
-                    VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(selRemoteVideo[0]), null,
2195
-                        Strophe.getResourceFromJid(jid));
2196
-                }
2197
-
2198
-                if (updateFocusedVideoSrc) {
2199
-                    focusedVideoInfo.src = APP.RTC.getVideoSrc(selRemoteVideo[0]);
2200
-                }
2201
-
2202
-                var videoId;
2203
-                if(resource == APP.xmpp.myResource())
2204
-                {
2205
-                    videoId = "localVideoContainer";
2206
-                }
2207
-                else
2208
-                {
2209
-                    videoId = "participant_" + resource;
2210
-                }
2211
-                var connectionIndicator = VideoLayout.connectionIndicators[videoId];
2212
-                if(connectionIndicator)
2213
-                    connectionIndicator.updatePopoverData();
2214
-
2215
-            } else {
2216
-                console.error('Could not find a stream or a sid.', sid, electedStream);
2217
-            }
2218
-        });
2219
-    };
2220
-
2221 2030
     /**
2222 2031
      * Updates local stats
2223 2032
      * @param percent

+ 0
- 32
modules/simulcast/SimulcastLogger.js View File

@@ -1,32 +0,0 @@
1
-/**
2
- *
3
- * @constructor
4
- */
5
-function SimulcastLogger(name, lvl) {
6
-    this.name = name;
7
-    this.lvl = lvl;
8
-}
9
-
10
-SimulcastLogger.prototype.log = function (text) {
11
-    if (this.lvl) {
12
-        console.log(text);
13
-    }
14
-};
15
-
16
-SimulcastLogger.prototype.info = function (text) {
17
-    if (this.lvl > 1) {
18
-        console.info(text);
19
-    }
20
-};
21
-
22
-SimulcastLogger.prototype.fine = function (text) {
23
-    if (this.lvl > 2) {
24
-        console.log(text);
25
-    }
26
-};
27
-
28
-SimulcastLogger.prototype.error = function (text) {
29
-    console.error(text);
30
-};
31
-
32
-module.exports = SimulcastLogger;

+ 0
- 268
modules/simulcast/SimulcastReceiver.js View File

@@ -1,268 +0,0 @@
1
-var SimulcastLogger = require("./SimulcastLogger");
2
-var SimulcastUtils = require("./SimulcastUtils");
3
-var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
4
-
5
-function SimulcastReceiver() {
6
-    this.simulcastUtils = new SimulcastUtils();
7
-    this.logger = new SimulcastLogger('SimulcastReceiver', 1);
8
-}
9
-
10
-SimulcastReceiver.prototype._remoteVideoSourceCache = '';
11
-SimulcastReceiver.prototype._remoteMaps = {
12
-    msid2Quality: {},
13
-    ssrc2Msid: {},
14
-    msid2ssrc: {},
15
-    receivingVideoStreams: {}
16
-};
17
-
18
-SimulcastReceiver.prototype._cacheRemoteVideoSources = function (lines) {
19
-    this._remoteVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
20
-};
21
-
22
-SimulcastReceiver.prototype._restoreRemoteVideoSources = function (lines) {
23
-    this.simulcastUtils._replaceVideoSources(lines, this._remoteVideoSourceCache);
24
-};
25
-
26
-SimulcastReceiver.prototype._ensureGoogConference = function (lines) {
27
-    var sb;
28
-
29
-    this.logger.info('Ensuring x-google-conference flag...')
30
-
31
-    if (this.simulcastUtils._indexOfArray('a=x-google-flag:conference', lines) === this.simulcastUtils._emptyCompoundIndex) {
32
-        // TODO(gp) do that for the audio as well as suggested by fippo.
33
-        // Add the google conference flag
34
-        sb = this.simulcastUtils._getVideoSources(lines);
35
-        sb = ['a=x-google-flag:conference'].concat(sb);
36
-        this.simulcastUtils._replaceVideoSources(lines, sb);
37
-    }
38
-};
39
-
40
-SimulcastReceiver.prototype._restoreSimulcastGroups = function (sb) {
41
-    this._restoreRemoteVideoSources(sb);
42
-};
43
-
44
-/**
45
- * Restores the simulcast groups of the remote description. In
46
- * transformRemoteDescription we remove those in order for the set remote
47
- * description to succeed. The focus needs the signal the groups to new
48
- * participants.
49
- *
50
- * @param desc
51
- * @returns {*}
52
- */
53
-SimulcastReceiver.prototype.reverseTransformRemoteDescription = function (desc) {
54
-    var sb;
55
-
56
-    if (!this.simulcastUtils.isValidDescription(desc)) {
57
-        return desc;
58
-    }
59
-
60
-    if (config.enableSimulcast) {
61
-        sb = desc.sdp.split('\r\n');
62
-
63
-        this._restoreSimulcastGroups(sb);
64
-
65
-        desc = new RTCSessionDescription({
66
-            type: desc.type,
67
-            sdp: sb.join('\r\n')
68
-        });
69
-    }
70
-
71
-    return desc;
72
-};
73
-
74
-SimulcastUtils.prototype._ensureOrder = function (lines) {
75
-    var videoSources, sb;
76
-
77
-    videoSources = this.parseMedia(lines, ['video'])[0];
78
-    sb = this._compileVideoSources(videoSources);
79
-
80
-    this._replaceVideoSources(lines, sb);
81
-};
82
-
83
-SimulcastReceiver.prototype._updateRemoteMaps = function (lines) {
84
-    var remoteVideoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0],
85
-        videoSource, quality;
86
-
87
-    // (re) initialize the remote maps.
88
-    this._remoteMaps.msid2Quality = {};
89
-    this._remoteMaps.ssrc2Msid = {};
90
-    this._remoteMaps.msid2ssrc = {};
91
-
92
-    var self = this;
93
-    if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
94
-        remoteVideoSources.groups.forEach(function (group) {
95
-            if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
96
-                quality = 0;
97
-                group.ssrcs.forEach(function (ssrc) {
98
-                    videoSource = remoteVideoSources.sources[ssrc];
99
-                    self._remoteMaps.msid2Quality[videoSource.msid] = quality++;
100
-                    self._remoteMaps.ssrc2Msid[videoSource.ssrc] = videoSource.msid;
101
-                    self._remoteMaps.msid2ssrc[videoSource.msid] = videoSource.ssrc;
102
-                });
103
-            }
104
-        });
105
-    }
106
-};
107
-
108
-SimulcastReceiver.prototype._setReceivingVideoStream = function (resource, ssrc) {
109
-    this._remoteMaps.receivingVideoStreams[resource] = ssrc;
110
-};
111
-
112
-/**
113
- * Returns a stream with single video track, the one currently being
114
- * received by this endpoint.
115
- *
116
- * @param stream the remote simulcast stream.
117
- * @returns {webkitMediaStream}
118
- */
119
-SimulcastReceiver.prototype.getReceivingVideoStream = function (stream) {
120
-    var tracks, i, electedTrack, msid, quality = 0, receivingTrackId;
121
-
122
-    var self = this;
123
-    if (config.enableSimulcast) {
124
-
125
-        stream.getVideoTracks().some(function (track) {
126
-            return Object.keys(self._remoteMaps.receivingVideoStreams).some(function (resource) {
127
-                var ssrc = self._remoteMaps.receivingVideoStreams[resource];
128
-                var msid = self._remoteMaps.ssrc2Msid[ssrc];
129
-                if (msid == [stream.id, track.id].join(' ')) {
130
-                    electedTrack = track;
131
-                    return true;
132
-                }
133
-            });
134
-        });
135
-
136
-        if (!electedTrack) {
137
-            // we don't have an elected track, choose by initial quality.
138
-            tracks = stream.getVideoTracks();
139
-            for (i = 0; i < tracks.length; i++) {
140
-                msid = [stream.id, tracks[i].id].join(' ');
141
-                if (this._remoteMaps.msid2Quality[msid] === quality) {
142
-                    electedTrack = tracks[i];
143
-                    break;
144
-                }
145
-            }
146
-
147
-            // TODO(gp) if the initialQuality could not be satisfied, lower
148
-            // the requirement and try again.
149
-        }
150
-    }
151
-
152
-    return (electedTrack)
153
-        ? new webkitMediaStream([electedTrack])
154
-        : stream;
155
-};
156
-
157
-SimulcastReceiver.prototype.getReceivingSSRC = function (jid) {
158
-    var resource = Strophe.getResourceFromJid(jid);
159
-    var ssrc = this._remoteMaps.receivingVideoStreams[resource];
160
-
161
-    // If we haven't receiving a "changed" event yet, then we must be receiving
162
-    // low quality (that the sender always streams).
163
-    if(!ssrc)
164
-    {
165
-        var remoteStreamObject = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
166
-        var remoteStream = remoteStreamObject.getOriginalStream();
167
-        var tracks = remoteStream.getVideoTracks();
168
-        if (tracks) {
169
-            for (var k = 0; k < tracks.length; k++) {
170
-                var track = tracks[k];
171
-                var msid = [remoteStream.id, track.id].join(' ');
172
-                var _ssrc = this._remoteMaps.msid2ssrc[msid];
173
-                var quality = this._remoteMaps.msid2Quality[msid];
174
-                if (quality == 0) {
175
-                    ssrc = _ssrc;
176
-                }
177
-            }
178
-        }
179
-    }
180
-
181
-    return ssrc;
182
-};
183
-
184
-SimulcastReceiver.prototype.getReceivingVideoStreamBySSRC = function (ssrc)
185
-{
186
-    var sid, electedStream;
187
-    var i, j, k;
188
-    var jid = APP.xmpp.getJidFromSSRC(ssrc);
189
-    if(jid && APP.RTC.remoteStreams[jid])
190
-    {
191
-        var remoteStreamObject = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
192
-        var remoteStream = remoteStreamObject.getOriginalStream();
193
-        var tracks = remoteStream.getVideoTracks();
194
-        if (tracks) {
195
-            for (k = 0; k < tracks.length; k++) {
196
-                var track = tracks[k];
197
-                var msid = [remoteStream.id, track.id].join(' ');
198
-                var tmp = this._remoteMaps.msid2ssrc[msid];
199
-                if (tmp == ssrc) {
200
-                    electedStream = new webkitMediaStream([track]);
201
-                    sid = remoteStreamObject.sid;
202
-                    // stream found, stop.
203
-                    break;
204
-                }
205
-            }
206
-        }
207
-
208
-    }
209
-    else
210
-    {
211
-        console.debug(APP.RTC.remoteStreams, jid, ssrc);
212
-    }
213
-
214
-    return {
215
-        sid: sid,
216
-        stream: electedStream
217
-    };
218
-};
219
-
220
-/**
221
- * Gets the fully qualified msid (stream.id + track.id) associated to the
222
- * SSRC.
223
- *
224
- * @param ssrc
225
- * @returns {*}
226
- */
227
-SimulcastReceiver.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
228
-    return this._remoteMaps.ssrc2Msid[ssrc];
229
-};
230
-
231
-/**
232
- * Removes the ssrc-group:SIM from the remote description bacause Chrome
233
- * either gets confused and thinks this is an FID group or, if an FID group
234
- * is already present, it fails to set the remote description.
235
- *
236
- * @param desc
237
- * @returns {*}
238
- */
239
-SimulcastReceiver.prototype.transformRemoteDescription = function (desc) {
240
-
241
-    if (desc && desc.sdp) {
242
-        var sb = desc.sdp.split('\r\n');
243
-
244
-        this._updateRemoteMaps(sb);
245
-        this._cacheRemoteVideoSources(sb);
246
-
247
-        // NOTE(gp) this needs to be called after updateRemoteMaps because we
248
-        // need the simulcast group in the _updateRemoteMaps() method.
249
-        this.simulcastUtils._removeSimulcastGroup(sb);
250
-
251
-        if (desc.sdp.indexOf('a=ssrc-group:SIM') !== -1) {
252
-            // We don't need the goog conference flag if we're not doing
253
-            // simulcast.
254
-            this._ensureGoogConference(sb);
255
-        }
256
-
257
-        desc = new RTCSessionDescription({
258
-            type: desc.type,
259
-            sdp: sb.join('\r\n')
260
-        });
261
-
262
-        this.logger.fine(['Transformed remote description', desc.sdp].join(' '));
263
-    }
264
-
265
-    return desc;
266
-};
267
-
268
-module.exports = SimulcastReceiver;

+ 0
- 521
modules/simulcast/SimulcastSender.js View File

@@ -1,521 +0,0 @@
1
-var SimulcastLogger = require("./SimulcastLogger");
2
-var SimulcastUtils = require("./SimulcastUtils");
3
-
4
-function SimulcastSender() {
5
-    this.simulcastUtils = new SimulcastUtils();
6
-    this.logger = new SimulcastLogger('SimulcastSender', 1);
7
-}
8
-
9
-SimulcastSender.prototype.displayedLocalVideoStream = null;
10
-
11
-SimulcastSender.prototype._generateGuid = (function () {
12
-    function s4() {
13
-        return Math.floor((1 + Math.random()) * 0x10000)
14
-            .toString(16)
15
-            .substring(1);
16
-    }
17
-
18
-    return function () {
19
-        return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
20
-            s4() + '-' + s4() + s4() + s4();
21
-    };
22
-}());
23
-
24
-// Returns a random integer between min (included) and max (excluded)
25
-// Using Math.round() gives a non-uniform distribution!
26
-SimulcastSender.prototype._generateRandomSSRC = function () {
27
-    var min = 0, max = 0xffffffff;
28
-    return Math.floor(Math.random() * (max - min)) + min;
29
-};
30
-
31
-SimulcastSender.prototype.getLocalVideoStream = function () {
32
-    return (this.displayedLocalVideoStream != null)
33
-        ? this.displayedLocalVideoStream
34
-        // in case we have no simulcast at all, i.e. we didn't perform the GUM
35
-        : APP.RTC.localVideo.getOriginalStream();
36
-};
37
-
38
-function NativeSimulcastSender() {
39
-    SimulcastSender.call(this); // call the super constructor.
40
-}
41
-
42
-NativeSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
43
-
44
-NativeSimulcastSender.prototype._localExplosionMap = {};
45
-NativeSimulcastSender.prototype._isUsingScreenStream = false;
46
-NativeSimulcastSender.prototype._localVideoSourceCache = '';
47
-
48
-NativeSimulcastSender.prototype.reset = function () {
49
-    this._localExplosionMap = {};
50
-    this._isUsingScreenStream = APP.desktopsharing.isUsingScreenStream();
51
-};
52
-
53
-NativeSimulcastSender.prototype._cacheLocalVideoSources = function (lines) {
54
-    this._localVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
55
-};
56
-
57
-NativeSimulcastSender.prototype._restoreLocalVideoSources = function (lines) {
58
-    this.simulcastUtils._replaceVideoSources(lines, this._localVideoSourceCache);
59
-};
60
-
61
-NativeSimulcastSender.prototype._appendSimulcastGroup = function (lines) {
62
-    var videoSources, ssrcGroup, simSSRC, numOfSubs = 2, i, sb, msid;
63
-
64
-    this.logger.info('Appending simulcast group...');
65
-
66
-    // Get the primary SSRC information.
67
-    videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
68
-
69
-    // Start building the SIM SSRC group.
70
-    ssrcGroup = ['a=ssrc-group:SIM'];
71
-
72
-    // The video source buffer.
73
-    sb = [];
74
-
75
-    // Create the simulcast sub-streams.
76
-    for (i = 0; i < numOfSubs; i++) {
77
-        // TODO(gp) prevent SSRC collision.
78
-        simSSRC = this._generateRandomSSRC();
79
-        ssrcGroup.push(simSSRC);
80
-
81
-        if (videoSources.base) {
82
-            sb.splice.apply(sb, [sb.length, 0].concat(
83
-                [["a=ssrc:", simSSRC, " cname:", videoSources.base.cname].join(''),
84
-                    ["a=ssrc:", simSSRC, " msid:", videoSources.base.msid].join('')]
85
-            ));
86
-        }
87
-
88
-        this.logger.info(['Generated substream ', i, ' with SSRC ', simSSRC, '.'].join(''));
89
-
90
-    }
91
-
92
-    // Add the group sim layers.
93
-    sb.splice(0, 0, ssrcGroup.join(' '))
94
-
95
-    this.simulcastUtils._replaceVideoSources(lines, sb);
96
-};
97
-
98
-// Does the actual patching.
99
-NativeSimulcastSender.prototype._ensureSimulcastGroup = function (lines) {
100
-
101
-    this.logger.info('Ensuring simulcast group...');
102
-
103
-    if (this.simulcastUtils._indexOfArray('a=ssrc-group:SIM', lines) === this.simulcastUtils._emptyCompoundIndex) {
104
-        this._appendSimulcastGroup(lines);
105
-        this._cacheLocalVideoSources(lines);
106
-    } else {
107
-        // verify that the ssrcs participating in the SIM group are present
108
-        // in the SDP (needed for presence).
109
-        this._restoreLocalVideoSources(lines);
110
-    }
111
-};
112
-
113
-/**
114
- * Produces a single stream with multiple tracks for local video sources.
115
- *
116
- * @param lines
117
- * @private
118
- */
119
-NativeSimulcastSender.prototype._explodeSimulcastSenderSources = function (lines) {
120
-    var sb, msid, sid, tid, videoSources, self;
121
-
122
-    this.logger.info('Exploding local video sources...');
123
-
124
-    videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
125
-
126
-    self = this;
127
-    if (videoSources.groups && videoSources.groups.length !== 0) {
128
-        videoSources.groups.forEach(function (group) {
129
-            if (group.semantics === 'SIM') {
130
-                group.ssrcs.forEach(function (ssrc) {
131
-
132
-                    // Get the msid for this ssrc..
133
-                    if (self._localExplosionMap[ssrc]) {
134
-                        // .. either from the explosion map..
135
-                        msid = self._localExplosionMap[ssrc];
136
-                    } else {
137
-                        // .. or generate a new one (msid).
138
-                        sid = videoSources.sources[ssrc].msid
139
-                            .substring(0, videoSources.sources[ssrc].msid.indexOf(' '));
140
-
141
-                        tid = self._generateGuid();
142
-                        msid = [sid, tid].join(' ');
143
-                        self._localExplosionMap[ssrc] = msid;
144
-                    }
145
-
146
-                    // Assign it to the source object.
147
-                    videoSources.sources[ssrc].msid = msid;
148
-
149
-                    // TODO(gp) Change the msid of associated sources.
150
-                });
151
-            }
152
-        });
153
-    }
154
-
155
-    sb = this.simulcastUtils._compileVideoSources(videoSources);
156
-
157
-    this.simulcastUtils._replaceVideoSources(lines, sb);
158
-};
159
-
160
-/**
161
- * GUM for simulcast.
162
- *
163
- * @param constraints
164
- * @param success
165
- * @param err
166
- */
167
-NativeSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
168
-
169
-    // There's nothing special to do for native simulcast, so just do a normal GUM.
170
-    navigator.webkitGetUserMedia(constraints, function (hqStream) {
171
-        success(hqStream);
172
-    }, err);
173
-};
174
-
175
-/**
176
- * Prepares the local description for public usage (i.e. to be signaled
177
- * through Jingle to the focus).
178
- *
179
- * @param desc
180
- * @returns {RTCSessionDescription}
181
- */
182
-NativeSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
183
-    var sb;
184
-
185
-    if (!this.simulcastUtils.isValidDescription(desc) || this._isUsingScreenStream) {
186
-        return desc;
187
-    }
188
-
189
-
190
-    sb = desc.sdp.split('\r\n');
191
-
192
-    this._explodeSimulcastSenderSources(sb);
193
-
194
-    desc = new RTCSessionDescription({
195
-        type: desc.type,
196
-        sdp: sb.join('\r\n')
197
-    });
198
-
199
-    this.logger.fine(['Exploded local video sources', desc.sdp].join(' '));
200
-
201
-    return desc;
202
-};
203
-
204
-/**
205
- * Ensures that the simulcast group is present in the answer, _if_ native
206
- * simulcast is enabled,
207
- *
208
- * @param desc
209
- * @returns {*}
210
- */
211
-NativeSimulcastSender.prototype.transformAnswer = function (desc) {
212
-
213
-    if (!this.simulcastUtils.isValidDescription(desc) || this._isUsingScreenStream) {
214
-        return desc;
215
-    }
216
-
217
-    var sb = desc.sdp.split('\r\n');
218
-
219
-    // Even if we have enabled native simulcasting previously
220
-    // (with a call to SLD with an appropriate SDP, for example),
221
-    // createAnswer seems to consistently generate incomplete SDP
222
-    // with missing SSRCS.
223
-    //
224
-    // So, subsequent calls to SLD will have missing SSRCS and presence
225
-    // won't have the complete list of SRCs.
226
-    this._ensureSimulcastGroup(sb);
227
-
228
-    desc = new RTCSessionDescription({
229
-        type: desc.type,
230
-        sdp: sb.join('\r\n')
231
-    });
232
-
233
-    this.logger.fine(['Transformed answer', desc.sdp].join(' '));
234
-
235
-    return desc;
236
-};
237
-
238
-
239
-/**
240
- *
241
- *
242
- * @param desc
243
- * @returns {*}
244
- */
245
-NativeSimulcastSender.prototype.transformLocalDescription = function (desc) {
246
-    return desc;
247
-};
248
-
249
-NativeSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
250
-    // Nothing to do here, native simulcast does that auto-magically.
251
-};
252
-
253
-NativeSimulcastSender.prototype.constructor = NativeSimulcastSender;
254
-
255
-function SimpleSimulcastSender() {
256
-    SimulcastSender.call(this);
257
-}
258
-
259
-SimpleSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
260
-
261
-SimpleSimulcastSender.prototype.localStream = null;
262
-SimpleSimulcastSender.prototype._localMaps = {
263
-    msids: [],
264
-    msid2ssrc: {}
265
-};
266
-
267
-/**
268
- * Groups local video sources together in the ssrc-group:SIM group.
269
- *
270
- * @param lines
271
- * @private
272
- */
273
-SimpleSimulcastSender.prototype._groupLocalVideoSources = function (lines) {
274
-    var sb, videoSources, ssrcs = [], ssrc;
275
-
276
-    this.logger.info('Grouping local video sources...');
277
-
278
-    videoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0];
279
-
280
-    for (ssrc in videoSources.sources) {
281
-        // jitsi-meet destroys/creates streams at various places causing
282
-        // the original local stream ids to change. The only thing that
283
-        // remains unchanged is the trackid.
284
-        this._localMaps.msid2ssrc[videoSources.sources[ssrc].msid.split(' ')[1]] = ssrc;
285
-    }
286
-
287
-    var self = this;
288
-    // TODO(gp) add only "free" sources.
289
-    this._localMaps.msids.forEach(function (msid) {
290
-        ssrcs.push(self._localMaps.msid2ssrc[msid]);
291
-    });
292
-
293
-    if (!videoSources.groups) {
294
-        videoSources.groups = [];
295
-    }
296
-
297
-    videoSources.groups.push({
298
-        'semantics': 'SIM',
299
-        'ssrcs': ssrcs
300
-    });
301
-
302
-    sb = this.simulcastUtils._compileVideoSources(videoSources);
303
-
304
-    this.simulcastUtils._replaceVideoSources(lines, sb);
305
-};
306
-
307
-/**
308
- * GUM for simulcast.
309
- *
310
- * @param constraints
311
- * @param success
312
- * @param err
313
- */
314
-SimpleSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
315
-
316
-    // TODO(gp) what if we request a resolution not supported by the hardware?
317
-    // TODO(gp) make the lq stream configurable; although this wouldn't work with native simulcast
318
-    var lqConstraints = {
319
-        audio: false,
320
-        video: {
321
-            mandatory: {
322
-                maxWidth: 320,
323
-                maxHeight: 180,
324
-                maxFrameRate: 15
325
-            }
326
-        }
327
-    };
328
-
329
-    this.logger.info('HQ constraints: ', constraints);
330
-    this.logger.info('LQ constraints: ', lqConstraints);
331
-
332
-
333
-    // NOTE(gp) if we request the lq stream first webkitGetUserMedia
334
-    // fails randomly. Tested with Chrome 37. As fippo suggested, the
335
-    // reason appears to be that Chrome only acquires the cam once and
336
-    // then downscales the picture (https://code.google.com/p/chromium/issues/detail?id=346616#c11)
337
-
338
-    var self = this;
339
-    navigator.webkitGetUserMedia(constraints, function (hqStream) {
340
-
341
-        self.localStream = hqStream;
342
-
343
-        // reset local maps.
344
-        self._localMaps.msids = [];
345
-        self._localMaps.msid2ssrc = {};
346
-
347
-        // add hq trackid to local map
348
-        self._localMaps.msids.push(hqStream.getVideoTracks()[0].id);
349
-
350
-        navigator.webkitGetUserMedia(lqConstraints, function (lqStream) {
351
-
352
-            self.displayedLocalVideoStream = lqStream;
353
-
354
-            // NOTE(gp) The specification says Array.forEach() will visit
355
-            // the array elements in numeric order, and that it doesn't
356
-            // visit elements that don't exist.
357
-
358
-            // add lq trackid to local map
359
-            self._localMaps.msids.splice(0, 0, lqStream.getVideoTracks()[0].id);
360
-
361
-            self.localStream.addTrack(lqStream.getVideoTracks()[0]);
362
-            success(self.localStream);
363
-        }, err);
364
-    }, err);
365
-};
366
-
367
-/**
368
- * Prepares the local description for public usage (i.e. to be signaled
369
- * through Jingle to the focus).
370
- *
371
- * @param desc
372
- * @returns {RTCSessionDescription}
373
- */
374
-SimpleSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
375
-    var sb;
376
-
377
-    if (!this.simulcastUtils.isValidDescription(desc)) {
378
-        return desc;
379
-    }
380
-
381
-    sb = desc.sdp.split('\r\n');
382
-
383
-    this._groupLocalVideoSources(sb);
384
-
385
-    desc = new RTCSessionDescription({
386
-        type: desc.type,
387
-        sdp: sb.join('\r\n')
388
-    });
389
-
390
-    this.logger.fine('Grouped local video sources');
391
-    this.logger.fine(desc.sdp);
392
-
393
-    return desc;
394
-};
395
-
396
-/**
397
- * Ensures that the simulcast group is present in the answer, _if_ native
398
- * simulcast is enabled,
399
- *
400
- * @param desc
401
- * @returns {*}
402
- */
403
-SimpleSimulcastSender.prototype.transformAnswer = function (desc) {
404
-    return desc;
405
-};
406
-
407
-
408
-/**
409
- *
410
- *
411
- * @param desc
412
- * @returns {*}
413
- */
414
-SimpleSimulcastSender.prototype.transformLocalDescription = function (desc) {
415
-
416
-    var sb = desc.sdp.split('\r\n');
417
-
418
-    this.simulcastUtils._removeSimulcastGroup(sb);
419
-
420
-    desc = new RTCSessionDescription({
421
-        type: desc.type,
422
-        sdp: sb.join('\r\n')
423
-    });
424
-
425
-    this.logger.fine('Transformed local description');
426
-    this.logger.fine(desc.sdp);
427
-
428
-    return desc;
429
-};
430
-
431
-SimpleSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
432
-    var trackid;
433
-
434
-    var self = this;
435
-    this.logger.log(['Requested to', enabled ? 'enable' : 'disable', ssrc].join(' '));
436
-    if (Object.keys(this._localMaps.msid2ssrc).some(function (tid) {
437
-        // Search for the track id that corresponds to the ssrc
438
-        if (self._localMaps.msid2ssrc[tid] == ssrc) {
439
-            trackid = tid;
440
-            return true;
441
-        }
442
-    }) && self.localStream.getVideoTracks().some(function (track) {
443
-        // Start/stop the track that corresponds to the track id
444
-        if (track.id === trackid) {
445
-            track.enabled = enabled;
446
-            return true;
447
-        }
448
-    })) {
449
-        this.logger.log([trackid, enabled ? 'enabled' : 'disabled'].join(' '));
450
-        $(document).trigger(enabled
451
-            ? 'simulcastlayerstarted'
452
-            : 'simulcastlayerstopped');
453
-    } else {
454
-        this.logger.error("I don't have a local stream with SSRC " + ssrc);
455
-    }
456
-};
457
-
458
-SimpleSimulcastSender.prototype.constructor = SimpleSimulcastSender;
459
-
460
-function NoSimulcastSender() {
461
-    SimulcastSender.call(this);
462
-}
463
-
464
-NoSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
465
-
466
-/**
467
- * GUM for simulcast.
468
- *
469
- * @param constraints
470
- * @param success
471
- * @param err
472
- */
473
-NoSimulcastSender.prototype.getUserMedia = function (constraints, success, err) {
474
-    navigator.webkitGetUserMedia(constraints, function (hqStream) {
475
-        success(hqStream);
476
-    }, err);
477
-};
478
-
479
-/**
480
- * Prepares the local description for public usage (i.e. to be signaled
481
- * through Jingle to the focus).
482
- *
483
- * @param desc
484
- * @returns {RTCSessionDescription}
485
- */
486
-NoSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
487
-    return desc;
488
-};
489
-
490
-/**
491
- * Ensures that the simulcast group is present in the answer, _if_ native
492
- * simulcast is enabled,
493
- *
494
- * @param desc
495
- * @returns {*}
496
- */
497
-NoSimulcastSender.prototype.transformAnswer = function (desc) {
498
-    return desc;
499
-};
500
-
501
-
502
-/**
503
- *
504
- *
505
- * @param desc
506
- * @returns {*}
507
- */
508
-NoSimulcastSender.prototype.transformLocalDescription = function (desc) {
509
-    return desc;
510
-};
511
-
512
-NoSimulcastSender.prototype._setLocalVideoStreamEnabled = function (ssrc, enabled) {
513
-
514
-};
515
-
516
-NoSimulcastSender.prototype.constructor = NoSimulcastSender;
517
-
518
-module.exports = {
519
-    "native": NativeSimulcastSender,
520
-    "no": NoSimulcastSender
521
-}

+ 0
- 233
modules/simulcast/SimulcastUtils.js View File

@@ -1,233 +0,0 @@
1
-var SimulcastLogger = require("./SimulcastLogger");
2
-
3
-/**
4
- *
5
- * @constructor
6
- */
7
-function SimulcastUtils() {
8
-    this.logger = new SimulcastLogger("SimulcastUtils", 1);
9
-}
10
-
11
-/**
12
- *
13
- * @type {{}}
14
- * @private
15
- */
16
-SimulcastUtils.prototype._emptyCompoundIndex = {};
17
-
18
-/**
19
- *
20
- * @param lines
21
- * @param videoSources
22
- * @private
23
- */
24
-SimulcastUtils.prototype._replaceVideoSources = function (lines, videoSources) {
25
-    var i, inVideo = false, index = -1, howMany = 0;
26
-
27
-    this.logger.info('Replacing video sources...');
28
-
29
-    for (i = 0; i < lines.length; i++) {
30
-        if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
31
-            // Out of video.
32
-            break;
33
-        }
34
-
35
-        if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
36
-            // In video.
37
-            inVideo = true;
38
-        }
39
-
40
-        if (inVideo && (lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:'
41
-            || lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:')) {
42
-
43
-            if (index === -1) {
44
-                index = i;
45
-            }
46
-
47
-            howMany++;
48
-        }
49
-    }
50
-
51
-    //  efficiency baby ;)
52
-    lines.splice.apply(lines,
53
-        [index, howMany].concat(videoSources));
54
-
55
-};
56
-
57
-SimulcastUtils.prototype.isValidDescription = function (desc)
58
-{
59
-    return desc && desc != null
60
-        && desc.type && desc.type != ''
61
-        && desc.sdp && desc.sdp != '';
62
-};
63
-
64
-SimulcastUtils.prototype._getVideoSources = function (lines) {
65
-    var i, inVideo = false, sb = [];
66
-
67
-    this.logger.info('Getting video sources...');
68
-
69
-    for (i = 0; i < lines.length; i++) {
70
-        if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
71
-            // Out of video.
72
-            break;
73
-        }
74
-
75
-        if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
76
-            // In video.
77
-            inVideo = true;
78
-        }
79
-
80
-        if (inVideo && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
81
-            // In SSRC.
82
-            sb.push(lines[i]);
83
-        }
84
-
85
-        if (inVideo && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
86
-            sb.push(lines[i]);
87
-        }
88
-    }
89
-
90
-    return sb;
91
-};
92
-
93
-SimulcastUtils.prototype.parseMedia = function (lines, mediatypes) {
94
-    var i, res = [], type, cur_media, idx, ssrcs, cur_ssrc, ssrc,
95
-        ssrc_attribute, group, semantics, skip = true;
96
-
97
-    this.logger.info('Parsing media sources...');
98
-
99
-    for (i = 0; i < lines.length; i++) {
100
-        if (lines[i].substring(0, 'm='.length) === 'm=') {
101
-
102
-            type = lines[i]
103
-                .substr('m='.length, lines[i].indexOf(' ') - 'm='.length);
104
-            skip = mediatypes !== undefined && mediatypes.indexOf(type) === -1;
105
-
106
-            if (!skip) {
107
-                cur_media = {
108
-                    'type': type,
109
-                    'sources': {},
110
-                    'groups': []
111
-                };
112
-
113
-                res.push(cur_media);
114
-            }
115
-
116
-        } else if (!skip && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
117
-
118
-            idx = lines[i].indexOf(' ');
119
-            ssrc = lines[i].substring('a=ssrc:'.length, idx);
120
-            if (cur_media.sources[ssrc] === undefined) {
121
-                cur_ssrc = {'ssrc': ssrc};
122
-                cur_media.sources[ssrc] = cur_ssrc;
123
-            }
124
-
125
-            ssrc_attribute = lines[i].substr(idx + 1).split(':', 2)[0];
126
-            cur_ssrc[ssrc_attribute] = lines[i].substr(idx + 1).split(':', 2)[1];
127
-
128
-            if (cur_media.base === undefined) {
129
-                cur_media.base = cur_ssrc;
130
-            }
131
-
132
-        } else if (!skip && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
133
-            idx = lines[i].indexOf(' ');
134
-            semantics = lines[i].substr(0, idx).substr('a=ssrc-group:'.length);
135
-            ssrcs = lines[i].substr(idx).trim().split(' ');
136
-            group = {
137
-                'semantics': semantics,
138
-                'ssrcs': ssrcs
139
-            };
140
-            cur_media.groups.push(group);
141
-        } else if (!skip && (lines[i].substring(0, 'a=sendrecv'.length) === 'a=sendrecv' ||
142
-            lines[i].substring(0, 'a=recvonly'.length) === 'a=recvonly' ||
143
-            lines[i].substring(0, 'a=sendonly'.length) === 'a=sendonly' ||
144
-            lines[i].substring(0, 'a=inactive'.length) === 'a=inactive')) {
145
-
146
-            cur_media.direction = lines[i].substring('a='.length);
147
-        }
148
-    }
149
-
150
-    return res;
151
-};
152
-
153
-/**
154
- * The _indexOfArray() method returns the first a CompoundIndex at which a
155
- * given element can be found in the array, or _emptyCompoundIndex if it is
156
- * not present.
157
- *
158
- * Example:
159
- *
160
- * _indexOfArray('3', [ 'this is line 1', 'this is line 2', 'this is line 3' ])
161
- *
162
- * returns {row: 2, column: 14}
163
- *
164
- * @param needle
165
- * @param haystack
166
- * @param start
167
- * @returns {}
168
- * @private
169
- */
170
-SimulcastUtils.prototype._indexOfArray = function (needle, haystack, start) {
171
-    var length = haystack.length, idx, i;
172
-
173
-    if (!start) {
174
-        start = 0;
175
-    }
176
-
177
-    for (i = start; i < length; i++) {
178
-        idx = haystack[i].indexOf(needle);
179
-        if (idx !== -1) {
180
-            return {row: i, column: idx};
181
-        }
182
-    }
183
-    return this._emptyCompoundIndex;
184
-};
185
-
186
-SimulcastUtils.prototype._removeSimulcastGroup = function (lines) {
187
-    var i;
188
-
189
-    for (i = lines.length - 1; i >= 0; i--) {
190
-        if (lines[i].indexOf('a=ssrc-group:SIM') !== -1) {
191
-            lines.splice(i, 1);
192
-        }
193
-    }
194
-};
195
-
196
-SimulcastUtils.prototype._compileVideoSources = function (videoSources) {
197
-    var sb = [], ssrc, addedSSRCs = [];
198
-
199
-    this.logger.info('Compiling video sources...');
200
-
201
-    // Add the groups
202
-    if (videoSources.groups && videoSources.groups.length !== 0) {
203
-        videoSources.groups.forEach(function (group) {
204
-            if (group.ssrcs && group.ssrcs.length !== 0) {
205
-                sb.push([['a=ssrc-group:', group.semantics].join(''), group.ssrcs.join(' ')].join(' '));
206
-
207
-                // if (group.semantics !== 'SIM') {
208
-                group.ssrcs.forEach(function (ssrc) {
209
-                    addedSSRCs.push(ssrc);
210
-                    sb.splice.apply(sb, [sb.length, 0].concat([
211
-                        ["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
212
-                        ["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
213
-                });
214
-                //}
215
-            }
216
-        });
217
-    }
218
-
219
-    // Then add any free sources.
220
-    if (videoSources.sources) {
221
-        for (ssrc in videoSources.sources) {
222
-            if (addedSSRCs.indexOf(ssrc) === -1) {
223
-                sb.splice.apply(sb, [sb.length, 0].concat([
224
-                    ["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
225
-                    ["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
226
-            }
227
-        }
228
-    }
229
-
230
-    return sb;
231
-};
232
-
233
-module.exports = SimulcastUtils;

+ 0
- 202
modules/simulcast/simulcast.js View File

@@ -1,202 +0,0 @@
1
-/*jslint plusplus: true */
2
-/*jslint nomen: true*/
3
-
4
-var SimulcastSender = require("./SimulcastSender");
5
-var NoSimulcastSender = SimulcastSender["no"];
6
-var NativeSimulcastSender = SimulcastSender["native"];
7
-var SimulcastReceiver = require("./SimulcastReceiver");
8
-var SimulcastUtils = require("./SimulcastUtils");
9
-var RTCEvents = require("../../service/RTC/RTCEvents");
10
-
11
-
12
-/**
13
- *
14
- * @constructor
15
- */
16
-function SimulcastManager() {
17
-
18
-    // Create the simulcast utilities.
19
-    this.simulcastUtils = new SimulcastUtils();
20
-
21
-    // Create remote simulcast.
22
-    this.simulcastReceiver = new SimulcastReceiver();
23
-
24
-    // Initialize local simulcast.
25
-
26
-    // TODO(gp) move into SimulcastManager.prototype.getUserMedia and take into
27
-    // account constraints.
28
-    if (!config.enableSimulcast) {
29
-        this.simulcastSender = new NoSimulcastSender();
30
-    } else {
31
-
32
-        var isChromium = window.chrome,
33
-            vendorName = window.navigator.vendor;
34
-        if(isChromium !== null && isChromium !== undefined
35
-            /* skip opera */
36
-            && vendorName === "Google Inc."
37
-            /* skip Chromium as suggested by fippo */
38
-            && !window.navigator.appVersion.match(/Chromium\//) ) {
39
-            var ver = parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10);
40
-            if (ver > 37) {
41
-                this.simulcastSender = new NativeSimulcastSender();
42
-            } else {
43
-                this.simulcastSender = new NoSimulcastSender();
44
-            }
45
-        } else {
46
-            this.simulcastSender = new NoSimulcastSender();
47
-        }
48
-
49
-    }
50
-    APP.RTC.addListener(RTCEvents.SIMULCAST_LAYER_CHANGED,
51
-        function (endpointSimulcastLayers) {
52
-            endpointSimulcastLayers.forEach(function (esl) {
53
-                var ssrc = esl.simulcastLayer.primarySSRC;
54
-                simulcast._setReceivingVideoStream(esl.endpoint, ssrc);
55
-            });
56
-        });
57
-    APP.RTC.addListener(RTCEvents.SIMULCAST_START, function (simulcastLayer) {
58
-        var ssrc = simulcastLayer.primarySSRC;
59
-        simulcast._setLocalVideoStreamEnabled(ssrc, true);
60
-    });
61
-    APP.RTC.addListener(RTCEvents.SIMULCAST_STOP, function (simulcastLayer) {
62
-        var ssrc = simulcastLayer.primarySSRC;
63
-        simulcast._setLocalVideoStreamEnabled(ssrc, false);
64
-    });
65
-
66
-}
67
-
68
-/**
69
- * Restores the simulcast groups of the remote description. In
70
- * transformRemoteDescription we remove those in order for the set remote
71
- * description to succeed. The focus needs the signal the groups to new
72
- * participants.
73
- *
74
- * @param desc
75
- * @returns {*}
76
- */
77
-SimulcastManager.prototype.reverseTransformRemoteDescription = function (desc) {
78
-    return this.simulcastReceiver.reverseTransformRemoteDescription(desc);
79
-};
80
-
81
-/**
82
- * Removes the ssrc-group:SIM from the remote description bacause Chrome
83
- * either gets confused and thinks this is an FID group or, if an FID group
84
- * is already present, it fails to set the remote description.
85
- *
86
- * @param desc
87
- * @returns {*}
88
- */
89
-SimulcastManager.prototype.transformRemoteDescription = function (desc) {
90
-    return this.simulcastReceiver.transformRemoteDescription(desc);
91
-};
92
-
93
-/**
94
- * Gets the fully qualified msid (stream.id + track.id) associated to the
95
- * SSRC.
96
- *
97
- * @param ssrc
98
- * @returns {*}
99
- */
100
-SimulcastManager.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
101
-    return this.simulcastReceiver.getRemoteVideoStreamIdBySSRC(ssrc);
102
-};
103
-
104
-/**
105
- * Returns a stream with single video track, the one currently being
106
- * received by this endpoint.
107
- *
108
- * @param stream the remote simulcast stream.
109
- * @returns {webkitMediaStream}
110
- */
111
-SimulcastManager.prototype.getReceivingVideoStream = function (stream) {
112
-    return this.simulcastReceiver.getReceivingVideoStream(stream);
113
-};
114
-
115
-/**
116
- *
117
- *
118
- * @param desc
119
- * @returns {*}
120
- */
121
-SimulcastManager.prototype.transformLocalDescription = function (desc) {
122
-    return this.simulcastSender.transformLocalDescription(desc);
123
-};
124
-
125
-/**
126
- *
127
- * @returns {*}
128
- */
129
-SimulcastManager.prototype.getLocalVideoStream = function() {
130
-    return this.simulcastSender.getLocalVideoStream();
131
-};
132
-
133
-/**
134
- * GUM for simulcast.
135
- *
136
- * @param constraints
137
- * @param success
138
- * @param err
139
- */
140
-SimulcastManager.prototype.getUserMedia = function (constraints, success, err) {
141
-
142
-    this.simulcastSender.getUserMedia(constraints, success, err);
143
-};
144
-
145
-/**
146
- * Prepares the local description for public usage (i.e. to be signaled
147
- * through Jingle to the focus).
148
- *
149
- * @param desc
150
- * @returns {RTCSessionDescription}
151
- */
152
-SimulcastManager.prototype.reverseTransformLocalDescription = function (desc) {
153
-    return this.simulcastSender.reverseTransformLocalDescription(desc);
154
-};
155
-
156
-/**
157
- * Ensures that the simulcast group is present in the answer, _if_ native
158
- * simulcast is enabled,
159
- *
160
- * @param desc
161
- * @returns {*}
162
- */
163
-SimulcastManager.prototype.transformAnswer = function (desc) {
164
-    return this.simulcastSender.transformAnswer(desc);
165
-};
166
-
167
-SimulcastManager.prototype.getReceivingSSRC = function (jid) {
168
-    return this.simulcastReceiver.getReceivingSSRC(jid);
169
-};
170
-
171
-SimulcastManager.prototype.getReceivingVideoStreamBySSRC = function (msid) {
172
-    return this.simulcastReceiver.getReceivingVideoStreamBySSRC(msid);
173
-};
174
-
175
-/**
176
- *
177
- * @param lines
178
- * @param mediatypes
179
- * @returns {*}
180
- */
181
-SimulcastManager.prototype.parseMedia = function(lines, mediatypes) {
182
-    var sb = lines.sdp.split('\r\n');
183
-    return this.simulcastUtils.parseMedia(sb, mediatypes);
184
-};
185
-
186
-SimulcastManager.prototype._setReceivingVideoStream = function(resource, ssrc) {
187
-    this.simulcastReceiver._setReceivingVideoStream(resource, ssrc);
188
-};
189
-
190
-SimulcastManager.prototype._setLocalVideoStreamEnabled = function(ssrc, enabled) {
191
-    this.simulcastSender._setLocalVideoStreamEnabled(ssrc, enabled);
192
-};
193
-
194
-SimulcastManager.prototype.resetSender = function() {
195
-    if (typeof this.simulcastSender.reset === 'function'){
196
-        this.simulcastSender.reset();
197
-    }
198
-};
199
-
200
-var simulcast = new SimulcastManager();
201
-
202
-module.exports = simulcast;

+ 0
- 97
modules/transform/transform.js View File

@@ -1,97 +0,0 @@
1
-var transform = require('sdp-transform');
2
-
3
-exports.write = function(session, opts) {
4
-
5
-    if (typeof session !== 'undefined' &&
6
-        typeof session.media !== 'undefined' &&
7
-        Array.isArray(session.media)) {
8
-
9
-        session.media.forEach(function (mLine) {
10
-            // expand sources to ssrcs
11
-            if (typeof mLine.sources !== 'undefined' &&
12
-                Object.keys(mLine.sources).length !== 0) {
13
-                mLine.ssrcs = [];
14
-                Object.keys(mLine.sources).forEach(function (ssrc) {
15
-                    var source = mLine.sources[ssrc];
16
-                    Object.keys(source).forEach(function (attribute) {
17
-                        mLine.ssrcs.push({
18
-                            id: ssrc,
19
-                            attribute: attribute,
20
-                            value: source[attribute]
21
-                        });
22
-                    });
23
-                });
24
-                delete mLine.sources;
25
-            }
26
-
27
-            // join ssrcs in ssrc groups
28
-            if (typeof mLine.ssrcGroups !== 'undefined' &&
29
-                Array.isArray(mLine.ssrcGroups)) {
30
-                mLine.ssrcGroups.forEach(function (ssrcGroup) {
31
-                    if (typeof ssrcGroup.ssrcs !== 'undefined' &&
32
-                        Array.isArray(ssrcGroup.ssrcs)) {
33
-                        ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');
34
-                    }
35
-                });
36
-            }
37
-        });
38
-    }
39
-
40
-    // join group mids
41
-    if (typeof session !== 'undefined' &&
42
-        typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
43
-
44
-        session.groups.forEach(function (g) {
45
-            if (typeof g.mids !== 'undefined' && Array.isArray(g.mids)) {
46
-                g.mids = g.mids.join(' ');
47
-            }
48
-        });
49
-    }
50
-
51
-    return transform.write(session, opts);
52
-};
53
-
54
-exports.parse = function(sdp) {
55
-    var session = transform.parse(sdp);
56
-
57
-    if (typeof session !== 'undefined' && typeof session.media !== 'undefined' &&
58
-        Array.isArray(session.media)) {
59
-
60
-        session.media.forEach(function (mLine) {
61
-            // group sources attributes by ssrc
62
-            if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
63
-                mLine.sources = {};
64
-                mLine.ssrcs.forEach(function (ssrc) {
65
-                    if (!mLine.sources[ssrc.id])
66
-                        mLine.sources[ssrc.id] = {};
67
-                    mLine.sources[ssrc.id][ssrc.attribute] = ssrc.value;
68
-                });
69
-
70
-                delete mLine.ssrcs;
71
-            }
72
-
73
-            // split ssrcs in ssrc groups
74
-            if (typeof mLine.ssrcGroups !== 'undefined' &&
75
-                Array.isArray(mLine.ssrcGroups)) {
76
-                mLine.ssrcGroups.forEach(function (ssrcGroup) {
77
-                    if (typeof ssrcGroup.ssrcs === 'string') {
78
-                        ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');
79
-                    }
80
-                });
81
-            }
82
-        });
83
-    }
84
-    // split group mids
85
-    if (typeof session !== 'undefined' &&
86
-        typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
87
-
88
-        session.groups.forEach(function (g) {
89
-            if (typeof g.mids === 'string') {
90
-                g.mids = g.mids.split(' ');
91
-            }
92
-        });
93
-    }
94
-
95
-    return session;
96
-};
97
-

+ 13
- 12
modules/xmpp/JingleSession.js View File

@@ -5,6 +5,7 @@ var SDPUtil = require("./SDPUtil");
5 5
 var SDP = require("./SDP");
6 6
 var RTCBrowserType = require("../../service/RTC/RTCBrowserType");
7 7
 var async = require("async");
8
+var transform = require("sdp-transform");
8 9
 
9 10
 // Jingle stuff
10 11
 function JingleSession(me, sid, connection, service) {
@@ -95,8 +96,8 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
95 96
     };
96 97
     this.peerconnection.onaddstream = function (event) {
97 98
         if (event.stream.id !== 'default') {
98
-            console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
99
-            self.remoteStreamAdded(event);
99
+        console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
100
+        self.remoteStreamAdded(event);
100 101
         } else {
101 102
             // This is a recvonly stream. Clients that implement Unified Plan,
102 103
             // such as Firefox use recvonly "streams/channels/tracks" for
@@ -199,7 +200,6 @@ JingleSession.prototype.accept = function () {
199 200
         // FIXME: change any inactive to sendrecv or whatever they were originally
200 201
         pranswer.sdp = pranswer.sdp.replace('a=inactive', 'a=sendrecv');
201 202
     }
202
-    pranswer = APP.simulcast.reverseTransformLocalDescription(pranswer);
203 203
     var prsdp = new SDP(pranswer.sdp);
204 204
     var accept = $iq({to: this.peerjid,
205 205
         type: 'set'})
@@ -652,9 +652,7 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
652 652
                         initiator: self.initiator,
653 653
                         responder: self.responder,
654 654
                         sid: self.sid });
655
-                var publicLocalDesc = APP.simulcast.reverseTransformLocalDescription(sdp);
656
-                var publicLocalSDP = new SDP(publicLocalDesc.sdp);
657
-                publicLocalSDP.toJingle(accept, self.initiator == self.me ? 'initiator' : 'responder', ssrcs);
655
+                self.localSDP.toJingle(accept, self.initiator == self.me ? 'initiator' : 'responder', ssrcs);
658 656
                 self.connection.sendIQ(accept,
659 657
                     function () {
660 658
                         var ack = {};
@@ -1011,7 +1009,7 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
1011 1009
     }
1012 1010
 
1013 1011
     if(!isAudio)
1014
-        APP.RTC.switchVideoStreams(new_stream, oldStream);
1012
+    APP.RTC.switchVideoStreams(new_stream, oldStream);
1015 1013
 
1016 1014
     // Conference is not active
1017 1015
     if(!oldSdp || !self.peerconnection) {
@@ -1239,14 +1237,17 @@ JingleSession.onJingleFatalError = function (session, error)
1239 1237
 JingleSession.prototype.setLocalDescription = function () {
1240 1238
     // put our ssrcs into presence so other clients can identify our stream
1241 1239
     var newssrcs = [];
1242
-    var media = APP.simulcast.parseMedia(this.peerconnection.localDescription);
1243
-    media.forEach(function (media) {
1240
+    var session = transform.parse(this.peerconnection.localDescription.sdp);
1241
+    session.media.forEach(function (media) {
1244 1242
 
1245
-        if(Object.keys(media.sources).length > 0) {
1243
+        if (media.ssrcs != null && media.ssrcs.length > 0) {
1246 1244
             // TODO(gp) maybe exclude FID streams?
1247
-            Object.keys(media.sources).forEach(function (ssrc) {
1245
+            media.ssrcs.forEach(function (ssrc) {
1246
+                if (ssrc.attribute !== 'cname') {
1247
+                    return;
1248
+                }
1248 1249
                 newssrcs.push({
1249
-                    'ssrc': ssrc,
1250
+                    'ssrc': ssrc.id,
1250 1251
                     'type': media.type,
1251 1252
                     'direction': media.direction
1252 1253
                 });

+ 34
- 27
modules/xmpp/TraceablePeerConnection.js View File

@@ -8,6 +8,8 @@ function TraceablePeerConnection(ice_config, constraints) {
8 8
     this.maxstats = 0; // limit to 300 values, i.e. 5 minutes; set to 0 to disable
9 9
     var Interop = require('sdp-interop').Interop;
10 10
     this.interop = new Interop();
11
+    var Simulcast = require('sdp-simulcast');
12
+    this.simulcast = new Simulcast({numOfLayers: 3, explodeRemoteSimulcast: false});
11 13
 
12 14
     // override as desired
13 15
     this.trace = function (what, info) {
@@ -111,34 +113,31 @@ if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
111 113
     TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
112 114
     TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
113 115
     TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
114
-        this.trace('getLocalDescription::preTransform (Plan A)', dumpSDP(this.peerconnection.localDescription));
115
-        // if we're running on FF, transform to Plan B first.
116 116
         var desc = this.peerconnection.localDescription;
117
+        this.trace('getLocalDescription::preTransform', dumpSDP(desc));
118
+
119
+        // if we're running on FF, transform to Plan B first.
117 120
         if (navigator.mozGetUserMedia) {
118 121
             desc = this.interop.toPlanB(desc);
119
-        } else {
120
-            desc = APP.simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription);
122
+            this.trace('getLocalDescription::postTransform (Plan B)', dumpSDP(desc));
121 123
         }
122
-        this.trace('getLocalDescription::postTransform (Plan B)', dumpSDP(desc));
123 124
         return desc;
124 125
     });
125 126
     TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
126
-        this.trace('getRemoteDescription::preTransform (Plan A)', dumpSDP(this.peerconnection.remoteDescription));
127
-        // if we're running on FF, transform to Plan B first.
128 127
         var desc = this.peerconnection.remoteDescription;
128
+        this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
129
+
130
+        // if we're running on FF, transform to Plan B first.
129 131
         if (navigator.mozGetUserMedia) {
130 132
             desc = this.interop.toPlanB(desc);
131
-        } else {
132
-            desc = APP.simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription);
133
+            this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
133 134
         }
134
-        this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
135 135
         return desc;
136 136
     });
137 137
 }
138 138
 
139 139
 TraceablePeerConnection.prototype.addStream = function (stream) {
140 140
     this.trace('addStream', stream.id);
141
-    APP.simulcast.resetSender();
142 141
     try
143 142
     {
144 143
         this.peerconnection.addStream(stream);
@@ -152,7 +151,6 @@ TraceablePeerConnection.prototype.addStream = function (stream) {
152 151
 
153 152
 TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams) {
154 153
     this.trace('removeStream', stream.id);
155
-    APP.simulcast.resetSender();
156 154
     if(stopStreams) {
157 155
         stream.getAudioTracks().forEach(function (track) {
158 156
             track.stop();
@@ -176,14 +174,13 @@ TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
176 174
 };
177 175
 
178 176
 TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
179
-    this.trace('setLocalDescription::preTransform (Plan B)', dumpSDP(description));
177
+    this.trace('setLocalDescription::preTransform', dumpSDP(description));
180 178
     // if we're running on FF, transform to Plan A first.
181 179
     if (navigator.mozGetUserMedia) {
182 180
         description = this.interop.toUnifiedPlan(description);
183
-    } else {
184
-        description = APP.simulcast.transformLocalDescription(description);
181
+        this.trace('setLocalDescription::postTransform (Plan A)', dumpSDP(description));
185 182
     }
186
-    this.trace('setLocalDescription::postTransform (Plan A)', dumpSDP(description));
183
+
187 184
     var self = this;
188 185
     this.peerconnection.setLocalDescription(description,
189 186
         function () {
@@ -203,15 +200,16 @@ TraceablePeerConnection.prototype.setLocalDescription = function (description, s
203 200
 };
204 201
 
205 202
 TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
206
-    this.trace('setRemoteDescription::preTransform (Plan B)', dumpSDP(description));
203
+    this.trace('setRemoteDescription::preTransform', dumpSDP(description));
204
+    // TODO the focus should squeze or explode the remote simulcast
205
+    description = this.simulcast.mungeRemoteDescription(description);
206
+    this.trace('setRemoteDescription::postTransform (simulcast)', dumpSDP(description));
207
+
207 208
     // if we're running on FF, transform to Plan A first.
208 209
     if (navigator.mozGetUserMedia) {
209 210
         description = this.interop.toUnifiedPlan(description);
211
+        this.trace('setRemoteDescription::postTransform (Plan A)', dumpSDP(description));
210 212
     }
211
-    else {
212
-        description = APP.simulcast.transformRemoteDescription(description);
213
-    }
214
-    this.trace('setRemoteDescription::postTransform (Plan A)', dumpSDP(description));
215 213
     var self = this;
216 214
     this.peerconnection.setRemoteDescription(description,
217 215
         function () {
@@ -244,12 +242,19 @@ TraceablePeerConnection.prototype.createOffer = function (successCallback, failu
244 242
     this.trace('createOffer', JSON.stringify(constraints, null, ' '));
245 243
     this.peerconnection.createOffer(
246 244
         function (offer) {
247
-            self.trace('createOfferOnSuccess::preTransform (Plan A)', dumpSDP(offer));
245
+            self.trace('createOfferOnSuccess::preTransform', dumpSDP(offer));
248 246
             // if we're running on FF, transform to Plan B first.
247
+            // NOTE this is not tested because in meet the focus generates the
248
+            // offer.
249 249
             if (navigator.mozGetUserMedia) {
250 250
                 offer = self.interop.toPlanB(offer);
251
+                self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
252
+            }
253
+
254
+            if (config.enableSimulcast && self.simulcast.isSupported()) {
255
+                offer = self.simulcast.mungeLocalDescription(offer);
256
+                self.trace('createOfferOnSuccess::postTransform (simulcast)', dumpSDP(offer));
251 257
             }
252
-            self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
253 258
             successCallback(offer);
254 259
         },
255 260
         function(err) {
@@ -265,14 +270,16 @@ TraceablePeerConnection.prototype.createAnswer = function (successCallback, fail
265 270
     this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
266 271
     this.peerconnection.createAnswer(
267 272
         function (answer) {
268
-            self.trace('createAnswerOnSuccess::preTransfom (Plan A)', dumpSDP(answer));
273
+            self.trace('createAnswerOnSuccess::preTransfom', dumpSDP(answer));
269 274
             // if we're running on FF, transform to Plan A first.
270 275
             if (navigator.mozGetUserMedia) {
271 276
                 answer = self.interop.toPlanB(answer);
272
-            } else {
273
-                answer = APP.simulcast.transformAnswer(answer);
277
+                self.trace('createAnswerOnSuccess::postTransfom (Plan B)', dumpSDP(answer));
278
+            }
279
+            if (config.enableSimulcast && self.simulcast.isSupported()) {
280
+                answer = self.simulcast.mungeLocalDescription(answer);
281
+                self.trace('createAnswerOnSuccess::postTransfom (simulcast)', dumpSDP(answer));
274 282
             }
275
-            self.trace('createAnswerOnSuccess::postTransfom (Plan B)', dumpSDP(answer));
276 283
             successCallback(answer);
277 284
         },
278 285
         function(err) {

+ 4
- 0
modules/xmpp/moderator.js View File

@@ -191,6 +191,10 @@ var Moderator = {
191 191
                 { name: 'startVideoMuted', value: config.startVideoMuted})
192 192
                 .up();
193 193
         }
194
+        elem.c(
195
+            'property',
196
+            { name: 'simulcastMode', value: 'rewriting'})
197
+            .up();
194 198
         elem.up();
195 199
         return elem;
196 200
     },

+ 1
- 0
package.json View File

@@ -20,6 +20,7 @@
20 20
       "i18next-client": "1.7.7",
21 21
       "sdp-interop": "0.1.4",
22 22
       "sdp-transform": "1.4.0",
23
+      "sdp-simulcast": "0.1.0",
23 24
       "async": "0.9.0",
24 25
       "retry": "0.6.1"
25 26
   },

+ 0
- 4
service/RTC/RTCEvents.js View File

@@ -2,10 +2,6 @@ var RTCEvents = {
2 2
     LASTN_CHANGED: "rtc.lastn_changed",
3 3
     DOMINANTSPEAKER_CHANGED: "rtc.dominantspeaker_changed",
4 4
     LASTN_ENDPOINT_CHANGED: "rtc.lastn_endpoint_changed",
5
-    SIMULCAST_LAYER_CHANGED: "rtc.simulcast_layer_changed",
6
-    SIMULCAST_LAYER_CHANGING: "rtc.simulcast_layer_changing",
7
-    SIMULCAST_START: "rtc.simlcast_start",
8
-    SIMULCAST_STOP: "rtc.simlcast_stop",
9 5
     AVAILABLE_DEVICES_CHANGED: "rtc.available_devices_changed"
10 6
 };
11 7
 

Loading…
Cancel
Save