Explorar el Código

feat(codec-selection): Use the new codec selection API (#2520)

* feat(codec-selection): Use the new codec selection API
https://github.com/w3ctag/design-reviews/issues/836. This allows the client to seamlessly switch between the codecs without having to trigger a renegotiation.
This feature is behind the flag testing.enableCodecSelectionAPI in config.js

* fix(stats): Fix local resolution stats.
The video codec for the local video sources needs to identified differently now, from the codecs field in the RTCRtpSendParameters returned by the browser. We no longer munge the remote SDP to switch to a different codec.

* feat(stats): Feed encodeTime stats for all local SSRCs to the codec selection mechanism.

* fix(codec-selection) Continue to mumge SDP for selecting H.264.

* feat(codec-selection) Make screenshare codec configurable.
If no 'screenshareCodec' is set under videoQuality or p2p settings, AV1 will be selected as default.

* squash: Address review comments

* Update modules/RTC/CodecSelection.js

Co-authored-by: Saúl Ibarra Corretgé <s@saghul.net>

* fix(codec-selection) Add codec to existing stats

---------

Co-authored-by: Saúl Ibarra Corretgé <s@saghul.net>
release-8443
Jaya Allamsetty hace 1 año
padre
commit
6bcc577acd
No account linked to committer's email address

+ 14
- 5
JitsiConference.js Ver fichero

390
                 ? config.videoQuality.mobileCodecPreferenceOrder
390
                 ? config.videoQuality.mobileCodecPreferenceOrder
391
                 : config.videoQuality?.codecPreferenceOrder,
391
                 : config.videoQuality?.codecPreferenceOrder,
392
             disabledCodec: _getCodecMimeType(config.videoQuality?.disabledCodec),
392
             disabledCodec: _getCodecMimeType(config.videoQuality?.disabledCodec),
393
-            preferredCodec: _getCodecMimeType(config.videoQuality?.preferredCodec)
393
+            preferredCodec: _getCodecMimeType(config.videoQuality?.preferredCodec),
394
+            screenshareCodec: browser.isMobileDevice()
395
+                ? _getCodecMimeType(config.videoQuality?.mobileScreenshareCodec)
396
+                : _getCodecMimeType(config.videoQuality?.screenshareCodec)
394
         },
397
         },
395
         p2p: {
398
         p2p: {
396
             preferenceOrder: browser.isMobileDevice() && config.p2p?.mobileCodecPreferenceOrder
399
             preferenceOrder: browser.isMobileDevice() && config.p2p?.mobileCodecPreferenceOrder
397
                 ? config.p2p.mobileCodecPreferenceOrder
400
                 ? config.p2p.mobileCodecPreferenceOrder
398
                 : config.p2p?.codecPreferenceOrder,
401
                 : config.p2p?.codecPreferenceOrder,
399
             disabledCodec: _getCodecMimeType(config.p2p?.disabledCodec),
402
             disabledCodec: _getCodecMimeType(config.p2p?.disabledCodec),
400
-            preferredCodec: _getCodecMimeType(config.p2p?.preferredCodec)
403
+            preferredCodec: _getCodecMimeType(config.p2p?.preferredCodec),
404
+            screenshareCodec: browser.isMobileDevice()
405
+                ? _getCodecMimeType(config.p2p?.mobileScreenshareCodec)
406
+                : _getCodecMimeType(config.p2p?.screenshareCodec)
401
         }
407
         }
402
     };
408
     };
403
 
409
 
2198
                 ...this.options.config,
2204
                 ...this.options.config,
2199
                 codecSettings: {
2205
                 codecSettings: {
2200
                     mediaType: MediaType.VIDEO,
2206
                     mediaType: MediaType.VIDEO,
2201
-                    codecList: this.codecSelection.getCodecPreferenceList('jvb')
2207
+                    codecList: this.codecSelection.getCodecPreferenceList('jvb'),
2208
+                    screenshareCodec: this.codecSelection.getScreenshareCodec('jvb')
2202
                 },
2209
                 },
2203
                 enableInsertableStreams: this.isE2EEEnabled() || FeatureFlags.isRunInLiteModeEnabled()
2210
                 enableInsertableStreams: this.isE2EEEnabled() || FeatureFlags.isRunInLiteModeEnabled()
2204
             });
2211
             });
2908
             ...this.options.config,
2915
             ...this.options.config,
2909
             codecSettings: {
2916
             codecSettings: {
2910
                 mediaType: MediaType.VIDEO,
2917
                 mediaType: MediaType.VIDEO,
2911
-                codecList: this.codecSelection.getCodecPreferenceList('p2p')
2918
+                codecList: this.codecSelection.getCodecPreferenceList('p2p'),
2919
+                screenshareCodec: this.codecSelection.getScreenshareCodec('p2p')
2912
             },
2920
             },
2913
             enableInsertableStreams: this.isE2EEEnabled() || FeatureFlags.isRunInLiteModeEnabled()
2921
             enableInsertableStreams: this.isE2EEEnabled() || FeatureFlags.isRunInLiteModeEnabled()
2914
         });
2922
         });
3263
             ...this.options.config,
3271
             ...this.options.config,
3264
             codecSettings: {
3272
             codecSettings: {
3265
                 mediaType: MediaType.VIDEO,
3273
                 mediaType: MediaType.VIDEO,
3266
-                codecList: this.codecSelection.getCodecPreferenceList('p2p')
3274
+                codecList: this.codecSelection.getCodecPreferenceList('p2p'),
3275
+                screenshareCodec: this.codecSelection.getScreenshareCodec('p2p')
3267
             },
3276
             },
3268
             enableInsertableStreams: this.isE2EEEnabled() || FeatureFlags.isRunInLiteModeEnabled()
3277
             enableInsertableStreams: this.isE2EEEnabled() || FeatureFlags.isRunInLiteModeEnabled()
3269
         });
3278
         });

+ 5
- 0
JitsiConferenceEventManager.js Ver fichero

734
             JitsiConferenceEvents.BEFORE_STATISTICS_DISPOSED);
734
             JitsiConferenceEvents.BEFORE_STATISTICS_DISPOSED);
735
     });
735
     });
736
 
736
 
737
+    conference.statistics.addEncodeTimeStatsListener((tpc, stats) => {
738
+        conference.eventEmitter.emit(
739
+            JitsiConferenceEvents.ENCODE_TIME_STATS_RECEIVED, tpc, stats);
740
+    });
741
+
737
     // if we are in startSilent mode we will not be sending/receiving so nothing to detect
