ソースを参照

feat(video-quality): control the sender resolution based on video quality settings (#1119)

* feat(video-quality): control the sender resolution based on video quality settings

* fix(video-quality): Apply the settings on newly created p2p/jvb jingle sessions
If a p2p/jvb session is not present when setSenderVideoConstraint is called,
make sure the settings are applied when they are created
dev1
Jaya Allamsetty 5年前
コミット
47b292e332
コミッターのメールアドレスに関連付けられたアカウントが存在しません

+ 48
- 0
JitsiConference.js ファイルの表示

235
     this.recordingManager = new RecordingManager(this.room);
235
     this.recordingManager = new RecordingManager(this.room);
236
     this._conferenceJoinAnalyticsEventSent = false;
236
     this._conferenceJoinAnalyticsEventSent = false;
237
 
237
 
238
+    /**
239
+     * Max frame height that the user prefers to send to the remote participants.
240
+     * @type {number}
241
+     */
242
+    this.maxFrameHeight = null;
243
+
238
     if (browser.supportsInsertableStreams()) {
244
     if (browser.supportsInsertableStreams()) {
239
         this._e2eeCtx = new E2EEContext({ salt: this.options.name });
245
         this._e2eeCtx = new E2EEContext({ salt: this.options.name });
240
     }
246
     }
1879
                 // to be turned off here.
1885
                 // to be turned off here.
1880
                 if (this.isP2PActive() && this.jvbJingleSession) {
1886
                 if (this.isP2PActive() && this.jvbJingleSession) {
1881
                     this._suspendMediaTransferForJvbConnection();
1887
                     this._suspendMediaTransferForJvbConnection();
1888
+                } else if (this.jvbJingleSession && this.maxFrameHeight) {
1889
+                    // Apply user preferred max frame height if it was called before this
1890
+                    // jingle session was created.
1891
+                    this.jvbJingleSession.setSenderVideoConstraint(this.maxFrameHeight)
1892
+                        .catch(err => {
1893
+                            logger.error(`Sender video constraints failed on jvb session - ${err}`);
1894
+                        });
1882
                 }
1895
                 }
1883
 
1896
 
1884
                 // Setup E2EE.
1897
                 // Setup E2EE.
2644
         () => {
2657
         () => {
2645
             logger.debug('Got RESULT for P2P "session-accept"');
2658
             logger.debug('Got RESULT for P2P "session-accept"');
2646
 
2659
 
2660
+            // Apply user preferred max frame height if it was called before this
2661
+            // jingle session was created.
2662
+            if (this.pendingVideoConstraintsOnP2P) {
2663
+                this.p2pJingleSession.setSenderVideoConstraint(this.maxFrameHeight)
2664
+                    .catch(err => {
2665
+                        logger.error(`Sender video constraints failed on p2p session - ${err}`);
2666
+                    });
2667
+            }
2668
+
2647
             // Setup E2EE.
2669
             // Setup E2EE.
2648
             for (const track of localTracks) {
2670
             for (const track of localTracks) {
2649
                 this._setupSenderE2EEForTrack(jingleSession, track);
2671
                 this._setupSenderE2EEForTrack(jingleSession, track);
3261
     this.rtc.setReceiverVideoConstraint(maxFrameHeight);
3283
     this.rtc.setReceiverVideoConstraint(maxFrameHeight);
3262
 };
3284
 };
3263
 
3285
 
3286
+/**
3287
+ * Sets the maximum video size the local participant should send to remote
3288
+ * participants.
3289
+ * @param {number} maxFrameHeight - The user preferred max frame height.
3290
+ * @returns {Promise} promise that will be resolved when the operation is
3291
+ * successful and rejected otherwise.
3292
+ */
3293
+JitsiConference.prototype.setSenderVideoConstraint = function(maxFrameHeight) {
3294
+    this.maxFrameHeight = maxFrameHeight;
3295
+    this.pendingVideoConstraintsOnP2P = true;
3296
+    const promises = [];
3297
+
3298
+    // We have to always set the sender video constraints on the jvb connection
3299
+    // when we switch from p2p to jvb connection since we need to check if the
3300
+    // tracks constraints have been modified when in p2p.
3301
+    if (this.jvbJingleSession) {
3302
+        promises.push(this.jvbJingleSession.setSenderVideoConstraint(maxFrameHeight));
3303
+    }
3304
+    if (this.p2pJingleSession) {
3305
+        this.pendingVideoConstraintsOnP2P = false;
3306
+        promises.push(this.p2pJingleSession.setSenderVideoConstraint(maxFrameHeight));
3307
+    }
3308
+
3309
+    return Promise.all(promises);
3310
+};
3311
+
3264
 /**
3312
 /**
3265
  * Creates a video SIP GW session and returns it if service is enabled. Before
3313
  * Creates a video SIP GW session and returns it if service is enabled. Before
3266
  * creating a session one need to check whether video SIP GW service is
3314
  * creating a session one need to check whether video SIP GW service is

+ 3
- 1
doc/API.md ファイルの表示

406
 34. setReceiverVideoConstraint(resolution) - set the desired resolution to get from JVB (180, 360, 720, 1080, etc).
406
 34. setReceiverVideoConstraint(resolution) - set the desired resolution to get from JVB (180, 360, 720, 1080, etc).
407
     You should use that method if you are using simulcast.
407
     You should use that method if you are using simulcast.
408
 
408
 
409
-35. isHidden - checks if local user has joined as a "hidden" user. This is a specialized role used for integrations.
409
+35. setSenderVideoConstraint(resolution) - set the desired resolution to send to JVB or the peer (180, 360, 720).
410
+
411
+36. isHidden - checks if local user has joined as a "hidden" user. This is a specialized role used for integrations.
410
 
412
 
411
 JitsiTrack
413
 JitsiTrack
412
 ======
414
 ======

+ 1
- 1
modules/RTC/JitsiLocalTrack.js ファイルの表示

551
                     = RTCUtils.obtainAudioAndVideoPermissions(streamOptions);
551
                     = RTCUtils.obtainAudioAndVideoPermissions(streamOptions);
552
             }
552
             }
553
 
553
 
554
-            promise.then(streamsInfo => {
554
+            promise = promise.then(streamsInfo => {
555
                 // The track kind for presenter track is video as well.
555
                 // The track kind for presenter track is video as well.
556
                 const mediaType = this.getType() === MediaType.PRESENTER ? MediaType.VIDEO : this.getType();
556
                 const mediaType = this.getType() === MediaType.PRESENTER ? MediaType.VIDEO : this.getType();
557
                 const streamInfo
557
                 const streamInfo

+ 31
- 0
modules/RTC/TPCUtils.js ファイルの表示

4
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
4
 import * as JitsiTrackEvents from '../../JitsiTrackEvents';
5
 import browser from '../browser';
5
 import browser from '../browser';
6
 import RTCEvents from '../../service/RTC/RTCEvents';
6
 import RTCEvents from '../../service/RTC/RTCEvents';
7
+import * as VideoType from '../../service/RTC/VideoType';
7
 
8
 
8
 const logger = getLogger(__filename);
9
 const logger = getLogger(__filename);
9
 const SIM_LAYER_1_RID = '1';
10
 const SIM_LAYER_1_RID = '1';
47
                 scaleResolutionDownBy: browser.isFirefox() ? 4.0 : 1.0
48
                 scaleResolutionDownBy: browser.isFirefox() ? 4.0 : 1.0
48
             }
49
             }
49
         ];
50
         ];
51
+
52
+        /**
53
+         * Resolution height constraints for the simulcast encodings that
54
+         * are configured for the video tracks.
55
+         */
56
+        this.simulcastStreamConstraints = [];
50
     }
57
     }
51
 
58
 
52
     /**
59
     /**
168
         });
175
         });
169
     }
176
     }
170
 
177
 
178
+    /**
179
+     * Constructs resolution height constraints for the simulcast encodings that are
180
+     * created for a given local video track.
181
+     * @param {MediaStreamTrack} track - the local video track.
182
+     * @returns {void}
183
+     */
184
+    _setSimulcastStreamConstraints(track) {
185
+        const height = track.getSettings().height;
186
+
187
+        for (const encoding in this.simulcastEncodings) {
188
+            if (this.simulcastEncodings.hasOwnProperty(encoding)) {
189
+                this.simulcastStreamConstraints.push({
190
+                    height: height / this.simulcastEncodings[encoding].scaleResolutionDownBy,
191
+                    rid: this.simulcastEncodings[encoding].rid
192
+                });
193
+            }
194
+        }
195
+    }
196
+
171
     /**
197
     /**
172
     * Adds {@link JitsiLocalTrack} to the WebRTC peerconnection for the first time.
198
     * Adds {@link JitsiLocalTrack} to the WebRTC peerconnection for the first time.
173
     * @param {JitsiLocalTrack} track - track to be added to the peerconnection.
199
     * @param {JitsiLocalTrack} track - track to be added to the peerconnection.
196
             // unused "recv-only" transceiver.
222
             // unused "recv-only" transceiver.
197
             this.pc.peerconnection.addTrack(track);
223
             this.pc.peerconnection.addTrack(track);
198
         }
224
         }
225
+
226
+        // Construct the simulcast stream constraints for the newly added track.
227
+        if (localTrack.isVideoTrack() && localTrack.videoType === VideoType.CAMERA && this.pc.isSimulcastOn()) {
228
+            this._setSimulcastStreamConstraints(localTrack.getTrack());
229
+        }
199
     }
230
     }
200
 
231
 
201
     /**
232
     /**

+ 106
- 0
modules/RTC/TraceablePeerConnection.js ファイルの表示

6
 
6
 
7
 import * as GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
7
 import * as GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
8
 import JitsiRemoteTrack from './JitsiRemoteTrack';
8
 import JitsiRemoteTrack from './JitsiRemoteTrack';
9
+import {
10
+    TRACK_ADDED,
11
+    TRACK_MUTE_CHANGED
12
+} from '../../JitsiConferenceEvents';
9
 import * as MediaType from '../../service/RTC/MediaType';
13
 import * as MediaType from '../../service/RTC/MediaType';
14
+import * as VideoType from '../../service/RTC/VideoType';
10
 import LocalSdpMunger from './LocalSdpMunger';
15
 import LocalSdpMunger from './LocalSdpMunger';
11
 import RTC from './RTC';
16
 import RTC from './RTC';
12
 import RTCUtils from './RTCUtils';
17
 import RTCUtils from './RTCUtils';
335
         }, 1000);
340
         }, 1000);
336
     }
341
     }
337
 
342
 
343
+    // Set sender video constraints when a new local video track is added
344
+    // to the conference or when it is unmuted.
345
+    this.senderVideoMaxHeight = null;
346
+    const maybeSetSenderVideoConstraints = track => {
347
+        if (track.isLocal()
348
+            && !track.isMuted()
349
+            && track.isVideoTrack()
350
+            && track.videoType === VideoType.CAMERA
351
+            && this.senderVideoMaxHeight) {
352
+            this.setSenderVideoConstraint(this.senderVideoMaxHeight)
353
+                .catch(err => {
354
+                    logger.error(`Settings sender video constraints failed: ${err}`);
355
+                });
356
+        }
357
+    };
358
+
359
+    this.rtc.conference.on(
360
+        TRACK_ADDED,
361
+        maybeSetSenderVideoConstraints);
362
+    this.rtc.conference.on(
363
+        TRACK_MUTE_CHANGED,
364
+        maybeSetSenderVideoConstraints);
365
+
338
     logger.info(`Create new ${this}`);
366
     logger.info(`Create new ${this}`);
339
 }
367
 }
340
 
368
 
1469
     if (browser.usesUnifiedPlan() && !browser.usesSdpMungingForSimulcast()) {
1497
     if (browser.usesUnifiedPlan() && !browser.usesSdpMungingForSimulcast()) {
1470
         this.tpcUtils.setEncodings(track);
1498
         this.tpcUtils.setEncodings(track);
1471
     }
1499
     }
1500
+
1501
+    // Construct the simulcast stream constraints for the newly added track.
1502
+    if (track.isVideoTrack() && track.videoType === VideoType.CAMERA && this.isSimulcastOn()) {
1503
+        this.tpcUtils._setSimulcastStreamConstraints(track.getTrack());
1504
+    }
1472
 };
1505
 };
1473
 
1506
 
1474
 /**
1507
 /**
2042
     });
2075
     });
2043
 };
2076
 };
2044
 
2077
 
2078
+/**
2079
+ * Changes the resolution of the video stream that is sent to the peer based on
2080
+ * the user preferred value. If simulcast is enabled on the peerconection, all the
2081
+ * simulcast encodings that have a resolution height lower or equal to the value
2082
+ * provided will remain active. For the non-simulcast case, video constraint is
2083
+ * applied on the track.
2084
+ * @param {number} frameHeight - The user preferred max frame height.
2085
+ * @returns {Promise} promise that will be resolved when the operation is
2086
+ * successful and rejected otherwise.
2087
+ */
2088
+TraceablePeerConnection.prototype.setSenderVideoConstraint = function(frameHeight) {
2089
+    this.senderVideoMaxHeight = frameHeight;
2090
+    const localVideoTrack = Array.from(this.localTracks.values()).find(t => t.isVideoTrack());
2091
+
2092
+    if (!localVideoTrack || localVideoTrack.isMuted() || localVideoTrack.videoType !== VideoType.CAMERA) {
2093
+        return Promise.resolve();
2094
+    }
2095
+    const track = localVideoTrack.getTrack();
2096
+
2097
+    if (this.isSimulcastOn()) {
2098
+        let promise = Promise.resolve();
2099
+
2100
+        // Check if the track constraints have been modified in p2p mode, apply
2101
+        // the constraints that were used for creating the track if that is the case.
2102
+        const height = localVideoTrack._constraints.height.ideal
2103
+            ? localVideoTrack._constraints.height.ideal
2104
+            : localVideoTrack._constraints.height;
2105
+
2106
+        if (track.getSettings().height !== height) {
2107
+            promise = track.applyConstraints(localVideoTrack._constraints);
2108
+        }
2109
+
2110
+        return promise
2111
+            .then(() => {
2112
+                // Determine the encodings that need to stay enabled based on the
2113
+                // new frameHeight provided.
2114
+                const encodingsEnabledState = this.tpcUtils.simulcastStreamConstraints
2115
+                    .map(constraint => constraint.height <= frameHeight);
2116
+                const videoSender = this.findSenderByKind(MediaType.VIDEO);
2117
+
2118
+                if (!videoSender) {
2119
+                    return Promise.reject(new Error('RTCRtpSender not found for local video'));
2120
+                }
2121
+                const parameters = videoSender.getParameters();
2122
+
2123
+                if (!parameters || !parameters.encodings || !parameters.encodings.length) {
2124
+                    return Promise.reject(new Error('RTCRtpSendParameters not found for local video track'));
2125
+                }
2126
+                logger.debug(`Setting max height of ${frameHeight} on local video`);
2127
+                for (const encoding in parameters.encodings) {
2128
+                    if (parameters.encodings.hasOwnProperty(encoding)) {
2129
+                        parameters.encodings[encoding].active = encodingsEnabledState[encoding];
2130
+                    }
2131
+                }
2132
+
2133
+                return videoSender.setParameters(parameters);
2134
+            });
2135
+    }
2136
+
2137
+    // Apply the height constraint on the local camera track
2138
+    const aspectRatio = (track.getSettings().width / track.getSettings().height).toPrecision(4);
2139
+
2140
+    logger.debug(`Setting max height of ${frameHeight} on local video`);
2141
+
2142
+    return track.applyConstraints(
2143
+        {
2144
+            aspectRatio,
2145
+            height: {
2146
+                ideal: frameHeight
2147
+            }
2148
+        });
2149
+};
2150
+
2045
 /**
2151
 /**
2046
  * Enables/disables video media transmission on this peer connection. When
2152
  * Enables/disables video media transmission on this peer connection. When
2047
  * disabled the SDP video media direction in the local SDP will be adjusted to
2153
  * disabled the SDP video media direction in the local SDP will be adjusted to

+ 10
- 0
modules/xmpp/JingleSessionPC.js ファイルの表示

1328
             IQ_TIMEOUT);
1328
             IQ_TIMEOUT);
1329
     }
1329
     }
1330
 
1330
 
1331
+    /**
1332
+     * Sets the resolution constraint on the local camera track.
1333
+     * @param {number} maxFrameHeight - The user preferred max frame height.
1334
+     * @returns {Promise} promise that will be resolved when the operation is
1335
+     * successful and rejected otherwise.
1336
+     */
1337
+    setSenderVideoConstraint(maxFrameHeight) {
1338
+        return this.peerconnection.setSenderVideoConstraint(maxFrameHeight);
1339
+    }
1340
+
1331
     /**
1341
     /**
1332
      * @inheritDoc
1342
      * @inheritDoc
1333
      */
1343
      */

読み込み中…
キャンセル
保存