浏览代码

Handle cases when new media devices are added/removed more precisely and more predictable

dev1
tsareg 9 年前
父节点
当前提交
403fcb0e25
共有 6 个文件被更改,包括 246 次插入76 次删除
  1. 2
    1
      JitsiConference.js
  2. 139
    0
      JitsiTrackError.js
  3. 46
    34
      JitsiTrackErrors.js
  4. 3
    1
      modules/RTC/JitsiLocalTrack.js
  5. 17
    14
      modules/RTC/RTCUtils.js
  6. 39
    26
      modules/RTC/ScreenObtainer.js

+ 2
- 1
JitsiConference.js 查看文件

@@ -13,6 +13,7 @@ var Statistics = require("./modules/statistics/statistics");
13 13
 var JitsiDTMFManager = require('./modules/DTMF/JitsiDTMFManager');
14 14
 var JitsiTrackEvents = require("./JitsiTrackEvents");
15 15
 var JitsiTrackErrors = require("./JitsiTrackErrors");
16
+var JitsiTrackError = require("./JitsiTrackError");
16 17
 var Settings = require("./modules/settings/Settings");
17 18
 var ComponentsVersions = require("./modules/version/ComponentsVersions");
18 19
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
@@ -320,7 +321,7 @@ JitsiConference.prototype.setSubject = function (subject) {
320 321
 JitsiConference.prototype.addTrack = function (track) {
321 322
     if(track.disposed)
322 323
     {
323
-        throw new Error(JitsiTrackErrors.TRACK_IS_DISPOSED);
324
+        throw new JitsiTrackError(JitsiTrackErrors.TRACK_IS_DISPOSED);
324 325
     }
325 326
 
326 327
     if (track.isVideoTrack() && this.rtc.getLocalVideoTrack()) {

+ 139
- 0
JitsiTrackError.js 查看文件

@@ -0,0 +1,139 @@
1
+var JitsiTrackErrors = require("./JitsiTrackErrors");
2
+
3
+var TRACK_ERROR_TO_MESSAGE_MAP = {};
4
+
5
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.UNSUPPORTED_RESOLUTION]
6
+    = "Video resolution is not supported: ";
7
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.FIREFOX_EXTENSION_NEEDED]
8
+    = "Firefox extension is not installed";
9
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR]
10
+    = "Failed to install Chrome extension";
11
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED]
12
+    = "User canceled Chrome's screen sharing prompt";
13
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.CHROME_EXTENSION_GENERIC_ERROR]
14
+    = "Unknown error from Chrome extension";
15
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.GENERAL]
16
+    = "Generic getUserMedia error";
17
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.PERMISSION_DENIED]
18
+    = "User denied permission to use device(s): ";
19
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.NOT_FOUND]
20
+    = "Requested device(s) was/were not found: ";
21
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.CONSTRAINT_FAILED]
22
+    = "Constraint could not be satisfied: ";
23
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_IS_DISPOSED]
24
+    = "Track has been already disposed";
25
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_MUTE_UNMUTE_IN_PROGRESS]
26
+    = "Track mute/unmute process is currently in progress";
27
+
28
+/**
29
+ * Object representing error that happened to a JitsiTrack. Can represent
30
+ * various types of errors. For error descriptions (@see JitsiTrackErrors).
31
+ * @constructor
32
+ * @extends Error
33
+ * @param {Object|string} error - error object or error name
34
+ * @param {Object|string} (options) - getUserMedia constraints object or error
35
+ *      message
36
+ */
37
+function JitsiTrackError(error, options) {
38
+    if (typeof error === "object" && typeof error.name !== "undefined") {
39
+        /**
40
+         * Additional information about original getUserMedia error
41
+         * and constraints.
42
+         * @type {{error: Object, constraints: Object }}
43
+         */
44
+        this.gum = {
45
+            error: error,
46
+            constraints: options
47
+        };
48
+
49
+        switch (error.name) {
50
+            case "PermissionDeniedError":
51
+            case "SecurityError":
52
+                this.name = JitsiTrackErrors.PERMISSION_DENIED;
53
+                this.message = error.message
54
+                    || TRACK_ERROR_TO_MESSAGE_MAP[
55
+                        JitsiTrackErrors.PERMISSION_DENIED]
56
+                        + Object.keys(options || {}).filter(function (k) {
57
+                            return !!options[k];
58
+                        }).join(", ");
59
+                break;
60
+            case "NotFoundError":
61
+                this.name = JitsiTrackErrors.NOT_FOUND;
62
+                this.message = error.message
63
+                    || TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.NOT_FOUND]
64
+                        + Object.keys(options || {}).filter(function (k) {
65
+                            return !!options[k];
66
+                        }).join(", ");
67
+                break;
68
+            case "ConstraintNotSatisfiedError":
69
+            case "OverconstrainedError":
70
+                var constraintName = error.constraintName;
71
+
72
+                if (options && options.video
73
+                    &&
74
+                    (constraintName === "minWidth" ||
75
+                        constraintName === "maxWidth" ||
76
+                        constraintName === "minHeight" ||
77
+                        constraintName === "maxHeight" ||
78
+                        constraintName === "width" ||
79
+                        constraintName === "height")) {
80
+                    this.name = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;
81
+                    this.message = error.message ||
82
+                        TRACK_ERROR_TO_MESSAGE_MAP[
83
+                            JitsiTrackErrors.UNSUPPORTED_RESOLUTION] +
84
+                        getResolutionFromFailedConstraint(constraintName,
85
+                            options);
86
+                } else {
87
+                    this.name = JitsiTrackErrors.CONSTRAINT_FAILED;
88
+                    this.message = error.message ||
89
+                        TRACK_ERROR_TO_MESSAGE_MAP[
90
+                            JitsiTrackErrors.CONSTRAINT_FAILED] +
91
+                        error.constraintName;
92
+                }
93
+                break;
94
+            default:
95
+                this.name = JitsiTrackErrors.GENERAL;
96
+                this.message = error.message ||
97
+                    TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.GENERAL];
98
+                break;
99
+        }
100
+    } else if (typeof error === "string") {
101
+        if (TRACK_ERROR_TO_MESSAGE_MAP[error]) {
102
+            this.name = error;
103
+            this.message = options || TRACK_ERROR_TO_MESSAGE_MAP[error];
104
+        } else {
105
+            // this is some generic error that do not fit any of our pre-defined
106
+            // errors, so don't give it any specific name, just store message
107
+            this.message = error;
108
+        }
109
+    } else {
110
+        throw new Error("Invalid arguments");
111
+    }
112
+
113
+    this.stack = error.stack || (new Error()).stack;
114
+}
115
+
116
+JitsiTrackError.prototype = Object.create(Error.prototype);
117
+JitsiTrackError.prototype.constructor = JitsiTrackError;
118
+
119
+/**
120
+ * Gets failed resolution constraint from corresponding object.
121
+ * @param {string} failedConstraintName
122
+ * @param {Object} constraints
123
+ * @returns {string|number}
124
+ */
125
+function getResolutionFromFailedConstraint(failedConstraintName, constraints) {
126
+    if (constraints && constraints.video && constraints.video.mandatory) {
127
+        if (failedConstraintName === "width") {
128
+            return constraints.video.mandatory.minWidth;
129
+        } else if (failedConstraintName === "height") {
130
+            return constraints.video.mandatory.minHeight;
131
+        } else {
132
+            return constraints.video.mandatory[failedConstraintName] || "";
133
+        }
134
+    }
135
+
136
+    return "";
137
+}
138
+
139
+module.exports = JitsiTrackError;

