소스 검색

fix(video-quality) Update frame heights in content-modify for p2p. (#1983)

* fix(video-quality) Update frame heights in content-modify for p2p.
When the user changes the preferred video quality settings from the UI, update the frame height values in content-modify for p2p connection when source-name signaling is enabled.

* fix(multi-stream) Send presence for desktop track before signaling when it is the first track to be added to the conference.
Also do not suppress renegotiation for desktop track on p2p since the browser doesn't fire negotiation needed event.

* squash: fix build

* squash: fix linter issues

* squash: add comment.
tags/v0.0.2
Jaya Allamsetty 3 년 전
부모
커밋
bddf523ac3
No account linked to committer's email address

+ 9
- 1
JitsiConference.js 파일 보기

@@ -1123,7 +1123,15 @@ JitsiConference.prototype.addTrack = function(track) {
1123 1123
         return Promise.reject(new Error(`Cannot add second ${mediaType} track to the conference`));
1124 1124
     }
1125 1125
 
1126
-    return this.replaceTrack(null, track);
1126
+    return this.replaceTrack(null, track)
1127
+        .then(() => {
1128
+            // Presence needs to be sent here for desktop track since we need the presence to reach the remote peer
1129
+            // before signaling so that a fake participant tile is created for screenshare. Otherwise, presence will
1130
+            // only be sent after a session-accept or source-add is ack'ed.
1131
+            if (track.getVideoType() === VideoType.DESKTOP && FeatureFlags.isMultiStreamSupportEnabled()) {
1132
+                this._updateRoomPresence(this.getActiveMediaSession());
1133
+            }
1134
+        });
1127 1135
 };
1128 1136
 
1129 1137
 /**

+ 2
- 1
modules/RTC/TraceablePeerConnection.js 파일 보기

@@ -1977,7 +1977,8 @@ TraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {
1977 1977
     // FIXME - This check needs to be removed when the client switches to the bridge based signaling for tracks.
1978 1978
     const isNewTrackScreenshare = !oldTrack
1979 1979
         && newTrack?.getVideoType() === VideoType.DESKTOP
1980
-        && FeatureFlags.isMultiStreamSupportEnabled();
1980
+        && FeatureFlags.isMultiStreamSupportEnabled()
1981
+        && !this.isP2P; // negotiationneeded is not fired on p2p peerconnection
1981 1982
     const negotiationNeeded = !isNewTrackScreenshare && Boolean(!oldTrack || !this.localTracks.has(oldTrack?.rtcId));
1982 1983
 
1983 1984
     if (this._usesUnifiedPlan) {

+ 50
- 3
modules/qualitycontrol/ReceiveVideoController.js 파일 보기

@@ -2,6 +2,7 @@ import { getLogger } from '@jitsi/logger';
2 2
 import isEqual from 'lodash.isequal';
3 3
 
4 4
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
5
+import { MediaType } from '../../service/RTC/MediaType';
5 6
 import FeatureFlags from '../flags/FeatureFlags';
6 7
 
7 8
 const logger = getLogger(__filename);
@@ -179,6 +180,14 @@ export default class ReceiveVideoController {
179 180
         // The number representing the maximum video height the local client should receive from the bridge.
180 181
         this._maxFrameHeight = MAX_HEIGHT_ONSTAGE;
181 182
 
183
+        /**
184
+         * The map that holds the max frame height requested for each remote source when source-name signaling is
185
+         * enabled.
186
+         *
187
+         * @type Map<string, number>
188
+         */
189
+        this._sourceReceiverConstraints = new Map();
190
+
182 191
         // Enable new receiver constraints by default unless it is explicitly disabled through config.js.
183 192
         const useNewReceiverConstraints = config?.useNewBandwidthAllocationStrategy ?? true;
184 193
 
@@ -199,6 +208,26 @@ export default class ReceiveVideoController {
199 208
             session => this._onMediaSessionStarted(session));
200 209
     }
201 210
 
