Explorar el Código

Implements first version of adaptive simulcast.

master
George Politis hace 10 años
padre
commit
36af4da83d

+ 16
- 1
app.js Ver fichero

279
         var ssrclines
279
         var ssrclines
280
             = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc:');
280
             = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc:');
281
         ssrclines = ssrclines.filter(function (line) {
281
         ssrclines = ssrclines.filter(function (line) {
282
-            return line.indexOf('mslabel:' + data.stream.label) !== -1;
282
+            // NOTE(gp) previously we filtered on the mslabel, but that property
283
+            // is not always present.
284
+            // return line.indexOf('mslabel:' + data.stream.label) !== -1;
285
+            return line.indexOf('msid:' + data.stream.id) !== -1;
283
         });
286
         });
284
         if (ssrclines.length) {
287
         if (ssrclines.length) {
285
             thessrc = ssrclines[0].substring(7).split(' ')[0];
288
             thessrc = ssrclines[0].substring(7).split(' ')[0];
292
             // presence to arrive.
295
             // presence to arrive.
293
 
296
 
294
             if (!ssrc2jid[thessrc]) {
297
             if (!ssrc2jid[thessrc]) {
298
+                // TODO(gp) limit wait duration to 1 sec.
295
                 setTimeout(function(d, s) {
299
                 setTimeout(function(d, s) {
296
                     return function() {
300
                     return function() {
297
                             waitForPresence(d, s);
301
                             waitForPresence(d, s);
1418
     }
1422
     }
1419
 );
1423
 );
1420
 
1424
 
1425
+$(document).bind("video.selected", function(event, isPresentation, userJid) {
1426
+    if (!isPresentation && _dataChannels && _dataChannels.length != 0) {
1427
+        _dataChannels[0].send(JSON.stringify({
1428
+            'colibriClass': 'SelectedEndpointChangedEvent',
1429
+            'selectedEndpoint': (isPresentation || !userJid)
1430
+                // TODO(gp) hmm.. I wonder which one of the Strophe methods to use..
1431
+                ? null : userJid.split('/')[1]
1432
+        }));
1433
+    }
1434
+});
1435
+
1421
 function callSipButtonClicked()
1436
 function callSipButtonClicked()
1422
 {
1437
 {
1423
     $.prompt('<h2>Enter SIP number</h2>' +
1438
     $.prompt('<h2>Enter SIP number</h2>' +

+ 14
- 0
data_channels.js Ver fichero

23
         //dataChannel.send("Hello bridge!");
23
         //dataChannel.send("Hello bridge!");
24
         // Sends 12 bytes binary message to the bridge
24
         // Sends 12 bytes binary message to the bridge
25
         //dataChannel.send(new ArrayBuffer(12));
25
         //dataChannel.send(new ArrayBuffer(12));
26
+
27
+        // TODO(gp) we are supposed to tell the bridge about video selections
28
+        // so that it can do adaptive simulcast, What if a video selection has
29
+        // been made while the data channels are down or broken?
26
     };
30
     };
27
 
31
 
28
     dataChannel.onerror = function (error)
32
     dataChannel.onerror = function (error)
89
                 var endpointSimulcastLayers = obj.endpointSimulcastLayers;
93
                 var endpointSimulcastLayers = obj.endpointSimulcastLayers;
90
                 $(document).trigger('simulcastlayerschanged', [endpointSimulcastLayers]);
94
                 $(document).trigger('simulcastlayerschanged', [endpointSimulcastLayers]);
91
             }
95
             }
96
+            else if ("StartSimulcastLayerEvent" === colibriClass)
97
+            {
98
+                var simulcastLayer = obj.simulcastLayer;
99
+                $(document).trigger('startsimulcastlayer', simulcastLayer);
100
+            }
101
+            else if ("StopSimulcastLayerEvent" === colibriClass)
102
+            {
103
+                var simulcastLayer = obj.simulcastLayer;
104
+                $(document).trigger('stopsimulcastlayer', simulcastLayer);
105
+            }
92
             else
106
             else
93
             {
107
             {
94
                 console.debug("Data channel JSON-formatted message: ", obj);
108
                 console.debug("Data channel JSON-formatted message: ", obj);

+ 1
- 1
libs/colibri/colibri.focus.js Ver fichero

530
     bridgeSDP.raw = bridgeSDP.session + bridgeSDP.media.join('');
530
     bridgeSDP.raw = bridgeSDP.session + bridgeSDP.media.join('');
531
     var bridgeDesc = new RTCSessionDescription({type: 'offer', sdp: bridgeSDP.raw});
531
     var bridgeDesc = new RTCSessionDescription({type: 'offer', sdp: bridgeSDP.raw});
532
     var simulcast = new Simulcast();
532
     var simulcast = new Simulcast();
533
-    var bridgeDesc = simulcast.transformBridgeDescription(bridgeDesc);
533
+    var bridgeDesc = simulcast.transformRemoteDescription(bridgeDesc);
534
 
534
 
535
     this.peerconnection.setRemoteDescription(bridgeDesc,
535
     this.peerconnection.setRemoteDescription(bridgeDesc,
536
         function () {
536
         function () {

+ 6
- 2
libs/strophe/strophe.jingle.adapter.js Ver fichero

130
     TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
130
     TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
131
     TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
131
     TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
132
         var simulcast = new Simulcast();
132
         var simulcast = new Simulcast();
133
-        var publicLocalDescription = simulcast.makeLocalDescriptionPublic(this.peerconnection.localDescription);
133
+        var publicLocalDescription = simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription);
134
         return publicLocalDescription;
134
         return publicLocalDescription;
135
     });
135
     });
136
-    TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() { return this.peerconnection.remoteDescription; });
136
+    TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
137
+        var simulcast = new Simulcast();
138
+        var publicRemoteDescription = simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription);
139
+        return publicRemoteDescription;
140
+    });
137
 }
141
 }
138
 
142
 
139
 TraceablePeerConnection.prototype.addStream = function (stream) {
143
 TraceablePeerConnection.prototype.addStream = function (stream) {

+ 2
- 2
libs/strophe/strophe.jingle.session.js Ver fichero

120
         pranswer.sdp = pranswer.sdp.replace('a=inactive', 'a=sendrecv');
120
         pranswer.sdp = pranswer.sdp.replace('a=inactive', 'a=sendrecv');
121
     }
121
     }
122
     var simulcast = new Simulcast();
122
     var simulcast = new Simulcast();
123
-    pranswer = simulcast.makeLocalDescriptionPublic(pranswer);
123
+    pranswer = simulcast.reverseTransformLocalDescription(pranswer);
124
     var prsdp = new SDP(pranswer.sdp);
124
     var prsdp = new SDP(pranswer.sdp);
125
     var accept = $iq({to: this.peerjid,
125
     var accept = $iq({to: this.peerjid,
126
         type: 'set'})
126
         type: 'set'})
568
                     responder: this.responder,
568
                     responder: this.responder,
569
                     sid: this.sid });
