Przeglądaj źródła

Merge pull request #124 from jitsi/tsareg-handle_create_local_tracks_errors_better

Tsareg handle create local tracks errors better
dev1
lyubomir 9 lat temu
rodzic
commit
f1dbc9f4e0

+ 8
- 9
JitsiConference.js Wyświetl plik

13
 var JitsiDTMFManager = require('./modules/DTMF/JitsiDTMFManager');
13
 var JitsiDTMFManager = require('./modules/DTMF/JitsiDTMFManager');
14
 var JitsiTrackEvents = require("./JitsiTrackEvents");
14
 var JitsiTrackEvents = require("./JitsiTrackEvents");
15
 var JitsiTrackErrors = require("./JitsiTrackErrors");
15
 var JitsiTrackErrors = require("./JitsiTrackErrors");
16
+var JitsiTrackError = require("./JitsiTrackError");
16
 var Settings = require("./modules/settings/Settings");
17
 var Settings = require("./modules/settings/Settings");
17
 var ComponentsVersions = require("./modules/version/ComponentsVersions");
18
 var ComponentsVersions = require("./modules/version/ComponentsVersions");
18
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
19
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
320
 JitsiConference.prototype.addTrack = function (track) {
321
 JitsiConference.prototype.addTrack = function (track) {
321
     if(track.disposed)
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
     if (track.isVideoTrack() && this.rtc.getLocalVideoTrack()) {
327
     if (track.isVideoTrack() && this.rtc.getLocalVideoTrack()) {
943
                         "Failed to accept incoming Jingle session", error);
944
                         "Failed to accept incoming Jingle session", error);
944
                 }
945
                 }
945
             );
946
             );
946
-            conference.statistics.startRemoteStats(
947
-                    jingleSession.peerconnection);
947
+            // Start callstats as soon as peerconnection is initialized,
948
+            // do not wait for XMPPEvents.PEERCONNECTION_READY, as it may never
949
+            // happen in case if user doesn't have or denied permission to
950
+            // both camera and microphone.
951
+            conference.statistics.startCallStats(jingleSession, conference.settings);
952
+            conference.statistics.startRemoteStats(jingleSession.peerconnection);
948
         } else {
953
         } else {
949
             // Error cause this should never happen unless something is wrong!
954
             // Error cause this should never happen unless something is wrong!
950
             var errmsg
955
             var errmsg
1295
                 conference.statistics.dispose();
1300
                 conference.statistics.dispose();
1296
             });
1301
             });
1297
 
1302
 