742
     // if we are in startSilent mode we will not be sending/receiving so nothing to detect
738
     if (!conference.options.config.startSilent) {
743
     if (!conference.options.config.startSilent) {
739
         conference.statistics.addByteSentStatsListener((tpc, stats) => {
744
         conference.statistics.addByteSentStatsListener((tpc, stats) => {

+ 3
- 0
JitsiConferenceEvents.spec.ts Ver fichero

27
         E2EE_VERIFICATION_AVAILABLE,
27
         E2EE_VERIFICATION_AVAILABLE,
28
         E2EE_VERIFICATION_READY,
28
         E2EE_VERIFICATION_READY,
29
         E2EE_VERIFICATION_COMPLETED,
29
         E2EE_VERIFICATION_COMPLETED,
30
+        ENCODE_TIME_STATS_RECEIVED,
30
         ENDPOINT_MESSAGE_RECEIVED,
31
         ENDPOINT_MESSAGE_RECEIVED,
31
         ENDPOINT_STATS_RECEIVED,
32
         ENDPOINT_STATS_RECEIVED,
32
         JVB121_STATUS,
33
         JVB121_STATUS,
166
         expect( BREAKOUT_ROOMS_MOVE_TO_ROOM ).toBe( 'conference.breakout-rooms.move-to-room' );
167
         expect( BREAKOUT_ROOMS_MOVE_TO_ROOM ).toBe( 'conference.breakout-rooms.move-to-room' );
167
         expect( BREAKOUT_ROOMS_UPDATED ).toBe( 'conference.breakout-rooms.updated' );
168
         expect( BREAKOUT_ROOMS_UPDATED ).toBe( 'conference.breakout-rooms.updated' );
168
         expect( METADATA_UPDATED ).toBe( 'conference.metadata.updated' );
169
         expect( METADATA_UPDATED ).toBe( 'conference.metadata.updated' );
170
+        expect( ENCODE_TIME_STATS_RECEIVED ).toBe( 'conference.encode_time_stats_received' );
169
 
171
 
170
         expect( JitsiConferenceEvents ).toBeDefined();
172
         expect( JitsiConferenceEvents ).toBeDefined();
171
 
173
 
188
         expect( JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED ).toBe( 'conference.dominantSpeaker' );
190
         expect( JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED ).toBe( 'conference.dominantSpeaker' );
189
         expect( JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP ).toBe( 'conference.createdTimestamp' );
191
         expect( JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP ).toBe( 'conference.createdTimestamp' );
190
         expect( JitsiConferenceEvents.DTMF_SUPPORT_CHANGED ).toBe( 'conference.dtmfSupportChanged' );
192
         expect( JitsiConferenceEvents.DTMF_SUPPORT_CHANGED ).toBe( 'conference.dtmfSupportChanged' );
193
+        expect( JitsiConferenceEvents.ENCODE_TIME_STATS_RECEIVED ).toBe( 'conference.encode_time_stats_received' );
191
         expect( JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED ).toBe( 'conference.endpoint_message_received' );
194
         expect( JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED ).toBe( 'conference.endpoint_message_received' );
192
         expect( JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED ).toBe( 'conference.endpoint_stats_received' );
195
         expect( JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED ).toBe( 'conference.endpoint_stats_received' );
193
         expect( JitsiConferenceEvents.JVB121_STATUS ).toBe( 'conference.jvb121Status' );
196
         expect( JitsiConferenceEvents.JVB121_STATUS ).toBe( 'conference.jvb121Status' );

+ 6
- 0
JitsiConferenceEvents.ts Ver fichero

193
 
193
 
194
     E2EE_VERIFICATION_READY = 'conference.e2ee.verification.ready',
194
     E2EE_VERIFICATION_READY = 'conference.e2ee.verification.ready',
195
 
195
 
196
+    /**
197
+     * Indicates that the encode time stats for the local video sources has been received.
198
+     */
199
+    ENCODE_TIME_STATS_RECEIVED = 'conference.encode_time_stats_received',
200
+
196
     /**
201
     /**
197
      * Indicates that a message from another participant is received on data
202
      * Indicates that a message from another participant is received on data
198
      * channel.
203
      * channel.
518
 export const E2EE_VERIFICATION_AVAILABLE = JitsiConferenceEvents.E2EE_VERIFICATION_AVAILABLE;
523
 export const E2EE_VERIFICATION_AVAILABLE = JitsiConferenceEvents.E2EE_VERIFICATION_AVAILABLE;
519
 export const E2EE_VERIFICATION_COMPLETED = JitsiConferenceEvents.E2EE_VERIFICATION_COMPLETED;
524
 export const E2EE_VERIFICATION_COMPLETED = JitsiConferenceEvents.E2EE_VERIFICATION_COMPLETED;
520
 export const E2EE_VERIFICATION_READY = JitsiConferenceEvents.E2EE_VERIFICATION_READY;
525
 export const E2EE_VERIFICATION_READY = JitsiConferenceEvents.E2EE_VERIFICATION_READY;
526
+export const ENCODE_TIME_STATS_RECEIVED = JitsiConferenceEvents.ENCODE_TIME_STATS_RECEIVED;
521
 export const ENDPOINT_MESSAGE_RECEIVED = JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED;
527
 export const ENDPOINT_MESSAGE_RECEIVED = JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED;
522
 export const ENDPOINT_STATS_RECEIVED = JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED;
528
 export const ENDPOINT_STATS_RECEIVED = JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED;
523
 export const FORWARDED_SOURCES_CHANGED = JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED;
529
 export const FORWARDED_SOURCES_CHANGED = JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED;

+ 101
- 8
modules/RTC/CodecSelection.js Ver fichero

30
      * @param {string} options.p2p settings (codec list, preferred and disabled) for the p2p connection.
30
      * @param {string} options.p2p settings (codec list, preferred and disabled) for the p2p connection.
31
      */
31
      */
32
     constructor(conference, options) {
32
     constructor(conference, options) {
33
+        this.codecPreferenceOrder = {};
33
         this.conference = conference;
34
         this.conference = conference;
35
+        this.encodeTimeStats = new Map();
34
         this.options = options;
36
         this.options = options;
35
-        this.codecPreferenceOrder = {};
37
+        this.screenshareCodec = {};
36
         this.visitorCodecs = [];
38
         this.visitorCodecs = [];
37
 
39
 
38
         for (const connectionType of Object.keys(options)) {
40
         for (const connectionType of Object.keys(options)) {
39
             // eslint-disable-next-line prefer-const
41
             // eslint-disable-next-line prefer-const
40
-            let { disabledCodec, preferredCodec, preferenceOrder } = options[connectionType];
42
+            let { disabledCodec, preferredCodec, preferenceOrder, screenshareCodec } = options[connectionType];
41
             const supportedCodecs = new Set(this._getSupportedVideoCodecs(connectionType));
43
             const supportedCodecs = new Set(this._getSupportedVideoCodecs(connectionType));
42
 
44
 
43
             // Default preference codec order when no codec preferences are set in config.js
45
             // Default preference codec order when no codec preferences are set in config.js
89
 
91
 
90
             logger.info(`Codec preference order for ${connectionType} connection is ${selectedOrder}`);
92
             logger.info(`Codec preference order for ${connectionType} connection is ${selectedOrder}`);
91
             this.codecPreferenceOrder[connectionType] = selectedOrder;
93
             this.codecPreferenceOrder[connectionType] = selectedOrder;
94
+
95
+            // Set the preferred screenshare codec.
96
+            if (screenshareCodec && supportedCodecs.has(screenshareCodec)) {
97
+                this.screenshareCodec[connectionType] = screenshareCodec;
98
+            }
92
         }
99
         }
93
 
100
 
94
         this.conference.on(
101
         this.conference.on(
103
         this.conference.on(
110
         this.conference.on(
104
             JitsiConferenceEvents.USER_LEFT,
111
             JitsiConferenceEvents.USER_LEFT,
105
             () => this._selectPreferredCodec());
112
             () => this._selectPreferredCodec());
113
+        this.conference.on(
114
+            JitsiConferenceEvents.ENCODE_TIME_STATS_RECEIVED,
115
+            (tpc, stats) => this._processEncodeTimeStats(tpc, stats));
106
     }
116
     }
107
 
117
 
108
     /**
118
     /**
127
         return supportedCodecs;
137
         return supportedCodecs;
128
     }
138
     }
129
 
139
 
140
+    /**
141
+     * Processes the encode time stats received for all the local video sources.
142
+     *
143
+     * @param {TraceablePeerConnection} tpc - the peerconnection for which stats were gathered.
144
+     * @param {Object} stats - the encode time stats for local video sources.
145
+     * @returns {void}
146
+     */
147
+    _processEncodeTimeStats(tpc, stats) {
148
+        const activeSession = this.conference.getActiveMediaSession();
149
+
150
+        // Process stats only for the active media session.
151
+        if (activeSession.peerconnection !== tpc) {
152
+            return;
153
+        }
154
+
155
+        const statsPerTrack = new Map();
156
+
157
+        for (const ssrc of stats.keys()) {
158
+            const { codec, encodeTime, qualityLimitationReason, resolution, timestamp } = stats.get(ssrc);
159
+            const track = tpc.getTrackBySSRC(ssrc);
160
+            let existingStats = statsPerTrack.get(track.rtcId);
161
+            const encodeResolution = Math.min(resolution.height, resolution.width);
162
+            const ssrcStats = {
163
+                encodeResolution,
164
+                encodeTime,
165
+                qualityLimitationReason
166
+            };
167
+
168
+            if (existingStats) {
169
+                existingStats.codec = codec;
170
+                existingStats.timestamp = timestamp;
171
+                existingStats.trackStats.push(ssrcStats);
172
+            } else {
173
+                existingStats = {
174
+                    codec,
175
+                    timestamp,
176
+                    trackStats: [ ssrcStats ]
177
+                };
178
+
179
+                statsPerTrack.set(track.rtcId, existingStats);
180
+            }
181
+        }
182
+
183
+        // Aggregate the stats for multiple simulcast streams with different SSRCs but for the same video stream.
184
+        for (const trackId of statsPerTrack.keys()) {
185
+            const { codec, timestamp, trackStats } = statsPerTrack.get(trackId);
186
+            const totalEncodeTime = trackStats
187
+                .map(stat => stat.encodeTime)
188
+                .reduce((totalValue, currentValue) => totalValue + currentValue, 0);
189
+            const avgEncodeTime = totalEncodeTime / trackStats.length;
190
+            const { qualityLimitationReason = 'none' }
191
+                = trackStats.find(stat => stat.qualityLimitationReason !== 'none') ?? {};
192
+            const encodeResolution = trackStats
193
+                .map(stat => stat.encodeResolution)
194
+                .reduce((resolution, currentValue) => Math.max(resolution, currentValue), 0);
195
+            const localTrack = this.conference.getLocalVideoTracks().find(t => t.rtcId === trackId);
196
+
197
+            const exisitingStats = this.encodeTimeStats.get(trackId);
198
+            const sourceStats = {
199
+                avgEncodeTime,
200
+                codec,
201
+                encodeResolution,
202
+                qualityLimitationReason,
203
+                localTrack,
204
+                timestamp
205
+            };
206
+
207
+            if (exisitingStats) {
208
+                exisitingStats.push(sourceStats);
209
+            } else {
210
+                this.encodeTimeStats.set(trackId, [ sourceStats ]);
211
+            }
212
+
213
+            logger.debug(`Encode stats for ${localTrack}: codec=${codec}, time=${avgEncodeTime},`
214
+                + `resolution=${encodeResolution}, qualityLimitationReason=${qualityLimitationReason}`);
215
+        }
216
+    }
217
+
130
     /**
218
     /**
131
      * Sets the codec on the media session based on the codec preference order configured in config.js and the supported
219
      * Sets the codec on the media session based on the codec preference order configured in config.js and the supported
132
      * codecs published by the remote participants in their presence.
220
      * codecs published by the remote participants in their presence.
139
         if (!session) {
227
         if (!session) {
140
             return;
228
             return;
141
         }
229
         }
142
-        const currentCodecOrder = session.peerconnection.getConfiguredVideoCodecs();
143
         const isJvbSession = session === this.conference.jvbJingleSession;
230
         const isJvbSession = session === this.conference.jvbJingleSession;
144
-
145
         let localPreferredCodecOrder = isJvbSession ? this.codecPreferenceOrder.jvb : this.codecPreferenceOrder.p2p;
231
         let localPreferredCodecOrder = isJvbSession ? this.codecPreferenceOrder.jvb : this.codecPreferenceOrder.p2p;
146
 
232
 
147
         // E2EE is curently supported only for VP8 codec.
233
         // E2EE is curently supported only for VP8 codec.
195
             return;
281
             return;
196
         }
282
         }
197
 
283
 
198
-        // Reconfigure the codecs on the media session.
199
-        if (!selectedCodecOrder.every((val, index) => val === currentCodecOrder[index])) {
200
-            session.setVideoCodecs(selectedCodecOrder);
201
-        }
284
+        session.setVideoCodecs(selectedCodecOrder);
202
     }
285
     }
203
 
286
 
204
     /**
287
     /**
225
     getCodecPreferenceList(connectionType) {
308
     getCodecPreferenceList(connectionType) {
226
         return this.codecPreferenceOrder[connectionType];
309
         return this.codecPreferenceOrder[connectionType];
227
     }
310
     }
311
+
312
+    /**
313
+     * Returns the preferred screenshare codec for the given connection type.
314
+     *
315
+     * @param {String} connectionType The media connection type, 'p2p' or 'jvb'.
316
+     * @returns CodecMimeType
317
+     */
318
+    getScreenshareCodec(connectionType) {
319
+        return this.screenshareCodec[connectionType];
320
+    }
228
 }
321
 }

+ 6
- 6
modules/RTC/CodecSelection.spec.js Ver fichero

135
             participant1 = new MockParticipant('remote-1');
135
             participant1 = new MockParticipant('remote-1');
136
             conference.addParticipant(participant1, [ 'vp9', 'vp8' ]);
136
             conference.addParticipant(participant1, [ 'vp9', 'vp8' ]);
137
 
137
 
138
-            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(0);
138
+            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(1);
139
 
139
 
140
             // Add a third user joining the call with a subset of codecs.
140
             // Add a third user joining the call with a subset of codecs.
141
             participant2 = new MockParticipant('remote-2');
141
             participant2 = new MockParticipant('remote-2');
145
 
145
 
146
             // Make p2 leave the call
146
             // Make p2 leave the call
147
             conference.removeParticipant(participant2);
147
             conference.removeParticipant(participant2);
148
-            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(1);
148
+            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(3);
149
         });
149
         });
150
 
150
 
151
         it('and remote endpoints use the old codec selection logic (RN)', () => {
151
         it('and remote endpoints use the old codec selection logic (RN)', () => {
163
 
163
 
164
             // Make p1 leave the call
164
             // Make p1 leave the call
165
             conference.removeParticipant(participant1);
165
             conference.removeParticipant(participant1);
166
-            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(2);
166
+            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(3);
167
         });
167
         });
168
     });