569
                     sid: this.sid });
570
             var simulcast = new Simulcast();
570
             var simulcast = new Simulcast();
571
-            var publicLocalDesc = simulcast.makeLocalDescriptionPublic(sdp);
571
+            var publicLocalDesc = simulcast.reverseTransformLocalDescription(sdp);
572
             var publicLocalSDP = new SDP(publicLocalDesc.sdp);
572
             var publicLocalSDP = new SDP(publicLocalDesc.sdp);
573
             publicLocalSDP.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder');
573
             publicLocalSDP.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder');
574
             this.connection.sendIQ(accept,
574
             this.connection.sendIQ(accept,

+ 211
- 35
simulcast.js Ver fichero

15
     "use strict";
15
     "use strict";
16
     // global state for all transformers.
16
     // global state for all transformers.
17
     var localExplosionMap = {}, localVideoSourceCache, emptyCompoundIndex,
17
     var localExplosionMap = {}, localVideoSourceCache, emptyCompoundIndex,
18
-        remoteMaps = {
18
+        remoteVideoSourceCache, remoteMaps = {
19
             msid2Quality: {},
19
             msid2Quality: {},
20
             ssrc2Msid: {},
20
             ssrc2Msid: {},
21
             receivingVideoStreams: {}
21
             receivingVideoStreams: {}
45
         this._replaceVideoSources(lines, localVideoSourceCache);
45
         this._replaceVideoSources(lines, localVideoSourceCache);
46
     };
46
     };
47
 
47
 
48
+    Simulcast.prototype._cacheRemoteVideoSources = function (lines) {
49
+        remoteVideoSourceCache = this._getVideoSources(lines);
50
+    };
51
+
52
+    Simulcast.prototype._restoreRemoteVideoSources = function (lines) {
53
+        this._replaceVideoSources(lines, remoteVideoSourceCache);
54
+    };
55
+
48
     Simulcast.prototype._replaceVideoSources = function (lines, videoSources) {
56
     Simulcast.prototype._replaceVideoSources = function (lines, videoSources) {
49
 
57
 
50
         var i, inVideo = false, index = -1, howMany = 0;
58
         var i, inVideo = false, index = -1, howMany = 0;
216
         }
224
         }
217
     };
225
     };