1298
-        conference.room.addListener(XMPPEvents.PEERCONNECTION_READY,
1299
-            function (session) {
1300
-                conference.statistics.startCallStats(
1301
-                    session, conference.settings);
1302
-            });
1303
-
1304
         conference.room.addListener(XMPPEvents.CONNECTION_ICE_FAILED,
1303
         conference.room.addListener(XMPPEvents.CONNECTION_ICE_FAILED,
1305
             function (pc) {
1304
             function (pc) {
1306
                 conference.statistics.sendIceConnectionFailedEvent(pc);
1305
                 conference.statistics.sendIceConnectionFailedEvent(pc);

+ 19
- 5
JitsiMeetJS.js Wyświetl plik

8
 var JitsiConferenceErrors = require("./JitsiConferenceErrors");
8
 var JitsiConferenceErrors = require("./JitsiConferenceErrors");
9
 var JitsiTrackEvents = require("./JitsiTrackEvents");
9
 var JitsiTrackEvents = require("./JitsiTrackEvents");
10
 var JitsiTrackErrors = require("./JitsiTrackErrors");
10
 var JitsiTrackErrors = require("./JitsiTrackErrors");
11
+var JitsiTrackError = require("./JitsiTrackError");
11
 var JitsiRecorderErrors = require("./JitsiRecorderErrors");
12
 var JitsiRecorderErrors = require("./JitsiRecorderErrors");
12
 var Logger = require("jitsi-meet-logger");
13
 var Logger = require("jitsi-meet-logger");
13
 var MediaType = require("./service/RTC/MediaType");
14
 var MediaType = require("./service/RTC/MediaType");
116
                 this._gumFailedHandler.forEach(function (handler) {
117
                 this._gumFailedHandler.forEach(function (handler) {
117
                     handler(error);
118
                     handler(error);
118
                 });
119
                 });
119
-                if(!this._gumFailedHandler.length)
120
+
121
+                if(!this._gumFailedHandler.length) {
120
                     Statistics.sendGetUserMediaFailed(error);
122
                     Statistics.sendGetUserMediaFailed(error);
121
-                if(error === JitsiTrackErrors.UNSUPPORTED_RESOLUTION) {
122
-                    var oldResolution = options.resolution || '360';
123
-                    var newResolution = getLowerResolution(oldResolution);
124
-                    if(newResolution === null)
123
+                }
124
+
125
+                if(error.name === JitsiTrackErrors.UNSUPPORTED_RESOLUTION) {
126
+                    var oldResolution = options.resolution || '360',
127
+                        newResolution = getLowerResolution(oldResolution);
128
+
129
+                    if (newResolution === null) {
125
                         return Promise.reject(error);
130
                         return Promise.reject(error);
131
+                    }
132
+
126
                     options.resolution = newResolution;
133
                     options.resolution = newResolution;
134
+
127
                     logger.debug("Retry createLocalTracks with resolution",
135
                     logger.debug("Retry createLocalTracks with resolution",
128
                                 newResolution);
136
                                 newResolution);
137
+
129
                     return LibJitsiMeet.createLocalTracks(options);
138
                     return LibJitsiMeet.createLocalTracks(options);
130
                 }
139
                 }
140
+
131
                 return Promise.reject(error);
141
                 return Promise.reject(error);
132
             }.bind(this));
142
             }.bind(this));
133
     },
143
     },
210
 // JitsiConnection.
220
 // JitsiConnection.
211
 LibJitsiMeet.JitsiConnection = JitsiConnection.bind(null, LibJitsiMeet);
221
 LibJitsiMeet.JitsiConnection = JitsiConnection.bind(null, LibJitsiMeet);
212
 
222
 
223
+// expose JitsiTrackError this way to give library consumers to do checks like
224
+// if (error instanceof JitsiMeetJS.JitsiTrackError) { }
225
+LibJitsiMeet.JitsiTrackError = JitsiTrackError;
226
+
213
 //Setups the promise object.
227
 //Setups the promise object.
214
 window.Promise = window.Promise || require("es6-promise").Promise;
228
 window.Promise = window.Promise || require("es6-promise").Promise;
215
 
229
 