168
     });
169
 
169
 
185
             participant1 = new MockParticipant('remote-1');
185
             participant1 = new MockParticipant('remote-1');
186
             conference.addParticipant(participant1, [ 'vp9', 'vp8', 'h264' ]);
186
             conference.addParticipant(participant1, [ 'vp9', 'vp8', 'h264' ]);
187
 
187
 
188
-            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(0);
188
+            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(1);
189
 
189
 
190
             // Add a third user joining the call with a subset of codecs.
190
             // Add a third user joining the call with a subset of codecs.
191
             participant2 = new MockParticipant('remote-2');
191
             participant2 = new MockParticipant('remote-2');
195
 
195
 
196
             // Make p2 leave the call
196
             // Make p2 leave the call
197
             conference.removeParticipant(participant2);
197
             conference.removeParticipant(participant2);
198
-            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(1);
198
+            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(3);
199
         });
199
         });
200
 
200
 
201
         it('and remote endpoint prefers a codec that is locally disabled', () => {
201
         it('and remote endpoint prefers a codec that is locally disabled', () => {
221
 
221
 
222
             // Make p1 leave the call
222
             // Make p1 leave the call
223
             conference.removeParticipant(participant1);
223
             conference.removeParticipant(participant1);
224
-            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(2);
224
+            expect(jingleSession.setVideoCodecs).toHaveBeenCalledTimes(3);
225
         });
225
         });
