瀏覽代碼

[RN] Create tracks right when they are required

When do we need tracks?

- Welcome page (only the video track)
- Conference (depends if starting with audio / video muted is requested)

When do we need to destroy the tracks?

- When we are not in a conference and there is no welcome page

In order to accommodate all the above use cases, a new component is introduced:
BlankWelcomePage. Its purpose is to take the place of the welcome page when it
is disabled. When this component is mounted local tracks are destroyed.

Analogously, a video track is created when the (real) welcome page is created,
and all the desired tracks are created then the Conference component is created.
What are desired tracks? These are the tracks we'd like to use for the
conference that is about to happen. By default both audio and video are desired.
It's possible, however, the user requested to start the call with no
video/audio, in which case it's muted in base/media and a track is not created.

The first time the app starts (with the welcome page) it will request permission
for video only, since there is no need for audio in the welcome page. Later,
when a conference is joined permission for audio will be requested when an audio
track is to be created. The audio track is not destroyed when the conference
ends. Yours truly thinks this is not needed since it's a stopped track which is
not using system resources.
master
Saúl Ibarra Corretgé 7 年之前
父節點
當前提交
9bca0e3b3d

+ 5
- 2
react/features/app/functions.native.js 查看文件

@@ -1,7 +1,7 @@
1 1
 import { isRoomValid } from '../base/conference';
2 2
 import { RouteRegistry } from '../base/react';
3 3
 import { Conference } from '../conference';
4
-import { WelcomePage } from '../welcome';
4
+import { BlankWelcomePage, WelcomePage } from '../welcome';
5 5
 
6 6
 /**
7 7
  * Determines which route is to be rendered in order to depict a specific Redux
@@ -28,7 +28,10 @@ export function _getRouteToRender(stateOrGetState) {
28 28
         // React-ive way anyway so it's all the same difference.
29 29
         const { app } = state['features/app'];
30 30
 
31
-        component = app && app.props.welcomePageEnabled ? WelcomePage : null;
31
+        component
32
+            = app && app.props.welcomePageEnabled
33
+                ? WelcomePage
34
+                : BlankWelcomePage;
32 35
     }
33 36
 
34 37
     return RouteRegistry.getRouteByComponent(component);

+ 1
- 29
react/features/app/middleware.js 查看文件

@@ -5,7 +5,6 @@ import {
5 5
     SET_LOCATION_URL
6 6
 } from '../base/connection';
7 7
 import { MiddlewareRegistry } from '../base/redux';
8
-import { createLocalTracksA, destroyLocalTracks } from '../base/tracks';
9 8
 
10 9
 MiddlewareRegistry.register(store => next => action => {
11 10
     switch (action.type) {
@@ -71,38 +70,11 @@ function _connectionEstablished(store, next, action) {
71 70
  * @private
72 71
  * @returns {void}
73 72
  */