+ 142
- 0
JitsiTrackError.js Wyświetl plik

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
+ * @param {('audio'|'video'|'desktop'|'screen')[]} (devices) - list of
37
+ *      getUserMedia requested devices
38
+ */
39
+function JitsiTrackError(error, options, devices) {
40
+    if (typeof error === "object" && typeof error.name !== "undefined") {
41
+        /**
42
+         * Additional information about original getUserMedia error
43
+         * and constraints.
44
+         * @type {{
45
+         *          error: Object,
46
+         *          constraints: Object,
47
+         *          devices: Array.<'audio'|'video'|'desktop'|'screen'>
48
+         *      }}
49
+         */
50
+        this.gum = {
51
+            error: error,
52
+            constraints: options,
53
+            devices: devices && Array.isArray(devices)
54
+                ? devices.slice(0)
55
+                : undefined
56
+        };
57
+
58
+        switch (error.name) {
59
+            case "PermissionDeniedError":
60
+            case "SecurityError":
61
+                this.name = JitsiTrackErrors.PERMISSION_DENIED;
62
+                this.message = TRACK_ERROR_TO_MESSAGE_MAP[
63
+                        JitsiTrackErrors.PERMISSION_DENIED]
64
+                        + (this.gum.devices || []).join(", ");
65
+                break;
66
+            case "NotFoundError":
67
+                this.name = JitsiTrackErrors.NOT_FOUND;
68
+                this.message = TRACK_ERROR_TO_MESSAGE_MAP[
69
+                        JitsiTrackErrors.NOT_FOUND]
70
+                        + (this.gum.devices || []).join(", ");
71
+                break;
72
+            case "ConstraintNotSatisfiedError":
73
+            case "OverconstrainedError":
74
+                var constraintName = error.constraintName;
75
+
76
+                if (options && options.video
77
+                    && (devices || []).indexOf('video') > -1
78
+                    &&
79
+                    (constraintName === "minWidth" ||
80
+                        constraintName === "maxWidth" ||
81
+                        constraintName === "minHeight" ||
82
+                        constraintName === "maxHeight" ||
83
+                        constraintName === "width" ||
84
+                        constraintName === "height")) {
85
+                    this.name = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;
86
+                    this.message = TRACK_ERROR_TO_MESSAGE_MAP[
87
+                            JitsiTrackErrors.UNSUPPORTED_RESOLUTION] +
88
+                        getResolutionFromFailedConstraint(constraintName,
89
+                            options);
90
+                } else {
91
+                    this.name = JitsiTrackErrors.CONSTRAINT_FAILED;
92
+                    this.message = TRACK_ERROR_TO_MESSAGE_MAP[
93
+                            JitsiTrackErrors.CONSTRAINT_FAILED] +
94
+                        error.constraintName;
95
+                }
96
+                break;
97
+            default:
98
+                this.name = JitsiTrackErrors.GENERAL;
99
+                this.message = error.message ||
100
+                    TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.GENERAL];
101
+                break;
102
+        }
103
+    } else if (typeof error === "string") {
104
+        if (TRACK_ERROR_TO_MESSAGE_MAP[error]) {
105
+            this.name = error;
106
+            this.message = options || TRACK_ERROR_TO_MESSAGE_MAP[error];
107
+        } else {
108
+            // this is some generic error that do not fit any of our pre-defined
109
+            // errors, so don't give it any specific name, just store message
110
+            this.message = error;
111
+        }
112
+    } else {
113
+        throw new Error("Invalid arguments");
114
+    }
115
+
116
+    this.stack = error.stack || (new Error()).stack;
117
+}
118
+
119
+JitsiTrackError.prototype = Object.create(Error.prototype);
120
+JitsiTrackError.prototype.constructor = JitsiTrackError;
121
+
122
+/**
123
+ * Gets failed resolution constraint from corresponding object.
124
+ * @param {string} failedConstraintName
125
+ * @param {Object} constraints
126
+ * @returns {string|number}
127
+ */
128
+function getResolutionFromFailedConstraint(failedConstraintName, constraints) {
129
+    if (constraints && constraints.video && constraints.video.mandatory) {
130
+        if (failedConstraintName === "width") {
131
+            return constraints.video.mandatory.minWidth;
132
+        } else if (failedConstraintName === "height") {
133
+            return constraints.video.mandatory.minHeight;
134
+        } else {
135
+            return constraints.video.mandatory[failedConstraintName] || "";
136
+        }
137
+    }
138
+
139
+    return "";
140
+}
141
+
142
+module.exports = JitsiTrackError;

+ 46
- 34
JitsiTrackErrors.js Wyświetl plik

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
 module.exports = {
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
     UNSUPPORTED_RESOLUTION: "gum.unsupported_resolution",
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
      * needed to proceed with screen sharing, and that it is not installed.
13
      * needed to proceed with screen sharing, and that it is not installed.
40
      */
14
      */
41
     FIREFOX_EXTENSION_NEEDED: "gum.firefox_extension_needed",
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
     CHROME_EXTENSION_INSTALLATION_ERROR:
20
     CHROME_EXTENSION_INSTALLATION_ERROR:
43
         "gum.chrome_extension_installation_error",
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
     CHROME_EXTENSION_USER_CANCELED:
26
     CHROME_EXTENSION_USER_CANCELED:
45
         "gum.chrome_extension_user_canceled",
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
     GENERAL: "gum.general",
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
     TRACK_IS_DISPOSED: "track.track_is_disposed",
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
     TRACK_MUTE_UNMUTE_IN_PROGRESS: "track.mute_unmute_inprogress"
60
     TRACK_MUTE_UNMUTE_IN_PROGRESS: "track.mute_unmute_inprogress"
49
 };
