浏览代码

feat(RTC): add support for creating non-standard tracks (#2409)

* feat(RTC): add support for creating non-standard tracks

* fix(RTC): add additional checks for creating tracks via mediastream

* fix(RTC): simplify options to create track

* fix(RTC): fix tests

* fix(RTC): update sourceId documentation
release-8443
Daniel McAssey 1年前
父节点
当前提交
8bee4514a0
没有帐户链接到提交者的电子邮件
共有 6 个文件被更改,包括 96 次插入4 次删除
  1. 39
    0
      JitsiMeetJS.spec.ts
  2. 33
    0
      JitsiMeetJS.ts
  3. 7
    1
      JitsiTrackErrors.spec.ts
  4. 13
    1
      JitsiTrackErrors.ts
  5. 2
    2
      modules/RTC/JitsiLocalTrack.js
  6. 2
    0
      types/hand-crafted/JitsiMeetJS.d.ts

+ 39
- 0
JitsiMeetJS.spec.ts 查看文件

1
+import JitsiMeetJS from './JitsiMeetJS';
2
+import { VideoType } from './service/RTC/VideoType';
3
+import { MediaType } from './service/RTC/MediaType';
4
+import { JitsiTrackErrors } from './JitsiTrackErrors';
5
+
6
+describe('JitsiMeetJS', () => {
7
+    describe('createLocalTracksFromMediaStreams', () => {
8
+        it('creates a local track from a media stream', () => {
9
+            const canvas = document.createElement('canvas');
10
+
11
+            const canvasStream = canvas.captureStream(5);
12
+            const trackInfo = {
13
+                stream: canvasStream,
14
+                sourceType: 'canvas',
15
+                mediaType: MediaType.VIDEO,
16
+                videoType: VideoType.DESKTOP
17
+            };
18
+            const newTracks = JitsiMeetJS.createLocalTracksFromMediaStreams([ trackInfo ]);
19
+
20
+            expect(newTracks).toBeDefined();
21
+            expect(newTracks.length).toBe(1);
22
+        });
23
+
24
+        it('throws an error if track is not the correct media type', () => {
25
+            const canvas = document.createElement('canvas');
26
+
27
+            const canvasStream = canvas.captureStream(5);
28
+            const trackInfo = {
29
+                stream: canvasStream,
30
+                sourceType: 'canvas',
31
+                mediaType: MediaType.AUDIO,
32
+                videoType: VideoType.DESKTOP
33
+            };
34
+
35
+            expect(() => JitsiMeetJS.createLocalTracksFromMediaStreams([ trackInfo ]))
36
+                .toThrowError(JitsiTrackErrors.TRACK_NO_STREAM_TRACKS_FOUND);
37
+        });
38
+    });
39
+});

+ 33
- 0
JitsiMeetJS.ts 查看文件

36
 import * as E2ePingEvents from './service/e2eping/E2ePingEvents';
36
 import * as E2ePingEvents from './service/e2eping/E2ePingEvents';
37
 import { createGetUserMediaEvent } from './service/statistics/AnalyticsEvents';
37
 import { createGetUserMediaEvent } from './service/statistics/AnalyticsEvents';
38
 import *  as RTCStatsEvents from './modules/RTCStats/RTCStatsEvents';
38
 import *  as RTCStatsEvents from './modules/RTCStats/RTCStatsEvents';
39
+import { VideoType } from './service/RTC/VideoType';
39
 
40
 
40
 const logger = Logger.getLogger(__filename);
41
 const logger = Logger.getLogger(__filename);
41
 
42
 
90
     }
91
     }
91
 }
92
 }
92
 
93
 
94
+interface ICreateLocalTrackFromMediaStreamOptions {
95
+    stream: MediaStream,
96
+    sourceType: string,
97
+    mediaType: MediaType,
98
+    videoType?: VideoType
99
+}
100
+
93
 /**
101
 /**
94
  * The public API of the Jitsi Meet library (a.k.a. {@code JitsiMeetJS}).
102
  * The public API of the Jitsi Meet library (a.k.a. {@code JitsiMeetJS}).
95
  */
103
  */
421
             });
429
             });
422
     },
430
     },
423
 
431
 
