Browse Source

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.
master
Leonard Kim 8 years ago
parent
commit
fe4de31e57
3 changed files with 78 additions and 41 deletions
  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 View File

42
     participantRoleChanged,
42
     participantRoleChanged,
43
     participantUpdated
43
     participantUpdated
44
 } from './react/features/base/participants';
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
 import {
50
 import {
47
     showDesktopPicker
51
     showDesktopPicker
48
 } from  './react/features/desktop-picker';
52
 } from  './react/features/desktop-picker';
1023
      * @returns {Promise}
1027
      * @returns {Promise}
1024
      */
1028
      */
1025
     useVideoStream(newStream) {
1029
     useVideoStream(newStream) {
1026
-        return room.replaceTrack(localVideo, newStream)
1030
+        return APP.store.dispatch(
1031
+            replaceLocalTrack(localVideo, newStream, room))
1027
             .then(() => {
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
                 localVideo = newStream;
1033
                 localVideo = newStream;
1037
                 if (newStream) {
1034
                 if (newStream) {
1038
                     this.videoMuted = newStream.isMuted();
1035
                     this.videoMuted = newStream.isMuted();
1058
      * @returns {Promise}
1055
      * @returns {Promise}
1059
      */
1056
      */
1060
     useAudioStream(newStream) {
1057
     useAudioStream(newStream) {
1061
-        return room.replaceTrack(localAudio, newStream)
1058
+        return APP.store.dispatch(
1059
+            replaceLocalTrack(localAudio, newStream, room))
1062
             .then(() => {
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
                 localAudio = newStream;
1061
                 localAudio = newStream;
1072
                 if (newStream) {
1062
                 if (newStream) {
1073
                     this.audioMuted = newStream.isMuted();
1063
                     this.audioMuted = newStream.isMuted();
1338
             APP.store.dispatch(trackRemoved(track));
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
         room.on(ConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, (id, lvl) => {
1331
         room.on(ConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, (id, lvl) => {
1360
             if(this.isLocalId(id) && localAudio && localAudio.isMuted()) {
1332
             if(this.isLocalId(id) && localAudio && localAudio.isMuted()) {
1361
                 lvl = 0;
1333
                 lvl = 0;

+ 58
- 1
react/features/base/tracks/actions.js View File

4
 } from '../lib-jitsi-meet';
4
 } from '../lib-jitsi-meet';
5
 import {
5
 import {
6
     CAMERA_FACING_MODE,
6
     CAMERA_FACING_MODE,
7
-    MEDIA_TYPE
7
+    MEDIA_TYPE,
8
+    setAudioMuted,
9
+    setVideoMuted
8
 } from '../media';
10
 } from '../media';
9
 import { getLocalParticipant } from '../participants';
11
 import { getLocalParticipant } from '../participants';
10
 
12
 
51
                     .map(t => t.jitsiTrack)));
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
  * Create an action for when a new track has been signaled to be added to the
112
  * Create an action for when a new track has been signaled to be added to the
56
  * conference.
113
  * conference.

+ 11
- 3
react/features/base/tracks/middleware.js View File

113
     case TRACK_UPDATED:
113
     case TRACK_UPDATED:
114
         // TODO Remove the below calls to APP.UI once components interested in
114
         // TODO Remove the below calls to APP.UI once components interested in
115
         // track mute changes are moved into react.
115
         // track mute changes are moved into react.
116
-        if (typeof APP !== 'undefined' && !action.track.local) {
116
+        if (typeof APP !== 'undefined') {
117
             const { jitsiTrack } = action.track;
117
             const { jitsiTrack } = action.track;
118
             const isMuted = jitsiTrack.isMuted();
118
             const isMuted = jitsiTrack.isMuted();
119
             const participantID = jitsiTrack.getParticipantId();
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
                 APP.UI.setVideoMuted(participantID, isMuted);
131
                 APP.UI.setVideoMuted(participantID, isMuted);
124
                 APP.UI.onPeerVideoTypeChanged(
132
                 APP.UI.onPeerVideoTypeChanged(
125
                     participantID, jitsiTrack.videoType);
133
                     participantID, jitsiTrack.videoType);

Loading…
Cancel
Save