226
     });
226
     });
227
 });
227
 });

+ 94
- 12
modules/RTC/TraceablePeerConnection.js Ver fichero

265
     browser.supportsCodecPreferences()
265
     browser.supportsCodecPreferences()
266
         && logger.info('Using RTCRtpTransceiver#setCodecPreferences for codec selection');
266
         && logger.info('Using RTCRtpTransceiver#setCodecPreferences for codec selection');
267
 
267
 
268
+    /**
269
+     * Flag used to indicate if the codecs are configured using the codec selection API without having the need to
270
+     * trigger a renegotiation for the change to be effective.
271
+     */
272
+    this._usesCodecSelectionAPI = this.options.usesCodecSelectionAPI;
273
+
268
     /**
274
     /**
269
      * Indicates whether an audio track has ever been added to the peer connection.
275
      * Indicates whether an audio track has ever been added to the peer connection.
270
      */
276
      */
807
 };
813
 };
808
 
814
 
809
 /**
815
 /**
810
- * Tries to find {@link JitsiTrack} for given SSRC number. It will search both
811
- * local and remote tracks bound to this instance.
816
+ * Tries to find {@link JitsiTrack} for given SSRC number. It will search both local and remote tracks bound to this
817
+ * instance.
812
  * @param {number} ssrc
818
  * @param {number} ssrc
813
  * @return {JitsiTrack|null}
819
  * @return {JitsiTrack|null}
814
  */
820
  */
817
         throw new Error(`SSRC ${ssrc} is not a number`);
823
         throw new Error(`SSRC ${ssrc} is not a number`);
818
     }
824
     }
819
     for (const localTrack of this.localTracks.values()) {
825
     for (const localTrack of this.localTracks.values()) {
820
-        if (this.getLocalSSRC(localTrack) === ssrc) {
826
+        const { ssrcs } = this.localSSRCs.get(localTrack.rtcId);
827
+
828
+        if (ssrcs.find(localSsrc => Number(localSsrc) === ssrc)) {
821
             return localTrack;
829
             return localTrack;
822
         }
830
         }
823
     }
831
     }
1421
         }
1429
         }
1422
 
1430
 
1423
         // Reorder the codecs based on the preferred settings.
1431
         // Reorder the codecs based on the preferred settings.
1424
-        for (const codec of this.codecSettings.codecList.slice().reverse()) {
1425
-            SDPUtil.preferCodec(mLine, codec, this.isP2P);
1432
+        if (!this.usesCodecSelectionAPI()) {
1433
+            for (const codec of this.codecSettings.codecList.slice().reverse()) {
1434
+                SDPUtil.preferCodec(mLine, codec, this.isP2P);
1435
+            }
1426
         }
1436
         }
