@@ -18,29 +18,72 @@ limitations under the License.
18
18
*/
19
19
20
20
import { logger } from "../logger" ;
21
+ import { MatrixClient } from "../client" ;
22
+ import { CallState } from "./call" ;
21
23
22
24
export class MediaHandler {
23
25
private audioInput : string ;
24
26
private videoInput : string ;
25
- private userMediaStreams : MediaStream [ ] = [ ] ;
26
- private screensharingStreams : MediaStream [ ] = [ ] ;
27
+ private localUserMediaStream ?: MediaStream ;
28
+ public userMediaStreams : MediaStream [ ] = [ ] ;
29
+ public screensharingStreams : MediaStream [ ] = [ ] ;
30
+
31
+ constructor ( private client : MatrixClient ) { }
27
32
28
33
/**
29
34
* Set an audio input device to use for MatrixCalls
30
35
* @param {string } deviceId the identifier for the device
31
36
* undefined treated as unset
32
37
*/
33
- public setAudioInput ( deviceId : string ) : void {
38
+ public async setAudioInput ( deviceId : string ) : Promise < void > {
39
+ if ( this . audioInput === deviceId ) return ;
40
+
34
41
this . audioInput = deviceId ;
42
+ await this . updateLocalUsermediaStreams ( ) ;
35
43
}
36
44
37
45
/**
38
46
* Set a video input device to use for MatrixCalls
39
47
* @param {string } deviceId the identifier for the device
40
48
* undefined treated as unset
41
49
*/
42
- public setVideoInput ( deviceId : string ) : void {
50
+ public async setVideoInput ( deviceId : string ) : Promise < void > {
51
+ if ( this . videoInput === deviceId ) return ;
52
+
43
53
this . videoInput = deviceId ;
54
+ await this . updateLocalUsermediaStreams ( ) ;
55
+ }
56
+
57
+ /**
58
+ * Requests new usermedia streams and replace the old ones
59
+ */
60
+ public async updateLocalUsermediaStreams ( ) : Promise < void > {
61
+ if ( this . userMediaStreams . length === 0 ) return ;
62
+
63
+ const callMediaStreamParams : Map < string , { audio : boolean , video : boolean } > = new Map ( ) ;
64
+ for ( const call of this . client . callEventHandler . calls . values ( ) ) {
65
+ callMediaStreamParams . set ( call . callId , {
66
+ audio : call . hasLocalUserMediaAudioTrack ,
67
+ video : call . hasLocalUserMediaVideoTrack ,
68
+ } ) ;
69
+ }
70
+
71
+ for ( const stream of this . userMediaStreams ) {
72
+ for ( const track of stream . getTracks ( ) ) {
73
+ track . stop ( ) ;
74
+ }
75
+ }
76
+
77
+ this . userMediaStreams = [ ] ;
78
+ this . localUserMediaStream = undefined ;
79
+
80
+ for ( const call of this . client . callEventHandler . calls . values ( ) ) {
81
+ if ( call . state === CallState . Ended || ! callMediaStreamParams . has ( call . callId ) ) continue ;
82
+
83
+ const { audio, video } = callMediaStreamParams . get ( call . callId ) ;
84
+ const stream = await this . getUserMediaStream ( audio , video ) ;
85
+ await call . updateLocalUsermediaStream ( stream ) ;
86
+ }
44
87
}
45
88
46
89
public async hasAudioDevice ( ) : Promise < boolean > {
@@ -65,20 +108,40 @@ export class MediaHandler {
65
108
66
109
let stream : MediaStream ;
67
110
68
- // Find a stream with matching tracks
69
- const matchingStream = this . userMediaStreams . find ( ( stream ) => {
70
- if ( shouldRequestAudio !== ( stream . getAudioTracks ( ) . length > 0 ) ) return false ;
71
- if ( shouldRequestVideo !== ( stream . getVideoTracks ( ) . length > 0 ) ) return false ;
72
- return true ;
73
- } ) ;
74
-
75
- if ( matchingStream ) {
76
- logger . log ( "Cloning user media stream" , matchingStream . id ) ;
77
- stream = matchingStream . clone ( ) ;
78
- } else {
111
+ if (
112
+ ! this . localUserMediaStream ||
113
+ ( this . localUserMediaStream . getAudioTracks ( ) . length === 0 && shouldRequestAudio ) ||
114
+ ( this . localUserMediaStream . getVideoTracks ( ) . length === 0 && shouldRequestVideo )
115
+ ) {
79
116
const constraints = this . getUserMediaContraints ( shouldRequestAudio , shouldRequestVideo ) ;
80
117
logger . log ( "Getting user media with constraints" , constraints ) ;
81
118
stream = await navigator . mediaDevices . getUserMedia ( constraints ) ;
119
+
120
+ for ( const track of stream . getTracks ( ) ) {
121
+ const settings = track . getSettings ( ) ;
122
+
123
+ if ( track . kind === "audio" ) {
124
+ this . audioInput = settings . deviceId ;
125
+ } else if ( track . kind === "video" ) {
126
+ this . videoInput = settings . deviceId ;
127
+ }
128
+ }
129
+
130
+ this . localUserMediaStream = stream ;
131
+ } else {
132
+ stream = this . localUserMediaStream . clone ( ) ;
133
+
134
+ if ( ! shouldRequestAudio ) {
135
+ for ( const track of stream . getAudioTracks ( ) ) {
136
+ stream . removeTrack ( track ) ;
137
+ }
138
+ }
139
+
140
+ if ( ! shouldRequestVideo ) {
141
+ for ( const track of stream . getVideoTracks ( ) ) {
142
+ stream . removeTrack ( track ) ;
143
+ }
144
+ }
82
145
}
83
146
84
147
if ( reusable ) {
@@ -103,6 +166,10 @@ export class MediaHandler {
103
166
logger . debug ( "Splicing usermedia stream out stream array" , mediaStream . id ) ;
104
167
this . userMediaStreams . splice ( index , 1 ) ;
105
168
}
169
+
170
+ if ( this . localUserMediaStream === mediaStream ) {
171
+ this . localUserMediaStream = undefined ;
172
+ }
106
173
}
107
174
108
175
/**
@@ -174,6 +241,7 @@ export class MediaHandler {
174
241
175
242
this . userMediaStreams = [ ] ;
176
243
this . screensharingStreams = [ ] ;
244
+ this . localUserMediaStream = undefined ;
177
245
}
178
246
179
247
private getUserMediaContraints ( audio : boolean , video : boolean ) : MediaStreamConstraints {
0 commit comments