+ 46
- 34
JitsiTrackErrors.js 查看文件

@@ -1,49 +1,61 @@
1
-var logger = require("jitsi-meet-logger").getLogger(__filename);
2
-
1
+/**
2
+ * Enumeration with the errors for the JitsiTrack objects.
3
+ * @type {{string: string}}
4
+ */
3 5
 module.exports = {
4 6
     /**
5
-     * Returns JitsiTrackErrors based on the error object passed by GUM
6
-     * @param error the error
7
-     * @param {Array} devices Array with the requested devices
8
-     */
9
-    parseError: function (error, devices) {
10
-        if (typeof error === "object") {
11
-          var constraintName = error.constraintName;
12
-          var name;
13
-          if (constraintName
14
-                  && (name = error.name)
15
-                  && (name == "ConstraintNotSatisfiedError"
16
-                      || name == "OverconstrainedError")
17
-                  && (constraintName == "minWidth"
18
-                      || constraintName == "maxWidth"
19
-                      || constraintName == "minHeight"
20
-                      || constraintName == "maxHeight"
21
-                      || constraintName == "width"
22
-                      || constraintName == "height")
23
-                  && (devices || []).indexOf("video") !== -1) {
24
-              return this.UNSUPPORTED_RESOLUTION;
25
-          }
26
-          if (error.type === "jitsiError") {
27
-              return error.errorObject;
28
-          }
29
-        }
30
-        // XXX We're about to lose the details represented by error and devices
31
-        // (because we're about to generalize them to GENERAL). At the very
32
-        // least log the details.
33
-        logger.error('Parsing error into ' + this.GENERAL + ': ' + error);
34
-        return this.GENERAL;
35
-    },
7
+     * An error which indicates that requested video resolution is not supported
8
+     * by a webcam.
9
+     */
36 10
     UNSUPPORTED_RESOLUTION: "gum.unsupported_resolution",
37 11
     /**
38
-     * An event which indicates that the jidesha extension for Firefox is
12
+     * An error which indicates that the jidesha extension for Firefox is
39 13
      * needed to proceed with screen sharing, and that it is not installed.
40 14
      */
41 15
     FIREFOX_EXTENSION_NEEDED: "gum.firefox_extension_needed",
16
+    /**
17
+     * An error which indicates that the jidesha extension for Chrome is
18
+     * failed to install.
19
+     */
42 20
     CHROME_EXTENSION_INSTALLATION_ERROR:
43 21
         "gum.chrome_extension_installation_error",
22
+    /**
23
+     * An error which indicates that user canceled screen sharing window
24
+     * selection dialog in jidesha extension for Chrome.
25
+     */
44 26
     CHROME_EXTENSION_USER_CANCELED:
45 27
         "gum.chrome_extension_user_canceled",
28
+    /**
29
+     * Generic error for jidesha extension for Chrome.
30
+     */
31
+    CHROME_EXTENSION_GENERIC_ERROR:
32
+        "gum.chrome_extension_generic_error",
33
+    /**
34
+     * Generic getUserMedia error.
35
+     */
46 36
     GENERAL: "gum.general",
37
+    /**
38
+     * An error which indicates that user denied permission to share requested
39
+     * device.
40
+     */
41
+    PERMISSION_DENIED: "gum.permission_denied",
42
+    /**
43
+     * An error which indicates that requested device was not found.
44
+     */
45
+    NOT_FOUND: "gum.not_found",
46
+    /**
47
+     * An error which indicates that some of requested constraints in
48
+     * getUserMedia call were not satisfied.
49
+     */
50
+    CONSTRAINT_FAILED: "gum.constraint_failed",
51
+    /**
52
+     * An error which indicates that track has been already disposed and cannot
53
+     * be longer used.
54
+     */
47 55
     TRACK_IS_DISPOSED: "track.track_is_disposed",
56
+    /**
57
+     * An error which indicates that track is currently in progress of muting or
58
+     * unmuting itself.
59
+     */
48 60
     TRACK_MUTE_UNMUTE_IN_PROGRESS: "track.mute_unmute_inprogress"
49 61
 };

