Bladeren bron

Merge pull request #232 from jitsi/implement_muted_ended_track_events

Implement interface for adding handlers to MediaStreamTrack object
dev1
Paweł Domas 9 jaren geleden
bovenliggende
commit
ef5a4e7083
6 gewijzigde bestanden met toevoegingen van 103 en 11 verwijderingen
  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 Bestand weergeven

@@ -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 Bestand weergeven

@@ -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 Bestand weergeven

@@ -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";

+ 64
- 3
modules/RTC/JitsiLocalTrack.js Bestand weergeven

@@ -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,17 @@ 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", () => {
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 123
 JitsiLocalTrack.prototype = Object.create(JitsiTrack.prototype);
@@ -215,7 +232,7 @@ JitsiLocalTrack.prototype._setMute = function (mute) {
215 232
                 this._removeStreamFromConferenceAsMute(() => {
216 233
                     //FIXME: Maybe here we should set the SRC for the containers
217 234
                     // to something
218
-                    RTCUtils.stopMediaStream(this.stream);
235
+                    this._stopMediaStream();
219 236
                     this._setStream(null);
220 237
                     resolve();
221 238
                 }, (err) => {
@@ -363,7 +380,7 @@ JitsiLocalTrack.prototype.dispose = function () {
363 380
     }
364 381
 
365 382
     if (this.stream) {
366
-        RTCUtils.stopMediaStream(this.stream);
383
+        this._stopMediaStream();
367 384
         this.detach();
368 385
     }
369 386
 
@@ -468,7 +485,7 @@ JitsiLocalTrack.prototype._setByteSent = function (bytesSent) {
468 485
         setTimeout(function () {
469 486
             if(this._bytesSent <= 0){
470 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 490
         }.bind(this), 3000);
474 491
         this._testByteSent = false;
@@ -516,5 +533,49 @@ JitsiLocalTrack.prototype.getCameraFacingMode = function () {
516 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 581
 module.exports = JitsiLocalTrack;

+ 21
- 7
modules/RTC/JitsiTrack.js Bestand weergeven

@@ -7,6 +7,15 @@ import * as JitsiTrackEvents from "../../JitsiTrackEvents";
7 7
 var EventEmitter = require("events");
8 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 20
  * This implements 'onended' callback normally fired by WebRTC after the stream
12 21
  * is stopped. There is no such behaviour yet in FF, so we have to add it.
@@ -90,15 +99,20 @@ function JitsiTrack(conference, stream, track, streamInactiveHandler, trackMedia
90 99
  * @param {Function} handler the handler.
91 100
  */
92 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 Bestand weergeven

@@ -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,10 @@ 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
+            return !tracks.some(track => !track._isReceivingData())? tracks :
86
+                Promise.reject(new JitsiTrackError(
87
+                    JitsiTrackErrors.NO_DATA_FROM_SOURCE));
83 88
     });
84 89
 };
85 90
 

Laden…
Annuleren
Opslaan