218
 
226
 
227
+    /**
228
+     * Produces a single stream with multiple tracks for local video sources.
229
+     *
230
+     * @param lines
231
+     * @private
232
+     */
219
     Simulcast.prototype._explodeLocalSimulcastSources = function (lines) {
233
     Simulcast.prototype._explodeLocalSimulcastSources = function (lines) {
220
         var sb, msid, sid, tid, videoSources, self;
234
         var sb, msid, sid, tid, videoSources, self;
221
 
235
 
259
         this._replaceVideoSources(lines, sb);
273
         this._replaceVideoSources(lines, sb);
260
     };
274
     };
261
 
275
 
276
+    /**
277
+     * Groups local video sources together in the ssrc-group:SIM group.
278
+     *
279
+     * @param lines
280
+     * @private
281
+     */
262
     Simulcast.prototype._groupLocalVideoSources = function (lines) {
282
     Simulcast.prototype._groupLocalVideoSources = function (lines) {
263
         var sb, videoSources, ssrcs = [], ssrc;
283
         var sb, videoSources, ssrcs = [], ssrc;
264
 
284
 
401
         return sb;
421
         return sb;
402
     };
422
     };
403
 
423
 
424
+    /**
425
+     * Ensures that the simulcast group is present in the answer, _if_ native
426
+     * simulcast is enabled,
427
+     *
428
+     * @param desc
429
+     * @returns {*}
430
+     */
404
     Simulcast.prototype.transformAnswer = function (desc) {
431
     Simulcast.prototype.transformAnswer = function (desc) {
405
         if (config.enableSimulcast && config.useNativeSimulcast) {
432
         if (config.enableSimulcast && config.useNativeSimulcast) {
406
 
433
 
429
         return desc;
456
         return desc;
430
     };
457
     };
431
 
458
 
432
-    Simulcast.prototype.makeLocalDescriptionPublic = function (desc) {
459
+    Simulcast.prototype._restoreSimulcastGroups = function (sb) {
460
+        this._restoreRemoteVideoSources(sb);
461
+    };
462
+
463
+    /**
464
+     * Restores the simulcast groups of the remote description. In
465
+     * transformRemoteDescription we remove those in order for the set remote
466
+     * description to succeed. The focus needs the signal the groups to new
467
+     * participants.
468
+     *
469
+     * @param desc
470
+     * @returns {*}
471
+     */
472
+    Simulcast.prototype.reverseTransformRemoteDescription = function (desc) {
433
         var sb;
473
         var sb;
434
 
474
 
435
-        if (!desc || desc == null)
475
+        if (!desc || desc == null) {
436
             return desc;
476
             return desc;
477
+        }
478
+
479
+        if (config.enableSimulcast) {
480
+            sb = desc.sdp.split('\r\n');
481
+
482
+            this._restoreSimulcastGroups(sb);
483
+
484
+            desc = new RTCSessionDescription({
485
+                type: desc.type,
486
+                sdp: sb.join('\r\n')
487
+            });
488
+        }
489
+
490
+        return desc;
491
+    };
492
+
493
+    /**
494
+     * Prepares the local description for public usage (i.e. to be signaled
495
+     * through Jingle to the focus).
496
+     *
497
+     * @param desc
498
+     * @returns {RTCSessionDescription}
499
+     */
500
+    Simulcast.prototype.reverseTransformLocalDescription = function (desc) {
501
+        var sb;
502
+
503
+        if (!desc || desc == null) {
504
+            return desc;
505
+        }
437
 
506
 
438
         if (config.enableSimulcast) {
507
         if (config.enableSimulcast) {
439
 
508
 
480
         this._replaceVideoSources(lines, sb);
549
         this._replaceVideoSources(lines, sb);
481
     };
550
     };
482
 
551
 
483
-    Simulcast.prototype.transformBridgeDescription = function (desc) {
484
-        if (config.enableSimulcast && config.useNativeSimulcast) {
485
-
486
-            var sb = desc.sdp.split('\r\n');
487
-
488
-            this._ensureGoogConference(sb);
489
-
490
-            desc = new RTCSessionDescription({
491
-                type: desc.type,
492
-                sdp: sb.join('\r\n')
493
-            });
494
-
495
-            if (this.debugLvl && this.debugLvl > 1) {
496
-                console.info('Transformed bridge description');
497
-                console.info(desc.sdp);
498
-            }
499
-        }
500
-
501
-        return desc;
502
-    };
503
-
504
     Simulcast.prototype._updateRemoteMaps = function (lines) {
552
     Simulcast.prototype._updateRemoteMaps = function (lines) {
505
         var remoteVideoSources = this._parseMedia(lines, ['video'])[0], videoSource, quality;
553
         var remoteVideoSources = this._parseMedia(lines, ['video'])[0], videoSource, quality;
506
 
554
 
555
+        // (re) initialize the remote maps.
556
+        remoteMaps = {
557
+            msid2Quality: {},
558
+            ssrc2Msid: {},
559
+            receivingVideoStreams: {}
560
+        };
561
+
507
         if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
562
         if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
508
             remoteVideoSources.groups.forEach(function (group) {
563
             remoteVideoSources.groups.forEach(function (group) {
509
                 if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
564
                 if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
518
         }
573
         }
519
     };
574
     };
520
 
575
 
576
+    /**
577
+     *
578
+     *
579
+     * @param desc
580
+     * @returns {*}
581
+     */
521
     Simulcast.prototype.transformLocalDescription = function (desc) {
582
     Simulcast.prototype.transformLocalDescription = function (desc) {
522
         if (config.enableSimulcast && !config.useNativeSimulcast) {
583
         if (config.enableSimulcast && !config.useNativeSimulcast) {
523
 
584
 
539
         return desc;
600
         return desc;
540
     };
601
     };
541
 
602
 
603
+    /**
604
+     * Removes the ssrc-group:SIM from the remote description bacause Chrome
605
+     * either gets confused and thinks this is an FID group or, if an FID group
606
+     * is already present, it fails to set the remote description.
607
+     *
608
+     * @param desc
609
+     * @returns {*}
610
+     */
542
     Simulcast.prototype.transformRemoteDescription = function (desc) {
611
     Simulcast.prototype.transformRemoteDescription = function (desc) {
543
         if (config.enableSimulcast) {
612
         if (config.enableSimulcast) {
544
 
613
 
545
             var sb = desc.sdp.split('\r\n');
614
             var sb = desc.sdp.split('\r\n');
546
 
615
 
547
             this._updateRemoteMaps(sb);
616
             this._updateRemoteMaps(sb);
548
-            this._removeSimulcastGroup(sb); // NOTE(gp) this needs to be called after updateRemoteMaps!
549
-            this._ensureGoogConference(sb);
617
+            this._cacheRemoteVideoSources(sb);
618
+            this._removeSimulcastGroup(sb); // NOTE(gp) this needs to be called after updateRemoteMaps because we need the simulcast group in the _updateRemoteMaps() method.
619
+
620
+            if (config.useNativeSimulcast) {
621
+                // We don't need the goog conference flag if we're not doing
622
+                // native simulcast.
623
+                this._ensureGoogConference(sb);
624
+            }
550
 
625
 
551
             desc = new RTCSessionDescription({
626
             desc = new RTCSessionDescription({
552
                 type: desc.type,
627
                 type: desc.type,
562
         return desc;
637
         return desc;
563
     };
638
     };
564
 
639
 
565
-    Simulcast.prototype.setReceivingVideoStream = function (ssrc) {
640
+    Simulcast.prototype._setReceivingVideoStream = function (ssrc) {
566
         var receivingTrack = remoteMaps.ssrc2Msid[ssrc],
641
         var receivingTrack = remoteMaps.ssrc2Msid[ssrc],
567
             msidParts = receivingTrack.split(' ');
642
             msidParts = receivingTrack.split(' ');
568
 
643
 
569
         remoteMaps.receivingVideoStreams[msidParts[0]] = msidParts[1];
644
         remoteMaps.receivingVideoStreams[msidParts[0]] = msidParts[1];
570
     };
645
     };
571
 
646
 
647
+    /**
648
+     * Returns a stream with single video track, the one currently being
649
+     * received by this endpoint.
650
+     *
651
+     * @param stream the remote simulcast stream.
652
+     * @returns {webkitMediaStream}
653
+     */
572
     Simulcast.prototype.getReceivingVideoStream = function (stream) {
654
     Simulcast.prototype.getReceivingVideoStream = function (stream) {
573
-        var tracks, track, i, electedTrack, msid, quality = 1, receivingTrackId;
655
+        var tracks, i, electedTrack, msid, quality = 1, receivingTrackId;
574
 
656
 
575
         if (config.enableSimulcast) {
657
         if (config.enableSimulcast) {
576
 
658
 
577
             if (remoteMaps.receivingVideoStreams[stream.id])
659
             if (remoteMaps.receivingVideoStreams[stream.id])
578
             {
660
             {
661
+                // the bridge has signaled us to receive a specific track.
579
                 receivingTrackId = remoteMaps.receivingVideoStreams[stream.id];
662
                 receivingTrackId = remoteMaps.receivingVideoStreams[stream.id];
580
                 tracks = stream.getVideoTracks();
663
                 tracks = stream.getVideoTracks();
581
                 for (i = 0; i < tracks.length; i++) {
664
                 for (i = 0; i < tracks.length; i++) {
587
             }
670
             }
588
 
671
 
589
             if (!electedTrack) {
672
             if (!electedTrack) {
673
+                // we don't have an elected track, choose by initial quality.
590
                 tracks = stream.getVideoTracks();
674
                 tracks = stream.getVideoTracks();
591
                 for (i = 0; i < tracks.length; i++) {
675
                 for (i = 0; i < tracks.length; i++) {
592
-                    track = tracks[i];
593
-                    msid = [stream.id, track.id].join(' ');
676
+                    msid = [stream.id, tracks[i].id].join(' ');
594
                     if (remoteMaps.msid2Quality[msid] === quality) {
677
                     if (remoteMaps.msid2Quality[msid] === quality) {
595
-                        electedTrack = track;
678
+                        electedTrack = tracks[i];
596
                         break;
679
                         break;
597
                     }
680
                     }
598
                 }
681
                 }
682
+
683
+                // TODO(gp) if the initialQuality could not be satisfied, lower
684
+                // the requirement and try again.
599
             }
685
             }
600
         }
686
         }
601
 
687
 
604
             : stream;
690
             : stream;
605
     };
691
     };
606
 
692
 
693
+    var stream;
694
+
695
+    /**
696
+     * GUM for simulcast.
697
+     *
698
+     * @param constraints
699
+     * @param success
700
+     * @param err
701
+     */
607
     Simulcast.prototype.getUserMedia = function (constraints, success, err) {
702
     Simulcast.prototype.getUserMedia = function (constraints, success, err) {
608
 
703
 
609
         // TODO(gp) what if we request a resolution not supported by the hardware?
704
         // TODO(gp) what if we request a resolution not supported by the hardware?
620
 
715
 
621
         if (config.enableSimulcast && !config.useNativeSimulcast) {
716
         if (config.enableSimulcast && !config.useNativeSimulcast) {
622
 
717
 
623
-            // NOTE(gp) if we request the lq stream first webkitGetUserMedia fails randomly. Tested with Chrome 37.
718
+            // NOTE(gp) if we request the lq stream first webkitGetUserMedia
719
+            // fails randomly. Tested with Chrome 37. As fippo suggested, the
720
+            // reason appears to be that Chrome only acquires the cam once and
721
+            // then downscales the picture (https://code.google.com/p/chromium/issues/detail?id=346616#c11)
624
 
722
 
625
             navigator.webkitGetUserMedia(constraints, function (hqStream) {
723
             navigator.webkitGetUserMedia(constraints, function (hqStream) {
626
 
724
 
641
                     localMaps.msids.splice(0, 0, lqStream.getVideoTracks()[0].id);
739
                     localMaps.msids.splice(0, 0, lqStream.getVideoTracks()[0].id);
642
 
740
 
643
                     hqStream.addTrack(lqStream.getVideoTracks()[0]);
741
                     hqStream.addTrack(lqStream.getVideoTracks()[0]);
742
+                    stream = hqStream;
644
                     success(hqStream);
743
                     success(hqStream);
645
                 }, err);
744
                 }, err);
646
             }, err);
745
             }, err);
656
 
755
 
657
                 // add hq stream to local map
756
                 // add hq stream to local map
658
                 localMaps.msids.push(hqStream.getVideoTracks()[0].id);
757
                 localMaps.msids.push(hqStream.getVideoTracks()[0].id);
659
-
758
+                stream = hqStream;
660
                 success(hqStream);
759
                 success(hqStream);
661
             }, err);
760
             }, err);
662
         }
761
         }
663
     };
762
     };
664
 
763
 
665
-    Simulcast.prototype.getRemoteVideoStreamIdBySSRC = function (primarySSRC) {
666
-        return remoteMaps.ssrc2Msid[primarySSRC];
764
+    /**
765
+     * Gets the fully qualified msid (stream.id + track.id) associated to the
766
+     * SSRC.
767
+     *
768
+     * @param ssrc
769
+     * @returns {*}
770
+     */
771
+    Simulcast.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
772
+        return remoteMaps.ssrc2Msid[ssrc];
667
     };
773
     };
668
 
774
 
669
     Simulcast.prototype.parseMedia = function (desc, mediatypes) {
775
     Simulcast.prototype.parseMedia = function (desc, mediatypes) {
670
         var lines = desc.sdp.split('\r\n');
776
         var lines = desc.sdp.split('\r\n');
671
         return this._parseMedia(lines, mediatypes);
777
         return this._parseMedia(lines, mediatypes);
672
     };
778
     };
779
+
780
+    Simulcast.prototype._startLocalVideoStream = function (ssrc) {
781
+        var trackid;
782
+
783
+        Object.keys(localMaps.msid2ssrc).some(function (tid) {
784
+            if (localMaps.msid2ssrc[tid] == ssrc)
785
+            {
786
+                trackid = tid;
787
+                return true;
788
+            }
789
+        });
790
+
791
+        stream.getVideoTracks().some(function(track) {
792
+            if (track.id === trackid) {
793
+                track.enabled = true;
794
+                return true;
795
+            }
796
+        });
797
+    };
798
+
799
+    Simulcast.prototype._stopLocalVideoStream = function (ssrc) {
800
+        var trackid;
801
+
802
+        Object.keys(localMaps.msid2ssrc).some(function (tid) {
803
+            if (localMaps.msid2ssrc[tid] == ssrc)
804
+            {
805
+                trackid = tid;
806
+                return true;
807
+            }
808
+        });
809
+
810
+        stream.getVideoTracks().some(function(track) {
811
+            if (track.id === trackid) {
812
+                track.enabled = false;
813
+                return true;
814
+            }
815
+        });
816
+    };
817
+
818
+    Simulcast.prototype.getLocalVideoStream = function() {
819
+        var track;
820
+
821
+        stream.getVideoTracks().some(function(t) {
822
+            if ((track = t).enabled) {
823
+                return true;
824
+            }
825
+        });
826
+
827
+        return new webkitMediaStream([track]);
828
+    };
829
+
830
+    $(document).bind('simulcastlayerschanged', function (event, endpointSimulcastLayers) {
831
+        endpointSimulcastLayers.forEach(function (esl) {
832
+            var ssrc = esl.simulcastLayer.primarySSRC;
833
+            var simulcast = new Simulcast();
834
+            simulcast._setReceivingVideoStream(ssrc);
835
+        });
836
+    });
837
+
838
+    $(document).bind('startsimulcastlayer', function(event, simulcastLayer) {
839
+        var ssrc = simulcastLayer.primarySSRC;
840
+        var simulcast = new Simulcast();
841
+        simulcast._startLocalVideoStream(ssrc);
842
+    });
843
+
844
+    $(document).bind('stopsimulcastlayer', function(event, simulcastLayer) {
845
+        var ssrc = simulcastLayer.primarySSRC;
846
+        var simulcast = new Simulcast();
847
+        simulcast._stopLocalVideoStream(ssrc);
848
+    });
673
 }());
849
 }());

+ 28
- 3
videolayout.js Ver fichero

116
 
116
 
117
             var isVisible = $('#largeVideo').is(':visible');
117
             var isVisible = $('#largeVideo').is(':visible');
118
 
118
 
119
+            // we need this here because after the fade the videoSrc may have
120
+            // changed.
121
+            var isDesktop = isVideoSrcDesktop(newSrc);
122
+
119
             $('#largeVideo').fadeOut(300, function () {
123
             $('#largeVideo').fadeOut(300, function () {
120
                 var oldSrc = $(this).attr('src');
124
                 var oldSrc = $(this).attr('src');
121
 
125
 
137
                 }
141
                 }
138
 
142
 
139
                 // Change the way we'll be measuring and positioning large video
143
                 // Change the way we'll be measuring and positioning large video
140
-                var isDesktop = isVideoSrcDesktop(newSrc);
144
+
141
                 getVideoSize = isDesktop
145
                 getVideoSize = isDesktop
142
                                 ? getDesktopVideoSize
146
                                 ? getDesktopVideoSize
143
                                 : getCameraVideoSize;
147
                                 : getCameraVideoSize;
209
 
213
 
210
         // Triggers a "video.selected" event. The "false" parameter indicates
214
         // Triggers a "video.selected" event. The "false" parameter indicates
211
         // this isn't a prezi.
215
         // this isn't a prezi.
212
-        $(document).trigger("video.selected", [false]);
216
+        $(document).trigger("video.selected", [false, userJid]);
213
 
217
 
214
         VideoLayout.updateLargeVideo(videoSrc, 1);
218
         VideoLayout.updateLargeVideo(videoSrc, 1);
215
 
219
 
1294
         }
1298
         }
1295
     });
1299
     });