61
 };

+ 25
- 2
doc/API.md Wyświetl plik

27
 
27
 
28
 * JitsiTrack
28
 * JitsiTrack
29
 
29
 
30
+* JitsiTrackError
31
+
30
 Usage
32
 Usage
31
 ======
33
 ======
32
 JitsiMeetJS
34
 JitsiMeetJS
56
 JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
58
 JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
57
 ```
59
 ```
58
 
60
 
59
-* ```JitsiMeetJS.createLocalTracks(options)``` - Creates the media tracks and returns them trough ```Promise``` object.
61
+* ```JitsiMeetJS.createLocalTracks(options)``` - Creates the media tracks and returns them trough ```Promise``` object. If rejected, passes ```JitsiTrackError``` instance to catch block.
60
     - options - JS object with configuration options for the local media tracks. You can change the following properties there:
62
     - options - JS object with configuration options for the local media tracks. You can change the following properties there:
61
         1. devices - array with the devices - "desktop", "video" and "audio" that will be passed to GUM. If that property is not set GUM will try to get all available devices.
63
         1. devices - array with the devices - "desktop", "video" and "audio" that will be passed to GUM. If that property is not set GUM will try to get all available devices.
62
         2. resolution - the prefered resolution for the local video.
64
         2. resolution - the prefered resolution for the local video.
132
 
134
 
133
 
135
 
134
 * ```JitsiMeetJS.errors``` - JS object that contains all errors used by the API. You can use that object to check the reported errors from the API
136
 * ```JitsiMeetJS.errors``` - JS object that contains all errors used by the API. You can use that object to check the reported errors from the API
135
-    We have two error types - connection and conference. You can access the events with the following code ```JitsiMeetJS.errors.<error_type>.<error_name>```.
137
+    We have three error types - connection, conference and track. You can access the events with the following code ```JitsiMeetJS.errors.<error_type>.<error_name>```.
136
     For example if you want to use the conference event that is fired when somebody leave conference you can use the following code - ```JitsiMeetJS.errors.conference.PASSWORD_REQUIRED```.
138
     For example if you want to use the conference event that is fired when somebody leave conference you can use the following code - ```JitsiMeetJS.errors.conference.PASSWORD_REQUIRED```.
137
     We support the following errors:
139
     We support the following errors:
138
     1. conference
140
     1. conference
154
         - PASSWORD_REQUIRED - passed when the connection to the server failed. You should try to authenticate with password.
156
         - PASSWORD_REQUIRED - passed when the connection to the server failed. You should try to authenticate with password.
155
         - CONNECTION_ERROR - indicates connection failures.
157
         - CONNECTION_ERROR - indicates connection failures.
156
         - OTHER_ERROR - all other errors
158
         - OTHER_ERROR - all other errors
159
+    3. track
160
+        - GENERAL - generic getUserMedia-related error.
161
+        - UNSUPPORTED_RESOLUTION - getUserMedia-related error, indicates that requested video resolution is not supported by camera.
162
+        - PERMISSION_DENIED - getUserMedia-related error, indicates that user denied permission to share requested device.
163
+        - NOT_FOUND - getUserMedia-related error, indicates that requested device was not found.
164
+        - CONSTRAINT_FAILED - getUserMedia-related error, indicates that some of requested constraints in getUserMedia call were not satisfied.
165
+        - TRACK_IS_DISPOSED - an error which indicates that track has been already disposed and cannot be longer used.
166
+        - TRACK_MUTE_UNMUTE_IN_PROGRESS - an error which indicates that track is currently in progress of muting or unmuting itself.
167
+        - CHROME_EXTENSION_GENERIC_ERROR - generic error for jidesha extension for Chrome.
168
+        - CHROME_EXTENSION_USER_CANCELED - an error which indicates that user canceled screen sharing window selection dialog in jidesha extension for Chrome.
169
+        - CHROME_EXTENSION_INSTALLATION_ERROR - an error which indicates that the jidesha extension for Chrome is failed to install.
170
+        - FIREFOX_EXTENSION_NEEDED - An error which indicates that the jidesha extension for Firefox is needed to proceed with screen sharing, and that it is not installed.
171
+        
157
 * ```JitsiMeetJS.logLevels``` - object with the log levels:
172
 * ```JitsiMeetJS.logLevels``` - object with the log levels:
158
     1. TRACE
173
     1. TRACE
159
     2. DEBUG
174
     2. DEBUG
359
 
374
 
360
 12. isEnded() - returns true if track is ended
375
 12. isEnded() - returns true if track is ended
361
 
376
 
377
+JitsiTrackError
378
+======
379
+The object represents error that happened to a JitsiTrack. Is inherited from JavaScript base ```Error``` object, 
380
+so ```"name"```, ```"message"``` and ```"stack"``` properties are available. For GUM-related errors,
381
+exposes additional ```"gum"``` property, which is an object with following properties:
382
+ - error - original GUM error
383
+ - constraints - GUM constraints object used for the call
384
+ - devices - array of devices requested in GUM call (possible values - "audio", "video", "screen", "desktop")
362
 
385
 
363
 Getting Started
386
 Getting Started
364
 ==============
387
 ==============

+ 3
- 1
modules/RTC/JitsiLocalTrack.js Wyświetl plik

4
 var RTCBrowserType = require("./RTCBrowserType");
4
 var RTCBrowserType = require("./RTCBrowserType");
5
 var JitsiTrackEvents = require('../../JitsiTrackEvents');
5
 var JitsiTrackEvents = require('../../JitsiTrackEvents');
6
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
6
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
7
+var JitsiTrackError = require("../../JitsiTrackError");
7
 var RTCEvents = require("../../service/RTC/RTCEvents");
8
 var RTCEvents = require("../../service/RTC/RTCEvents");
8
 var RTCUtils = require("./RTCUtils");
9
 var RTCUtils = require("./RTCUtils");
9
 var VideoType = require('../../service/RTC/VideoType');
10
 var VideoType = require('../../service/RTC/VideoType');
141
     return new Promise(function (resolve, reject) {
142
     return new Promise(function (resolve, reject) {
142
 
143
 
143
         if(this.inMuteOrUnmuteProgress) {
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
             return;
147
             return;
146
         }
148
         }
147
         this.inMuteOrUnmuteProgress = true;
149
         this.inMuteOrUnmuteProgress = true;

+ 40
- 19
modules/RTC/RTCUtils.js Wyświetl plik

14
 var EventEmitter = require("events");
14
 var EventEmitter = require("events");
15
 var screenObtainer = require("./ScreenObtainer");
15
 var screenObtainer = require("./ScreenObtainer");
16
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
16
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
17
+var JitsiTrackError = require("../../JitsiTrackError");
17
 var MediaType = require("../../service/RTC/MediaType");
18
 var MediaType = require("../../service/RTC/MediaType");
18
 var VideoType = require("../../service/RTC/VideoType");
19
 var VideoType = require("../../service/RTC/VideoType");
19
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
20
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
473
             });
474
             });
474
             logger.error(
475
             logger.error(
475
                 "failed to obtain " + device + " stream - stop", error);
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
                     setAvailableDevices(um, false);
804
                     setAvailableDevices(um, false);
803
                     logger.warn('Failed to get access to local media. Error ',
805
                     logger.warn('Failed to get access to local media. Error ',
804
                         error, constraints);
806
                         error, constraints);
807
+
805
                     if (failure_callback) {
808
                     if (failure_callback) {
806
-                        failure_callback(error, resolution);
809
+                        failure_callback(
810
+                            new JitsiTrackError(error, constraints, um));
807
                     }
811
                     }
808
                 });