1427
     }
1437
     }
1428
 
1438
 
1593
  * video in the local SDP.
1603
  * video in the local SDP.
1594
  */
1604
  */
1595
 TraceablePeerConnection.prototype.getConfiguredVideoCodec = function() {
1605
 TraceablePeerConnection.prototype.getConfiguredVideoCodec = function() {
1606
+    const localVideoTrack = this.getLocalVideoTracks()[0];
1607
+
1608
+    if (this.usesCodecSelectionAPI() && localVideoTrack) {
1609
+        const rtpSender = this.findSenderForTrack(localVideoTrack.getTrack());
1610
+        const { codecs } = rtpSender.getParameters();
1611
+
1612
+        return codecs[0].mimeType.split('/')[1].toLowerCase();
1613
+    }
1614
+
1596
     const sdp = this.peerconnection.remoteDescription?.sdp;
1615
     const sdp = this.peerconnection.remoteDescription?.sdp;
1597
     const defaultCodec = CodecMimeType.VP8;
1616
     const defaultCodec = CodecMimeType.VP8;
1598
 
1617
 
1673
     }
1692
     }
1674
 
1693
 
1675
     this.codecSettings.codecList = codecList;
1694
     this.codecSettings.codecList = codecList;
1695
+
1696
+    if (this.usesCodecSelectionAPI()) {
1697
+        this.configureVideoSenderEncodings();
1698
+    }
1676
 };
1699
 };
1677
 
1700
 
1678
 /**
1701
 /**
1863
     return this.tpcUtils.replaceTrack(localTrack, null).then(() => false);
1886
     return this.tpcUtils.replaceTrack(localTrack, null).then(() => false);
1864
 };
1887
 };
1865
 
1888
 
1889
+/**
1890
+ * Returns true if the codec selection API is used for switching between codecs for the video sources.
1891
+ *
1892
+ * @returns {boolean}
1893
+ */
1894
+TraceablePeerConnection.prototype.usesCodecSelectionAPI = function() {
1895
+    // Browser throws an error when H.264 is set on the encodings. Therefore, munge the SDP when H.264 needs to be
1896
+    // selected.
1897
+    // TODO: Remove this check when the above issue is fixed.
1898
+    return this._usesCodecSelectionAPI && this.codecSettings.codecList[0] !== CodecMimeType.H264;
1899
+};
1900
+
1866
 TraceablePeerConnection.prototype.createDataChannel = function(label, opts) {
1901
 TraceablePeerConnection.prototype.createDataChannel = function(label, opts) {
1867
     this.trace('createDataChannel', label, opts);
1902
     this.trace('createDataChannel', label, opts);
1868
 
1903
 
1914
     });
1949
     });
1915
 };
1950
 };
1916
 
1951
 
1952
+/**
1953
+ * Returns the codec to be used for screenshare based on the supported codecs and the preferred codec requested
1954
+ * through config.js setting.
1955
+ *
1956
+ * @param {CodecMimeType} defaultCodec - the preferred codec for video tracks.
1957
+ * @returns {CodecMimeType}
1958
+ */
1959
+TraceablePeerConnection.prototype._getPreferredCodecForScreenshare = function(defaultCodec) {
1960
+    // Use the same codec for both camera and screenshare if the client doesn't support the codec selection API.
1961
+    if (!this.usesCodecSelectionAPI()) {
1962
+        return defaultCodec;
1963
+    }
1964
+
1965
+    const { screenshareCodec } = this.codecSettings;
1966
+
1967
+    if (screenshareCodec && this.codecSettings.codecList.find(c => c === screenshareCodec)) {
1968
+        return screenshareCodec;
1969
+    }
1970
+
1971
+    // Default to AV1 for screenshare if its supported and is not overriden through config.js.
1972
+    if (this.codecSettings.codecList.find(c => c === CodecMimeType.AV1)) {
1973
+        return CodecMimeType.AV1;
1974
+    }
1975
+
1976
+    return defaultCodec;
1977
+};
1978
+
1917
 /**
1979
 /**
1918
  * Munges the stereo flag as well as the opusMaxAverageBitrate in the SDP, based
1980
  * Munges the stereo flag as well as the opusMaxAverageBitrate in the SDP, based
1919
  * on values set through config.js, if present.
1981
  * on values set through config.js, if present.
2121
  * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.
2183
  * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.
2122
  */
2184
  */
2123
 TraceablePeerConnection.prototype.configureVideoSenderEncodings = function(localVideoTrack = null) {
2185
 TraceablePeerConnection.prototype.configureVideoSenderEncodings = function(localVideoTrack = null) {
2186
+    const preferredCodec = this.codecSettings.codecList[0];
2187
+
2124
     if (localVideoTrack) {
2188
     if (localVideoTrack) {
2125
         return this.setSenderVideoConstraints(
2189
         return this.setSenderVideoConstraints(
2126
             this._senderMaxHeights.get(localVideoTrack.getSourceName()),
2190
             this._senderMaxHeights.get(localVideoTrack.getSourceName()),
2127
-            localVideoTrack);
2191
+            localVideoTrack,
2192
+            preferredCodec);
2128
     }
2193
     }
2129
     const promises = [];
2194
     const promises = [];
2130
 
2195
 
2131
     for (const track of this.getLocalVideoTracks()) {
2196
     for (const track of this.getLocalVideoTracks()) {
2132
         const maxHeight = this._senderMaxHeights.get(track.getSourceName()) ?? VIDEO_QUALITY_LEVELS[0].height;
2197
         const maxHeight = this._senderMaxHeights.get(track.getSourceName()) ?? VIDEO_QUALITY_LEVELS[0].height;
2133
 
2198
 
2134
-        promises.push(this.setSenderVideoConstraints(maxHeight, track));
2199
+        promises.push(this.setSenderVideoConstraints(maxHeight, track, preferredCodec));
2135
     }
2200
     }
2136
 
2201
 
2137
     return Promise.allSettled(promises);
2202
     return Promise.allSettled(promises);
2230
  *
2295
  *
2231
  * @param {number} frameHeight - The max frame height to be imposed on the outgoing video stream.
2296
  * @param {number} frameHeight - The max frame height to be imposed on the outgoing video stream.
2232
  * @param {JitsiLocalTrack} - The local track for which the sender constraints have to be applied.
2297
  * @param {JitsiLocalTrack} - The local track for which the sender constraints have to be applied.
2298
+ * @param {preferredCodec} - The video codec that needs to be configured on the sender associated with the video source.
2233
  * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.
2299
  * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.
2234
  */
2300
  */
2235
-TraceablePeerConnection.prototype.setSenderVideoConstraints = function(frameHeight, localVideoTrack) {
2301
+TraceablePeerConnection.prototype.setSenderVideoConstraints = function(frameHeight, localVideoTrack, preferredCodec) {
2236
     if (frameHeight < 0 || isNaN(frameHeight)) {
2302
     if (frameHeight < 0 || isNaN(frameHeight)) {
2237
         throw new Error(`Invalid frameHeight: ${frameHeight}`);
2303
         throw new Error(`Invalid frameHeight: ${frameHeight}`);
2238
     }
2304
     }
2248
         return Promise.resolve();
2314
         return Promise.resolve();
2249
     }
2315
     }
2250
 
2316
 
2317
+    const codec = preferredCodec ?? this.codecSettings.codecList[0];
2318
+
2251
     return this._updateVideoSenderParameters(
2319
     return this._updateVideoSenderParameters(
2252
-        () => this._updateVideoSenderEncodings(frameHeight, localVideoTrack));
2320
+        () => this._updateVideoSenderEncodings(frameHeight, localVideoTrack, codec));
2253
 };
2321
 };