+ 3
- 1
modules/RTC/JitsiLocalTrack.js 查看文件

@@ -4,6 +4,7 @@ var JitsiTrack = require("./JitsiTrack");
4 4
 var RTCBrowserType = require("./RTCBrowserType");
5 5
 var JitsiTrackEvents = require('../../JitsiTrackEvents');
6 6
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
7
+var JitsiTrackError = require("../../JitsiTrackError");
7 8
 var RTCEvents = require("../../service/RTC/RTCEvents");
8 9
 var RTCUtils = require("./RTCUtils");
9 10
 var VideoType = require('../../service/RTC/VideoType');
@@ -141,7 +142,8 @@ function createMuteUnmutePromise(track, mute)
141 142
     return new Promise(function (resolve, reject) {
142 143
 
143 144
         if(this.inMuteOrUnmuteProgress) {
144
-            reject(new Error(JitsiTrackErrors.TRACK_MUTE_UNMUTE_IN_PROGRESS));
145
+            reject(new JitsiTrackError(
146
+                JitsiTrackErrors.TRACK_MUTE_UNMUTE_IN_PROGRESS));
145 147
             return;
146 148
         }
147 149
         this.inMuteOrUnmuteProgress = true;

+ 17
- 14
modules/RTC/RTCUtils.js 查看文件

@@ -14,6 +14,7 @@ var SDPUtil = require("../xmpp/SDPUtil");
14 14
 var EventEmitter = require("events");
15 15
 var screenObtainer = require("./ScreenObtainer");
16 16
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
17
+var JitsiTrackError = require("../../JitsiTrackError");
17 18
 var MediaType = require("../../service/RTC/MediaType");
18 19
 var VideoType = require("../../service/RTC/VideoType");
19 20
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
@@ -473,7 +474,8 @@ function obtainDevices(options) {
473 474
             });
474 475
             logger.error(
475 476
                 "failed to obtain " + device + " stream - stop", error);
476
-            options.errorCallback(JitsiTrackErrors.parseError(error, devices));
477
+
478
+            options.errorCallback(error);
477 479
         });