432
+    /**
433
+     * Manually create JitsiLocalTrack's from the provided track info, by exposing the RTC method
434
+     *
435
+     * @param {Array<ICreateLocalTrackFromMediaStreamOptions>} tracksInfo - array of track information
436
+     * @returns {Array<JitsiLocalTrack>} - created local tracks
437
+     */
438
+    createLocalTracksFromMediaStreams(tracksInfo) {
439
+        return RTC.createLocalTracks(tracksInfo.map((trackInfo) => {
440
+            const tracks = trackInfo.stream.getTracks()
441
+                .filter(track => track.kind === trackInfo.mediaType);
442
+
443
+            if (!tracks || tracks.length === 0) {
444
+                throw new JitsiTrackError(JitsiTrackErrors.TRACK_NO_STREAM_TRACKS_FOUND, null, null);
445
+            }
446
+
447
+            if (tracks.length > 1) {
448
+                throw new JitsiTrackError(JitsiTrackErrors.TRACK_TOO_MANY_TRACKS_IN_STREAM, null, null);
449
+            }
450
+
451
+            trackInfo.track = tracks[0];
452
+
453
+            return trackInfo;
454
+        }));
455
+    },
456
+
424
     /**
457
     /**
425
      * Create a TrackVADEmitter service that connects an audio track to an VAD (voice activity detection) processor in
458
      * Create a TrackVADEmitter service that connects an audio track to an VAD (voice activity detection) processor in
426
      * order to obtain VAD scores for individual PCM audio samples.
459
      * order to obtain VAD scores for individual PCM audio samples.

+ 7
- 1
JitsiTrackErrors.spec.ts 查看文件

16
         TRACK_IS_DISPOSED,
16
         TRACK_IS_DISPOSED,
17
         TRACK_NO_STREAM_FOUND,
17
         TRACK_NO_STREAM_FOUND,
18
         UNSUPPORTED_RESOLUTION,
18
         UNSUPPORTED_RESOLUTION,
19
+        TRACK_TOO_MANY_TRACKS_IN_STREAM,
20
+        TRACK_NO_STREAM_TRACKS_FOUND,
19
         JitsiTrackErrors,
21
         JitsiTrackErrors,
20
         ...others
22
         ...others
21
     } = exported;
23
     } = exported;
33
         expect( TRACK_IS_DISPOSED ).toBe( 'track.track_is_disposed' );
35
         expect( TRACK_IS_DISPOSED ).toBe( 'track.track_is_disposed' );
34
         expect( TRACK_NO_STREAM_FOUND ).toBe( 'track.no_stream_found' );
36
         expect( TRACK_NO_STREAM_FOUND ).toBe( 'track.no_stream_found' );
35
         expect( UNSUPPORTED_RESOLUTION ).toBe( 'gum.unsupported_resolution' );
37
         expect( UNSUPPORTED_RESOLUTION ).toBe( 'gum.unsupported_resolution' );
38
+        expect( TRACK_TOO_MANY_TRACKS_IN_STREAM ).toBe( 'track.too_many_tracks_in_stream' );
39
+        expect( TRACK_NO_STREAM_TRACKS_FOUND ).toBe( 'track.no_stream_tracks_found' );
36
 
40
 
37
         expect( JitsiTrackErrors ).toBeDefined();
41
         expect( JitsiTrackErrors ).toBeDefined();
38
 
42
 
48
         expect( JitsiTrackErrors.TRACK_IS_DISPOSED ).toBe( 'track.track_is_disposed' );
52
         expect( JitsiTrackErrors.TRACK_IS_DISPOSED ).toBe( 'track.track_is_disposed' );
49
         expect( JitsiTrackErrors.TRACK_NO_STREAM_FOUND ).toBe( 'track.no_stream_found' );
53
         expect( JitsiTrackErrors.TRACK_NO_STREAM_FOUND ).toBe( 'track.no_stream_found' );
50
         expect( JitsiTrackErrors.UNSUPPORTED_RESOLUTION ).toBe( 'gum.unsupported_resolution' );
54
         expect( JitsiTrackErrors.UNSUPPORTED_RESOLUTION ).toBe( 'gum.unsupported_resolution' );
55
+        expect( JitsiTrackErrors.TRACK_TOO_MANY_TRACKS_IN_STREAM ).toBe( 'track.too_many_tracks_in_stream' );
56
+        expect( JitsiTrackErrors.TRACK_NO_STREAM_TRACKS_FOUND ).toBe( 'track.no_stream_tracks_found' );
51
     } );
57
     } );
52
 
58
 
53
     it( "unknown members", () => {
59
     it( "unknown members", () => {
54
         const keys = Object.keys( others );
60
         const keys = Object.keys( others );
55
         expect( keys ).withContext( `Extra members: ${ keys.join( ", " ) }` ).toEqual( [] );
61
         expect( keys ).withContext( `Extra members: ${ keys.join( ", " ) }` ).toEqual( [] );
56
     } );
62
     } );
57
-} );
63
+} );

+ 13
- 1
JitsiTrackErrors.ts 查看文件

68
      * An error which indicates that requested video resolution is not supported
68
      * An error which indicates that requested video resolution is not supported
69
      * by a webcam.
69
      * by a webcam.
70
      */