2254
 
2322
 
2255
 /**
2323
 /**
2274
  *
2342
  *
2275
  * @param {number} frameHeight - The max frame height to be imposed on the outgoing video stream.
2343
  * @param {number} frameHeight - The max frame height to be imposed on the outgoing video stream.
2276
  * @param {JitsiLocalTrack} - The local track for which the sender constraints have to be applied.
2344
  * @param {JitsiLocalTrack} - The local track for which the sender constraints have to be applied.
2345
+ * @param {preferredCodec} - The video codec that needs to be configured on the sender associated with the video source.
2277
  * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.
2346
  * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.
2278
  */
2347
  */
2279
-TraceablePeerConnection.prototype._updateVideoSenderEncodings = function(frameHeight, localVideoTrack) {
2348
+TraceablePeerConnection.prototype._updateVideoSenderEncodings = function(frameHeight, localVideoTrack, preferredCodec) {
2280
     const videoSender = this.findSenderForTrack(localVideoTrack.getTrack());
2349
     const videoSender = this.findSenderForTrack(localVideoTrack.getTrack());
2350
+    const isScreensharingTrack = localVideoTrack.getVideoType() === VideoType.DESKTOP;
2281
 
2351
 
2282
     if (!videoSender) {
2352
     if (!videoSender) {
2283
         return Promise.resolve();
2353
         return Promise.resolve();
2288
         return Promise.resolve();
2358
         return Promise.resolve();
2289
     }
2359
     }
2290
 
2360
 
2291
-    const isSharingLowFpsScreen = localVideoTrack.getVideoType() === VideoType.DESKTOP && this._capScreenshareBitrate;
2361
+    const isSharingLowFpsScreen = isScreensharingTrack && this._capScreenshareBitrate;
2292
 
2362
 
2293
     // Set the degradation preference.
2363
     // Set the degradation preference.
2294
     const preference = isSharingLowFpsScreen
2364
     const preference = isSharingLowFpsScreen
2298
     parameters.degradationPreference = preference;
2368
     parameters.degradationPreference = preference;
2299
 
2369
 
2300
     // Calculate the encodings active state based on the resolution requested by the bridge.
2370
     // Calculate the encodings active state based on the resolution requested by the bridge.
2301
-    const codec = this.getConfiguredVideoCodec();
2371
+    const codecForCamera = preferredCodec ?? this.getConfiguredVideoCodec(localVideoTrack);
2372
+    const codec = isScreensharingTrack ? this._getPreferredCodecForScreenshare(codecForCamera) : codecForCamera;
2302
     const activeState = this.tpcUtils.calculateEncodingsActiveState(localVideoTrack, codec, frameHeight);
2373
     const activeState = this.tpcUtils.calculateEncodingsActiveState(localVideoTrack, codec, frameHeight);
2303
     let bitrates = this.tpcUtils.calculateEncodingsBitrates(localVideoTrack, codec, frameHeight);
2374
     let bitrates = this.tpcUtils.calculateEncodingsBitrates(localVideoTrack, codec, frameHeight);
2304
     const scalabilityModes = this.tpcUtils.calculateEncodingsScalabilityMode(localVideoTrack, codec, frameHeight);
2375
     const scalabilityModes = this.tpcUtils.calculateEncodingsScalabilityMode(localVideoTrack, codec, frameHeight);
2321
         if (parameters.encodings.hasOwnProperty(idx)) {
2392
         if (parameters.encodings.hasOwnProperty(idx)) {
2322
             const {
2393
             const {
2323
                 active = undefined,
2394
                 active = undefined,
2395
+                codec: currentCodec = undefined,
2324
                 maxBitrate = undefined,
2396
                 maxBitrate = undefined,
2325
                 scalabilityMode = undefined,
2397
                 scalabilityMode = undefined,
2326
                 scaleResolutionDownBy = undefined
2398
                 scaleResolutionDownBy = undefined
2353
             } else {
2425
             } else {
2354
                 parameters.encodings[idx].scalabilityMode = undefined;
2426
                 parameters.encodings[idx].scalabilityMode = undefined;
2355
             }
2427
             }
2428
+
2429
+            const expectedPattern = `${MediaType.VIDEO}/${codec.toUpperCase()}`;
2430
+
2431
+            // Configure the codec here if its supported.
2432
+            if (this.usesCodecSelectionAPI() && currentCodec?.mimeType !== expectedPattern) {
2433
+                const matchingCodec = parameters.codecs.find(pt => pt.mimeType === expectedPattern);
2434
+
2435
+                parameters.encodings[idx].codec = matchingCodec;
2436
+                needsUpdate = true;
2437
+            }
2356
         }
2438
         }
2357
     }
2439
     }
2358
 
2440
 

+ 12
- 0
modules/browser/BrowserCapabilities.js Ver fichero

140
             && !this.isWebKitBased();
140
             && !this.isWebKitBased();
141
     }
141
     }
142
 
142
 