812
                 });
809
         } catch (e) {
813
         } catch (e) {
810
             logger.error('GUM failed: ', e);
814
             logger.error('GUM failed: ', e);
815
+
811
             if (failure_callback) {
816
             if (failure_callback) {
812
-                failure_callback(e);
817
+                failure_callback(new JitsiTrackError(e, constraints, um));
813
             }
818
             }
814
         }
819
         }
815
     },
820
     },
880
                     this.getUserMediaWithConstraints(
885
                     this.getUserMediaWithConstraints(
881
                         options.devices,
886
                         options.devices,
882
                         function (stream) {
887
                         function (stream) {
883
-                            if((options.devices.indexOf("audio") !== -1 &&
884
-                                !stream.getAudioTracks().length) ||
885
-                                (options.devices.indexOf("video") !== -1 &&
886
-                                !stream.getVideoTracks().length))
888
+                            var audioDeviceRequested = options.devices.indexOf("audio") !== -1;
889
+                            var videoDeviceRequested = options.devices.indexOf("video") !== -1;
890
+                            var audioTracksReceived = !!stream.getAudioTracks().length;
891
+                            var videoTracksReceived = !!stream.getVideoTracks().length;
892
+
893
+                            if((audioDeviceRequested && !audioTracksReceived) ||
894
+                                (videoDeviceRequested && !videoTracksReceived))
887
                             {
895
                             {
888
                                 self.stopMediaStream(stream);
896
                                 self.stopMediaStream(stream);
889
-                                reject(JitsiTrackErrors.parseError(
890
-                                    new Error("Unable to get the audio and " +
891
-                                        "video tracks."),
892
-                                    options.devices));
897
+
898
+                                // We are getting here in case if we requested
899
+                                // 'audio' or 'video' devices or both, but
900
+                                // didn't get corresponding MediaStreamTrack in
901
+                                // response stream. We don't know the reason why
902
+                                // this happened, so reject with general error.
903
+                                var devices = [];
904
+
905
+                                if (audioDeviceRequested && !audioTracksReceived) {
906
+                                    devices.push("audio");
907
+                                }
908
+
909
+                                if (videoDeviceRequested && !videoTracksReceived) {
910
+                                    devices.push("video");
911
+                                }
912
+
913
+                                reject(new JitsiTrackError(
914
+                                    { name: "UnknownError" },
915
+                                    getConstraints(options.devices, options),
916
+                                    devices)
917
+                                );
893
                                 return;
918
                                 return;
894
                             }
919
                             }
895
                             if(hasDesktop) {
920
                             if(hasDesktop) {
899
                                             desktopStream: desktopStream});
924
                                             desktopStream: desktopStream});
900
                                     }, function (error) {
925
                                     }, function (error) {
901
                                         self.stopMediaStream(stream);
926
                                         self.stopMediaStream(stream);
902
-                                        reject(
903
-                                            JitsiTrackErrors.parseError(error,
904
-                                                options.devices));
927
+
928
+                                        reject(error);
905
                                     });
929
                                     });
906
                             } else {
930
                             } else {
907
                                 successCallback({audioVideo: stream});
931
                                 successCallback({audioVideo: stream});
908
                             }
932
                             }
909
                         },
933
                         },
910
                         function (error) {
934
                         function (error) {
911
-                            reject(JitsiTrackErrors.parseError(error,
912
-                                options.devices));
935
+                            reject(error);
913
                         },
936
                         },
914
                         options);
937
                         options);
915
                 } else if (hasDesktop) {
938
                 } else if (hasDesktop) {
917
                         function (stream) {
940
                         function (stream) {
918
                             successCallback({desktopStream: stream});
941
                             successCallback({desktopStream: stream});
919
                         }, function (error) {
942
                         }, function (error) {
920
-                            reject(
921
-                                JitsiTrackErrors.parseError(error,
922
-                                    ["desktop"]));
943
+                            reject(error);
923
                         });
944
                         });