70
      */
71
-    UNSUPPORTED_RESOLUTION = 'gum.unsupported_resolution'
71
+    UNSUPPORTED_RESOLUTION = 'gum.unsupported_resolution',
72
+
73
+    /**
74
+     * An error which indicates that there are too many tracks in the provided media stream
75
+     */
76
+    TRACK_TOO_MANY_TRACKS_IN_STREAM = 'track.too_many_tracks_in_stream',
77
+
78
+    /**
79
+     * An error which indicates that no tracks were found in the media stream
80
+     */
81
+    TRACK_NO_STREAM_TRACKS_FOUND = 'track.no_stream_tracks_found',
72
 }
82
 }
73
 
83
 
74
 // exported for backward compatibility
84
 // exported for backward compatibility
84
 export const TRACK_IS_DISPOSED = JitsiTrackErrors.TRACK_IS_DISPOSED;
94
 export const TRACK_IS_DISPOSED = JitsiTrackErrors.TRACK_IS_DISPOSED;
85
 export const TRACK_NO_STREAM_FOUND = JitsiTrackErrors.TRACK_NO_STREAM_FOUND;
95
 export const TRACK_NO_STREAM_FOUND = JitsiTrackErrors.TRACK_NO_STREAM_FOUND;
86
 export const UNSUPPORTED_RESOLUTION = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;
96
 export const UNSUPPORTED_RESOLUTION = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;
97
+export const TRACK_TOO_MANY_TRACKS_IN_STREAM = JitsiTrackErrors.TRACK_TOO_MANY_TRACKS_IN_STREAM;
98
+export const TRACK_NO_STREAM_TRACKS_FOUND = JitsiTrackErrors.TRACK_NO_STREAM_TRACKS_FOUND;

+ 2
- 2
modules/RTC/JitsiLocalTrack.js 查看文件

47
      * @param {number} trackInfo.resolution - The the video resolution if it's a video track
47
      * @param {number} trackInfo.resolution - The the video resolution if it's a video track
48
      * @param {string} trackInfo.deviceId - The ID of the local device for this track.
48
      * @param {string} trackInfo.deviceId - The ID of the local device for this track.
49
      * @param {string} trackInfo.facingMode - Thehe camera facing mode used in getUserMedia call (for mobile only).
49
      * @param {string} trackInfo.facingMode - Thehe camera facing mode used in getUserMedia call (for mobile only).
50
-     * @param {sourceId} trackInfo.sourceId - The id of the desktop sharing source. NOTE: defined for desktop sharing
51
-     * tracks only.
50
+     * @param {sourceId} trackInfo.sourceId - The id of the desktop sharing source, which is the Chrome media source ID,
51
+     * returned by Desktop Picker on Electron. NOTE: defined for desktop sharing tracks only.
52
      */
52
      */
53
     constructor({
53
     constructor({
54
         deviceId,
54
         deviceId,

+ 2
- 0
types/hand-crafted/JitsiMeetJS.d.ts 查看文件

122
 
122
 
123
   getActiveAudioDevice: () => Promise<Object>; // TODO: can we improve on object?
123
   getActiveAudioDevice: () => Promise<Object>; // TODO: can we improve on object?
124
 
124
 
125
+  createLocalTracksFromMediaStreams: ( tracksInfo: unknown[] ) => JitsiLocalTrack[]; // TODO:
126
+
125
   // isDeviceListAvailable: () => boolean; // obsosete
127
   // isDeviceListAvailable: () => boolean; // obsosete
126
 
128
 
127
   // isDeviceChangeAvailable: ( deviceType: string ) => boolean; // obsosete
129
   // isDeviceChangeAvailable: ( deviceType: string ) => boolean; // obsosete

正在加载...
取消
保存