143
+    /**
144
+     * Checks if the browser supports the new codec selection API, i.e., checks if dictionary member
145
+     * RTCRtpEncodingParameters.codec as defined in
146
+     * https://w3c.github.io/webrtc-extensions/#dom-rtcrtpencodingparameters-codec is supported by the browser. It
147
+     * allows the application to change the current codec used by each RTCRtpSender without a renegotiation.
148
+     *
149
+     * @returns {boolean}
150
+     */
151
+    supportsCodecSelectionAPI() {
152
+        return this.isChromiumBased() && this.isEngineVersionGreaterThan(125);
153
+    }
154
+
143
     /**
155
     /**
144
      * Returns true if the browser supports Dependency Descriptor header extension.
156
      * Returns true if the browser supports Dependency Descriptor header extension.
145
      *
157
      *

+ 37
- 3
modules/statistics/RTPStatsCollector.js Ver fichero

84
     this.codec = codec || '';
84
     this.codec = codec || '';
85
 };
85
 };
86
 
86
 
87
+SsrcStats.prototype.setEncodeStats = function(encodeStats) {
88
+    this.encodeStats = encodeStats || {};
89
+};
90
+
87
 /**
91
 /**
88
  *
92
  *
89
  */
93
  */
493
  */
497
  */
494
 StatsCollector.prototype.processStatsReport = function() {
498
 StatsCollector.prototype.processStatsReport = function() {
495
     const byteSentStats = {};
499
     const byteSentStats = {};
500
+    const encodedTimeStatsPerSsrc = new Map();
496
 
501
 
497
     this.currentStatsReport.forEach(now => {
502
     this.currentStatsReport.forEach(now => {
498
         const before = this.previousStatsReport ? this.previousStatsReport.get(now.id) : null;
503
         const before = this.previousStatsReport ? this.previousStatsReport.get(now.id) : null;
638
 
643
 
639
             if (codec) {
644
             if (codec) {
640
                 /**
645
                 /**
641
-                 * The mime type has the following form: video/VP8 or audio/ISAC,
642
-                 * so we what to keep just the type after the '/', audio and video
643
-                 * keys will be added on the processing side.
646
+                 * The mime type has the following form: video/VP8 or audio/ISAC, so we what to keep just the type
647
+                 * after the '/', audio and video keys will be added on the processing side.
644
                  */
648
                  */
645
                 const codecShortType = codec.mimeType.split('/')[1];
649
                 const codecShortType = codec.mimeType.split('/')[1];
646
 
650
 
647
                 codecShortType && ssrcStats.setCodec(codecShortType);
651
                 codecShortType && ssrcStats.setCodec(codecShortType);
652
+
653
+                // Calculate the encodeTime stat for outbound video streams.
654
+                const track = this.peerconnection.getTrackBySSRC(ssrc);
655
+
656
+                if (now.type === 'outbound-rtp'
657
+                    && now.active
658
+                    && track?.isVideoTrack()
659
+                    && this.peerconnection.usesCodecSelectionAPI()
660
+                    && before?.totalEncodeTime
661
+                    && before?.framesEncoded
662
+                    && now.frameHeight
663
+                    && now.frameWidth) {
664
+                    const encodeTimeDelta = now.totalEncodeTime - before.totalEncodeTime;
665
+                    const framesEncodedDelta = now.framesEncoded - before.framesEncoded;
666
+                    const encodeTimePerFrameInMs = 1000 * encodeTimeDelta / framesEncodedDelta;
667
+                    const encodeTimeStats = {
668
+                        codec: codecShortType,
669
+                        encodeTime: encodeTimePerFrameInMs,
670
+                        qualityLimitationReason: now.qualityLimitationReason,
671
+                        resolution,
672
+                        timestamp: now.timestamp
673
+                    };
674
+
675
+                    encodedTimeStatsPerSsrc.set(ssrc, encodeTimeStats);
676
+                    ssrcStats.setEncodeStats(encodedTimeStatsPerSsrc);
677
+                }
648
             }
678
             }
649
 
679
 
650
         // Continue to use the 'track' based stats for Firefox and Safari and older versions of Chromium.
680
         // Continue to use the 'track' based stats for Firefox and Safari and older versions of Chromium.
692
         this.eventEmitter.emit(StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);
722
         this.eventEmitter.emit(StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);
693
     }
723
     }
694
 
724
 
725
+    if (encodedTimeStatsPerSsrc.size) {
726
+        this.eventEmitter.emit(StatisticsEvents.ENCODE_TIME_STATS, this.peerconnection, encodedTimeStatsPerSsrc);
727
+    }
728
+
695
     this._processAndEmitReport();
729
     this._processAndEmitReport();
696
 };
730
 };

+ 8
- 0
modules/statistics/statistics.js Ver fichero

203
         listener);
203
         listener);
204
 };
204
 };
205
 
205
 
206
+Statistics.prototype.addEncodeTimeStatsListener = function(listener) {
207
+    this.eventEmitter.on(StatisticsEvents.ENCODE_TIME_STATS, listener);
208
+};
209
+
210
+Statistics.prototype.removeEncodeTimeStatsListener = function(listener) {
211
+    this.eventEmitter.removeListener(StatisticsEvents.ENCODE_TIME_STATS, listener);
212
+};
213
+
206
 Statistics.prototype.addByteSentStatsListener = function(listener) {
214
 Statistics.prototype.addByteSentStatsListener = function(listener) {
207
     this.eventEmitter.on(StatisticsEvents.BYTE_SENT_STATS, listener);
215
     this.eventEmitter.on(StatisticsEvents.BYTE_SENT_STATS, listener);
208
 };
216
 };

+ 19
- 2
modules/xmpp/JingleSessionPC.js Ver fichero

3
 import { $build, $iq, Strophe } from 'strophe.js';
3
 import { $build, $iq, Strophe } from 'strophe.js';
4
 
4
 
5
 import { JitsiTrackEvents } from '../../JitsiTrackEvents';
5
 import { JitsiTrackEvents } from '../../JitsiTrackEvents';
6
+import { CodecMimeType } from '../../service/RTC/CodecMimeType';
6
 import { MediaDirection } from '../../service/RTC/MediaDirection';
7
 import { MediaDirection } from '../../service/RTC/MediaDirection';
7
 import { MediaType } from '../../service/RTC/MediaType';
8
 import { MediaType } from '../../service/RTC/MediaType';