478 480
 }
479 481
 
@@ -802,8 +804,9 @@ var RTCUtils = {
802 804
                     setAvailableDevices(um, false);
803 805
                     logger.warn('Failed to get access to local media. Error ',
804 806
                         error, constraints);
807
+
805 808
                     if (failure_callback) {
806
-                        failure_callback(error, resolution);
809
+                        failure_callback(new JitsiTrackError(error, constraints));
807 810
                     }
808 811
                 });
809 812
         } catch (e) {
@@ -886,10 +889,13 @@ var RTCUtils = {
886 889
                                 !stream.getVideoTracks().length))
887 890
                             {
888 891
                                 self.stopMediaStream(stream);
889
-                                reject(JitsiTrackErrors.parseError(
890
-                                    new Error("Unable to get the audio and " +
891
-                                        "video tracks."),
892
-                                    options.devices));
892
+
893
+                                reject(
894
+                                    new JitsiTrackError({
895
+                                        name: JitsiTrackErrors.NOT_FOUND
896
+                                    }),
897
+                                    getConstraints(options.devices, options)
898
+                                );
893 899
                                 return;
894 900
                             }
895 901
                             if(hasDesktop) {
@@ -899,17 +905,16 @@ var RTCUtils = {
899 905
                                             desktopStream: desktopStream});
900 906
                                     }, function (error) {
901 907
                                         self.stopMediaStream(stream);
902
-                                        reject(
903
-                                            JitsiTrackErrors.parseError(error,
904
-                                                options.devices));
908
+
909
+                                        reject(error);
910
+
905 911
                                     });
906 912
                             } else {
907 913
                                 successCallback({audioVideo: stream});
908 914
                             }
909 915
                         },
910 916
                         function (error) {
911
-                            reject(JitsiTrackErrors.parseError(error,
912
-                                options.devices));
917
+                            reject(error);
913 918
                         },
914 919
                         options);
915 920
                 } else if (hasDesktop) {
@@ -917,9 +922,7 @@ var RTCUtils = {
917 922
                         function (stream) {
918 923
                             successCallback({desktopStream: stream});
919 924
                         }, function (error) {
920
-                            reject(
921
-                                JitsiTrackErrors.parseError(error,
922
-                                    ["desktop"]));
925
+                            reject(error);
923 926
                         });
924 927
                 }
925 928
             }

+ 39
- 26
modules/RTC/ScreenObtainer.js 查看文件

@@ -4,6 +4,7 @@ var logger = require("jitsi-meet-logger").getLogger(__filename);
4 4
 var RTCBrowserType = require("./RTCBrowserType");
5 5
 var AdapterJS = require("./adapter.screenshare");
6 6
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
7
+var JitsiTrackError = require("../../JitsiTrackError");
7 8
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
8 9
 
