Bladeren bron

feat(tracks): place local tracks in the redux store

- Add tracks to the redux store by intercepting where the
  tracks actually get used via conference.replaceTrack
- While the replace call is unique to web, the _dispose and
 _addTracks calls use existing native code implementations
- Between _dispose and addTracks is a call to update mute state.
  Without it, when changing devices or videoType while muted,
  the user will stay muted (whereas existing web behavior
  causes unmute). This is due to middelware calling
  _syncTrackMutedState to make the track mute if the user is
  currently muted.
- Move the rest of ConferenceEvents.TRACK_MUTE_CHANGED into
  middleware so the event is no longer used
- Note: This change does not guarantee the track state in the
  redux store will be 100% accurate, specifically the attribute
  videoStarted. Muted and videoType should be accurate.
j8
Leonard Kim 8 jaren geleden
bovenliggende
commit
fe4de31e57
3 gewijzigde bestanden met toevoegingen van 78 en 41 verwijderingen
  1. 9
    37
      conference.js
  2. 58
    1
      react/features/base/tracks/actions.js
  3. 11
    3
      react/features/base/tracks/middleware.js

+ 9
- 37
conference.js Bestand weergeven

@@ -42,7 +42,11 @@ import {
42 42
     participantRoleChanged,
43 43
     participantUpdated
44 44
 } from './react/features/base/participants';
45
-import { trackAdded, trackRemoved } from './react/features/base/tracks';
45
+import {
46
+    replaceLocalTrack,
47
+    trackAdded,
48
+    trackRemoved
49
+} from './react/features/base/tracks';
46 50
 import {
47 51
     showDesktopPicker
48 52
 } from  './react/features/desktop-picker';
