Browse Source

Merge pull request #232 from jitsi/implement_muted_ended_track_events

Implement interface for adding handlers to MediaStreamTrack object
dev1
Paweł Domas 9 years ago
parent
commit
ef5a4e7083
6 changed files with 103 additions and 11 deletions
  1. 2
    0
      JitsiTrackError.js
  2. 5
    0
      JitsiTrackErrors.js
  3. 5
    0
      JitsiTrackEvents.js
  4. 64
    3
      modules/RTC/JitsiLocalTrack.js
  5. 21
    7
      modules/RTC/JitsiTrack.js
  6. 6
    1
      modules/RTC/RTC.js

+ 2
- 0
JitsiTrackError.js View File

26
     = "Track does not have an associated Media Stream";
26
     = "Track does not have an associated Media Stream";
27
 TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_MUTE_UNMUTE_IN_PROGRESS]
27
 TRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_MUTE_UNMUTE_IN_PROGRESS]
28
     = "Track mute/unmute process is currently in progress";
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
  * Represents an error that occurred to a JitsiTrack. Can represent various
33
  * Represents an error that occurred to a JitsiTrack. Can represent various

+ 5
- 0
JitsiTrackErrors.js View File

61
  * by a webcam.
61
  * by a webcam.
62
  */
62
  */
63
 export const UNSUPPORTED_RESOLUTION = "gum.unsupported_resolution";
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
  * The video type("camera" or "desktop") of the track was changed.
22
  * The video type("camera" or "desktop") of the track was changed.
23
  */
23
  */
24
 export const TRACK_VIDEOTYPE_CHANGED = "track.videoTypeChanged";
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";

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

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

+ 21
- 7
modules/RTC/JitsiTrack.js View File

7
 var EventEmitter = require("events");
7
 var EventEmitter = require("events");
8
 var MediaType = require("../../service/RTC/MediaType");
8
 var MediaType = require("../../service/RTC/MediaType");
9
 
9
 
10
+/**
11
+ * Maps our handler types to MediaStreamTrack properties.
12
+ */
13
+var trackHandler2Prop = {
14
+    "track_mute": "onmute",//Not supported on FF
15
+    "track_unmute": "onunmute",
16
+    "track_ended": "onended"
17
+};
18
+
10
 /**
19
 /**
11
  * This implements 'onended' callback normally fired by WebRTC after the stream
20
  * This implements 'onended' callback normally fired by WebRTC after the stream
12
  * is stopped. There is no such behaviour yet in FF, so we have to add it.
21
  * is stopped. There is no such behaviour yet in FF, so we have to add it.
90
  * @param {Function} handler the handler.
99
  * @param {Function} handler the handler.
91
  */
100
  */
92
 JitsiTrack.prototype._setHandler = function (type, handler) {
101
 JitsiTrack.prototype._setHandler = function (type, handler) {
93
-    if(this.stream) {
94
-        if(type === "inactive") {
95
-            if (RTCBrowserType.isFirefox()) {
96
-                implementOnEndedHandling(this);
97
-            }
98
-            addMediaStreamInactiveHandler(this.stream, handler);
102
+    this.handlers[type] = handler;
103
+    if(!this.stream)
104
+        return;
105
+
106
+    if(type === "inactive") {
107
+        if (RTCBrowserType.isFirefox()) {
108
+            implementOnEndedHandling(this);
99
         }
109
         }
110
+        addMediaStreamInactiveHandler(this.stream, handler);
111
+    } else if(trackHandler2Prop.hasOwnProperty(type)) {
112
+        this.stream.getVideoTracks().forEach(function (track) {
113
+            track[trackHandler2Prop[type]] = handler;
114
+        }, this);
100
     }
115
     }
101
-    this.handlers[type] = handler;
102
 }
116
 }
103
 
117
 
104
 /**
118
 /**

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

6
 var RTCUtils = require("./RTCUtils.js");
6
 var RTCUtils = require("./RTCUtils.js");
7
 var JitsiTrack = require("./JitsiTrack");
7
 var JitsiTrack = require("./JitsiTrack");
8
 var JitsiLocalTrack = require("./JitsiLocalTrack.js");
8
 var JitsiLocalTrack = require("./JitsiLocalTrack.js");
9
+var JitsiTrackErrors = require("../../JitsiTrackErrors");
10
+var JitsiTrackError = require("../../JitsiTrackError");
9
 var DataChannels = require("./DataChannels");
11
 var DataChannels = require("./DataChannels");
10
 var JitsiRemoteTrack = require("./JitsiRemoteTrack.js");
12
 var JitsiRemoteTrack = require("./JitsiRemoteTrack.js");
11
 var MediaType = require("../../service/RTC/MediaType");
13
 var MediaType = require("../../service/RTC/MediaType");
79
 RTC.obtainAudioAndVideoPermissions = function (options) {
81
 RTC.obtainAudioAndVideoPermissions = function (options) {
80
     return RTCUtils.obtainAudioAndVideoPermissions(options).then(
82
     return RTCUtils.obtainAudioAndVideoPermissions(options).then(
81
         function (tracksInfo) {
83
         function (tracksInfo) {
82
-            return createLocalTracks(tracksInfo, options);
84
+            var tracks = createLocalTracks(tracksInfo, options);
85
+            return !tracks.some(track => !track._isReceivingData())? tracks :
86
+                Promise.reject(new JitsiTrackError(
87
+                    JitsiTrackErrors.NO_DATA_FROM_SOURCE));
83
     });
88
     });
84
 };
89
 };
85
 
90
 

Loading…
Cancel
Save