8
 import {
9
 import {
399
         pcOptions.capScreenshareBitrate = false;
400
         pcOptions.capScreenshareBitrate = false;
400
         pcOptions.codecSettings = options.codecSettings;
401
         pcOptions.codecSettings = options.codecSettings;
401
         pcOptions.enableInsertableStreams = options.enableInsertableStreams;
402
         pcOptions.enableInsertableStreams = options.enableInsertableStreams;
403
+        pcOptions.usesCodecSelectionAPI = this.usesCodecSelectionAPI
404
+            = browser.supportsCodecSelectionAPI() && options.testing?.enableCodecSelectionAPI;
402
 
405
 
403
         if (options.videoQuality) {
406
         if (options.videoQuality) {
404
             const settings = Object.entries(options.videoQuality)
407
             const settings = Object.entries(options.videoQuality)
1174
      * Updates the codecs on the peerconnection and initiates a renegotiation for the
1177
      * Updates the codecs on the peerconnection and initiates a renegotiation for the
1175
      * new codec config to take effect.
1178
      * new codec config to take effect.
1176
      *
1179
      *
1177
-     * @param {CodecMimeType} preferred the preferred codec.
1178
-     * @param {CodecMimeType} disabled the codec that needs to be disabled.
1180
+     * @param {Array<CodecMimeType>} codecList the preferred codecs.
1179
      */
1181
      */
1180
     setVideoCodecs(codecList) {
1182
     setVideoCodecs(codecList) {
1183
+
1181
         if (this._assertNotEnded()) {
1184
         if (this._assertNotEnded()) {
1182
             logger.info(`${this} setVideoCodecs: ${codecList}`);
1185
             logger.info(`${this} setVideoCodecs: ${codecList}`);
1183
             this.peerconnection.setVideoCodecs(codecList);
1186
             this.peerconnection.setVideoCodecs(codecList);
1184
 
1187
 
1188
+            // Browser throws an error when H.264 is set on the encodings. Therefore, munge the SDP when H.264 needs to
1189
+            // be selected.
1190
+            // TODO: Remove this check when the above issue is fixed.
1191
+            if (this.usesCodecSelectionAPI && codecList[0] !== CodecMimeType.H264) {
1192
+                return;
1193
+            }
1194
+
1195
+            // Skip renegotiation when the selected codec order matches with that of the remote SDP.
1196
+            const currentCodecOrder = this.peerconnection.getConfiguredVideoCodecs();
1197
+
1198
+            if (codecList.every((val, index) => val === currentCodecOrder[index])) {
1199
+                return;
1200
+            }
1201
+
1185
             // Initiate a renegotiate for the codec setting to take effect.
1202
             // Initiate a renegotiate for the codec setting to take effect.
1186
             const workFunction = finishedCallback => {
1203
             const workFunction = finishedCallback => {
1187
                 this._renegotiate()
1204
                 this._renegotiate()

+ 3
- 0
service/statistics/Events.spec.ts Ver fichero

8
         BEFORE_DISPOSED,
8
         BEFORE_DISPOSED,
9
         BYTE_SENT_STATS,
9
         BYTE_SENT_STATS,
10
         CONNECTION_STATS,
10
         CONNECTION_STATS,
11
+        ENCODE_TIME_STATS,
11
         LONG_TASKS_STATS,
12
         LONG_TASKS_STATS,
12
         Events,
13
         Events,
13
         ...others
14
         ...others
18
         expect( BEFORE_DISPOSED ).toBe( 'statistics.before_disposed' );
19
         expect( BEFORE_DISPOSED ).toBe( 'statistics.before_disposed' );
19
         expect( BYTE_SENT_STATS ).toBe( 'statistics.byte_sent_stats' );
20
         expect( BYTE_SENT_STATS ).toBe( 'statistics.byte_sent_stats' );
20
         expect( CONNECTION_STATS ).toBe( 'statistics.connectionstats' );
21
         expect( CONNECTION_STATS ).toBe( 'statistics.connectionstats' );
22
+        expect( ENCODE_TIME_STATS ).toBe( 'statistics.encode_time_stats' );
21
         expect( LONG_TASKS_STATS ).toBe( 'statistics.long_tasks_stats' );
23
         expect( LONG_TASKS_STATS ).toBe( 'statistics.long_tasks_stats' );
22
 
24
 
23
         expect( Events ).toBeDefined();
25
         expect( Events ).toBeDefined();
26
         expect( Events.BEFORE_DISPOSED ).toBe( 'statistics.before_disposed' );
28
         expect( Events.BEFORE_DISPOSED ).toBe( 'statistics.before_disposed' );
27
         expect( Events.BYTE_SENT_STATS ).toBe( 'statistics.byte_sent_stats' );
29
         expect( Events.BYTE_SENT_STATS ).toBe( 'statistics.byte_sent_stats' );
28
         expect( Events.CONNECTION_STATS ).toBe( 'statistics.connectionstats' );
30
         expect( Events.CONNECTION_STATS ).toBe( 'statistics.connectionstats' );
31
+        expect( Events.ENCODE_TIME_STATS ).toBe( 'statistics.encode_time_stats' );
29
         expect( Events.LONG_TASKS_STATS ).toBe( 'statistics.long_tasks_stats' );
32
         expect( Events.LONG_TASKS_STATS ).toBe( 'statistics.long_tasks_stats' );
30
     } );
33
     } );
31
 
34
 

+ 6
- 0
service/statistics/Events.ts Ver fichero

31
      */
31
      */
32
     CONNECTION_STATS = 'statistics.connectionstats',
32
     CONNECTION_STATS = 'statistics.connectionstats',
33
 
33
 
34
+    /**
35
+     * An event carrying the encode time stats for all the local video sources.
36
+     */
37
+    ENCODE_TIME_STATS = 'statistics.encode_time_stats',
38
+
34
     /**
39
     /**
35
      * An event carrying performance stats.
40
      * An event carrying performance stats.
36
      */
41
      */
42
 export const BEFORE_DISPOSED = Events.BEFORE_DISPOSED;
47
 export const BEFORE_DISPOSED = Events.BEFORE_DISPOSED;
43
 export const BYTE_SENT_STATS = Events.BYTE_SENT_STATS;
48
 export const BYTE_SENT_STATS = Events.BYTE_SENT_STATS;
44
 export const CONNECTION_STATS = Events.CONNECTION_STATS;
49
 export const CONNECTION_STATS = Events.CONNECTION_STATS;
50
+export const ENCODE_TIME_STATS = Events.ENCODE_TIME_STATS;
45
 export const LONG_TASKS_STATS = Events.LONG_TASKS_STATS;
51
 export const LONG_TASKS_STATS = Events.LONG_TASKS_STATS;

Loading…
Cancelar
Guardar