Browse Source

feat(JitsiLocalTrack): Implement camera issues detection

tags/v0.0.2
hristoterezov 8 years ago
parent
commit
0d037bad65
5 changed files with 99 additions and 4 deletions
  1. 2
    0
      JitsiTrackError.js
  2. 5
    0
      JitsiTrackErrors.js
  3. 5
    0
      JitsiTrackEvents.js
  4. 74
    3
      modules/RTC/JitsiLocalTrack.js
  5. 13
    1
      modules/RTC/RTC.js

+ 2
- 0
JitsiTrackError.js View File

@@ -26,6 +26,8 @@ TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_NO_STREAM_FOUND]
26 26
     = "Track does not have an associated Media Stream";
27 27
 TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_MUTE_UNMUTE_IN_PROGRESS]
28 28
     = "Track mute/unmute process is currently in progress";
29
+TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.NO_DATA_FROM_SOURCE]
30
+    = "The track has stopped receiving data from it's source";
29 31
 
30 32
 /**
31 33
  * Represents an error that occurred to a JitsiTrack. Can represent various

+ 5
- 0
JitsiTrackErrors.js View File

@@ -61,3 +61,8 @@ export const TRACK_NO_STREAM_FOUND = "track.no_stream_found";
61 61
  * by a webcam.
62 62
  */
63 63
 export const UNSUPPORTED_RESOLUTION = "gum.unsupported_resolution";
64
+/**
65
+ * Indicates that the track is no receiving any data without reason(the
66
+ * stream was stopped, etc)
67
+ */
68
+export const NO_DATA_FROM_SOURCE = "track.no_data_from_source";

+ 5
- 0
JitsiTrackEvents.js View File

@@ -22,3 +22,8 @@ export const TRACK_MUTE_CHANGED = "track.trackMuteChanged";
22 22
  * The video type("camera" or "desktop") of the track was changed.
23 23
  */
24 24
 export const TRACK_VIDEOTYPE_CHANGED = "track.videoTypeChanged";
25
+/**
26
+ * Indicates that the track is no receiving any data without reason(the
27
+ * stream was stopped, etc)
28
+ */
29
+export const NO_DATA_FROM_SOURCE = "track.no_data_from_source";

+ 74
- 3
modules/RTC/JitsiLocalTrack.js View File