1296
 
1300
 
1301
+    $(document).bind('startsimulcastlayer', function(event, simulcastLayer) {
1302
+        var localVideoSelector = $('#' + 'localVideo_' + connection.jingle.localVideo.id);
1303
+        var simulcast = new Simulcast();
1304
+        var stream = simulcast.getLocalVideoStream();
1305
+
1306
+        // Attach WebRTC stream
1307
+        RTC.attachMediaStream(localVideoSelector, stream);
1308
+
1309
+        localVideoSrc = $(localVideoSelector).attr('src');
1310
+    });
1311
+
1312
+    $(document).bind('stopsimulcastlayer', function(event, simulcastLayer) {
1313
+        var localVideoSelector = $('#' + 'localVideo_' + connection.jingle.localVideo.id);
1314
+        var simulcast = new Simulcast();
1315
+        var stream = simulcast.getLocalVideoStream();
1316
+
1317
+        // Attach WebRTC stream
1318
+        RTC.attachMediaStream(localVideoSelector, stream);
1319
+
1320
+        localVideoSrc = $(localVideoSelector).attr('src');
1321
+    });
1322
+
1297
     /**
1323
     /**
1298
      * On simulcast layers changed event.
1324
      * On simulcast layers changed event.
1299
      */
1325
      */
1302
         endpointSimulcastLayers.forEach(function (esl) {
1328
         endpointSimulcastLayers.forEach(function (esl) {
1303
 
1329
 
1304
             var primarySSRC = esl.simulcastLayer.primarySSRC;
1330
             var primarySSRC = esl.simulcastLayer.primarySSRC;
1305
-            simulcast.setReceivingVideoStream(primarySSRC);
1306
             var msid = simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
1331
             var msid = simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
1307
 
1332
 
1308
             // Get session and stream from msid.
1333
             // Get session and stream from msid.

Loading…
Cancelar
Guardar