@@ -18,29 +18,72 @@ limitations under the License.
1818*/
1919
2020import { logger } from "../logger" ;
21+ import { MatrixClient } from "../client" ;
22+ import { CallState } from "./call" ;
2123
2224export class MediaHandler {
2325 private audioInput : string ;
2426 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 ) { }
2732
2833 /**
2934 * Set an audio input device to use for MatrixCalls
3035 * @param {string } deviceId the identifier for the device
3136 * undefined treated as unset
3237 */
33- public setAudioInput ( deviceId : string ) : void {
38+ public async setAudioInput ( deviceId : string ) : Promise < void > {
39+ if ( this . audioInput === deviceId ) return ;
40+
3441 this . audioInput = deviceId ;
42+ await this . updateLocalUsermediaStreams ( ) ;
3543 }
3644
3745 /**
3846 * Set a video input device to use for MatrixCalls
3947 * @param {string } deviceId the identifier for the device
4048 * undefined treated as unset
4149 */
42- public setVideoInput ( deviceId : string ) : void {
50+ public async setVideoInput ( deviceId : string ) : Promise < void > {
51+ if ( this . videoInput === deviceId ) return ;
52+
4353 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+ }
4487 }
4588
4689 public async hasAudioDevice ( ) : Promise < boolean > {
@@ -65,20 +108,40 @@ export class MediaHandler {
65108
66109 let stream : MediaStream ;
67110
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+ ) {
79116 const constraints = this . getUserMediaContraints ( shouldRequestAudio , shouldRequestVideo ) ;
80117 logger . log ( "Getting user media with constraints" , constraints ) ;
81118 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+ }
82145 }
83146
84147 if ( reusable ) {
@@ -103,6 +166,10 @@ export class MediaHandler {
103166 logger . debug ( "Splicing usermedia stream out stream array" , mediaStream . id ) ;
104167 this . userMediaStreams . splice ( index , 1 ) ;
105168 }
169+
170+ if ( this . localUserMediaStream === mediaStream ) {
171+ this . localUserMediaStream = undefined ;
172+ }
106173 }
107174
108175 /**
@@ -174,6 +241,7 @@ export class MediaHandler {
174241
175242 this . userMediaStreams = [ ] ;
176243 this . screensharingStreams = [ ] ;
244+ this . localUserMediaStream = undefined ;
177245 }
178246
179247 private getUserMediaContraints ( audio : boolean , video : boolean ) : MediaStreamConstraints {
0 commit comments