74
-function _navigate({ dispatch, getState }) {
73
+function _navigate({ getState }) {
75 74
     const state = getState();
76 75
     const { app, getRouteToRender } = state['features/app'];
77 76
     const routeToRender = getRouteToRender && getRouteToRender(state);
78 77
 
79
-    // FIXME The following is logic specific to the user experience of the
80
-    // mobile/React Native app. Firstly, I don't like that it's here at all.
81
-    // Secondly, I copied the mobile/React Native detection from
82
-    // react/features/base/config/reducer.js because I couldn't iron out an
83
-    // abstraction. Because of the first point, I'm leaving the second point
84
-    // unresolved to attract attention to the fact that the following needs more
85
-    // thinking.
86
-    if (navigator.product === 'ReactNative') {
87
-        // Create/destroy the local tracks as needed: create them the first time
88
-        // we are going to render an actual route (be that the WelcomePage or
89
-        // the Conference).
90
-        //
91
-        // When the WelcomePage is disabled, the app will transition to the
92
-        // null/undefined route. Detect these transitions and create/destroy the
93
-        // local tracks so the camera doesn't stay open if the app is not
94
-        // rendering any component.
95
-        if (typeof routeToRender === 'undefined' || routeToRender === null) {
96
-            // Destroy the local tracks if there is no route to render and there
97
-            // is no WelcomePage.
98
-            app.props.welcomePageEnabled || dispatch(destroyLocalTracks());
99
-        } else {
100
-            // Create the local tracks if they haven't been created yet.
101
-            state['features/base/tracks'].some(t => t.local)
102
-                || dispatch(createLocalTracksA());
103
-        }
104
-    }
105
-
106 78
     return app._navigate(routeToRender);
107 79
 }
108 80
 

+ 32
- 0
react/features/base/tracks/actions.js 查看文件

@@ -10,6 +10,38 @@ import { getLocalParticipant } from '../participants';
10 10
 import { TRACK_ADDED, TRACK_REMOVED, TRACK_UPDATED } from './actionTypes';
11 11
 import { createLocalTracksF } from './functions';
12 12
 
13
+/**
14
+ * Requests the creating of the desired media type tracks. Desire is expressed
15
+ * by base/media. This function will dispatch a {@code createLocalTracksA}
16
+ * action for the "missing" types, that is, the ones which base/media would
17
+ * like to have (unmuted tracks) but are not present yet.
18
+ *
19
+ * @returns {Function}
20
+ */
21
+export function createDesiredLocalTracks() {
22
+    return (dispatch, getState) => {
23
+        const state = getState();
24
+        const desiredTypes = [];
25
+
26
+        state['features/base/media'].audio.muted
27
+            || desiredTypes.push(MEDIA_TYPE.AUDIO);
28
+        Boolean(state['features/base/media'].video.muted)
29
+            || desiredTypes.push(MEDIA_TYPE.VIDEO);
30
+
31
+        const availableTypes
32
+            = state['features/base/tracks']
33
+                .filter(t => t.local)
34
+                .map(t => t.mediaType);
35
+
36
+        // We need to create the desired tracks which are not already available.
37
+        const createTypes
38
+            = desiredTypes.filter(type => availableTypes.indexOf(type) === -1);
39
+
40
+        createTypes.length
41
+            && dispatch(createLocalTracksA({ devices: createTypes }));
42
+    };
43
+}
44
+
13 45
 /**
14 46
  * Request to start capturing local audio and/or video. By default, the user
15 47
  * facing camera will be selected.

+ 7
- 4
react/features/conference/components/Conference.native.js 查看文件

@@ -4,6 +4,7 @@ import { connect as reactReduxConnect } from 'react-redux';
4 4
 import { connect, disconnect } from '../../base/connection';
5 5
 import { DialogContainer } from '../../base/dialog';
6 6
 import { Container } from '../../base/react';
7
+import { createDesiredLocalTracks } from '../../base/tracks';
7 8
 import { Filmstrip } from '../../filmstrip';
8 9
 import { LargeVideo } from '../../large-video';
9 10
 import { OverlayContainer } from '../../overlay';
@@ -222,19 +223,21 @@ class Conference extends Component {
222 223
 function _mapDispatchToProps(dispatch) {
223 224
     return {
224 225
         /**
225
-         * Dispatched an action connecting to the conference.
226
+         * Dispatches actions to create the desired local tracks and for
227
+         * connecting to the conference.
226 228
          *
227
-         * @returns {Object} Dispatched action.
229
+         * @returns {void}
228 230
          * @private
229 231
          */
230 232
         _onConnect() {
233
+            dispatch(createDesiredLocalTracks());
231 234
             dispatch(connect());
232 235
         },
233 236
 
234 237
         /**
235 238
          * Dispatches an action disconnecting from the conference.
236 239
          *
237
-         * @returns {Object} Dispatched action.
240
+         * @returns {void}
238 241
          * @private
239 242
          */
240 243
         _onDisconnect() {
@@ -246,7 +249,7 @@ function _mapDispatchToProps(dispatch) {
246 249
          *
247 250
          * @param {boolean} visible - True to show the Toolbox or false to hide
248 251
          * it.
249
-         * @returns {Object} Dispatched action.
252
+         * @returns {void}
250 253
          * @private
251 254
          */
252 255
         _setToolboxVisible(visible: boolean) {

+ 49
- 0
react/features/welcome/components/BlankWelcomePage.js 查看文件

@@ -0,0 +1,49 @@
1
+import PropTypes from 'prop-types';
2
+import { Component } from 'react';
3
+import { connect } from 'react-redux';
4
+
5
+import { destroyLocalTracks } from '../../base/tracks';
6
+
7
+/**
8
+ * Component for rendering a blank welcome page. It renders absolutely nothing
9
+ * and destroys local tracks upon being mounted, since no media is desired when
10
+ * this component is rendered.
11
+ *
12
+ * The use case is mainly mobile, where SDK users probably disable the welcome
13
+ * page, but using it on the web in the future is not out of the question.
14
+ */
15
+class BlankWelcomePage extends Component {
16
+    /**
17
+     * {@code BlankWelcomePage} component's property types.
18
+     *
19
+     * @static
20
+     */
21
+    static propTypes = {
22
+        dispatch: PropTypes.func
23
+    };
24
+
25
+    /**
26
+     * Destroys the local tracks (if any) since no media is desired when this
27
+     * component is rendered.
28
+     *
29
+     * @inheritdoc
30
+     * @returns {void}
31
+     */
32
+    componentWillMount() {
33
+        this.props.dispatch(destroyLocalTracks());
34
+    }
35
+
36
+    /**
37
+     * Implements React's {@link Component#render()}. In this particular case
38
+     * we return null, because the entire purpose of this component is to render
39
+     * nothing.
40
+     *
41
+     * @inheritdoc
42
+     * @returns {null}
43
+     */
44
+    render() {
45
+        return null;
46
+    }
47
+}
48
+
49
+export default connect()(BlankWelcomePage);

+ 16
- 1
react/features/welcome/components/WelcomePage.native.js 查看文件

@@ -3,8 +3,10 @@ import { TextInput, TouchableHighlight, View } from 'react-native';
3 3
 import { connect } from 'react-redux';
4 4
 
5 5
 import { translate } from '../../base/i18n';
6
+import { MEDIA_TYPE } from '../../base/media';
6 7
 import { Link, Text } from '../../base/react';
7 8
 import { ColorPalette } from '../../base/styles';
9
+import { createLocalTracksA } from '../../base/tracks';
8 10
 
9 11
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
10 12
 import styles from './styles';
@@ -35,7 +37,20 @@ class WelcomePage extends AbstractWelcomePage {
35 37
      *
36 38
      * @static
37 39
      */
38
-    static propTypes = AbstractWelcomePage.propTypes
40
+    static propTypes = AbstractWelcomePage.propTypes;
41
+
42
+    /**
43
+     * Creates a video track if not already available.
44
+     *
45
+     * @inheritdoc
46
+     * @returns {void}
47
+     */
48
+    componentWillMount() {
49
+        const { dispatch, _localVideoTrack } = this.props;
50
+
51
+        (typeof _localVideoTrack === 'undefined')
52
+            && dispatch(createLocalTracksA({ devices: [ MEDIA_TYPE.VIDEO ] }));
53
+    }
39 54
 
40 55
     /**
41 56
      * Renders a prompt for entering a room name.

+ 1
- 0
react/features/welcome/components/index.js 查看文件

@@ -1 +1,2 @@
1
+export { default as BlankWelcomePage } from './BlankWelcomePage';
1 2
 export { default as WelcomePage } from './WelcomePage';

+ 10
- 1
react/features/welcome/route.js 查看文件

@@ -2,12 +2,21 @@
2 2
 
3 3
 import { RouteRegistry } from '../base/react';
4 4
 
5
-import { WelcomePage } from './components';
5
+import { BlankWelcomePage, WelcomePage } from './components';
6 6
 import { generateRoomWithoutSeparator } from './roomnameGenerator';
7 7
 
8 8
 declare var APP: Object;
9 9
 declare var config: Object;
10 10
 
11
+/**
12
+ * Register route for BlankWelcomePage.
13
+ */
14
+RouteRegistry.register({
15
+    component: BlankWelcomePage,
16
+    undefined,
17
+    path: '/#blank'
18
+});
19
+
11 20
 /**
12 21
  * Register route for WelcomePage.
13 22
  */

Loading…
取消
儲存