9 10
 /**
@@ -66,7 +67,11 @@ var ScreenObtainer = {
66 67
 
67 68
         if (RTCBrowserType.isNWJS()) {
68 69
             obtainDesktopStream = function (onSuccess, onFailure) {
69
-                window.JitsiMeetNW.obtainDesktopStream (onSuccess, onFailure);
70
+                window.JitsiMeetNW.obtainDesktopStream (
71
+                    onSuccess, function (error, constraints) {
72
+                        onFailure && onFailure(
73
+                            new JitsiTrackError(error, constraints));
74
+                    });
70 75
             };
71 76
         } else if (RTCBrowserType.isTemasysPluginUsed()) {
72 77
             if (!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature) {
@@ -174,10 +179,8 @@ var ScreenObtainer = {
174 179
 
175 180
         // Make sure desktopsharing knows that we failed, so that it doesn't get
176 181
         // stuck in 'switching' mode.
177
-        errorCallback({
178
-            type: "jitsiError",
179
-            errorObject: JitsiTrackErrors.FIREFOX_EXTENSION_NEEDED
180
-        });
182
+        errorCallback(
183
+            new JitsiTrackError(JitsiTrackErrors.FIREFOX_EXTENSION_NEEDED));
181 184
     },
182 185
     /**
183 186
      * Asks Chrome extension to call chooseDesktopMedia and gets chrome
@@ -209,23 +212,28 @@ var ScreenObtainer = {
209 212
                         }, 500);
210 213
                     },
211 214
                     function (arg) {
212
-                        logger.log("Failed to install the extension from:"
213
-                            + getWebStoreInstallUrl(self.options), arg);
214
-                        failCallback({
215
-                            type: "jitsiError",
216
-                            errorObject: JitsiTrackErrors
217
-                                .CHROME_EXTENSION_INSTALLATION_ERROR
218
-                        });
215
+                        var msg = "Failed to install the extension from "
216
+                            + getWebStoreInstallUrl(self.options);
217
+
218
+                        logger.log(msg, arg);
219
+
220
+                        failCallback(new JitsiTrackError(
221
+                            JitsiTrackErrors
222
+                                .CHROME_EXTENSION_INSTALLATION_ERROR,
223
+                            msg
224
+                        ));
219 225
                     }
220 226
                 );
221 227
             } catch(e) {
222
-                logger.log("Failed to install the extension from:"
223
-                    + self.getWebStoreInstallUrl(this.options), e);
224
-                failCallback({
225
-                    type: "jitsiError",
226
-                    errorObject:
227
-                        JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR
228
-                });
228
+                var msg = "Failed to install the extension from "
229
+                    + getWebStoreInstallUrl(self.options);
230
+
231
+                logger.log(msg, e);
232
+
233
+                failCallback(new JitsiTrackError(
234
+                    JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR,
235
+                    msg
236
+                ));
229 237
             }
230 238
         }
231 239
     }
@@ -348,7 +356,13 @@ function doGetStreamFromExtension(options, streamCallback, failCallback) {
348 356
         },
349 357
         function (response) {
350 358
             if (!response) {
351
-                failCallback(chrome.runtime.lastError);
359
+                // possibly re-wraping error message to make code consistent
360
+                var lastError = chrome.runtime.lastError;
361
+                failCallback(lastError instanceof Error
362
+                    ? lastError
363
+                    : new JitsiTrackError(
364
+                        JitsiTrackErrors.CHROME_EXTENSION_GENERIC_ERROR,
365
+                        lastError));
352 366
                 return;
353 367
             }
354 368
             logger.log("Response from extension: ", response);
@@ -366,15 +380,14 @@ function doGetStreamFromExtension(options, streamCallback, failCallback) {
366 380
                 // then the callback is called with an empty streamId.
367 381
                 if(response.streamId === "")
368 382
                 {
369
-                    failCallback({
370
-                        type: "jitsiError",
371
-                        errorObject:
372
-                            JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED
373
-                    });
383
+                    failCallback(new JitsiTrackError(
384
+                        JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED));
374 385
                     return;
375 386
                 }
376 387
 
377
-                failCallback("Extension failed to get the stream");
388
+                failCallback(new JitsiTrackError(
389
+                    JitsiTrackErrors.CHROME_EXTENSION_GENERIC_ERROR,
390
+                    response.error));
378 391
             }
379 392
         }
380 393
     );

正在加载...
取消
保存