211
+    /**
212
+     * Returns a map of all the remote source names and the corresponding max frame heights.
213
+     *
214
+     * @param {number} maxFrameHeight
215
+     * @returns
216
+     */
217
+    _getDefaultSourceReceiverConstraints(mediaSession, maxFrameHeight) {
218
+        if (!FeatureFlags.isSourceNameSignalingEnabled()) {
219
+            return null;
220
+        }
221
+        const remoteVideoTracks = mediaSession.peerconnection?.getRemoteTracks(null, MediaType.VIDEO) || [];
222
+        const receiverConstraints = new Map();
223
+
224
+        for (const track of remoteVideoTracks) {
225
+            receiverConstraints.set(track.getSourceName(), maxFrameHeight);
226
+        }
227
+
228
+        return receiverConstraints;
229
+    }
230
+
202 231
     /**
203 232
      * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media
204 233
      * session. The preferred receive frameHeight is applied on the media session.
@@ -209,7 +238,7 @@ export default class ReceiveVideoController {
209 238
      */
210 239
     _onMediaSessionStarted(mediaSession) {
211 240
         if (mediaSession.isP2P || !this._receiverVideoConstraints) {
212
-            mediaSession.setReceiverVideoConstraint(this._maxFrameHeight);
241
+            mediaSession.setReceiverVideoConstraint(this._maxFrameHeight, this._sourceReceiverConstraints);
213 242
         } else {
214 243
             this._receiverVideoConstraints.updateReceiveResolution(this._maxFrameHeight);
215 244
             this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);
@@ -287,7 +316,9 @@ export default class ReceiveVideoController {
287 316
 
288 317
         for (const session of this._conference.getMediaSessions()) {
289 318
             if (session.isP2P || !this._receiverVideoConstraints) {
290
-                maxFrameHeight && session.setReceiverVideoConstraint(maxFrameHeight);
319
+                session.setReceiverVideoConstraint(
320
+                    maxFrameHeight,
321
+                    this._getDefaultSourceReceiverConstraints(this._maxFrameHeight));
291 322
             } else {
292 323
                 const resolutionUpdated = this._receiverVideoConstraints.updateReceiveResolution(maxFrameHeight);
293 324
 
@@ -330,7 +361,23 @@ export default class ReceiveVideoController {
330 361
 
331 362
             const p2pSession = this._conference.getMediaSessions().find(session => session.isP2P);
332 363
 
333
-            if (p2pSession) {
364
+            if (!p2pSession) {
365
+                return;
366
+            }
367
+
368
+            if (FeatureFlags.isSourceNameSignalingEnabled()) {
369
+                const mappedConstraints = Array.from(Object.entries(constraints.constraints))
370
+                    .map(constraint => {
371
+                        constraint[1] = constraint[1].maxHeight;
372
+
373
+                        return constraint;
374
+                    });
375
+
376
+                this._sourceReceiverConstraints = new Map(mappedConstraints);
377
+
378
+                // Send the receiver constraints to the peer through a "content-modify" message.
379
+                p2pSession.setReceiverVideoConstraint(null, this._sourceReceiverConstraints);
380
+            } else {
334 381
                 let maxFrameHeight = Object.values(constraints.constraints)[0]?.maxHeight;
335 382
 
336 383
                 if (!maxFrameHeight) {

+ 17
- 9
modules/qualitycontrol/SendVideoController.js 파일 보기

@@ -66,19 +66,25 @@ export default class SendVideoController {
66 66
     /**
67 67
      * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media
68 68
      * session. It doesn't mean it's already active though. For example the JVB connection may be created after
69
-     * the conference has entered the p2p mode already.
69
+-    * the conference has entered the p2p mode already.
70 70
      *
71 71
      * @param {JingleSessionPC} mediaSession - the started media session.
72 72
      * @private
73 73
      */
74 74
     _onMediaSessionStarted(mediaSession) {
75
-        mediaSession.addListener(
76
-            MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED,
77
-            session => {
78
-                if (session === this._conference.getActiveMediaSession()) {
79
-                    this._configureConstraintsForLocalSources();
80
-                }
81
-            });
75
+        if (FeatureFlags.isSourceNameSignalingEnabled()) {
76
+            mediaSession.addListener(
77
+                MediaSessionEvents.REMOTE_SOURCE_CONSTRAINTS_CHANGED,
78
+                (session, sourceConstraints) => {
79
+                    session === this._conference.getActiveMediaSession()
80
+                        && sourceConstraints.forEach(constraint => this._onSenderConstraintsReceived(constraint));
81
+                });
82
+        } else {
83
+            mediaSession.addListener(
84
+                MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED,
85
+                session => session === this._conference.getActiveMediaSession()
86
+                    && this._configureConstraintsForLocalSources());
87
+        }
82 88
     }
83 89
 
84 90
     /**
@@ -148,7 +154,9 @@ export default class SendVideoController {
148 154
         const activeMediaSession = this._conference.getActiveMediaSession();
149 155
         const remoteRecvMaxFrameHeight = activeMediaSession
150 156
             ? activeMediaSession.isP2P
151
-                ? activeMediaSession.getRemoteRecvMaxFrameHeight()
157
+                ? sourceName
158
+                    ? this._sourceSenderConstraints.get(sourceName)
159
+                    : activeMediaSession.getRemoteRecvMaxFrameHeight()
152 160
                 : sourceName ? this._sourceSenderConstraints.get(sourceName) : this._senderVideoConstraints?.idealHeight
153 161
             : undefined;
154 162
 

+ 97
- 22
modules/xmpp/JingleSessionPC.js 파일 보기

@@ -122,6 +122,34 @@ export default class JingleSessionPC extends JingleSession {
122 122
         return maxFrameHeightSel.length ? Number(maxFrameHeightSel.text()) : null;
123 123
     }
124 124
 
125
+    /**
126
+     * Parses the source-name and max frame height value of the 'content-modify' IQ when source-name signaling
127
+     * is enabled.
128
+     *
129
+     * @param {jQuery} jingleContents - A jQuery selector pointing to the '>jingle' element.
130
+     * @returns {Object|null}
131
+     */
132
+    static parseSourceMaxFrameHeight(jingleContents) {
133
+        const receiverConstraints = [];
134
+        const sourceFrameHeightSel = jingleContents.find('>content[name="video"]>source-frame-height');
135
+        let maxHeight, sourceName;
136
+
137
+        if (sourceFrameHeightSel.length) {
138
+            sourceFrameHeightSel.each((_, source) => {
139
+                sourceName = source.getAttribute('sourceName');
140
+                maxHeight = source.getAttribute('maxHeight');
141
+                receiverConstraints.push({
142
+                    maxHeight,
143
+                    sourceName
144
+                });
145
+            });
146
+
147
+            return receiverConstraints;
148
+        }
149
+
150
+        return null;
151
+    }
152
+
125 153
     /* eslint-disable max-params */
126 154
 
127 155
     /**
@@ -208,6 +236,14 @@ export default class JingleSessionPC extends JingleSession {
208 236
          */
209 237
         this.localRecvMaxFrameHeight = undefined;
210 238
 
239
+        /**
240
+         * Receiver constraints (max height) set by the application per remote source. Will be used for p2p connection
241
+         * in lieu of localRecvMaxFrameHeight when source-name signaling is enabled.
242
+         *
243
+         * @type {Map<string, number>}
244
+         */
245
+        this._sourceReceiverConstraints = undefined;
246
+
211 247
         /**
212 248
          * Indicates whether or not this session is willing to send/receive
213 249
          * video media. When set to <tt>false</tt> the underlying peer
@@ -263,6 +299,13 @@ export default class JingleSessionPC extends JingleSession {
263 299
          */
264 300
         this.remoteRecvMaxFrameHeight = undefined;
265 301
 
302
+        /**
303
+         * Remote preference for the receive video max frame heights when source-name signaling is enabled.
304
+         *
305
+         * @type {Map<string, number>|undefined}
306
+         */
307
+        this.remoteSourceMaxFrameHeights = undefined;
308
+
266 309
         /**
267 310
          * The queue used to serialize operations done on the peerconnection.
268 311
          *
@@ -640,6 +683,19 @@ export default class JingleSessionPC extends JingleSession {
640 683
         return undefined;
641 684
     }
642 685
 
686
+    /**
687
+     * Remote preference for receive video max frame heights when source-name signaling is enabled.
688
+     *
689
+     * @returns {Map<string, number>|undefined}
690
+     */
691
+    getRemoteSourcesRecvMaxFrameHeight() {
692
+        if (this.isP2P) {
693
+            return this.remoteSourceMaxFrameHeights;
694
+        }
695
+
696
+        return undefined;
697
+    }
698
+
643 699
     /**
644 700
      * Sends given candidate in Jingle 'transport-info' message.
645 701
      * @param {RTCIceCandidate} candidate the WebRTC ICE candidate instance
@@ -1168,7 +1224,9 @@ export default class JingleSessionPC extends JingleSession {
1168 1224
                         // up our SDP translation chain (simulcast, video mute, RTX etc.)
1169 1225
                         // #2 Sends the max frame height if it was set, before the session-initiate/accept
1170 1226
                         if (this.isP2P
1171
-                            && (!this._localVideoActive || this.localRecvMaxFrameHeight)) {
1227
+                            && (!this._localVideoActive
1228
+                                || this.localRecvMaxFrameHeight
1229
+                                || this._sourceReceiverConstraints)) {
1172 1230
                             this.sendContentModify();
1173 1231
                         }
1174 1232
                     }
@@ -1407,9 +1465,24 @@ export default class JingleSessionPC extends JingleSession {
1407 1465
             sessionModify = sessionModify
1408 1466
                 .c('max-frame-height', { xmlns: 'http://jitsi.org/jitmeet/video' })
1409 1467
                 .t(maxFrameHeight);
1468
+            logger.info(`${this} sending content-modify, video senders: ${senders},`
1469
+                + ` max frame height: ${maxFrameHeight}`);
1470
+        }
1471
+
1472
+        if (typeof this._sourceReceiverConstraints !== 'undefined') {
1473
+            this._sourceReceiverConstraints.forEach((maxHeight, sourceName) => {
1474
+                sessionModify
1475
+                    .c('source-frame-height', { xmlns: 'http://jitsi.org/jitmeet/video' })
1476
+                    .attrs({
1477
+                        sourceName,
1478
+                        maxHeight
1479
+                    });
1480
+
1481
+                sessionModify.up();
1482
+                logger.info(`${this} sending content-modify for source-name: ${sourceName}, maxHeight: ${maxHeight}`);
1483
+            });
1410 1484
         }
1411 1485
 
1412
-        logger.info(`${this} sending content-modify, video senders: ${senders}, max frame height: ${maxFrameHeight}`);
1413 1486
         logger.debug(sessionModify.tree());
1414 1487
 
1415 1488
         this.connection.sendIQ(
@@ -1424,11 +1497,17 @@ export default class JingleSessionPC extends JingleSession {
1424 1497
      * the remote party.
1425 1498
      *
1426 1499
      * @param {Number} maxFrameHeight - the new value to set.
1500
+     * @param {Map<string, number>} sourceReceiverConstraints - The receiver constraints per source.
1427 1501
      */
1428
-    setReceiverVideoConstraint(maxFrameHeight) {
1429
-        logger.info(`${this} setReceiverVideoConstraint - max frame height: ${maxFrameHeight}`);
1502
+    setReceiverVideoConstraint(maxFrameHeight, sourceReceiverConstraints) {
1503
+        logger.info(`${this} setReceiverVideoConstraint - max frame height: ${maxFrameHeight}`
1504
+            + ` sourceReceiverConstraints: ${sourceReceiverConstraints}`);
1430 1505
 
1431
-        this.localRecvMaxFrameHeight = maxFrameHeight;
1506
+        if (FeatureFlags.isSourceNameSignalingEnabled()) {
1507
+            this._sourceReceiverConstraints = sourceReceiverConstraints;
1508
+        } else {
1509
+            this.localRecvMaxFrameHeight = maxFrameHeight;
1510
+        }
1432 1511
 
1433 1512
         if (this.isP2P) {
1434 1513
             // Tell the remote peer about our receive constraint. If Jingle session is not yet active the state will
@@ -1436,8 +1515,6 @@ export default class JingleSessionPC extends JingleSession {
1436 1515
             if (this.state === JingleSessionState.ACTIVE) {
1437 1516
                 this.sendContentModify();
1438 1517
             }
1439
-        } else {
1440
-            this.rtc.setReceiverVideoConstraint(maxFrameHeight);
1441 1518
         }
1442 1519
     }
1443 1520
 
@@ -2524,32 +2601,31 @@ export default class JingleSessionPC extends JingleSession {
2524 2601
      * @see {@link _localVideoActive}
2525 2602
      */
2526 2603
     modifyContents(jingleContents) {
2527
-        const newVideoSenders
2528
-            = JingleSessionPC.parseVideoSenders(jingleContents);
2529
-        const newMaxFrameHeight
2530
-            = JingleSessionPC.parseMaxFrameHeight(jingleContents);
2604
+        const newVideoSenders = JingleSessionPC.parseVideoSenders(jingleContents);
2605
+        const newMaxFrameHeight = JingleSessionPC.parseMaxFrameHeight(jingleContents);
2606
+        const sourceMaxFrameHeights = JingleSessionPC.parseSourceMaxFrameHeight(jingleContents);
2531 2607
 
2532 2608
         // frame height is optional in our content-modify protocol
2533 2609
         if (newMaxFrameHeight) {
2534 2610
             logger.info(`${this} received remote max frame height: ${newMaxFrameHeight}`);
2535 2611
             this.remoteRecvMaxFrameHeight = newMaxFrameHeight;
2536
-            this.eventEmitter.emit(
2537
-                MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED, this);
2612
+            this.eventEmitter.emit(MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED, this);
2613
+        }
2614
+
2615
+        if (sourceMaxFrameHeights) {
2616
+            this.remoteSourceMaxFrameHeights = sourceMaxFrameHeights;
2617
+            this.eventEmitter.emit(MediaSessionEvents.REMOTE_SOURCE_CONSTRAINTS_CHANGED, this, sourceMaxFrameHeights);
2538 2618
         }
2539 2619
 
2540 2620
         if (newVideoSenders === null) {
2541
-            logger.error(
2542
-                `${this} - failed to parse video "senders" attribute in`
2543
-                    + '"content-modify" action');
2621
+            logger.error(`${this} - failed to parse video "senders" attribute in "content-modify" action`);
2544 2622
 
2545 2623
             return;
2546 2624
         }
2547 2625
 
2548 2626
         const workFunction = finishedCallback => {
2549
-            if (this._assertNotEnded('content-modify')
2550
-                    && this._modifyRemoteVideoActive(newVideoSenders)) {
2551
-                // Will do the sRD/sLD cycle to update SDPs and adjust
2552
-                // the media direction
2627
+            if (this._assertNotEnded() && this._modifyRemoteVideoActive(newVideoSenders)) {
2628
+                // Will do the sRD/sLD cycle to update SDPs and adjust the media direction.
2553 2629
                 this._renegotiate()
2554 2630
                     .then(finishedCallback, finishedCallback /* (error) */);
2555 2631
             } else {
@@ -2591,8 +2667,7 @@ export default class JingleSessionPC extends JingleSession {
2591 2667
             this._remoteVideoActive = isRemoteVideoActive;
2592 2668
         }
2593 2669
 
2594
-        return this.peerconnection.setVideoTransferActive(
2595
-            this._localVideoActive && this._remoteVideoActive);
2670
+        return this.peerconnection.setVideoTransferActive(this._localVideoActive && this._remoteVideoActive);
2596 2671
     }
2597 2672
 
2598 2673
     /**

+ 91
- 2
modules/xmpp/JingleSessionPC.spec.js 파일 보기

@@ -1,5 +1,6 @@
1 1
 /* global $, jQuery */
2 2
 import { MockRTC } from '../RTC/MockClasses';
3
+import FeatureFlags from '../flags/FeatureFlags';
3 4
 
4 5
 import JingleSessionPC from './JingleSessionPC';
5 6
 import * as JingleSessionState from './JingleSessionState';
@@ -23,7 +24,23 @@ function createContentModify(senders = 'both', maxFrameHeight) {
23 24
     return $(modifyContentsIq).find('>jingle');
24 25
 }
25 26
 
26
-describe('JingleSessionPC', () => {
27
+/**
28
+ * Creates 'content-modify' Jingle IQ.
29
+ * @returns {jQuery}
30
+ */
31
+function createContentModifyForSourceNames() {
32
+    const modifyContentsIq = jQuery.parseXML(
33
+        '<jingle action="content-modify" initiator="peer2" sid="sid12345" xmlns="urn:xmpp:jingle:1">'
34
+        + '<content name="video" senders="both">'
35
+        + '<source-frame-height maxHeight="180" sourceName="8d519815-v0" xmlns="http://jitsi.org/jitmeet/video"/>'
36
+        + '<source-frame-height maxHeight="2160" sourceName="8d519815-v1" xmlns="http://jitsi.org/jitmeet/video"/>'
37
+        + '</content>'
38
+        + '</jingle>');
39
+
40
+    return $(modifyContentsIq).find('>jingle');
41
+}
42
+
43
+describe('JingleSessionPC w/o source-name signaling', () => {
27 44
     let jingleSession;
28 45
     let connection;
29 46
     let rtc;
@@ -62,7 +79,11 @@ describe('JingleSessionPC', () => {
62 79
         // connection.connect('jid', undefined, () => { }); */
63 80
     });
64 81
 
65
-    describe('send/receive video constraints', () => {
82
+    describe('send/receive video constraints w/o source-name', () => {
83
+        beforeEach(() => {
84
+            FeatureFlags.init({ sourceNameSignaling: false });
85
+        });
86
+
66 87
         it('sends content-modify with recv frame size', () => {
67 88
             const sendIQSpy = spyOn(connection, 'sendIQ').and.callThrough();
68 89
 
@@ -120,4 +141,72 @@ describe('JingleSessionPC', () => {
120 141
             });
121 142
         });
122 143
     });
144
+
145
+    describe('send/receive video constraints w/ source-name', () => {
146
+        beforeEach(() => {
147
+            FeatureFlags.init({ sourceNameSignaling: true });
148
+        });
149
+
150
+        it('sends content-modify with recv frame size', () => {
151
+            const sendIQSpy = spyOn(connection, 'sendIQ').and.callThrough();
152
+            const sourceConstraints = new Map();
153
+
154
+            sourceConstraints.set('8d519815-v0', 180);
155
+            sourceConstraints.set('8d519815-v1', 2160);
156
+
157
+            jingleSession.setReceiverVideoConstraint(null, sourceConstraints);
158
+
159
+            expect(jingleSession.getState()).toBe(JingleSessionState.PENDING);
160
+
161
+            return new Promise((resolve, reject) => {
162
+                jingleSession.acceptOffer(
163
+                    offerIQ,
164
+                    resolve,
165
+                    reject,
166
+                    /* local tracks */ []);
167
+            }).then(() => {
168
+                expect(jingleSession.getState()).toBe(JingleSessionState.ACTIVE);
169
+
170
+                // FIXME content-modify is sent before session-accept
171
+                expect(sendIQSpy.calls.count()).toBe(2);
172
+
173
+                expect(sendIQSpy.calls.first().args[0].toString()).toBe(
174
+                    '<iq to="peer2" type="set" xmlns="jabber:client">'
175
+                    + '<jingle action="content-modify" initiator="peer2" sid="sid12345" xmlns="urn:xmpp:jingle:1">'
176
+                    + '<content name="video" senders="both">'
177
+                    + '<source-frame-height maxHeight="180" sourceName="8d519815-v0"'
178
+                    + ' xmlns="http://jitsi.org/jitmeet/video"/>'
179
+                    + '<source-frame-height maxHeight="2160" sourceName="8d519815-v1"'
180
+                    + ' xmlns="http://jitsi.org/jitmeet/video"/>'
181
+                    + '</content>'
182
+                    + '</jingle>'
183
+                    + '</iq>');
184
+            });
185
+        });
186
+        it('fires an event when remote peer sends content-modify', () => {
187
+            let remoteSourcesRecvMaxFrameHeight;
188
+            const remoteVideoConstraintsListener = () => {
189
+                remoteSourcesRecvMaxFrameHeight = jingleSession.getRemoteSourcesRecvMaxFrameHeight();
190
+            };
191
+
192
+            jingleSession.addListener(
193
+                MediaSessionEvents.REMOTE_SOURCE_CONSTRAINTS_CHANGED,
194
+                remoteVideoConstraintsListener);
195
+
196
+            return new Promise((resolve, reject) => {
197
+                jingleSession.acceptOffer(
198
+                    offerIQ,
199
+                    resolve,
200
+                    reject,
201
+                    /* local tracks */ []);
202
+            }).then(() => {
203
+                jingleSession.modifyContents(createContentModifyForSourceNames());
204
+                const v0Height = remoteSourcesRecvMaxFrameHeight[0].maxHeight;
205
+                const v1Height = remoteSourcesRecvMaxFrameHeight[1].maxHeight;
206
+
207
+                expect(v0Height).toBe('180');
208
+                expect(v1Height).toBe('2160');
209
+            });
210
+        });
211
+    });
123 212
 });

+ 2
- 0
modules/xmpp/MediaSessionEvents.spec.ts 파일 보기

@@ -4,11 +4,13 @@ import { default as exported } from "./MediaSessionEvents";
4 4
 
5 5
 describe( "/modules/xmpp/MediaSessionEvents members", () => {
6 6
     const {
7
+        REMOTE_SOURCE_CONSTRAINTS_CHANGED,
7 8
         REMOTE_VIDEO_CONSTRAINTS_CHANGED,
8 9
         ...others
9 10
     } = exported;
10 11
 
11 12
     it( "known members", () => {
13
+        expect( REMOTE_SOURCE_CONSTRAINTS_CHANGED ).toBe( 'media_session.REMOTE_SOURCE_CONSTRAINTS_CHANGED' );
12 14
         expect( REMOTE_VIDEO_CONSTRAINTS_CHANGED ).toBe( 'media_session.REMOTE_VIDEO_CONSTRAINTS_CHANGED' );
13 15
     } );
14 16
 

+ 5
- 0
modules/xmpp/MediaSessionEvents.ts 파일 보기

@@ -1,4 +1,9 @@
1 1
 enum MediaSessionEvents {
2
+    /**
3
+     * Event triggered when the remote party signals video max frame heights for its local sources.
4
+     */
5
+    REMOTE_SOURCE_CONSTRAINTS_CHANGED = 'media_session.REMOTE_SOURCE_CONSTRAINTS_CHANGED',
6
+
2 7
     /**
3 8
      * Event triggered when the remote party signals it's receive video max frame height.
4 9
      */

+ 14
- 0
types/auto/modules/qualitycontrol/ReceiveVideoController.d.ts 파일 보기

@@ -16,8 +16,22 @@ export default class ReceiveVideoController {
16 16
     _rtc: any;
17 17
     _lastN: any;
18 18
     _maxFrameHeight: number;
19
+    /**
20
+     * The map that holds the max frame height requested for each remote source when source-name signaling is
21
+     * enabled.
22
+     *
23
+     * @type Map<string, number>
24
+     */
25
+    _sourceReceiverConstraints: Map<string, number>;
19 26
     _receiverVideoConstraints: ReceiverVideoConstraints;
20 27
     _selectedEndpoints: any[];
28
+    /**
29
+     * Returns a map of all the remote source names and the corresponding max frame heights.
30
+     *
31
+     * @param {number} maxFrameHeight
32
+     * @returns
33
+     */
34
+    _getDefaultSourceReceiverConstraints(mediaSession: any, maxFrameHeight: number): Map<any, any>;
21 35
     /**
22 36
      * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media
23 37
      * session. The preferred receive frameHeight is applied on the media session.

+ 1
- 1
types/auto/modules/qualitycontrol/SendVideoController.d.ts 파일 보기

@@ -32,7 +32,7 @@ export default class SendVideoController {
32 32
     /**
33 33
      * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media
34 34
      * session. It doesn't mean it's already active though. For example the JVB connection may be created after
35
-     * the conference has entered the p2p mode already.
35
+-    * the conference has entered the p2p mode already.
36 36
      *
37 37
      * @param {JingleSessionPC} mediaSession - the started media session.
38 38
      * @private

+ 29
- 1
types/auto/modules/xmpp/JingleSessionPC.d.ts 파일 보기

@@ -41,6 +41,14 @@ export default class JingleSessionPC extends JingleSession {
41 41
      * @returns {Number|null}
42 42
      */
43 43
     static parseMaxFrameHeight(jingleContents: any): number | null;
44
+    /**
45
+     * Parses the source-name and max frame height value of the 'content-modify' IQ when source-name signaling
46
+     * is enabled.
47
+     *
48
+     * @param {jQuery} jingleContents - A jQuery selector pointing to the '>jingle' element.
49
+     * @returns {Object|null}
50
+     */
51
+    static parseSourceMaxFrameHeight(jingleContents: any): any | null;
44 52
     /**
45 53
      * Creates new <tt>JingleSessionPC</tt>
46 54
      * @param {string} sid the Jingle Session ID - random string which identifies the session
@@ -106,6 +114,13 @@ export default class JingleSessionPC extends JingleSession {
106 114
      * @type {Number|undefined}
107 115
      */
108 116
     localRecvMaxFrameHeight: number | undefined;
117
+    /**
118
+     * Receiver constraints (max height) set by the application per remote source. Will be used for p2p connection
119
+     * in lieu of localRecvMaxFrameHeight when source-name signaling is enabled.
120
+     *
121
+     * @type {Map<string, number>}
122
+     */
123
+    _sourceReceiverConstraints: Map<string, number>;
109 124
     /**
110 125
      * Indicates whether or not this session is willing to send/receive
111 126
      * video media. When set to <tt>false</tt> the underlying peer
@@ -155,6 +170,12 @@ export default class JingleSessionPC extends JingleSession {
155 170
      * @type {Number|undefined}
156 171
      */
157 172
     remoteRecvMaxFrameHeight: number | undefined;
173
+    /**
174
+     * Remote preference for the receive video max frame heights when source-name signaling is enabled.
175
+     *
176
+     * @type {Map<string, number>|undefined}
177
+     */
178
+    remoteSourceMaxFrameHeights: Map<string, number> | undefined;
158 179
     /**
159 180
      * The queue used to serialize operations done on the peerconnection.
160 181
      *
@@ -204,6 +225,12 @@ export default class JingleSessionPC extends JingleSession {
204 225
      * @returns {Number|undefined}
205 226
      */
206 227
     getRemoteRecvMaxFrameHeight(): number | undefined;
228
+    /**
229
+     * Remote preference for receive video max frame heights when source-name signaling is enabled.
230
+     *
231
+     * @returns {Map<string, number>|undefined}
232
+     */
233
+    getRemoteSourcesRecvMaxFrameHeight(): Map<string, number> | undefined;
207 234
     /**
208 235
      * Sends given candidate in Jingle 'transport-info' message.
209 236
      * @param {RTCIceCandidate} candidate the WebRTC ICE candidate instance
@@ -327,8 +354,9 @@ export default class JingleSessionPC extends JingleSession {
327 354
      * the remote party.
328 355
      *
329 356
      * @param {Number} maxFrameHeight - the new value to set.
357
+     * @param {Map<string, number>} sourceReceiverConstraints - The receiver constraints per source.
330 358
      */
331
-    setReceiverVideoConstraint(maxFrameHeight: number): void;
359
+    setReceiverVideoConstraint(maxFrameHeight: number, sourceReceiverConstraints: Map<string, number>): void;
332 360
     /**
333 361
      * Sends Jingle 'transport-accept' message which is a response to
334 362
      * 'transport-replace'.

+ 4
- 0
types/auto/modules/xmpp/MediaSessionEvents.d.ts 파일 보기

@@ -1,4 +1,8 @@
1 1
 declare enum MediaSessionEvents {
2
+    /**
3
+     * Event triggered when the remote party signals video max frame heights for its local sources.
4
+     */
5
+    REMOTE_SOURCE_CONSTRAINTS_CHANGED = "media_session.REMOTE_SOURCE_CONSTRAINTS_CHANGED",
2 6
     /**
3 7
      * Event triggered when the remote party signals it's receive video max frame height.
4 8
      */

Loading…
취소
저장