@@ -1023,16 +1027,9 @@ export default {
1023 1027
      * @returns {Promise}
1024 1028
      */
1025 1029
     useVideoStream(newStream) {
1026
-        return room.replaceTrack(localVideo, newStream)
1030
+        return APP.store.dispatch(
1031
+            replaceLocalTrack(localVideo, newStream, room))
1027 1032
             .then(() => {
1028
-                // We call dispose after doing the replace because
1029
-                //  dispose will try and do a new o/a after the
1030
-                //  track removes itself.  Doing it after means
1031
-                //  the JitsiLocalTrack::conference member is already
1032
-                //  cleared, so it won't try and do the o/a
1033
-                if (localVideo) {
1034
-                    localVideo.dispose();
1035
-                }
1036 1033
                 localVideo = newStream;
1037 1034
                 if (newStream) {
1038 1035
                     this.videoMuted = newStream.isMuted();
@@ -1058,16 +1055,9 @@ export default {
1058 1055
      * @returns {Promise}
1059 1056
      */
1060 1057
     useAudioStream(newStream) {
1061
-        return room.replaceTrack(localAudio, newStream)
1058
+        return APP.store.dispatch(
1059
+            replaceLocalTrack(localAudio, newStream, room))
1062 1060
             .then(() => {
1063
-                // We call dispose after doing the replace because
1064
-                //  dispose will try and do a new o/a after the
1065
-                //  track removes itself.  Doing it after means
1066
-                //  the JitsiLocalTrack::conference member is already
1067
-                //  cleared, so it won't try and do the o/a
1068
-                if (localAudio) {
1069
-                    localAudio.dispose();
1070
-                }
1071 1061
                 localAudio = newStream;
1072 1062
                 if (newStream) {
1073 1063
                     this.audioMuted = newStream.isMuted();
@@ -1338,24 +1328,6 @@ export default {
1338 1328
             APP.store.dispatch(trackRemoved(track));
1339 1329
         });
1340 1330
 
1341
-        room.on(ConferenceEvents.TRACK_MUTE_CHANGED, (track) => {
1342
-            if (!track || !track.isLocal()) {
1343
-                return;
1344
-            }
1345
-
1346
-            const handler = (track.getType() === "audio")?
1347
-                APP.UI.setAudioMuted : APP.UI.setVideoMuted;
1348
-            const mute = track.isMuted();
1349
-            const id = APP.conference.getMyUserId();
1350
-
1351
-            if (track.getType() === "audio") {
1352
-                this.audioMuted = mute;
1353
-            } else {
1354
-                this.videoMuted = mute;
1355
-            }
1356
-
1357
-            handler(id , mute);
1358
-        });
1359 1331
         room.on(ConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, (id, lvl) => {
1360 1332
             if(this.isLocalId(id) && localAudio && localAudio.isMuted()) {
1361 1333
                 lvl = 0;

+ 58
- 1
react/features/base/tracks/actions.js Bestand weergeven

@@ -4,7 +4,9 @@ import JitsiMeetJS, {
4 4
 } from '../lib-jitsi-meet';
5 5
 import {
6 6
     CAMERA_FACING_MODE,
7
-    MEDIA_TYPE
7
+    MEDIA_TYPE,
8
+    setAudioMuted,
9
+    setVideoMuted
8 10
 } from '../media';
9 11
 import { getLocalParticipant } from '../participants';
10 12
 
@@ -51,6 +53,61 @@ export function destroyLocalTracks() {
51 53
                     .map(t => t.jitsiTrack)));
52 54
 }
53 55
 
56
+/**
57
+ * Replaces one track with another for one renegotiation instead of invoking
58
+ * two renegotations with a separate removeTrack and addTrack. Disposes the
59
+ * removed track as well.
60
+ *
61
+ * @param {JitsiLocalTrack|null} oldTrack - The track to dispose.
62
+ * @param {JitsiLocalTrack|null} newTrack - The track to use instead.
63
+ * @param {JitsiConference} [conference] - The conference from which to remove
64
+ * and add the tracks. If one is not provied, the conference in the redux store
65
+ * will be used.
66
+ * @returns {Function}
67
+ */
68
+export function replaceLocalTrack(oldTrack, newTrack, conference) {
69
+    return (dispatch, getState) => {
70
+        const currentConference = conference
71
+            || getState()['features/base/conference'].conference;
72
+
73
+        return currentConference.replaceTrack(oldTrack, newTrack)
74
+            .then(() => {
75
+                // We call dispose after doing the replace because
76
+                //  dispose will try and do a new o/a after the
77
+                //  track removes itself.  Doing it after means
78
+                //  the JitsiLocalTrack::conference member is already
79
+                //  cleared, so it won't try and do the o/a
80
+                const disposePromise = oldTrack
81
+                    ? dispatch(_disposeAndRemoveTracks([ oldTrack ]))
82
+                    : Promise.resolve();
83
+
84
+                return disposePromise
85
+                    .then(() => {
86
+                        if (newTrack) {
87
+                            // The mute state of the new track should be
88
+                            // reflected in the app's mute state. For example,
89
+                            // if the app is currently muted and changing to a
90
+                            // new track that is not muted, the app's mute
91
+                            // state should be falsey. As such, emit a mute
92
+                            // event here to set up the app to reflect the
93
+                            // track's mute state. If this is not done, the
94
+                            // current mute state of the app will be reflected
95
+                            // on the track, not vice-versa.
96
+                            const muteAction = newTrack.isVideoTrack()
97
+                                ? setVideoMuted : setAudioMuted;
98
+
99
+                            return dispatch(muteAction(newTrack.isMuted()));
100
+                        }
101
+                    })
102
+                    .then(() => {
103
+                        if (newTrack) {
104
+                            return dispatch(_addTracks([ newTrack ]));
105
+                        }
106
+                    });
107
+            });
108
+    };
109
+}
110
+
54 111
 /**
55 112
  * Create an action for when a new track has been signaled to be added to the
56 113
  * conference.

+ 11
- 3
react/features/base/tracks/middleware.js Bestand weergeven

@@ -113,13 +113,21 @@ MiddlewareRegistry.register(store => next => action => {
113 113
     case TRACK_UPDATED:
114 114
         // TODO Remove the below calls to APP.UI once components interested in
115 115
         // track mute changes are moved into react.
116
-        if (typeof APP !== 'undefined' && !action.track.local) {
116
+        if (typeof APP !== 'undefined') {
117 117
             const { jitsiTrack } = action.track;
118 118
             const isMuted = jitsiTrack.isMuted();
119 119
             const participantID = jitsiTrack.getParticipantId();
120
-            const { videoType } = jitsiTrack;
120
+            const isVideoTrack = jitsiTrack.isVideoTrack();
121
+
122
+            if (jitsiTrack.isLocal()) {
123
+                if (isVideoTrack) {
124
+                    APP.conference.videoMuted = isMuted;
125
+                } else {
126
+                    APP.conference.audioMuted = isMuted;
127
+                }
128
+            }
121 129
 
122
-            if (videoType) {
130
+            if (isVideoTrack) {
123 131
                 APP.UI.setVideoMuted(participantID, isMuted);
124 132
                 APP.UI.onPeerVideoTypeChanged(
125 133
                     participantID, jitsiTrack.videoType);

Laden…
Annuleren
Opslaan