924
                 }
945
                 }
925
             }
946
             }

+ 34
- 28
modules/RTC/ScreenObtainer.js Wyświetl plik

4
 var RTCBrowserType = require("./RTCBrowserType");
4
 var RTCBrowserType = require("./RTCBrowserType");
5
 var AdapterJS = require("./adapter.screenshare");
5
 var AdapterJS = require("./adapter.screenshare");
6
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
6
 var JitsiTrackErrors = require("../../JitsiTrackErrors");
7
+var JitsiTrackError = require("../../JitsiTrackError");
7
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
8
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
8
 
9
 
9
 /**
10
 /**
66
 
67
 
67
         if (RTCBrowserType.isNWJS()) {
68
         if (RTCBrowserType.isNWJS()) {
68
             obtainDesktopStream = function (onSuccess, onFailure) {
69
             obtainDesktopStream = function (onSuccess, onFailure) {
69
-                window.JitsiMeetNW.obtainDesktopStream (onSuccess, onFailure);
70
+                window.JitsiMeetNW.obtainDesktopStream (
71
+                    onSuccess, function (error, constraints) {
72
+                        onFailure && onFailure(new JitsiTrackError(
73
+                            error, constraints, ["desktop"]));
74
+                    });
70
             };
75
             };
71
         } else if (RTCBrowserType.isTemasysPluginUsed()) {
76
         } else if (RTCBrowserType.isTemasysPluginUsed()) {
72
             if (!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature) {
77
             if (!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature) {
174
 
179
 
175
         // Make sure desktopsharing knows that we failed, so that it doesn't get
180
         // Make sure desktopsharing knows that we failed, so that it doesn't get
176
         // stuck in 'switching' mode.
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
      * Asks Chrome extension to call chooseDesktopMedia and gets chrome
186
      * Asks Chrome extension to call chooseDesktopMedia and gets chrome
208
                                 streamCallback, failCallback);
211
                                 streamCallback, failCallback);
209
                         }, 500);
212
                         }, 500);
210
                     },
213
                     },
211
-                    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
-                        });
219
-                    }
214
+                    handleExtensionInstallationError
220
                 );
215
                 );
221
             } catch(e) {
216
             } 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
-                });
217
+                handleExtensionInstallationError(e);
229
             }
218
             }
230
         }
219
         }
220
+
221
+        function handleExtensionInstallationError(e) {
222
+            var msg = "Failed to install the extension from "
223
+                + getWebStoreInstallUrl(self.options);
224
+
225
+            logger.log(msg, e);
226
+
227
+            failCallback(new JitsiTrackError(
228
+                JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR,
229
+                msg
230
+            ));
231
+        }
231
     }
232
     }
232
 };
233
 };
233
 
234
 
348
         },
349
         },
349
         function (response) {
350
         function (response) {
350
             if (!response) {
351
             if (!response) {
351
-                failCallback(chrome.runtime.lastError);
352
+                // possibly re-wraping error message to make code consistent
353
+                var lastError = chrome.runtime.lastError;
354
+                failCallback(lastError instanceof Error
355
+                    ? lastError
356
+                    : new JitsiTrackError(
357
+                        JitsiTrackErrors.CHROME_EXTENSION_GENERIC_ERROR,
358
+                        lastError));
352
                 return;
359
                 return;
353
             }
360
             }
354
             logger.log("Response from extension: ", response);
361
             logger.log("Response from extension: ", response);
366
                 // then the callback is called with an empty streamId.
373
                 // then the callback is called with an empty streamId.
367
                 if(response.streamId === "")
374
                 if(response.streamId === "")
368
                 {
375
                 {
369
-                    failCallback({
370
-                        type: "jitsiError",
371
-                        errorObject:
372
-                            JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED
373
-                    });
376
+                    failCallback(new JitsiTrackError(
377
+                        JitsiTrackErrors.CHROME_EXTENSION_USER_CANCELED));
374
                     return;
378
                     return;
375
                 }
379
                 }
376
 
380
 
377
-                failCallback("Extension failed to get the stream");
381
+                failCallback(new JitsiTrackError(
382
+                    JitsiTrackErrors.CHROME_EXTENSION_GENERIC_ERROR,
383
+                    response.error));
378
             }
384
             }
379
         }
385
         }
380
     );
386
     );

+ 42
- 3
modules/statistics/statistics.js Wyświetl plik

6
 var StatisticsEvents = require("../../service/statistics/Events");
6
 var StatisticsEvents = require("../../service/statistics/Events");
7
 var CallStats = require("./CallStats");
7
 var CallStats = require("./CallStats");
8
 var ScriptUtil = require('../util/ScriptUtil');
8
 var ScriptUtil = require('../util/ScriptUtil');
9
+var JitsiTrackError = require("../../JitsiTrackError");
9
 
10
 
10
 // Since callstats.io is a third party, we cannot guarantee the quality of their
11
 // Since callstats.io is a third party, we cannot guarantee the quality of their
11
 // service. More specifically, their server may take noticeably long time to
12
 // service. More specifically, their server may take noticeably long time to
28
  */