@@ -73,6 +73,12 @@ function JitsiLocalTrack(stream, track, mediaType, videoType, resolution,
73 73
     // called.
74 74
     this._realDeviceId = this.deviceId === '' ? undefined : this.deviceId;
75 75
 
76
+    /**
77
+     * Indicates that we have called RTCUtils.stopMediaStream for the
78
+     * MediaStream related to this JitsiTrack object.
79
+     */
80
+    this.stopStreamInProgress = false;
81
+
76 82
     this._onDeviceListChanged = function (devices) {
77 83
         self._setRealDeviceIdFromDeviceList(devices);
78 84
 
@@ -101,6 +107,19 @@ function JitsiLocalTrack(stream, track, mediaType, videoType, resolution,
101 107
 
102 108
     RTCUtils.addListener(RTCEvents.DEVICE_LIST_CHANGED,
103 109
         this._onDeviceListChanged);
110
+
111
+    if(this.isVideoTrack() && this.videoType === VideoType.CAMERA) {
112
+        this._setHandler("track_mute", function () {
113
+            if(!this._checkForCameraIssues())
114
+                return;
115
+            this.eventEmitter.emit(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
116
+        }.bind(this));
117
+        this._setHandler("track_ended", function () {
118
+            if(!this._checkForCameraIssues())
119
+                return;
120
+            this.eventEmitter.emit(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
121
+        }.bind(this));
122
+    }
104 123
 }
105 124
 
106 125
 JitsiLocalTrack.prototype = Object.create(JitsiTrack.prototype);
@@ -215,7 +234,7 @@ JitsiLocalTrack.prototype._setMute = function (mute) {
215 234
                 this._removeStreamFromConferenceAsMute(() => {
216 235
                     //FIXME: Maybe here we should set the SRC for the containers
217 236
                     // to something
218
-                    RTCUtils.stopMediaStream(this.stream);
237
+                    this._stopMediaStream();
219 238
                     this._setStream(null);
220 239
                     resolve();
221 240
                 }, (err) => {
@@ -363,7 +382,7 @@ JitsiLocalTrack.prototype.dispose = function () {
363 382
     }
364 383
 
365 384
     if (this.stream) {
366
-        RTCUtils.stopMediaStream(this.stream);
385
+        this._stopMediaStream();
367 386
         this.detach();
368 387
     }
369 388
 
@@ -468,7 +487,7 @@ JitsiLocalTrack.prototype._setByteSent = function (bytesSent) {
468 487
         setTimeout(function () {
469 488
             if(this._bytesSent <= 0){
470 489
                 //we are not receiving anything from the microphone
471
-                this.eventEmitter.emit(JitsiTrackEvents.TRACK_AUDIO_NOT_WORKING);
490
+                this.eventEmitter.emit(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
472 491
             }
473 492
         }.bind(this), 3000);
474 493
         this._testByteSent = false;
@@ -516,5 +535,57 @@ JitsiLocalTrack.prototype.getCameraFacingMode = function () {
516 535
     return undefined;
517 536
 };
518 537
 
538
+/**
539
+ * Stops the associated MediaStream.
540
+ */
541
+JitsiLocalTrack.prototype._stopMediaStream = function () {
542
+    this.stopStreamInProgress = true;
543
+    RTCUtils.stopMediaStream(this.stream);
544
+    this.stopStreamInProgress = false;
545
+}
546
+
547
+/**
548
+ * Detects camera issues on ended and mute events from MediaStreamTrack.
549
+ * @returns {boolean} true if an issue is detected and false otherwise
550
+ */
551
+JitsiLocalTrack.prototype._checkForCameraIssues = function () {
552
+    if(!this.isVideoTrack() || this.stopStreamInProgress ||
553
+        this.videoType === VideoType.DESKTOP)
554
+        return false;
555
+
556
+    return !this._isReceivingData();
557
+}
558
+
559
+/**
560
+ * Checks whether the attached MediaStream is reveiving data from source or
561
+ * not. If the stream property is null(because of mute or another reason) this
562
+ * method will return false.
563
+ * NOTE: This method doesn't indicate problem with the streams directly.
564
+ * For example in case of video mute the method will return false or if the
565
+ * user has disposed the track.
566
+ * @returns {boolean} true if the stream is receiving data and false otherwise.
567
+ */
568
+JitsiLocalTrack.prototype._isReceivingData = function () {
569
+    if(!this.stream)
570
+        return false;
571
+    var isReceivingData = false;
572
+    var tracks = this.stream.getTracks();
573
+    tracks.some(function (track) {
574
+        // In older version of the spec there is no muted property and
575
+        // readyState can have value muted. In the latest versions
576
+        // readyState can have values "live" and "ended" and there is
577
+        // muted boolean property. If the stream is muted that means that
578
+        // we aren't receiving any data from the source. We want to notify
579
+        // the users for error if the stream is muted or ended on it's
580
+        // creation.
581
+        if((!("readyState" in track) || track.readyState === "live")
582
+            && (!("muted" in track) || track.muted === false)) {
583
+            isReceivingData = true;
584
+            return true;
585
+        }
586
+        return false;
587
+    });
588
+    return isReceivingData;
589
+}
519 590
 
520 591
 module.exports = JitsiLocalTrack;

+ 13
- 1
modules/RTC/RTC.js View File

@@ -6,6 +6,8 @@ var RTCEvents = require("../../service/RTC/RTCEvents.js");
6 6
 var RTCUtils = require("./RTCUtils.js");
7 7
 var JitsiTrack = require("./JitsiTrack");
8 8
 var JitsiLocalTrack = require("./JitsiLocalTrack.js");
9
+var JitsiTrackErrors = require("../../JitsiTrackErrors");
10
+var JitsiTrackError = require("../../JitsiTrackError");
9 11
 var DataChannels = require("./DataChannels");
10 12
 var JitsiRemoteTrack = require("./JitsiRemoteTrack.js");
11 13
 var MediaType = require("../../service/RTC/MediaType");
@@ -79,7 +81,17 @@ function RTC(conference, options) {
79 81
 RTC.obtainAudioAndVideoPermissions = function (options) {
80 82
     return RTCUtils.obtainAudioAndVideoPermissions(options).then(
81 83
         function (tracksInfo) {
82
-            return createLocalTracks(tracksInfo, options);
84
+            var tracks = createLocalTracks(tracksInfo, options);
85
+            var allTracksAreReceivingData = true;
86
+            tracks.some(function (track) {
87
+                if(!track._isReceivingData()) {
88
+                    allTracksAreReceivingData = false;
89
+                    return true;
90
+                }
91
+                return false;
92
+            });
93
+            return allTracksAreReceivingData? tracks : Promise.reject(
94
+                new JitsiTrackError(JitsiTrackErrors.NO_DATA_FROM_SOURCE));
83 95
     });
84 96
 };
85 97
 

Loading…
Cancel
Save