29
  */
29
 var LOG_INTERVAL = 60000;
30
 var LOG_INTERVAL = 60000;
30
 
31
 
32
+/**
33
+ * callstats strips any additional fields from Error except for "name", "stack",
34
+ * "message" and "constraintName". So we need to bundle additional information
35
+ * from JitsiTrackError into error passed to callstats to preserve valuable
36
+ * information about error.
37
+ * @param {JitsiTrackError} error
38
+ */
39
+function formatJitsiTrackErrorForCallStats(error) {
40
+    var err = new Error();
41
+
42
+    // Just copy original stack from error
43
+    err.stack = error.stack;
44
+
45
+    // Combine name from error's name plus (possibly) name of original GUM error
46
+    err.name = (error.name || "Unknown error") + (error.gum && error.gum.error
47
+        && error.gum.error.name ? " - " + error.gum.error.name : "");
48
+
49
+    // Put all constraints into this field. For constraint failed errors we will
50
+    // still know which exactly constraint failed as it will be a part of
51
+    // message.
52
+    err.constraintName = error.gum && error.gum.constraints
53
+        ? JSON.stringify(error.gum.constraints) : "";
54
+
55
+    // Just copy error's message.
56
+    err.message = error.message;
57
+
58
+    return err;
59
+}
60
+
31
 function Statistics(xmpp, options) {
61
 function Statistics(xmpp, options) {
32
     this.rtpStats = null;
62
     this.rtpStats = null;
33
     this.eventEmitter = new EventEmitter();
63
     this.eventEmitter = new EventEmitter();
236
  * @param {Error} e error to send
266
  * @param {Error} e error to send
237
  */
267
  */
238
 Statistics.prototype.sendGetUserMediaFailed = function (e) {
268
 Statistics.prototype.sendGetUserMediaFailed = function (e) {
239
-    if(this.callstats)
240
-        CallStats.sendGetUserMediaFailed(e, this.callstats);
269
+    if(this.callstats) {
270
+        CallStats.sendGetUserMediaFailed(
271
+            e instanceof JitsiTrackError
272
+                ? formatJitsiTrackErrorForCallStats(e)
273
+                : e,
274
+            this.callstats);
275
+    }
241
 };
276
 };
242
 
277
 
243
 /**
278
 /**
246
  * @param {Error} e error to send
281
  * @param {Error} e error to send
247
  */
282
  */
248
 Statistics.sendGetUserMediaFailed = function (e) {
283
 Statistics.sendGetUserMediaFailed = function (e) {
249
-    CallStats.sendGetUserMediaFailed(e, null);
284
+    CallStats.sendGetUserMediaFailed(
285
+        e instanceof JitsiTrackError
286
+            ? formatJitsiTrackErrorForCallStats(e)
287
+            : e,
288
+        null);
250
 };
289
 };
251
 
290
 
252
 /**
291
 /**

Ładowanie…
Anuluj
Zapisz